上一篇 《如何在Linux部署Docker&Compose》 讲了怎么把容器跑起来。容器跑起来之后还有个长期问题:镜像更新了怎么及时跟上?安全补丁靠手动一个个 pull 很容易拖几个月。这篇讲怎么让容器自动更新,主线是 Watchtower(全自动),外加一套 Compose 脚本半自动方案,并把风险和回滚讲清楚——自动更新省事,但用错了也会半夜把服务更挂。
先想清楚:哪些容器适合自动更新
自动更新的前提是镜像用 latest 或大版本标签(如 nginx:1),发布方出了新版就自动替换。这对无状态、发布稳定的服务很合适,但不是所有容器都适合:
| 类型 | 建议 | 原因 |
|---|---|---|
| 无状态小服务(博客、面板、工具站) | 自动更新 | 挂了影响小,新版一般向后兼容 |
| 反向代理、网关 | 自动更新或只通知 | 影响面大,建议至少开通知 |
| 数据库(PostgreSQL、MySQL、Redis) | 不要全自动 | 大版本升级涉及数据格式,必须人工确认 |
| 有状态/核心生产服务 | 只通知,手动更新 | 先看 changelog 再决定 |
还有一条铁律:重要服务把镜像版本固定住(如 postgres:16.4),自动更新只碰那些明确用浮动标签的容器。下面 Watchtower 的「只更新带标签的容器」就是为这个准备的。
方案对比
| 方案 | 自动化程度 | 适合场景 |
|---|---|---|
手动 docker compose pull && up -d | 无 | 偶尔维护、服务少 |
| 脚本 + 定时任务 | 半自动 | Compose 项目、想自己掌控节奏 |
| Watchtower | 全自动 | 容器较多、想一劳永逸 |
| Diun | 只通知不更新 | 核心服务,先知道再手动更 |
主线:Watchtower 全自动更新
Watchtower 会定时检查你正在运行的容器有没有新镜像,有就拉取新镜像、用原来的参数重建容器,整个过程不用人管。
用 Docker Compose 部署(推荐)
和上一篇的习惯保持一致,用 Compose 管理:
services: watchtower: image: nickfedor/watchtower:latest container_name: watchtower restart: unless-stopped environment: - TZ=Asia/Shanghai volumes: - /var/run/docker.sock:/var/run/docker.sock command: --interval 10800 --cleanup启动:
docker compose up -dWARNING镜像别用错:原版
containrrr/watchtower已经停止维护并归档,它内置的 Docker 客户端 API 版本只有 1.25,而 Docker 29 的守护进程最低要求 1.40 以上。用原版镜像装出来的 Watchtower 会在日志里反复报错client version 1.25 is too old. Minimum supported API version is 1.40,容器看着在跑,实际上永远不会更新任何东西。现在应当使用持续维护的分支nickfedor/watchtower,配置、标签、参数与原版完全兼容,本篇所有示例都以它为准。如果你已经装了原版,换掉镜像重新up -d即可,原有配置不用改。
两个关键参数:
--interval 10800:每 10800 秒(3 小时)检查一次。太频繁没意义还费流量,3 小时是个舒服的节奏。--cleanup:更新成功后删掉旧镜像,不然旧镜像会一层层堆满磁盘。
WARNINGWatchtower 需要挂载
/var/run/docker.sock,等于给它开了 Docker 的完整控制权。只在自己的机器上用,镜像只从官方仓库拉,别在不可信环境里部署。
只更新指定容器(更安全的做法)
默认 Watchtower 会盯上所有容器。更稳妥的做法是加 --label-enable,只更新那些你亲自打了标签的容器:
command: --interval 10800 --cleanup --label-enable然后在想自动更新的服务上加标签(Compose 写法):
services: nginx: image: nginx:latest labels: - com.centurylinklabs.watchtower.enable=true没打标签的容器(比如数据库)Watchtower 看都不看,先看通知再手动处理,两边都不耽误。
先只监控,不动手
第一次用不放心,可以先跑一阵子「只监控」模式:发现新镜像只写日志、不更新。
command: --interval 10800 --monitor-only观察几天日志,确认它盯的容器没问题,再把 --monitor-only 去掉转正。
更新通知
更新是自动的,但你应该知道它更了什么。Watchtower 支持通过 shoutrrr 发通知(Telegram、邮件、企业微信等都行),以 Telegram 为例:
environment: - TZ=Asia/Shanghai - WATCHTOWER_NOTIFICATIONS=shoutrrr - WATCHTOWER_NOTIFICATION_URL=telegram://<机器人Token>@telegram?chats=<你的ChatID>之后每次更新都会收到一条消息:哪个容器、从哪个版本更到了哪个版本,一目了然。
更新前后钩子
个别容器更新前需要先做点事(比如备份、优雅停机),可以在目标容器上加生命周期标签:
labels: - com.centurylinklabs.watchtower.lifecycle.pre-update=/scripts/backup.sh - com.centurylinklabs.watchtower.lifecycle.post-update=/scripts/check.sh脚本要放在容器内部,路径是容器里的路径。pre-update 返回非 0 会中止这次更新,正好用来做「备份失败就不许更新」的保险。
半自动方案:Compose 脚本 + 定时任务
如果你的服务全是 Compose 项目,又想保留「更新前我说了算」的感觉,可以用脚本定时拉取重建,自己掌控名单:
#!/bin/bashcd /opt/myapp || exit 1docker compose pulldocker compose up -ddocker image prune -fchmod +x /opt/scripts/docker-update.sh挂到 crontab,每天凌晨 4 点执行并留日志:
0 4 * * * /opt/scripts/docker-update.sh >> /var/log/docker-update.log 2>&1这个方案的好处是更新范围就是脚本里写的项目,坏处是每个项目要单独维护一份脚本,项目多了不如 Watchtower 省心。
WARNING脚本里的
docker image prune -f只删没有容器在用的悬空镜像,相对安全;但千万不要写成docker system prune -a,那会把停着的容器、没在跑的镜像一起清掉,上一篇已经强调过这个坑。
版本固定与回滚
自动更新偶尔会翻车:新版改了配置格式、或者本身带 bug。提前做好两件事,翻车了 5 分钟就能退回去:
1. Compose 文件进 Git。 每次改配置都提交,出事能查到上次的配置长什么样。
2. 知道旧镜像还在不在。 开了 --cleanup 旧镜像会被删掉,回滚要重新拉旧版本,所以关键服务的镜像标签别只写 latest,写到小版本:myapp:2.3.1。回滚时改回上一个版本号重新拉取即可:
# 把 compose 里的镜像改回旧版本后docker compose pulldocker compose up -d如果没开 --cleanup,旧镜像还躺在本机,可以直接给它重新打上标签再用:
docker images # 找到旧镜像的 IMAGE IDdocker tag <旧IMAGE_ID> myapp:rollback常见问题排查
日志反复报 client version 1.25 is too old。 这是装了原版 containrrr/watchtower 的典型症状:它的客户端 API 太旧,被新版 Docker 直接拒绝,一个容器都更新不了。把镜像换成维护分支 nickfedor/watchtower 重新部署即可,配置不用动。
Watchtower 没动静。 先看它的日志,它会明确写每个容器检查的结果和跳过原因:
docker logs -f watchtower提示权限不足。 基本都是 /var/run/docker.sock 没挂对,或者宿主机 Docker 的 socket 路径不同,检查 Compose 的 volumes。
更新后容器起不来。 先看新容器日志确认是新版的问题,再按上面的回滚步骤退回旧版本,别在故障现场改配置硬撑:
docker logs <容器名>磁盘越来越满。 多半是没开 --cleanup,旧镜像在堆积。手动清一次悬空镜像即可:
docker image prune -f停用自动更新
想停掉很简单,停掉 Watchtower 就行,其他容器不受影响:
docker compose stop watchtowerdocker compose rm watchtower小结
- 无状态小服务交给 Watchtower 全自动,3 小时检查一次、开
--cleanup、开通知; - 放不下心的先用
--label-enable只更新打了标签的容器,或者--monitor-only先观察几天; - 数据库和核心服务固定版本、只通知不自动更;
- Compose 文件进 Git、关键服务版本写到小版本,翻车能立刻回滚。
自动更新的定位是「把重复劳动交给工具」,不是「把判断也交出去」。名单分好类、通知开起来、回滚留好路,它就是个很省心的帮手。