Watchtower 容器镜像自动更新实战:monitor-only 试跑、label 白名单与数据库为何绝不能交给它

容器镜像更新的那个「隐形债」

跑容器的人都有同一个坏习惯:把服务用 docker run 或 compose 拉起来之后,就再也不管镜像了。容器本身很稳,一年不重启也没事,可镜像里的基础系统、Nginx、PHP、依赖库却停留在你当初 docker pull 的那一天。半年后你打开 docker images,可能会发现某个镜像的底层是早已停止维护的 alpine 3.16,里面躺着好几个已知 CVE。

问题在于,没有漏洞扫描器你根本感知不到这个债。而手动更新镜像又是个纯粹的体力活:docker pull → docker compose up -d → 祈祷没坏。十几个容器轮一遍,还得挑半夜做。

Watchtower 就是为这个场景生的工具:它定时轮询镜像仓库,发现你正在运行的镜像有了新版本,就自动拉取、优雅重建容器、并清理旧镜像。配置得当的话,你可以在「自动更新」和「可控更新」之间找到平衡。

Watchtower 到底怎么工作

它的逻辑其实很朴素:

  1. 每隔一段时间(默认 24 小时),读取你给它监控的每个容器的镜像名和 tag;
  2. 去对应的 registry 查询该 tag 现在对应的 digest(或比较 created 时间);
  3. 如果本地镜像已过期,就 docker pull 新镜像;
  4. 用和原容器相同的配置(端口、卷、环境变量、网络、重启策略)重建一个新容器,停掉旧的;
  5. 可选地删除旧镜像。

关键点在第 4 步:它没有任何「迁移脚本」概念。所以能否安全重建,完全取决于——你的服务是不是无状态的、依赖的数据是不是都在卷/外部存储里。数据库这种有状态服务绝对不能交给 Watchtower 自动更新,这点后面详说。

最简部署:先只监控,不动手

新手最常见的翻车是「一上来就让它全自动更新所有容器」,结果某个 tag 变了的大版本(比如 latest 从 v5 跳到 v6)直接把服务干崩。所以我的建议是分两步走:先部署成「只报告不更新」,观察几天。

docker run -d \
  --name watchtower \
  --restart unless-stopped \
  -v /var/run/docker.sock:/var/run/docker.sock \
  containrrr/watchtower \
  --monitor-only \
  --interval 3600 \
  --cleanup

参数含义:

  • --monitor-only:只检测、只打日志,不执行更新。先跑一周看它每天报哪些容器过期。
  • --interval 3600:每小时检查一次。默认是 86400(一天)。
  • --cleanup:更新后删掉旧镜像,省磁盘。

注意 Watchtower 需要挂载 docker.sock(它也是靠 Docker API 干活),所以它同样有「等于 root 权限」的风险。它的容器不要对外暴露端口,这本身就只是个后台任务。

看日志,确认它在报什么

docker logs watchtower --tail 100

你会在日志里看到类似:

level=info msg="Found new image" container=/typecho image=typecho/typecho:latest
level=info msg="Session done" failed=0 updated=0 scanned=12

scanned 是被检查的容器数,updated 因为是 monitor-only 所以一直是 0。观察几天,把那些「频繁变化、更新风险高」的容器(数据库、有挂载配置的)先排除,剩下的再纳入自动更新名单。

精细化控制:只更新你批准的那几个

Watchtower 支持三种圈定范围的方式,我强烈建议用「白名单」而不是「全局更新」。

方式一:只更新指定容器

docker run -d \
  --name watchtower \
  --restart unless-stopped \
  -v /var/run/docker.sock:/var/run/docker.sock \
  containrrr/watchtower \
  --cleanup \
  --interval 3600 \
  traefik caddy uptime-kuma

最后的三个名字是容器名,只有这三个会被更新。这是我推荐的默认姿势——把「无状态、配置全在卷里、更新风险低」的服务放进去。

方式二:用容器标签排除

更优雅的做法是在 compose 里给不想被自动更新的容器打标签:

services:
  db:
    image: mariadb:11
    labels:
      com.centurylinklabs.watchtower.enable: "false"

然后启动 Watchtower 时加上 --label-enable,它就只更新显式标了 enable=true 的容器。这个姿势最符合「默认拒绝」的安全原则。

方式三:按镜像名过滤

--enable-image-name

配合正则,可以只更新某些前缀的镜像。日常用得少,知道有这回事即可。

那数据库到底要不要自动更新

直接说结论:不要。原因有三个:

  1. 大版本升级不可逆。MySQL 8.0 → 8.4、PostgreSQL 15 → 16 都可能改动数据目录格式,镜像重建后新版本可能拒绝启动,或者更糟——悄悄改了存储引擎。
  2. 升级前要备份。Watchtower 不会帮你 mysqldump。一旦新版本起不来,你的数据在一个「新版本读不了」的目录里。
  3. 重建的瞬间有 downtime。数据库重建期间所有依赖它的服务都会报错,如果这时候恰好有写入,可能丢数据。

正确做法:数据库镜像锁定具体版本号(写 mariadb:11.4 而不是 mariadb:latest),升级时人工操作——先备份、再看 changelog、停机窗口内升级。Watchtower 只用来管那些「删了重建无痛」的服务。

五个必踩的坑

坑一:用了 :latest,结果被更新到不兼容的大版本

这是最常见的翻车。很多人图省事写 image: nginx:latest,Watchtower 忠实地把 latest 拉到最新,某天 latest 从 1.25 变 1.27,配置文件语法变了,容器起不来。解决方案:关键服务锁小版本(如 nginx:1.27-alpine),或者干脆锁 digest。

坑二:有状态服务的匿名卷没持久化

如果容器里的数据写进了没有显式挂载的目录,重建后数据就没了。用 docker inspect 看容器的 Mounts,确认关键路径都在命名卷或宿主目录上。

坑三:更新成功但服务没起来,Watchtower 不告警

Watchtower 只管「拉镜像+重建」,不管新容器健康不健康。重建成功它就算成功,哪怕新镜像启动即崩。所以你必须配合一个外部监控(Uptime Kuma、黑盒探测)来兜底。这是很多人对它失望的根本原因——它没有回滚机制。

坑四:私有仓库需要认证

如果镜像是从私有 registry 拉的,Watchtower 需要能读到那台的凭据。挂载 ~/.docker/config.json 进去:-v /root/.docker/config.json:/config.json 并设置 DOCKER_CONFIG,否则每次检查都会 401。

坑五:凌晨更新打断备份任务

默认调度是 24 小时一次,但触发时间取决于容器启动那一刻。如果你半夜 3 点跑备份,而 Watchtower 恰好也在那时重启数据库依赖的服务,两者会打架。用 --schedule(cron 表达式)把它定在明确的低峰时段,比如每天凌晨 5 点。

一个我认为最稳的组合

综合下来,我的生产配置是这样:

services:
  watchtower:
    image: containrrr/watchtower:latest
    restart: unless-stopped
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      WATCHTOWER_CLEANUP: "true"
      WATCHTOWER_LABEL_ENABLE: "true"
      WATCHTOWER_SCHEDULE: "0 0 5 * * *"
      WATCHTOWER_NOTIFICATIONS: shoutrrr
      WATCHTOWER_NOTIFICATION_URL: "smtp://user:pass@smtp.example.com:587/?from=watchtower@example.com&to=me@example.com"

核心思路:默认不更新(label-enable),定点执行(凌晨 5 点),更新后发通知(SMTP/邮件/钉钉)。这样一来,能自动更新的都是你亲手批准过的无状态服务,而且每次更新你都会收到一封邮件,出问题时你最早知道。

小结

Watchtower 不是「装上就一劳永逸」的魔法,它是一个需要你主动划边界的工具。把它当成「自动维护无状态服务的小助手」,而不是「全站自动更新的开关」,它就能实实在在帮你省掉手动 pull + up 的琐碎。记住三条底线:数据库别交出去、关键版本要锁死、更新结果必须靠外部监控兜底。做到这三点,你就能安全地享受「镜像永不过期」的安心感。

Last modification:October 5th, 2026 at 10:26 pm

Leave a Comment