跳到主要内容

watchtower:让容器自动跟上镜像更新

阅读需 4 分钟

上篇写的 autodok 是「按需触发」派:CI 构建完镜像后 POST 一下,它才去更新容器。这篇写它的对立面——轮询派的代表 watchtower:自己定时去问镜像仓库「有没有新镜像」,有就拉下来原地重启容器,全程不需要 CI 参与。

两种方式没有优劣,只有适不适合,文末给出选型对比。

项目地址​

工作原理一句话:watchtower 挂载 docker.sock,按设定的周期拿着运行中容器的镜像去仓库比对 digest,发现新镜像就拉取,然后用原来的启动配置(卷、端口、环境变量)重建容器。

部署方式​

最简一行跑起来:

docker run -d \
--name watchtower \
-v /var/run/docker.sock:/var/run/docker.sock \
containrrr/watchtower

实际使用推荐 compose,把常用参数写清楚:

services:
watchtower:
image: containrrr/watchtower
container_name: watchtower
restart: always
volumes:
- /var/run/docker.sock:/var/run/docker.sock
command: --interval 3600 --cleanup --rolling-restart

--cleanup 让更新后顺手删掉旧镜像,避免磁盘被旧镜像悄悄吃满;--interval 3600 把默认 24 小时的轮询周期改成 1 小时。

常用配置速查​

参数作用
--interval 3600轮询周期(秒),默认 86400(24 小时)
--schedule "0 0 4 * * *"用 cron 表达式定时(六段式,含秒),与 interval 二选一
--cleanup更新后删除旧镜像
--rolling-restart多副本时逐个重启,避免同时断
--monitor-only只检测不更新,有更新时发通知(生产建议先用这个观察一阵)
--include-stopped / --include-restarting把停止/重启中的容器也纳入管理
--remove-anonymous-volumes更新时连带删除匿名卷(注意数据安全)
--run-once只跑一轮就退出,适合手动执行或定时任务
--label-enable只管理带 com.centurylinklabs.watchtower.enable=true 标签的容器

只更新指定容器​

默认管理所有容器,生产上一般要收窄范围。两种姿势:

命令行点名:直接在命令末尾写容器名,只管理列出的:

docker run -d --name watchtower \
-v /var/run/docker.sock:/var/run/docker.sock \
containrrr/watchtower myapp nginx --interval 3600

标签过滤(推荐,配合 --label-enable):只动显式打标的容器,新增容器默认不受管,最安全:

services:
myapp:
image: registry.example.com/myapp:latest
labels:
- com.centurylinklabs.watchtower.enable=true

反过来,给容器打 com.centurylinklabs.watchtower.enable=false 可以明确排除。

更新通知​

更新动作发生在凌晨四点而你毫不知情,出了问题就是事故。watchtower 内置 shoutrrr 通知,配置一个 URL 即可,支持 Slack、Discord、Gotify、邮件 SMTP、Microsoft Teams、Telegram、ntfy 等几十种渠道,多个地址空格分隔:

environment:
- WATCHTOWER_NOTIFICATION_URL=slack://watchtower@token-a/token-b/token-c gotify://gotify.example.com/token

配合 --notification-title-tag 加标题前缀,多台机器跑多个 watchtower 时能分清通知来自哪台。

HTTP API 模式:也能被 CI 触发​

很多人不知道 watchtower 也有按需触发的一面。开启 HTTP API 模式后,它暴露 POST /v1/update 端点,外部(比如 CI 流水线)可以主动触发更新:

services:
watchtower:
image: containrrr/watchtower
volumes:
- /var/run/docker.sock:/var/run/docker.sock
command: --http-api-update
environment:
- WATCHTOWER_HTTP_API_TOKEN=mytoken
ports:
- 8080:8080
# 触发所有受管容器的更新
curl -H "Authorization: Bearer mytoken" localhost:8080/v1/update

# 只更新指定镜像
curl -H "Authorization: Bearer mytoken" "localhost:8080/v1/update?image=foo/bar,foo/baz"

注意:开了 API 模式后默认关闭周期轮询,想两者并存要加 --http-api-periodic-polls。这个设计思路和 autodok 一致——把触发权交还给流水线。

私有镜像仓库​

拉私有仓库的镜像需要凭据,两种方式:

# 方式一:环境变量
-e REPO_USER=username -e REPO_PASS=password

# 方式二:挂载 docker login 后的 config.json(推荐)
-v $HOME/.docker/config.json:/config.json

方式二有个坑:bind mount 绑定的是 inode,docker login 或编辑器保存文件时是「新建再替换」,inode 变了,容器里的凭据就停留在旧版本。解决办法是挂软链接——先 ln -s ~/.docker/config.json ~/docker-config.json,再挂载软链接,inode 就稳定了。

实践中的几个坑​

  • 生产慎用全自动:数据库、有状态服务不要交给 watchtower 自动更新,镜像一换数据面就重建,滚动更新和备份策略都不是它的职责。这类服务打 enable=false 排除掉
  • 先 monitor-only 观察:上线初用 --monitor-only + 通知跑一两周,确认更新频率和对象符合预期再开自动更新
  • cleanup 是把双刃剑:删旧镜像省磁盘,但出问题时少了「秒回滚旧镜像」这条路。重要服务可以不开 cleanup,改靠定期 docker image prune
  • 生命周期钩子:需要在更新前执行备份/排空流量等操作,可以看官方文档的 lifecycle hooks,给容器配 pre-update/post-update 命令

watchtower vs autodok 选型​

维度watchtower(轮询)autodok(按需)
触发方式自己定期查仓库等 CI POST /update
更新时机有延迟(取决于轮询周期)精确到「这次构建」
CI 依赖无,仓库里有新镜像就动需要流水线里加一步
通知能力内置 shoutrrr 多渠道无(看 CI 自己的通知)
适用场景个人服务、第三方镜像跟进、无 CI 的小项目有完整 CI/CD、要求更新时机精确的环境

两者不互斥:我自己的用法是生产用 autodok 跟着流水线走,个人小服务用 watchtower 开着 label 过滤自动跟进。

小结​

watchtower 的价值在于「零接入成本」:挂个 socket、起个容器,存量服务立刻获得自动更新能力。代价是更新时机的控制权不在你手里。用 label 过滤收窄管理范围、配上通知渠道、重要服务坚决排除,它就是一个省心的守夜人。