自己跑 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 脚本就不想看了,但这段逻辑其实挺直白。

它无非就是:

  1. 如果没装 git-lfs,先装
  2. 第一次运行时,克隆备份仓库并初始化 LFS
  3. 遍历 Docker 卷,把每个卷单独打成 tar.gz
  4. 顺手把 /opt/1panel/apps 也打进去
  5. 清理 7 天前的本地备份目录
  6. 把压缩包复制到同步目录,提交并推到 GitHub

这里我比较喜欢的一点是,链路没有额外引入很重的依赖。Shell、Git、Git LFS、Docker 卷路径,这些东西本来就在机器上或者很容易装。后面哪一步坏了,也比较容易定位。

真正需要注意的反而不是“脚本长不长”,而是两个细节:

  • git commit 在没有新文件变更时会直接失败
  • docker volume ls -q 会把所有卷都扫进去,所以你最好知道自己这台机器上到底有哪些卷

如果你后面要继续打磨,可以先从这两个点入手。

1Panel 这边别塞长脚本,计划任务只负责调用

我不太建议把一大段备份脚本直接贴进 1Panel 计划任务里。短期看似乎省事,长期维护会很烦,尤其是你以后要改仓库地址、保留天数、用户信息的时候。

更省心的方式是:

  1. 先在命令行里把 docker-sync 手工跑通
  2. 再进 1Panel 创建计划任务
  3. 任务类型选 Shell 脚本
  4. 每天跑一次,比如凌晨 03:00
  5. 脚本内容只写:
docker-sync

这样后面如果某天备份失败,你只需要 SSH 上去手工跑一次 docker-sync,马上就能看日志,不用先回面板里翻长脚本。

真要恢复的时候,流程其实比备份更重要

很多备份方案写到最后,只停在“文件已经存起来了”。但实际出事时,你最关心的是下一步怎么恢复,而不是备份当初多优雅。

如果服务器挂了,恢复大致就是这条线:

  1. 在新服务器克隆备份仓库
  2. 找到你要的那个时间点压缩包
  3. 解压对应的 vol_xxx.tar.gz
  4. 用 docker volume create 重新创建目标卷
  5. 把数据拷回这个卷的 Mountpoint
  6. 重新创建容器并挂载这个卷

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 服务器真正拥有一套稳定可用的自动备份”。