Docker 数据卷自动备份到 GitHub:适合 1Panel 的低成本容灾方案
自己跑 Docker 服务,平时最容易忽略的一件事,就是你以为“容器都在正常运行”,所以备份这件事可以晚点再说。真正出问题时,麻烦往往不是把容器重新拉起来,而是卷里的数据、面板里的配置、还有你自己改过的那些东西,到底还能不能找回来。
我最后定了一套比较克制的方案,不追求很重的备份系统,也不先上对象存储。思路很简单:每天把 Docker 卷和 1Panel 配置打包一份,本地只留最近 7 天,再把压缩包同步到 GitHub 私有仓库。这样做不花哨,但它至少把“最近几天能快速恢复”和“远端还有一份兜底”这两件事先解决了。
如果你现在正好在搜这些关键词,比如 Docker 自动备份、Docker 卷备份到 GitHub、1Panel 备份方案,这篇文章基本就是围绕这几个问题展开的。
我为什么没把它做成一套很重的备份系统
很多人一提备份,第一反应就是对象存储、版本归档、异地多副本、专门备份平台。方向当然没问题,但对个人服务器或者小团队机器来说,真正卡住的往往不是“方案不够先进”,而是根本没有一套能稳定每天跑的东西。
我更在意的是三件事:
- 先自动跑起来,不靠人记着手工执行
- 出问题时最近几天能快速恢复
- 备份文件别只放在当前这台服务器上
所以这次没有把链路做得特别复杂,而是压成了下面这个结构:
Docker 卷 / 1Panel 配置
↓
docker-sync 脚本
↓
本地生成 backup_<timestamp>
↓
删除 7 天前旧备份
↓
复制压缩包到 Git 仓库目录
↓
Git LFS 跟踪 .tar.gz
↓
推送到 GitHub 私有仓库
它不适合特别大的备份量,也不是严格意义上的企业级容灾。但如果你现在是用 1Panel 跑几个站、几个数据库、几个服务,这种“7 天本地 + GitHub 私有仓库同步”的组合其实挺实用。
先把 GitHub 信任打通,不然后面都白搭
这件事我建议先做,而且一定要先手工验证一次。原因很简单,后面无论你用 Shell、1Panel 计划任务还是别的自动化方式,最终都得落到“这台服务器能不能把东西推到 GitHub”。
在服务器上先执行:
ssh-keygen -t ed25519 -C "[email protected]"
cat ~/.ssh/id_ed25519.pub
ssh -T [email protected]
第一条是生成密钥,第二条把公钥拷出来放进 GitHub 的 SSH keys,第三条是第一次手工验证连接。
这一步别偷懒。很多自动任务一开始就失败,不是脚本有问题,而是 SSH 压根没通。你把这件事提前确认掉,后面排查会省很多时间。
顺手说一下,备份仓库最好单独建,不要和业务代码混在一起。我这里用的是这种形式:
[email protected]:kuangyongjie/my-docker-backup.git
仓库最好设成 Private,这个没什么好犹豫的。
我把整个流程压成了一个 docker-sync 命令
如果备份逻辑只是临时放在某个脚本目录里,过两个月你大概率就忘了它到底在哪、该怎么跑、改了什么。我的习惯是,能长期维护的东西,入口最好固定。
所以我最后直接把它封装成了一个系统命令:
/usr/local/bin/docker-sync
这样无论是我自己手工执行,还是后面让 1Panel 调度,调用方式都一样。下面这段脚本就是核心:
cat << 'EOF' > /usr/local/bin/docker-sync
#!/bin/bash
GITHUB_REPO="[email protected]:kuangyongjie/my-docker-backup.git"
GIT_USER_NAME="kuangyongjie"
GIT_USER_EMAIL="[email protected]"
LOCAL_SYNC_PATH="/root/github_sync_space"
if ! command -v git-lfs &> /dev/null; then
apt-get update && apt-get install -y git git-lfs && git lfs install
fi
if [ ! -d "$LOCAL_SYNC_PATH/.git" ]; then
echo "--- 第一次运行:初始化同步目录 ---"
rm -rf "$LOCAL_SYNC_PATH"
GIT_SSH_COMMAND="ssh -o StrictHostKeyChecking=no" git clone "$GITHUB_REPO" "$LOCAL_SYNC_PATH"
cd "$LOCAL_SYNC_PATH"
git lfs track "*.tar.gz" && git add .gitattributes
git config user.name "$GIT_USER_NAME" && git config user.email "$GIT_USER_EMAIL"
git commit -m "Init LFS"
else
cd "$LOCAL_SYNC_PATH" && git pull origin main --rebase
fi
TAG=$(date +%Y%m%d_%H%M%S)
DIR="/root/docker_backups/backup_$TAG"
mkdir -p "$DIR"
echo "--- 正在抓取本地数据 ---"
for vol in $(docker volume ls -q); do
tar -zcf "$DIR/vol_$vol.tar.gz" -C $(docker volume inspect $vol --format '{{.Mountpoint}}') .
done
tar -zcf "$DIR/1panel_apps_config.tar.gz" /opt/1panel/apps
echo "--- 正在清理本地 7 天前的旧备份 ---"
find /root/docker_backups/ -maxdepth 1 -name "backup_*" -type d -mtime +7 -exec rm -rf {} +
echo "--- 正在同步到 GitHub ---"
cp "$DIR"/*.tar.gz "$LOCAL_SYNC_PATH/"
cd "$LOCAL_SYNC_PATH"
git add .
git commit -m "Auto Backup $TAG"
git push origin main
echo "--- 完美!所有任务已完成 ---"
EOF
chmod +x /usr/local/bin/docker-sync
这段脚本其实没那么复杂,拆开看就六件事
很多人一看到长一点的 Shell 脚本就不想看了,但这段逻辑其实挺直白。
它无非就是:
- 如果没装
git-lfs,先装 - 第一次运行时,克隆备份仓库并初始化 LFS
- 遍历 Docker 卷,把每个卷单独打成
tar.gz - 顺手把
/opt/1panel/apps也打进去 - 清理 7 天前的本地备份目录
- 把压缩包复制到同步目录,提交并推到 GitHub
这里我比较喜欢的一点是,链路没有额外引入很重的依赖。Shell、Git、Git LFS、Docker 卷路径,这些东西本来就在机器上或者很容易装。后面哪一步坏了,也比较容易定位。
真正需要注意的反而不是“脚本长不长”,而是两个细节:
git commit在没有新文件变更时会直接失败docker volume ls -q会把所有卷都扫进去,所以你最好知道自己这台机器上到底有哪些卷
如果你后面要继续打磨,可以先从这两个点入手。
1Panel 这边别塞长脚本,计划任务只负责调用
我不太建议把一大段备份脚本直接贴进 1Panel 计划任务里。短期看似乎省事,长期维护会很烦,尤其是你以后要改仓库地址、保留天数、用户信息的时候。
更省心的方式是:
- 先在命令行里把
docker-sync手工跑通 - 再进 1Panel 创建计划任务
- 任务类型选 Shell 脚本
- 每天跑一次,比如凌晨
03:00 - 脚本内容只写:
docker-sync
这样后面如果某天备份失败,你只需要 SSH 上去手工跑一次 docker-sync,马上就能看日志,不用先回面板里翻长脚本。
真要恢复的时候,流程其实比备份更重要
很多备份方案写到最后,只停在“文件已经存起来了”。但实际出事时,你最关心的是下一步怎么恢复,而不是备份当初多优雅。
如果服务器挂了,恢复大致就是这条线:
- 在新服务器克隆备份仓库
- 找到你要的那个时间点压缩包
- 解压对应的
vol_xxx.tar.gz - 用
docker volume create重新创建目标卷 - 把数据拷回这个卷的
Mountpoint - 重新创建容器并挂载这个卷
1Panel 相关配置也是类似思路,先把配置目录拿回来,再重新挂到新的运行环境里。
这里真正麻烦的不是“会不会解压”,而是你平时有没有把卷、服务、用途对应清楚。否则到了恢复现场,文件在,但你不知道哪个卷对应哪个服务,时间照样会被拖很久。
我自己会常看的几个命令
平时维护时,我比较常用的其实就这几个:
| 需求 | 命令 |
|---|---|
| 手动立即备份 | docker-sync |
| 看本地备份占了多少空间 | du -sh /root/docker_backups |
| 看本地备份目录 | ls -lh /root/docker_backups |
| 看最近同步记录 | cd /root/github_sync_space && git log -n 5 |
| 调整本地保留天数 | nano /usr/local/bin/docker-sync |
如果你只是想把 7 天改成 14 天,或者改成 3 天,直接改 find ... -mtime +7 那一段就行,不需要重构整个脚本。
这套方案最容易翻车的地方
先说结论,通常不是压缩命令本身有多难,而是下面这些地方最容易出问题:
- 服务器到 GitHub 的 SSH 信任没配完整
git-lfs没装或者没初始化- 本地同步目录第一次初始化时权限不对
- 计划任务能跑,但仓库里其实没有新文件,所以
git commit失败
还有一个现实问题也得提前接受:GitHub 私有仓库可以拿来做这种 Docker 备份同步,但它更适合配置、小型数据库卷、常见应用数据这类体量还算可控的场景。真要是备份越来越大,后面迟早还是要往对象存储或者专门的备份介质迁。
所以这套方案的定位我觉得要看准一点,它不是“最终版容灾体系”,而是“先让你的 1Panel / Docker 服务器真正拥有一套稳定可用的自动备份”。