docker commit 后新容器缺少文件:挂载卷不会打入镜像
容器挂载目录的文件不会随 docker commit 或 export 带走。核对实际卷名,同机重新挂载,跨主机分离备份与镜像,并保留一致性和回退条件。
在容器的挂载目录里新增文件,执行 docker commit 得到新镜像,再用新镜像启动容器,文件却不见了。改用 docker export 导出也没有带走这些文件。若文件写入的是挂载卷,这两个命令都不会把卷内容打包进去;成功生成镜像或 tar 文件,不能证明业务数据已经备份。
下面针对 Docker Engine 的普通容器和数据卷。先保留原容器与原卷,确认数据到底在哪里,再决定重新挂载还是迁移。不要先删除旧环境来验证新镜像是否完整。
从文件路径找到实际数据卷
以 app-source 代替原容器名:
docker inspect --format '{{json .Mounts}}' app-source
找到覆盖目标文件路径的 Destination。如果文件是 /cookbook/foo.txt,且 /cookbook 对应 Type: volume,记录该项的 Name,并核对卷本身:
docker volume inspect ACTUAL_VOLUME_NAME
ACTUAL_VOLUME_NAME 必须换成刚确认存在的卷名。匿名卷也有实际名称,不能只凭容器内路径推断它与新容器用的是同一个卷。如果类型是 bind,数据在指定的宿主路径,后续应按绑定挂载处理,不要把它误写成命名卷。
docker commit 保存容器的文件变化与部分配置,挂载卷的数据不在其范围内。docker export 同样排除卷;挂载覆盖了镜像原有目录时,export 得到的是底下的目录内容,而不是当前挂载的数据。再对新镜像导出一次也补不回缺失的卷文件。
原主机上的数据仍在时,先核对原卷
卷还存在、数据也没有被清理时,可以将同一个卷挂载到另一个容器。下面只示范只读查看已确认的普通文件卷,不运行原应用:
docker volume inspect ACTUAL_VOLUME_NAME
docker run --rm --entrypoint sh \
--mount type=volume,src=ACTUAL_VOLUME_NAME,dst=/inspect-data,readonly,volume-nocopy \
busybox:1.36 -c 'ls -la /inspect-data'
先确认卷存在很重要:把名称拼错后直接运行容器,Docker 可能创建另一个空卷,进一步造成“数据丢了”的错觉。只读与 volume-nocopy 分别避免此检查容器写入数据和向空卷复制镜像内容;它们不代表正在运行的业务不会继续改动这个卷。
核对文件名、大小及业务认可的内容。确认后,在应用的正式启动定义中引用这个实际卷,并使用原应用要求的容器内路径。不要把 /inspect-data 当成应用的新数据目录,也不要让两个不支持共享写入的应用同时使用同一卷。
匿名卷不会在每次新建容器时自动复用。若后续改成明确的卷管理方式,应保留数据映射和迁移记录;不能只改一个名字,就假定旧卷内容已经转移。
要搬到另一台主机,镜像和数据分开迁移
新主机除了镜像,还需要原卷数据及正确的挂载定义。同名卷在另一台主机上并不自动含有相同文件。
普通文件可在停止写入并确认一致性后单独备份、恢复到新卷,再核对文件内容和权限。数据库等持续写入的数据应使用对应应用支持的备份与恢复流程;直接复制正在变化的文件,可能得到无法恢复的一组数据。commit 默认暂停容器也不会使卷进入镜像,不能借此代替数据库备份。
恢复先在独立的新卷完成,验证后再切换应用。保留原卷、原启动定义与可恢复备份,直到业务检查结束;不要将恢复操作覆盖到仍在服务的唯一数据卷。
如果这些文件本来是固定的应用资源,应从受控构建输入制作镜像,而不是依靠运行时数据卷和 commit 收集。业务数据、密钥和用户上传文件不应为了方便搬运而随意打进镜像。
验收时同时核对镜像和挂载
新容器启动后,再看 Mounts 是否指向预定的数据位置,并核对应用读取到的内容。镜像标签正确、容器能启动,只能证明部分步骤成功。迁移场景还需验证文件完整性、权限及应用的一致性检查。
若发现新环境不完整,停止向新环境写入,按切换前保留的定义恢复原服务。新旧环境已分别产生写入时,应先处理数据差异,不能靠重新挂回旧卷自动合并。整个排查过程都不需要用清理全部卷的命令试错。


