在不经过ssh的情况下完成服务部署和备份

2026-07-13

前言

这篇笔记的主题是在不经过ssh的前提下,完成服务的部署(主要是简单的jar file和编译后静态页面)以及服务器上的数据备份。实际背景是本人在前段时间开发一个BMS下载服务时,购买了一个物理位置在美国的vps。在刚开始的时候就发现无法裸连ssh上到该服务器,但是随后发现可以通过ssh over proxy的方式连到该服务器,也就没有太在意这个事情。直到前段时间发现这个策略也行不通了,导致日常上去mariadumpscp都很成问题。于是琢磨出一个http的方案绕过去

前置准备

由于不使用ssh/scp,所以第一步是要想到一个办法简单的把文件扔到服务器上。于是我想到了在服务器上开一个简单的,基于http的文件服务器,在探索了一会以后我发现了这个:miniserve。他的特色就是极简,启动在cli里拉起来就可以,同时也有一个网页的前端和简单的auth。

下载比较简单,直接从github releases拿一个binary下来就可以了。启动我是配置了systemd,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[Unit]
Description=miniserve file server
After=network.target

[Service]
Type=simple
User=uploader
Group=uploader
WorkingDirectory=/data/upload
ExecStart=/usr/local/bin/miniserve -i 127.0.0.1 -p 9000 -u --auth "admin:sha256:4d8a914e2cd8401871d17fdbada03c39f8b49afe8d6296d05767f37a845529af" --route-prefix /_internal_upload .
Restart=on-failure

[Install]
WantedBy=multi-user.target

这里面比较有意思的是显然如果你直接放一个明文到他的启动命令里,可能会因为ps暴露,所以你可以给他提供一个sha256的hash串,这样就不会明文暴露了。

注:这里启动命令里我加了一个–route-prefix,这个是因为我的服务器在同一个域名下同时运行了一个托管了/根目录的页面服务和这个文件服务,这个prefix是将miniserve的所有页面路径前加一个层级。因为不加这个层级,在miniserve中访问一个文件会导致拼接出这样一个路径:https://xxx.com/文件名,导致被另外一个服务接到。

这里的前缀在nginx中需要做一个转发:

1
2
3
4
5
6
7
8
9
10
11
location /_internal_upload/ {
proxy_pass http://127.0.0.1:9000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;

client_max_body_size 2G; # remember this limitation!!
proxy_read_timeout 300s;
proxy_connect_timeout 300s;
}

在实际用的时候,一开始我是直接让他把WorkingDirectory写到/root/下面的,但是随后发现没有权限,后面想了一下因为运行的用户不是root,而如果简单的把服务直接扔给root运行又很ugly,于是单独开了一个没有登录权限的用户把他的home目录指向了/data/upload,然后简单赋个权就搞定了 :D

部署服务

在完成了文件服务之后,剩下的就是把文件迁移了,至于文件迁移完成之后的步骤,我的服务器上写了对应的service,简单重启一下就可以了。迁移文件这个事情在linux服务器上比较有优势:可以用ionotify + IN_CLOSE_WRITE来检测一个文件是否是已经写完。不过这个思路我没有实践,我的脚本是使用的go的fsnotify库+检测write事件一段时间后没有再write直接认为已经写完了的,这里的缺陷就是fsnotify这个库为了跨平台支持,只有写文件的事件但是没有写文件结束的事件,需要自己判断

数据同步

具体来说我需要收集我服务器上的mariadb的数据,和部署服务的思路是类似但做法是相反的:首先在服务器上跑一个定时任务,内容是把数据库的数据dump出来,然后把数据推到文件服务里暴露。这个过程做成每天一次,然后我在自己的客户机上下载对应的文件即可。具体的脚本我是用的fish写的,内容比较简单:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#!/usr/bin/fish

##### Configurations

# Where the final result should be
set OUTPUT_DESTINATION /data/upload/backup
# What's the archive file name?
set OUTPUT_FILENAME "ginger_backup_"(date +%Y_%m_%d)".sql.gz"
# How long the data would be perserved
set PRESERVATION_DAYS 30

##### Script Starts Here :)

mkdir -p $OUTPUT_DESTINATION

echo (date)" - Start backuping gingerrush's database, the output will be " $OUTPUT_FILENAME
mariadb-dump --single-transaction --routines --triggers ginger_rush | gzip >$OUTPUT_DESTINATION/$OUTPUT_FILENAME

if test $status -eq 0
echo (date)" Backup successfully"

chown uploader:uploader $OUTPUT_DESTINATION/$OUTPUT_FILENAME
chmod 644 $OUTPUT_DESTINATION/$OUTPUT_FILENAME
find $OUTPUT_DESTINATION -name "*.sql.gz" -mtime +$PRESERVATION_DAYS -delete
echo (date)" Cleaned old backups"
else
echo (date)" Backup failed" >&2
exit 1
end

当然我这里比较图省事直接把数据写到暴露的位置了。可能还是写好了再move过去比较好。

然后就可以通过http下载备份了,在用的机器上写个脚本自动获取就结束了:D

评论
分享