
最近在折腾家里的服务器,Docker 容器跑了几十个在固态盘上。数据通过文件拷贝备份还算简单,但最让我头大的是容器的配置——每个容器的端口映射、环境变量、挂载路径,一旦机器挂了,想凭记忆重新 docker run 或者是手写 docker-compose 恢复,简直是灾难。
我的需求很简单:
首先找到了 ghcr.io/red5d/docker-autocompose 这个神器,它可以读取当前运行的容器,直接吐出标准的 docker-compose.yml 文件。
需要备份的时候手动跑命令,确实好用:
bashdocker run --rm -v /var/run/docker.sock:/var/run/docker.sock ghcr.io/red5d/docker-autocompose $(docker ps -aq) > backup.yml
但手动跑显然不是长久之计。
为了自动化,我最开始的思路比较粗暴:搞个 Python 容器,在里面安装 pip,再装 autocompose,然后写个循环脚本。
结果踩雷了: 每次容器启动都要重新联网下载安装包,国内网络环境一抖动,备份就挂。而且为了运行一个小脚本,弄个几百兆的 Python 环境常驻后台,太浪费资源。
最终方案:
后来一想,宿主机本身就有 Docker 环境,我干嘛要在容器里再装一遍?
我只需要一个只有几 MB 大小的 docker:cli 容器,挂载 docker.sock。它不干苦力,只负责“发号施令”,指挥宿主机去运行备份工具。
这样做的好处是:除了第一次下载镜像之后无需下载、秒启动、断网也能跑。
这是我现在正在用的版本,直接部署:
注意看代码里的中文注释,都是踩过的坑:
yamlversion: '3.8'
services:
backup-commander:
# 直接用官方极简的 docker 命令行镜像,只有几 MB
image: docker:cli
container_name: auto-docker-backup
restart: unless-stopped
# 必须开启特权模式,否则可能没权限操作宿主机的 sock
privileged: true
environment:
- TZ=Asia/Shanghai
- KEEP_DAYS=30 # 保留最近30天的备份
volumes:
# 核心:把宿主机的 Docker 通讯接口挂进去,让容器能“控制”宿主机
- /var/run/docker.sock:/var/run/docker.sock
# 这里的路径改成你自己的挂载盘路径
- /tmp/zfsv3/nvme12/17733717568/data/docker-backup:/backup
# 具体的备份脚本逻辑
entrypoint:
- /bin/sh
- -c
- |
echo "✅ 备份守护进程已启动..."
while true; do
# 【天坑预警】在 Compose 里写 Shell 变量必须用两个 $ 转义
# 如果写 $DATE,会被识别成宿主机的环境变量,导致变成空值
DATE=$$(date +%Y%m%d_%H%M)
FILE="/backup/docker-backup-$$DATE.yml"
echo "🚀 [$$DATE] 正在执行备份..."
# 这里并不是在容器内安装工具,而是指挥宿主机临时起一个 autocompose 容器
# 用完即焚 (--rm),直接输出结果到我们的挂载目录
docker run --rm \
-v /var/run/docker.sock:/var/run/docker.sock \
ghcr.io/red5d/docker-autocompose $$(docker ps -aq) > "$$FILE"
# 检查一下文件有没有生成,顺便清理旧版本备份
if [ -s "$$FILE" ]; then
echo "🎉 备份成功!文件已生成: $$FILE"
# 删掉超过 KEEP_DAYS 天的旧文件,防止把硬盘塞满
find /backup -name "docker-backup-*.yml" -mtime +$${KEEP_DAYS} -delete
else
echo "❌ 备份失败(文件为空),建议检查日志"
fi
echo "💤 任务完成,休眠 24 小时..."
sleep 86400
done
在部署过程中遇到过两个报错,甚至让我一度怀疑人生:
报错 invalid volume path:
如果你用 Portainer 或者 CasaOS 这类面板部署,它可能会拦截 /tmp 开头的挂载路径。
docker compose up -d 启动,一步到位。变量也就是那个 $$ 的问题:
脚本里明明写了 DATE=$(date...),但跑起来文件名总是叫 docker-backup-.yml,中间没有日期。
$DATE 当作宿主机变量去解析。$$DATE,告诉 Docker 这是一个脚本内部变量,别给我替换了。总结:
如无必要,勿增实体。这个方案利用了 Docker 自身的缓存和运行能力,既不需要在宿主机装杂七杂八的脚本,也不用维护臃肿的备份容器。现在的状态是:平时几乎不占资源,一旦要恢复,把生成的 YAML 扔回去 up 一下就全回来了。
本文作者:小转圈
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!