小张在公司负责运维三台跑着微服务的服务器,某天早上登录发现一台 ref="/tag/2019/" style="color:#EB6E00;font-weight:bold;">Docker 主机磁盘异常,几个关键业务容器直接起不来。他手忙脚乱翻日志,才发现 PostgreSQL 容器挂掉后,宿主机上连个像样的 SQL 备份文件都没留——原来之前只做了镜像打包,没碰过容器里真正跑的数据。
备份不是存镜像,是存“活数据”
很多人误以为 docker commit 打个新镜像就等于备份了,其实那只是把容器当时的文件系统快照保存下来,而运行中的数据库、Redis 缓存、上传的用户文件这些动态数据根本没包含进去。真正的备份对象,是容器挂载的 /var/lib/postgresql/data、/data/redis 或你自定义的 -v /home/app/uploads:/app/uploads 这类卷(volume)里的内容。
用 volume 实现可落地的备份流程
假设你用的是命名卷:
docker volume create pg-data启动时挂载:
docker run -d --name pg-prod -v pg-data:/var/lib/postgresql/data -e POSTGRES_PASSWORD=123456 -p 5432:5432 postgres:15备份操作就很简单:
docker run --rm -v pg-data:/volume -v $(pwd):/backup alpine tar czf /backup/pg-data-$(date +%Y%m%d).tar.gz -C /volume .这行命令起一个临时 Alpine 容器,把 pg-data 卷挂进来,再把整个卷内容打包到当前目录。恢复时反过来:
docker run --rm -v pg-data:/volume -v $(pwd):/backup alpine tar xzf /backup/pg-data-20241015.tar.gz -C /volume别忘定时任务和远程落盘
光手动备份不靠谱。在宿主机加个 crontab:
0 2 * * * /usr/bin/docker run --rm -v pg-data:/volume -v /backup:/backup alpine tar czf /backup/pg-data-$(/bin/date +\%Y\%m\%d).tar.gz -C /volume .再配合 rsync 或 rclone 把 /backup 目录同步到 NAS 或对象存储,比如腾讯云 COS:
rclone copy /backup cos-backup:container-backup --include "pg-data-*.tar.gz"更省心的方案:用 Duplicacy 或 Restic
如果团队开始管理十几二十个容器,建议直接上专业工具。比如 Restic 支持加密、增量、快照回溯,对 volume 备份很友好:
restic -r s3:http://cos.ap-beijing.myqcloud.com/mybucket/backups init
restic -r s3:http://cos.ap-beijing.myqcloud.com/mybucket/backups --verbose backup /var/lib/docker/volumes/pg-data/_data恢复某个旧快照也只需一条命令:
restic -r s3:http://cos.ap-beijing.myqcloud.com/mybucket/backups restore 6a8b9c0d --target /tmp/pg-restore真实场景提醒几条
• MySQL 容器备份前记得 docker exec mysql-prod mysqladmin flush-logs --flush-privileges,避免 binlog 错位;
• Redis 如果开了 RDB,优先备份 /data/dump.rdb 文件而非整个 volume;
• Nginx 静态站点用 volume 挂载 HTML 目录?备份时注意排除 .git 和临时日志,不然体积翻倍还拖慢速度;
• 测试恢复流程不能只看“命令没报错”,一定要进容器验证 psql -U app -c "SELECT COUNT(*) FROM users;" 能跑通才算数。
备份不是为应付检查,而是哪天半夜告警电话响起时,你能喝口茶,敲完三行命令,看着容器重新亮起绿色状态灯。