TW●汤伟的数字生活NOTES ON A DIGITAL LIFE
个人数字生活文库始于 2026 · 持续修订总目录 ↗
全部文章

容器与自托管 / 实战手册

Docker 升级与回退:镜像替换之后,数据还能回去吗

明确 pull、up 和 restart 的边界,固定版本并保留回退材料,以业务验证决定升级是否完成。

阅读设置字号仅保存在当前浏览器

先写出这次到底升级什么

本文由 AI 按 Docker 官方文档整理,核对日期为 2026-09-08,未在 Mac、Linux 或群晖设备上亲测。read 为查询;write 包括文件修改、拉取镜像和启动容器;danger 包括停机、替换运行容器以及可能破坏数据的操作。

示例继续使用项目 bloglab、服务 web 和项目根目录中的 compose.yaml,静态数据卷为 bloglab_site。要求当前服务已经部署,维护者知道端口 18080 的用途,完成可恢复备份,并获得可以中断该服务的维护窗口。本文使用固定 Nginx 版本说明机械步骤,不暗示某个历史标签是今天的安全推荐;真实升级须由维护者选择经项目发布说明确认兼容的目标版本,不能把本文示例号当成升级策略。

升级对象至少分为应用镜像、运行配置和数据格式。前端静态站点换一个 Nginx 补丁版本,与数据库跨大版本升级的风险完全不同;前者主要验证启动与页面,后者可能涉及磁盘格式、扩展版本及应用 SQL 兼容性。升级记录应写清现用镜像、目标版本、必要迁移、验证项目和失败时的恢复入口。演练环境与正式服务应有不同项目名、端口和数据副本,不能让测试迁移碰到正式卷。

查看升级前状态bash · macOS Docker Desktop / Linux Docker Engine / DSM(先查本机命令帮助)
查询 / 诊断
docker compose -p bloglab -f compose.yaml ps -a
docker compose -p bloglab -f compose.yaml images
docker compose -p bloglab -f compose.yaml config --images

分别确认服务状态、容器使用的本地镜像及配置引用;配置中的标签和当前运行容器未必一致。

保存旧配置和旧镜像的可追溯信息

标签不是不可变版本证据。即使 compose.yaml 写着同一个标签,仓库也可能更新标签指向;容器仍使用创建时选定的镜像,不会因为远端标签变化自动变新。升级前同时记录容器的 image ID 和本地 RepoDigests:前者确认运行内容,后者在有仓库摘要时可用于固定远端内容。不同平台镜像、清单摘要和本地镜像 ID 不是同一个概念,记录时保留原始字段含义,不能把 image ID 随意拼成仓库摘要。

把原始配置复制到受控的回退文件,并确认没有覆盖上一次记录。真实配置可能包含秘密,即使只是拷贝也应放在合适权限的本地目录,不能直接提交公开仓库。保留旧镜像或按可信摘要确认还能取回旧镜像,也要保留升级前数据备份。不要在升级刚通过一条健康检查后就清理所有旧镜像与备份,观察期内出现业务兼容问题还需要这些材料。

记录运行镜像,不展开环境变量bash · macOS Docker Desktop / Linux Docker Engine / DSM(先查本机命令帮助)
查询 / 诊断
docker compose -p bloglab -f compose.yaml ps -a -q web
# CONTAINER_ID 替换为上一条的一个真实 ID
docker inspect --format 'ref={{.Config.Image}} image_id={{.Image}}' CONTAINER_ID
# IMAGE_ID 替换为上一条的 image_id
docker image inspect --format '{{json .RepoDigests}}' IMAGE_ID

只查询选定字段;RepoDigests 为空时不能假装已有仓库摘要。多个副本逐一检查。

保留旧配置文件bash · macOS Docker Desktop / Linux Docker Engine / DSM(先查本机命令帮助)
会修改状态
cp -n compose.yaml compose.rollback-20260908.yaml

在项目根目录执行,会写新文件;-n 避免覆盖。若文件已存在应改新文件名并人工核对,不能默认已有文件就是当前版本。

把一次升级写成可以比较的变更单

本例完整变更单应有升级前、候选验证、正式替换和观察结束四个节点。升级前填当前运行镜像编号及可重新获取的引用,候选栏填维护者核对过的目标引用,备份栏填已经恢复验证的静态卷归档,验收栏填首页和资源路径。所有结果先写“待执行”,实际完成后再填写真实值;不能把示意标签变更写成已经完成的补丁升级,也不能使用阅读本文的日期冒充设备操作时间。

开始前把回退配置打开审阅,确认它引用的是保留的旧内容。如果旧配置也依赖环境变量,仅复制文件不足以固定回退版本,因为当前终端的新变量仍会参与插值。最容易审查的静态单服务方案,是在回退文件里写明已经记录并确认本地存在的旧不可变引用,再用配置查询查看最终镜像。目标配置则继续使用必填变量。这样的分工让两个文件在执行时有各自明确的含义,不依赖操作者记住上一次终端状态。

变更单还要写明此次不涉及数据格式变化的判断依据。对于本例,这来自静态内容只读挂载且只替换网页服务器的设计;真实应用则需要阅读应用版本说明,不能凭“只是换镜像”推断数据不变。如果升级要求写入迁移,正式切换前要先建立独立数据副本和对应恢复方案,原卷不得接入未知候选程序。停机时长预算应覆盖验收和回退所需时间,而不是只统计拉取进度。

变更记录模板text · macOS Docker Desktop / GNU/Linux Docker Engine;DSM 先核对插件能力
会修改状态
项目:bloglab
执行时间与时区:待执行
旧运行镜像编号:待填写
旧不可变镜像引用:待填写
候选镜像引用及平台:待填写
备份归档与恢复验收:待填写
首页、资源验收:待执行
回退条件与观察结束时间:待填写

模板保存到个人记录文件时属于 write;所有字段均未执行。

先验证配置,再下载目标镜像

本文目标配置通过 WEB_IMAGE 变量传入完整镜像引用,用必填表达式阻止忘记设置变量时意外选择其他镜像。把目标版本对应的完整引用填进去,也可以使用仓库地址加摘要。示例给出 nginx:1.28.0-alpine 作为语法值;若你当前已经运行该内容,就不会发生真实升级。要演练替换,应自行准备另一个兼容且可信的版本,并先在隔离端口测试,不能为了看到重建日志而随意换一个不支持的镜像。

pull 只把镜像下载到当前引擎存储,不会把现有服务自动换成新镜像。下载失败时原容器通常仍继续运行,适合先完成网络准备再开始维护。config -q 只验证 Compose 配置可解析且结构一致,不会验证应用业务、端口是否空闲或镜像是否兼容。下载后可检查本地镜像信息并核对发布者给出的摘要;本地出现一个同名标签,并不足以证明已运行的服务使用它。

目标配置示例yaml · macOS Docker Desktop / Linux Docker Engine / DSM(先查本机命令帮助)
会修改状态
services:
  web:
    image: ${WEB_IMAGE:?set WEB_IMAGE to a verified image reference}
    ports:
      - "127.0.0.1:18080:80"
    volumes:
      - site:/usr/share/nginx/html:ro
    restart: unless-stopped
volumes:
  site: {}

先备份再保存为 compose.yaml,保持项目名与卷键不变。这里只替换镜像引用;生产配置中的其他必要项应完整保留。

设置引用并预检bash · macOS Docker Desktop / Linux Docker Engine / DSM(先查本机命令帮助)
会修改状态
export WEB_IMAGE='nginx:1.28.0-alpine'
docker compose -p bloglab -f compose.yaml config -q
docker compose -p bloglab -f compose.yaml config --images

WEB_IMAGE 是公开镜像引用,不是凭据。把示例值替换成你已确认的目标;export 修改当前 shell 环境,不会升级服务。

预先下载目标bash · macOS Docker Desktop / Linux Docker Engine / DSM(先查本机命令帮助)
会修改状态
docker compose -p bloglab -f compose.yaml pull web

联网下载并改变本地镜像缓存;不启动、不重建现有 web。失败应先诊断,不要忽略失败继续升级。

先在另一端口检验候选镜像

候选实例直接复用本例只读静态卷,但使用独立容器名和端口;这是仅对无数据库、无后台写入的静态网页成立的选择。如果应用启动会迁移、发通知或执行定时任务,就必须改为隔离数据和外部副作用的演练,不能照搬共享卷。先确认候选端口空闲、名称未被使用,以及目标镜像已在本地,再启动候选。候选失败不需要停止原服务,因为正式替换尚未开始。

候选验收采用与旧版本相同的请求集合。首页成功以后,选一个页面实际引用的静态资源,检查状态、类型和内容;若站点有深层路径,直接打开该路径并刷新,确认与原站点的路由约定一致。本例默认网页服务器不会自动为任意前端路由提供回退页面,深层路径报不存在可能是原配置就不支持,不能误报为升级回归。对比必须依据升级前留下的真实基线,不能凭印象判断新版本更快或更稳定。

候选通过后再进入正式重建;正式服务启动后重复相同验收,不能因为候选通过就省略。两者端口和实例不同,正式替换仍可能遇到端口竞争、配置文件选错或镜像引用不一致。检查新容器的实际镜像编号,不只看标签文本,然后读取这次启动之后的日志。如果候选需要较长启动时间,应先了解它的就绪条件并调整验收时机,而不是把连接失败后无限重试当成正常流程。

启动候选静态实例bash · macOS Docker Desktop / GNU/Linux Docker Engine;DSM 先核对插件能力
会修改状态
docker run -d --pull=never --name bloglab-candidate -p 127.0.0.1:18082:80 --mount type=volume,src=bloglab_site,dst=/usr/share/nginx/html,readonly "${WEB_IMAGE:?set verified candidate image}"

write:仅本例静态只读卷允许共享;候选必须已拉取。

查询候选入口bash · macOS Docker Desktop / GNU/Linux Docker Engine;DSM 先核对插件能力
查询 / 诊断
curl --noproxy '*' --fail --show-error --connect-timeout 3 --max-time 10 http://127.0.0.1:18082/

read:与旧入口做相同内容验收,不声称已测。

重建一个服务,并验证用户能用的功能

准备完成后再让 Compose 按新配置重建 web。单服务示例使用 --no-deps 限定操作范围;真实项目若升级要求协调数据库、缓存或迁移任务,这个选项可能不合适,必须按依赖方案一起安排。--pull never 和 --no-build 把这一步限定为使用已准备镜像,但相关参数仍应先在本机帮助确认。旧 DSM 插件缺少选项时,不可删除约束后盲跑,应先确认实际命令行为和可用维护方案。

重建会停止旧容器并创建新容器,单副本服务可能出现中断。restart 则通常重启既有容器,不能当成应用新镜像的替代。完成后依次确认容器未反复退出、日志没有新错误、镜像 ID 符合目标,再访问首页和关键路径。对前端站点还应验证静态资源地址、缓存、深层路由刷新和 API 请求;健康状态只是预设检查的结果,不是覆盖这些业务路径的自动证明。

应用已经准备好的镜像bash · macOS Docker Desktop / Linux Docker Engine / DSM(先查本机命令帮助)
高风险操作
docker compose -p bloglab -f compose.yaml up -d --no-deps --no-build --pull never web

可能停止并替换运行容器,造成短暂中断;挂载卷继续保留。只适用于依赖不需同步改变的这次操作。

检查升级后表现bash · macOS Docker Desktop / Linux Docker Engine / DSM(先查本机命令帮助)
查询 / 诊断
docker compose -p bloglab -f compose.yaml ps -a
docker compose -p bloglab -f compose.yaml logs --since 10m --tail 200 --timestamps web

检查重复启动、权限和依赖错误,再从宿主机浏览器访问 http://127.0.0.1:18080 并完成业务验证。

回退镜像与回退数据是两个动作

如果只有运行镜像变化、数据格式未变,通常可以恢复旧配置并重建回旧镜像。下面回退命令使用保留的旧配置,但要求其中引用确实对应旧镜像,旧镜像也仍在本地;若旧标签被新 pull 覆盖,必须先把回退配置改成记录的可信旧摘要或已保留的旧镜像引用。仅仅换回文件名不会让一个已经被覆盖的浮动标签自动变回旧内容。

若新版程序已经做了不可逆迁移,旧程序可能无法读取新数据,甚至继续写坏数据。此时应停止相关写入者,在隔离环境恢复升级前备份,完成匹配版本验证,再决定切换;不能直接把旧镜像接回已经升级的原卷试运气。恢复旧数据还会丢失备份点之后的新写入,需明确可接受的数据损失窗口,并考虑是否有产品支持的正向修复方案。没有通用 Docker 参数能够撤销任意应用数据迁移。

本例命名卷覆盖站点目录,所以更换 Nginx 镜像不会自动刷新卷中的旧网页内容。程序运行时升级和静态内容发布也应分开记录。不要为了看到新页面就删除卷,更不要把 down -v 或 prune --volumes 写进升级模板。成功发布的完成条件是业务验证、观察期和恢复材料齐备,而不是终端显示一个 Started。

仅在数据兼容且旧镜像可用时回退bash · macOS Docker Desktop / Linux Docker Engine / DSM(先查本机命令帮助)
高风险操作
docker compose -p bloglab -f compose.rollback-20260908.yaml config -q
docker compose -p bloglab -f compose.rollback-20260908.yaml up -d --no-deps --no-build --pull never web

danger:会替换运行服务。前提是回退配置使用已核实旧镜像且数据未发生不兼容迁移;否则停止在这里,执行独立数据恢复方案。

按失败发生的阶段选择回退动作

第一种失败发生在下载阶段,此时只处理仓库、平台或本地容量,原实例继续使用旧镜像,不需要执行回退重建。第二种发生在候选阶段,保留候选日志,停止候选后修改目标选择,正式服务仍保持原状态。第三种发生在正式替换之后,先确认是进程无法启动还是请求内容不符合预期;达到变更单预设的回退条件,就使用已验证的旧引用重建,不在停机窗口里无限试错。

如果新容器能运行但页面内容错误,先核对静态卷和请求路径。卷中的旧网页不会因为服务器镜像升级自动刷新,所以“页面没有变”本身不是升级失败证据;容器镜像编号才回答运行程序是否替换。若首页正常而某个资源失败,记录具体路径、状态和新旧差异,再确定是否属于兼容回归。回退后也要重复首页与资源验收,并确认实际镜像重新指向旧内容,否则一条回退命令退出成功还不能结束维护。

观察期按服务用途约定,例如覆盖一次自己实际会做的页面访问和资源加载,记录起止时间而不宣称某个固定分钟数适用于所有系统。候选实例可在正式验收后点名停止,旧镜像和升级前归档则保留到回退需求结束。若这次只验证了本机回环访问,就明确记录没有验证跨设备访问、反向代理缓存、宿主重启和异机恢复。完成的升级记录应包含成功路径与实际触发的失败分支,使下一次变更能复用证据而非照抄一句“更新正常”。

示意升级前后记录采用符号表示内容身份:升级前运行内容为甲,候选为乙,候选首页通过但正式资源路径出现回归时触发回退,回退后应重新确认运行内容为甲,且静态卷仍为原卷。甲乙并非伪造摘要,实际操作必须填入查询取得的真实引用。失败原因记录为资源路径兼容问题待核对,不能把恢复可用自动写成根因已解决。

正式入口验收bash · macOS Docker Desktop / GNU/Linux Docker Engine;DSM 先核对插件能力
查询 / 诊断
curl --noproxy '*' --fail --show-error --connect-timeout 3 --max-time 10 http://127.0.0.1:18080/

查询正式静态入口;回退后同样执行。

停止已完成验证的候选bash · macOS Docker Desktop / GNU/Linux Docker Engine;DSM 先核对插件能力
高风险操作
docker stop -t 30 bloglab-candidate

danger:只停止点名的候选;不删除卷或旧镜像。

已填写的示意材料text · macOS Docker Desktop / GNU/Linux Docker Engine;DSM 先核对插件能力
查询 / 诊断
阶段          运行内容  数据卷          动作
升级前        甲        bloglab_site    记录基线并备份
候选验证      乙        同卷只读        18082 首页及资源对照
正式替换      乙        同卷只读        18080 重复验收
资源回归分支  甲        同卷只读        用旧固定引用重建再验收
上述均为示意路径,未发生真实升级

阅读用示意材料;不是命令输出或实测记录,复制保存为记录文件属于 write。

延伸阅读

docker compose pull docker compose up docker compose config docker image pull docker container inspect Control startup and shutdown order docker compose down Volumes:生命周期、挂载与备份恢复 curl 官方选项手册 docker container run docker container stop
同一专题 · 继续阅读容器、镜像、数据卷:从前端项目到可维护的 Compose 服务容器备份要能恢复:静态数据卷、数据库一致性与隔离演练镜像拉不下来:按 DNS、证书、认证、限流、架构和代理逐层定位查看整个专题 ↗