遇到这类问题时,我通常不会先去翻 docker-compose.yml 里写了哪些 volumes,而是先看容器实际挂载。原因很简单,compose 文件可能被改动过,或者卷被其他容器复用,只看配置容易漏掉真实状态。
先确认卷的类型和挂载来源
在动手备份之前,先弄清当前服务用到的数据卷类型。运行 docker volume ls 可以列出所有命名卷,而在 docker-compose.yml 中通过 volumes 段声明的卷名属于命名卷,它们由 Docker 统一管理,实际数据存放在宿主机的 /var/lib/docker/volumes/ 目录下。另一方面,如果 volumes 段里写的是宿主机绝对路径或相对路径,则属于绑定挂载,数据直接位于宿主机对应目录。使用 docker inspect 查看容器的 Mounts 字段也能确认来源。判断清楚后,你就能决定备份的目标是直接打包宿主机路径,还是借助辅助容器来打包卷内容。
这一步的价值在于,不同挂载类型的备份方式完全不同。命名卷可以借助辅助容器打包,绑定挂载直接操作宿主机目录就行。如果一开始判断错了,后面所有命令都可能落在错误的目标上。确认时可以用 docker inspect CONTAINER --format '{{json .Mounts}}' 来查看挂载点、源路径和类型,比肉眼读 compose 文件更准确。
备份命名卷时用辅助容器打包
备份命名卷时,最稳妥的方法是启动一个临时的辅助容器,把需要备份的卷挂载进去,再通过 tar 打包。例如,先进入 compose 文件所在目录,确认服务用的是卷名 data,然后执行 docker run --rm --volumes-from -v $(pwd):/backup alpine tar czf /backup/volume-backup.tar /data,其中 /data 是容器内挂载点。这样即使原容器已经停止也能备份。需要注意的是,如果服务本身可以停机,建议先 docker compose stop 停掉相关服务,避免备份过程中有进程持续写入,导致备份文件不完整。备份结束后把 tar 包放到独立磁盘或对象存储。
实际运行这条命令前,需要把 --volumes-from 后面补上具体的容器名,比如 --volumes-from web_app_1。如果你不想依赖容器名,也可以直接 docker run --rm -v data:/data -v $(pwd):/backup alpine tar czf /backup/volume-backup.tar /data,这样只挂载目标卷,不关心容器状态。两种方式都能工作,关键是确保卷名对应正确。
如果是绑定挂载,就不需要辅助容器了。在宿主机上直接切换到挂载目录的父路径,然后 tar czf backup.tar -C 目标目录 . 即可。但要注意,绑定挂载往往包含配置文件或日志文件,打包前先想清楚哪些目录需要,哪些可以排除。
数据库类容器需要单独处理一致性
对于数据库容器,尤其是 MySQL、PostgreSQL 这类有事务状态的服务,直接打包数据卷文件虽然可行,但可能得到崩溃恢复状态,而不是一致快照。正确做法是先停掉容器,再备份,或者使用数据库自带的导出工具(如 mysqldump、pg_dump)生成逻辑备份,然后再备份对应数据文件。如果服务不能停机,需要依赖数据库的在线备份能力,比如 MySQL 的 XtraBackup 或 PostgreSQL 的 pg_basebackup,这些工具能够保证备份期间的数据一致性。为了简化流程,可以编写一个脚本,先暂停服务,执行备份,再自动启动服务。
这里说的“先停掉容器”指的是 docker compose stop,不是暂停。如果用了 docker compose pause,进程仍然在内存里,磁盘上的数据可能还没完全落盘。对于不能停机的场景,mysqldump 这类逻辑备份往往是更安全的选择,但恢复时要注意版本兼容。
备份之后必须验证可恢复
备份文件生成后,我会先做一次列表检查,用 docker run --rm -v $(pwd):/backup alpine tar tzf /backup/volume-backup.tar 看看卷内的关键目录是否存在。进一步的做法是恢复到临时卷:先 docker volume create temp_vol,再解压到该卷,然后启动一个临时容器挂载 temp_vol 验证数据可读。验证通过后再删除临时卷。
这个恢复演练应该纳入常规流程,而不是等到故障发生时才第一次尝试。备份文件如果从来没有被恢复过,就不能认为是可靠的备份。恢复时还要注意 tar 包内的路径前缀,如果备份时用的是 /data,解压时也要对应挂载到 /data,否则文件会落在预期之外的位置。
备份存放和恢复时的风险边界
备份文件不要和源数据放在同一块磁盘上。如果整台宿主机磁盘损坏,备份和源数据会一起消失。日常维护时,我会把 tar 包复制到另一块数据盘,或者同步到远程存储。可靠性要求高的场景,可以保留最近三份备份,并定期做一次恢复演练。
另外,恢复时要注意容器镜像版本变化。有些软件的数据格式在不同版本间不保证兼容,直接把老卷恢复到新版本容器可能无法启动。遇到这种情况,需要结合数据库导出工具来做迁移,而不是单纯依赖 tar 包。还有一个容易忽略的点:在恢复前先确认卷名是否被占用,如果存在同名旧卷,通常需要先删除再创建,但删除前要确认没有其他容器还在引用,以免误删数据。
最后一个小提醒:备份脚本里设置好 umask,避免生成权限过宽的文件。同时注意卷内如果有软链接或特殊权限,tar 默认行为可能丢属性,建议在打包时加上 --preserve-permissions 参数。备份完成后,ls -lh 只能看体积,真正的可靠性要靠恢复演练来证明。