编辑
2025-11-29
技术漫谈
00
请注意,本文编写于 31 天前,最后修改于 31 天前,其中某些信息可能已经过时。

目录

实战记录:如何用 Docker 优雅地备份 Docker?(抛弃 Crontab 和 脚本)
1\. 核心工具:docker-autocompose
2\. 踩坑过程:从“臃肿”到“极简”
3\. 最终作业 (docker-compose.yml)
4\. 两个避坑指南

Gemini_Generated_Image_4azzon4azzon4azz.png

实战记录:如何用 Docker 优雅地备份 Docker?(抛弃 Crontab 和 脚本)

最近在折腾家里的服务器,Docker 容器跑了几十个在固态盘上。数据通过文件拷贝备份还算简单,但最让我头大的是容器的配置——每个容器的端口映射、环境变量、挂载路径,一旦机器挂了,想凭记忆重新 docker run 或者是手写 docker-compose 恢复,简直是灾难。

我的需求很简单:

  1. 要把现有的容器“逆向”生成配置:不用手动一个个备份 YAML。
  2. 全自动:每天凌晨自己跑,不用操心。
  3. 干净:不想在宿主机上搞乱七八糟的 Crontab 脚本,既然用了 Docker,就用 Docker 的方式解决问题。

1. 核心工具:docker-autocompose

首先找到了 ghcr.io/red5d/docker-autocompose 这个神器,它可以读取当前运行的容器,直接吐出标准的 docker-compose.yml 文件。

需要备份的时候手动跑命令,确实好用:

bash
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock ghcr.io/red5d/docker-autocompose $(docker ps -aq) > backup.yml

但手动跑显然不是长久之计。

2. 踩坑过程:从“臃肿”到“极简”

为了自动化,我最开始的思路比较粗暴:搞个 Python 容器,在里面安装 pip,再装 autocompose,然后写个循环脚本。

结果踩雷了: 每次容器启动都要重新联网下载安装包,国内网络环境一抖动,备份就挂。而且为了运行一个小脚本,弄个几百兆的 Python 环境常驻后台,太浪费资源。

最终方案: 后来一想,宿主机本身就有 Docker 环境,我干嘛要在容器里再装一遍? 我只需要一个只有几 MB 大小的 docker:cli 容器,挂载 docker.sock。它不干苦力,只负责“发号施令”,指挥宿主机去运行备份工具。

这样做的好处是:除了第一次下载镜像之后无需下载、秒启动、断网也能跑

3. 最终作业 (docker-compose.yml)

这是我现在正在用的版本,直接部署:

注意看代码里的中文注释,都是踩过的坑:

yaml
version: '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

4. 两个避坑指南

在部署过程中遇到过两个报错,甚至让我一度怀疑人生:

  1. 报错 invalid volume path: 如果你用 Portainer 或者 CasaOS 这类面板部署,它可能会拦截 /tmp 开头的挂载路径。

    • 解决:别跟面板较劲,直接 SSH 连上去用 docker compose up -d 启动,一步到位。
  2. 变量也就是那个 $$ 的问题: 脚本里明明写了 DATE=$(date...),但跑起来文件名总是叫 docker-backup-.yml,中间没有日期。

    • 原因:Docker Compose 预处理时,会把 $DATE 当作宿主机变量去解析。
    • 解决:必须写成 $$DATE,告诉 Docker 这是一个脚本内部变量,别给我替换了。

总结: 如无必要,勿增实体。这个方案利用了 Docker 自身的缓存和运行能力,既不需要在宿主机装杂七杂八的脚本,也不用维护臃肿的备份容器。现在的状态是:平时几乎不占资源,一旦要恢复,把生成的 YAML 扔回去 up 一下就全回来了。

本文作者:小转圈

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!