无停机更新容器
如何更新镜像以保持服务在线并可回滚:固定版本、健康检查、操作顺序以及自动化。
更新于 2026年8月23日
更新容器并不是“拉取新镜像”,而是“用新版本替换正在运行的版本,并且能够回滚”。这个区别只会在一个时刻显现:新版本启动失败时。
一套可行的流程
cd /opt/myapp
docker compose pull
docker compose up -d
docker compose ps
docker compose logs -f --tail 100
Compose 只会重建镜像发生变化的服务,其余服务保持不变。停机时间仅为重启容器所需的几秒钟。
但在执行之前,请先满足两个条件。
第一:版本已固定。 文件中必须写明 postgres:17.2,而不是 postgres:latest。否则某天 pull 会带来一个重写数据格式的大版本,回滚将变得不可能。
第二:备份是新的。 一次破坏数据库的更新会造成不可逆的损坏。在大版本变更之前,请先做一次转储——参见需要备份哪些内容。
无中断替换
对于反向代理后面的 Web 服务,如果容器配置了健康检查,停机时间几乎可以降为零:代理只会向已就绪的实例发送请求。
services:
app:
image: ghcr.io/example/app:1.5.0
healthcheck:
test: ["CMD", "curl", "-fsS", "http://localhost:8080/health"]
interval: 5s
timeout: 3s
retries: 6
start_period: 20s
docker inspect -f '{{.State.Health.Status}}' <container>
如果没有健康检查,Compose 会在进程启动后立即认为容器已就绪——而此时应用程序仍在读取配置并执行迁移。
在新版本至少运行一小时之前,不要删除旧镜像。回滚意味着恢复
image: ...:1.4.2并执行docker compose up -d,但前提是旧镜像仍在磁盘上。更新后立刻执行docker image prune -a会让您失去这个选项。
回滚
sed -i 's|app:1.5.0|app:1.4.2|' compose.yaml
docker compose up -d app
docker compose logs -f --tail 50 app
如果新版本已经更改了数据库结构,仅回滚镜像是不够的——您需要从备份恢复。因此大版本更新应在空闲时段进行,并确保备份已经过验证:参见恢复演练。
自动化:请谨慎
Watchtower 可以自行更新容器。对于家庭服务来说这很方便;对于生产环境来说则是风险:更新在夜间到来,破坏数据库,却无人察觉。一个合理的折中方案是只发送通知:
services:
watchtower:
image: containrrr/watchtower
restart: unless-stopped
volumes:
- /var/run/docker.sock:/var/run/docker.sock
command: --monitor-only --schedule "0 0 6 * * *"
您会得知有新版本发布,然后在合适的时间手动更新。
重启机器
系统软件包的更新与容器相互独立。内核更新后需要重启,带有 restart: unless-stopped 的容器会自动恢复运行。请提前测试这一点——在空闲时段执行 sudo reboot,而不是在故障期间。只要系统上安装了 qemu-guest-agent,从控制面板执行软重启即可正常工作。
常见错误
- 只执行
pull而不执行up -d。 新镜像已下载,但旧容器仍在运行。 - 磁盘被旧镜像塞满。 每月执行一次
docker image prune——但不要在更新后立刻执行。参见磁盘空间。 - 您一次性更新了所有内容。 某个服务出问题——而您无法判断是哪一个。请一次只更新一个服务。
- 您没有阅读发行说明。 配置文件不兼容是大版本变更后最常见的故障原因;参见常见 Docker 问题中的分析。