Watchtower 实战:Docker 镜像自动更新

Watchtower 是运维 Docker 容器时最省心的自动更新工具:它能定时轮询镜像仓库,一旦发现你运行的镜像有了新版本,就自动拉取、停止旧容器、用完全相同的启动参数拉起新容器,全程无需人工干预。对于个人服务器、家庭 NAS 或边缘节点上那几十个常驻容器来说,这意味着安全补丁和 bug 修复能在发布当天就被应用,而不是在“哪天有空了再手动 docker compose pull”的拖延里慢慢过期。

一、为什么需要 Watchtower

没有自动更新时,一次典型的升级流程是:登录服务器 → docker compose pull → docker compose up -d → 祈祷配置没变 → 检查日志。容器一多,这套动作就会被无限推迟。更危险的是,镜像里的底层基础镜像(如 nginx:alpine、python:3.12-slim)常年不更新,CVE 补丁永远停在半年前。Watchtower 把“拉取 + 重建 + 重启”变成后台静默动作,把人的注意力留给真正需要判断的版本升级。

二、五分钟跑起来:最小 docker-compose

最干净的部署方式,是让 Watchtower 单独作为一个容器,去监控其它容器。下面这份 compose 文件让 Watchtower 每 30 秒检查一次更新,并在更新后清理掉旧镜像:

version: "3.8"

services:
  watchtower:
    image: containrrr/watchtower:latest
    container_name: watchtower
    restart: unless-stopped
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      - TZ=Asia/Shanghai
      - WATCHTOWER_POLL_INTERVAL=30
      - WATCHTOWER_CLEANUP=true
      - WATCHTOWER_REMOVE_VOLUMES=false
      - WATCHTOWER_INCLUDE_STOPPED=false
    command: --interval 30 --cleanup

关键点只有两个:挂载 /var/run/docker.sock 让 Watchtower 拥有控制 Docker 守护进程的权限;以及把更新节奏交给 --interval(秒)。把这份文件放进你已有的 Docker 容器编排体系 里统一管理即可。

三、它到底是怎么更新的

理解原理能帮你避开大部分“明明有新版本却没更新”的误解:

  • 轮询 registry:按 interval 向镜像仓库发起 manifest 请求,而不是看本地 tag。
  • 比对 digest:比较远程镜像的 SHA256 digest 与当前运行容器的 digest,跟 tag 字符串无关。这是为什么 :latest 也能被识别。
  • 拉取新镜像:digest 不同则 docker pull 新层。
  • 优雅重建:发送停止信号给旧容器,再用原容器的启动命令、环境变量、挂载、网络参数原样创建新容器。

正因为“按原参数重建”,Watchtower 不会帮你改配置——它只换镜像,不动你的 docker run 参数。配置变更仍需你自己改 compose。

四、只盯你想要的容器:标签与排除

默认情况下 Watchtower 会接管宿主机上所有容器,这在生产环境往往太激进。用标签精细控制更稳妥:

# 让某个容器被监控
labels:
  - "com.centurylinklabs.watchtower.enable=true"

# 让某个容器永不被自动更新(如数据库)
labels:
  - "com.centurylinklabs.watchtower.enable=false"

启动时加 --label-enable,Watchtower 就只处理显式打开标签的容器;数据库、有状态中间件这类“动了就出事”的服务,建议一律打上 enable=false。下面这张表总结了常用开关:

环境变量 / 参数作用推荐值
WATCHTOWER_POLL_INTERVAL轮询间隔(秒)30–300
WATCHTOWER_CLEANUP更新后删除旧镜像true
WATCHTOWER_INCLUDE_STOPPED是否更新已停止容器false
WATCHTOWER_ROLLING_RESTART逐个滚动重启多副本时 true
WATCHTOWER_MONITOR_ONLY只报告不更新先用 true 观察

五、别静默更新:接上通知

自动更新最怕“悄悄变了但没人知道”。Watchtower 支持钉钉、飞书、Slack、邮件、Webhook 等多种通知。用 Webhook 最灵活,可以推到你自己的告警总线:

environment:
  - WATCHTOWER_NOTIFICATIONS=shoutrrr
  - WATCHTOWER_NOTIFICATION_URL=generic://hooks.example.com/watchtower?title=Watchtower

新手建议先把 WATCHTOWER_MONITOR_ONLY=true 打开跑一两天,确认哪些容器会被更新、通知能不能收到,再切回真正自动更新,避免一觉醒来服务被某个不兼容的新版本打挂。

六、和 CI/CD、反向代理怎么配合

Watchtower 解决的是“拉取并重建”,不是“构建”。镜像该由 GitHub Actions 这类 CI 流水线 在 push 时构建并推送到仓库,Watchtower 只负责把新镜像落到生产。二者职责清晰:CI 产出,Watchtower 消费。

对于跑在 Watchtower 后面的 Web 服务,前面通常有 Nginx 反向代理。更新瞬间旧容器停止、新容器启动,会有几百毫秒的空窗,Nginx 的 upstream 健康检查会临时摘除节点,一般用户无感知。若你的服务对零中断极其敏感,应改用 blue-green 或滚动发布方案,而非依赖 Watchtower 的硬重启。

七、安全与权限:最小暴露面

Watchtower 挂着 Docker socket,等于拿到了宿主机的 root 等价权限,必须保护好。把它放进只对内网开放的网段,并用 fail2ban 自动封禁暴破 守住 SSH 与面板入口;编辑配置时习惯用 VS Code 远程开发 走加密通道,避免明文把 socket 暴露出去。私有仓库记得挂上 registry 认证:

environment:
  - WATCHTOWER_REPO_USER=youruser
  - WATCHTOWER_REPO_PASS=yourtoken
# 或用 Docker config / 文件挂载更安全地注入

八、常见坑与排查清单

  • digest 没变就不更新:有些镜像构建时没打内容哈希,或你用的 tag 指向了不可变构建,需确认远程确实推送了新 digest。
  • 私有仓库 401:漏配 REPO_USER/REPO_PASS,或用了需要 docker login 的私有 registry,可挂载 ~/.docker/config.json。
  • 更新后端口冲突:旧容器没被彻底删掉,检查是否设了 REMOVE_VOLUMES 或容器名冲突。
  • 资源占用高:interval 设太小会频繁请求 registry,生产建议 ≥60 秒。

九、落地清单

  1. 用 --label-enable 只监控无状态服务,数据库打 enable=false。
  2. 先开 MONITOR_ONLY=true 观察 1–2 天,确认更新范围与通知正常。
  3. 配置钉钉/飞书/Webhook 通知,更新留痕。
  4. 开启 CLEANUP 清理旧镜像,避免磁盘被旧层占满。
  5. 缩小 Docker socket 暴露面,配合 fail2ban 与内网隔离。

十、更新翻车了怎么回滚

自动更新最大的顾虑是“新版本有 bug 怎么办”。因为没有版本号概念,Watchtower 只认 digest,所以回滚要靠你自己在仓库里保留一个可锚定的旧版本。最稳妥的做法是给关键服务同时打一个带日期或 commit 的固定 tag,出问题立刻切回:

# 出事前先给当前镜像打一个保险标签
docker tag myapp:latest myapp:rollback-20261022
docker push myapp:rollback-20261022

# 翻车后,把 compose 里 image 改回保险标签再 up
docker compose up -d

更省心的做法是直接用 digest 锁定:把 image: myapp@sha256:<旧digest> 写进 compose,这样无论 Watchtower 怎么拉新,这个服务都钉在已知良好的版本上,等你在预发环境验证完新版本再放开。

十一、三种更新策略怎么选

Watchtower 只是众多更新方式里最轻量的一种。不同成熟度的基础设施,适合不同策略:

策略适用场景优点风险
Watchtower 自动拉取个人/边缘节点、无状态服务零运维、即时打补丁无版本门禁、可能拉到坏版本
CI 构建 + 手动部署中小团队、可控发布每次发布有记录可回滚依赖人工、易拖延
K8s 滚动更新生产集群、多副本零中断、健康检查护航基础设施重、学习成本高

经验法则是:能接受几分钟空窗的无状态服务,放心交给 Watchtower;有状态、强一致或面向公网的核心链路,老老实实走带审批的发布流程。把二者分层,既享受自动化的省心,又不承担“半夜被坏镜像打挂”的代价。

Watchtower 不是银弹,它最适合“希望容器永远跑在最新稳定镜像上”的常驻服务场景。把它和 CI 构建、反向代理、安全加固编排在一起,你就能用极低的维护成本,让一整屋容器始终保持新鲜。

上一篇 MySQL 连接数满排查实战:根因与解决方案
下一篇 StampedLock 实战:高并发读多写少的乐观读锁方案
最新评论
Docker 网络模式详解:bridge/host 生产选型 - FSData
Docker 网络模式详解:bridge/host 生产选型 - FSData
8月20日
[…] 注意最后那条:把端口绑定到 127.0.0.1,再由宿主机上的 Nginx 反代进来,比直接 -p 80:80 暴露安全得多,也方便统一做
Vue3 + TypeScript 工程化:类型安全的前端架构 - FSData
Vue3 + TypeScript 工程化:类型安全的前端架构 - FSData
8月20日
[…] Pinia 的工程化用法,可以参考我们之前的实战文章 《Vue3 组合式 API + Pinia:状态管理从入门到实战》,那里覆盖了 Sto
2026 年最值得装的 10 个 VS Code 插件(AI/前端/后端/效率全覆盖) - FSData
2026 年最值得装的 10 个 VS Code 插件(AI/前端/后端/效率全覆盖) - FSData
8月19日
[…] 状态,点一下就能起停容器,配合我们讲过的 Docker Compose 编排 […]
Redis 缓存设计:穿透、击穿、雪崩与最佳实践 - FSData
Redis 缓存设计:穿透、击穿、雪崩与最佳实践 - FSData
8月19日
[…] 缓存三大坑的本质都是”缓存失效导致请求穿透到数据库”。记住口诀:穿透用布隆、击穿用锁、雪崩用错峰。配合合理的过期策略
GitHub Actions 实战:从零搭建 CI/CD 流水线 - FSData
GitHub Actions 实战:从零搭建 CI/CD 流水线 - FSData
8月19日
[…] Docker 镜像部署。结合我们之前写的 Docker Compose 一键编排实战,可以在 CI […]