Docker 自动重启后仍有旧文件,--restart 与 --rm 冲突
重启同一容器会保留可写层;每轮干净执行应由调度器新建容器。先区分可写层和挂载数据,并在使用 --rm 前保留产物、日志及匿名卷数据。
构建代理或一次性任务执行结束后,Docker 的重启策略把容器重新拉起,但上一轮生成的文件仍在。再加上 --rm 想让下一轮恢复干净环境,又收到 Conflicting options: --restart and --rm。
普通 Docker Engine 容器的重启策略重启的是同一个容器。它不会重新创建可写层,也不会将该层还原为镜像初始状态。每轮需要独立环境时,应让任务调度方创建新的容器,而不是反复重启旧容器。
先确认残留文件属于哪一层
以下 build-worker 是已有容器的占位名称:
docker inspect --format '{{.Id}} {{.Image}}' build-worker
docker inspect --format '{{json .HostConfig.RestartPolicy}}' build-worker
docker inspect --format '{{json .Mounts}}' build-worker
docker diff build-worker
容器 ID 用来区分重启与新建,镜像 ID 用来核对基础环境。docker diff 查看相对镜像发生的容器文件系统变化;挂载目录的数据仍要结合 Mounts 单独检查。
写入容器可写层的数据会在同一个容器停止、再次启动后保留。写入命名卷或绑定挂载的数据则位于可写层之外,即使新建容器,只要挂载回同一数据位置,也可能看到原内容。先定位工作目录的存储方式,不能把所有残留都归因于重启策略。
--rm 不会安排下一轮运行
--rm 让容器退出后被自动移除;它不是“删除后立即创建替身”的选项。它与 Docker 重启策略不能组合使用,换成 always 或 unless-stopped 也不能把这两个生命周期要求合并。
需要每轮干净环境时,让 CI 或现有任务调度器每次执行新的 docker run --rm。由外层决定下一轮何时开始、失败是否重试和重试次数,容器本身不再配置 Docker 自动重启策略。不要同时让 Docker 和宿主进程管理器争抢同一个容器的重启职责。
下面两条命令只用于隔离测试,使用不带业务挂载的示例镜像。第一轮写入临时标记,退出后删除该容器;第二轮创建另一个容器,检查相同路径:
docker run --rm --entrypoint sh busybox:1.36 \
-c 'printf "%s\n" "first run" > /tmp/task-marker'
docker run --rm --entrypoint sh busybox:1.36 \
-c 'test ! -e /tmp/task-marker && printf "%s\n" "fresh writable layer"'
没有共享挂载、镜像本身也没有该标记时,第二轮预期找不到上一轮创建的文件。这仅说明两个新容器的可写层隔离,不证明业务使用的缓存、外部数据库或挂载目录也已清空。示例不设置固定容器名,避免与已有容器冲突。
实际任务应替换成经过确认的镜像、入口和参数。为了可重复构建,还需固定所用镜像版本或摘要;“新容器”并不等于“自动拉取了远端最新镜像”。
保留产物与诊断信息,再启用自动删除
--rm 会移除容器可写层,并清理与它关联的匿名卷。命名卷不会因此删除,绑定挂载的宿主文件也不会被自动清空。启用前应确认哪些日志、构建产物或诊断文件需要由外层收集,哪些目录本来就要跨轮共享。
如果构建工作目录需要每轮隔离,应为各轮提供独立目录或使用可写层,并明确产物导出位置。不要把共享缓存和待清理的工作区混成一个目录,也不要用 docker volume prune 或删除全部卷来实现“干净启动”。这些操作可能影响其他任务的数据。
现有容器里还有需要保留的文件时,不要立即删除它。先结束或按业务流程停止当前任务,保存产物与日志,再切换下一轮的启动方式。由 Compose、CI 平台或其他管理器维护的容器,应修改对应定义,避免外部脚本仍按旧策略重建。
验收每轮身份及残留范围
至少检查两轮独立任务:容器 ID 应不同,基础镜像应符合预定版本,上一轮可写层标记不应出现在下一轮;故意保留的共享缓存则按其策略验收。再检查成功和失败路径能否取回日志、产物与退出状态。
需要回退时恢复原来的任务启动定义和重试策略,但已自动删除的可写层不会随配置回退恢复。应在切换前完成必要留存,不能把容器删除当作可以无代价撤销的重启。


