4.5 备份与恢复策略


4.5 备份与恢复策略

本节摘要:备份与恢复策略回答"数据丢了怎么捞回来"。核心思路是备份对象分层:数据卷、数据库逻辑备份、配置文件、镜像四类对象分开处理,各用各的工具。数据库用 pg_dump、mysqldump 在容器内执行逻辑备份;数据卷用临时容器打包 tar 或直接复制目录两种方式;恢复流程必须定期演练,自动化交给 cron 加脚本。本节给出数据库备份命令、卷备份两种方式的对比、备份 sidecar 容器完整示例和一张备份对象分层图。

阅读收获

  • 能说出备份对象的四个层级及各自的工具
  • 能在容器内执行 pg_dump 与 mysqldump 逻辑备份,说清为什么选逻辑备份
  • 能用临时容器打包和直接复制目录两种方式备份数据卷,并比较优劣
  • 能设计一次恢复演练并验证数据完整性
  • 能用 cron 加脚本把备份流程自动化

一、先分层:备份对象不是一锅烩

备份最常见的错误是"备份了个寂寞":只备份了数据库文件,配置文件丢了照样起不来;或者只备份了数据卷,镜像仓库挂了却没法重建。正确的做法是把备份对象分成四层,各自用对应的工具和频率。

第一层是数据卷,存应用的关键数据(数据库文件、用户上传),是备份的重中之重。第二层是数据库逻辑备份,用数据库自带的导出工具生成可移植的备份文件——它与卷备份互为补充:卷备份是"整块硬盘的镜像",逻辑备份是"能导入任何实例的搬家包"。第三层是配置文件:docker-compose.yml 定义了应用整体架构,.env 模板和各类挂载配置定义了运行参数,这些文件进版本控制就是最好的备份。第四层是镜像:自定义镜像虽然能重新构建,但仓库不可用或网络不通时,本地的 docker save 产物就是救命稻草。

备份对象 工具或方式 频率建议 恢复方式
数据卷 临时容器 tar 打包或直接复制目录 每天 解压回卷目录
数据库 容器内 pg_dump、mysqldump 每天加定期全量 导入备份文件
Compose 文件与配置 Git 版本控制 每次变更 检出历史版本
镜像 docker save 或推私有仓库 每次构建发布 docker load 或仓库拉取
环境变量与密钥 .env 模板进 Git,真实值进 secrets 每次变更 按模板重建

二、数据库逻辑备份:在容器内执行

容器化数据库的备份必须用数据库自己的工具,直接在容器内执行。以 MySQL 为例:

docker exec -it <mysql-container-id> mysqldump -u root -p<password> <database-name> > /backup/db.sql

这条命令把 mysqldump 的输出重定向到宿主机 /backup/db.sql。两个细节要注意:-p 和密码之间不要加空格,否则 mysqldump 会交互式询问密码,脚本就卡死了;输出重定向写在 docker exec 外面,让备份文件直接落在宿主机,而不是容器内——容器随时可能被重建,文件放容器里等于没备份。

PostgreSQL 对应的是 pg_dump。建议加 -F c 用自定义压缩格式,比纯 SQL 文本小得多,还支持选择性恢复:

docker exec <pg-container-id> pg_dump -U myuser -d mydb -F c -f /backup/mydb.dump

MongoDB 则是 mongodump,行为类似。为什么数据库要单独做逻辑备份,而不是只备份数据卷?因为卷里的数据文件依赖特定版本:恢复时镜像版本不一致,数据文件可能直接打不开;而逻辑备份是纯数据导出,导入到任何兼容版本的实例都能用。另一个理由是粒度:逻辑备份能只导出一个库、一张表,卷备份只能整块来。逻辑备份的代价是恢复速度慢、大库耗时,所以生产环境通常是"卷备份保底,逻辑备份保搬家"。

三、数据卷备份:两种方式,各有用处

数据卷备份有两条路线,我们实际都在用,按场景选。

方式一:临时容器打包。 启动一个一次性容器,把要备份的卷和备份目录都挂进去,用 tar 打包:

docker run --rm -v my-app_data:/data -v /backup:/backup alpine:latest tar czvf /backup/data.tar.gz -C /data .

拆开看:--rm 让容器用完即删,不留垃圾;-v my-app_data:/data 挂载要备份的卷;-v /backup:/backup 挂载宿主机备份目录;alpine:latest 是打包用的轻量镜像;tar czvf /backup/data.tar.gz -C /data . 在容器里把 /data 的内容压缩成 gz 包。-C /data . 的组合保证解压时不会多套一层目录。

方式二:直接复制目录。 named volume 在宿主机上有真实路径,位于 /var/lib/docker/volumes 下,直接复制目录即可:

docker run --rm -v my-app_data:/data alpine:latest tar czf - -C /data . > my-app_data.tar.gz

或者干脆用 docker cp 从容器里拷出挂载点。两种方式对比如下:

对比项 临时容器 tar 打包 直接复制目录
依赖工具 只需要 Docker 需要宿主机目录访问权限
数据一致性 无特殊保证,建议停写时备份 无特殊保证,建议停写时备份
文件归属 打包时保留权限属性 复制可能丢属主信息
适合场景 标准做法,脚本化友好 快速人工捞数据

两条路线有一个共同的硬约束:备份期间数据要处于一致状态。数据库类服务最好先停写或用事务快照,否则备份文件里可能是写到一半的脏数据。这也是为什么数据库更推荐逻辑备份——pg_dump、mysqldump 自己处理一致性,不会备份出半截事务。

四、恢复演练:备份的及格线

恢复比备份重要,但只有演练过才能确认恢复真的可行。我们的经验是:没演练过的备份方案,在事故当天大概率会翻车——要么备份文件损坏,要么恢复命令记错,要么权限对不上。

演练流程分三步。第一步模拟数据丢失:真的删掉一个数据卷或把数据库 drop 掉,别用假数据糊弄,演练的意义就在于体会"真丢了"的紧张感。第二步执行恢复:卷备份用 tar 解压回卷目录:

docker run --rm -v my-app_data:/data -v /backup:/backup alpine:latest tar xzvf /backup/data.tar.gz -C /data

数据库用导入命令把备份文件灌回去。第三步验证数据完整性:查关键表行数、对比业务日期的数据、跑一遍应用的冒烟用例,确认恢复后的数据完整一致。演练要记入文档,恢复步骤截图存档,事故发生时照着做就行。

恢复手册要写清五件事:备份文件的存放位置与命名规则、恢复命令原文、验证步骤、预计恢复时长、需要联系的负责人。手册跟着演练更新,每次演练后把踩过的坑补进去。演练环境用测试实例,但命令和备份文件必须与生产完全一致——在测试环境验证过的命令,事故当天照抄就能用。

五、备份自动化:cron 加脚本

手工备份坚持不了几周,自动化是唯一出路。最朴素也最可靠的方案是 cron 加脚本。cron 行直接调度 docker 命令也能跑,比如每天零点备份卷:

0 0 * * * docker run --rm -v my-app_data:/data -v /backup:/backup alpine:latest tar czvf /backup/data.tar.gz -C /data .

但更稳妥的是把备份逻辑写进脚本再让 cron 调:脚本里做文件名带日期、只保留最近 N 份、备份完发结果通知这三件事。文件名带日期(data-2024-05-01.tar.gz)是为了保留多版本,只保留最近 N 份是为了防止备份文件把磁盘反向吃满——备份系统自己磁盘满了,比业务故障还尴尬。增量备份的思路是:全量每月一次、增量每天一次,大幅减少备份时间和存储空间;异地备份则是把备份文件再同步到不同地理位置的存储,防机房级灾难。

数据库备份同样可以自动化,我们更推荐把它做成 compose 里的一个 sidecar 服务,随应用一起起停:

version: "3.9" services: db: image: postgres:14 environment: POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} POSTGRES_USER: myuser POSTGRES_DB: mydb volumes: - db_data:/var/lib/postgresql/data restart: unless-stopped backup: image: postgres:14 depends_on: - db environment: PGPASSWORD: ${POSTGRES_PASSWORD} volumes: - ./backup:/backup entrypoint: - /bin/sh - -c - | while true; do pg_dump -h db -U myuser -d mydb -F c -f /backup/mydb_$(date +%F).dump sleep 86400 done volumes: db_data:

解读这个配方:backup 服务复用 postgres:14 镜像,自带 pg_dump 工具,不用额外安装;-h db 通过 compose 网络直接连数据库容器;PGPASSWORD 从环境变量注入密码;$(date +%F) 让备份文件名带日期,每天一份;sleep 86400 循环一天一次,等效于容器内的 cron。备份产物落在宿主机 ./backup 目录,再配合宿主机 crontab 做异地同步。这个方案的好处是备份逻辑跟应用一起进版本控制,新机器拉起 compose 就自带备份,不会漏配。

备份的整体思路用一张流程图收尾:

六、备份验证与常见坑

自动化跑起来之后,最容易被忽略的是"备份本身有没有生效"。我们的做法是给备份加三重验证。第一重,备份日志:每次备份脚本写一行结果日志,记录成功与否、文件大小、耗时,每天检查有没有失败记录。第二重,文件抽查:每周手动看一眼备份目录,文件存在、大小合理、日期正确,别等恢复时才发现备份文件是 0 字节。第三重,恢复演练:也就是第四节讲的真删真恢复,至少每月一次,按演练结果更新恢复手册。

还有几个常见坑值得单列。备份和业务同盘:备份目录和数据卷在同一块磁盘,磁盘故障一起报销,备份等于白做。备份脚本没设时区:cron 默认按服务器时区执行,跨时区团队对不上备份时间点,脚本里显式带上时区逻辑。mysqldump 大库不加控制:大库全量导出期间写入频繁,备份数据可能不一致,考虑在低峰期执行或配合主从库做备份。只留一份备份:昨天的备份覆盖今天的,等数据损坏到恢复时才发现上一份也是坏的——保留最近 N 份,至少覆盖"上一份是坏的"这种场景。

增量与异地的组合是最后的强化:全量每周一次、增量每天一次,备份窗口和存储成本都降下来;异地同步把备份文件推到另一个物理位置,防机房级故障。到这一步,备份体系才算闭环——能备份、能验证、能恢复、能扛灾。

⚠️ 备份文件不要和业务数据放在同一块磁盘上。磁盘故障时两者一起消失,备份就白做了。至少做到备份目录独立挂载,最好异地同步。

💡 恢复演练要真删真恢复。在测试环境模拟一次完整事故:删卷、恢复、验证、写手册。演练中踩过的每个坑,都是事故当天替你挡下的子弹。

一节小结

  • 备份对象分四层:数据卷、数据库、配置文件、镜像,各有各的工具与频率。
  • 数据库逻辑备份在容器内执行:mysqldump、pg_dump 加 -F c,输出重定向到宿主机。
  • 卷备份两种方式:临时容器 tar 打包是标准做法,直接复制目录适合快速捞数据。
  • 备份期间数据要一致:数据库靠逻辑备份工具保证,卷备份建议停写时做。
  • 恢复演练是及格线:模拟丢失、执行恢复、验证完整性三步缺一不可。
  • 自动化靠 cron 加脚本:文件名带日期、只留最近 N 份、备份完成要通知。
  • sidecar 备份容器:复用数据库镜像自带工具,随 compose 一起版本控制。
  • 备份文件独立于业务磁盘:同盘备份等于没有备份。

下一节,把前面所有手工步骤交给流水线——CI/CD 流程集成。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U