Featured image of post Watchtower 容器自动更新:守护所有容器的升级

Watchtower 容器自动更新:守护所有容器的升级

用 Watchtower 自动监控并升级 Docker 镜像版本,支持选择性更新、定时调度、远程仓库认证、远程主机升级、邮件通知

Docker 镜像升级是日常运维里最烦又最不能不做的事——CVE 漏洞、版本过期、安全审计都要求镜像保持最新。Watchtower 把这件"每月手动 docker pull + restart"的事自动化:它会按周期拉取镜像、检查新版本、自动重启容器

阅读对象:管 5+ 容器实例的运维 / DevOps 覆盖范围:Watchtower 基础用法 + 选择性更新 + 标签筛选 + 远程仓库认证 + 远程主机升级 + 定时调度 + 监控日志

一、为什么需要 Watchtower

手动升级 Docker 容器的痛点:

  • CVE 爆出 → 需要紧急升级 nginx → 10 台机器挨个 pull + restart
  • 测试环境新版本发布 → 手动改 5 个 compose 文件
  • 半夜收到 Prometheus 告警 → 紧急 docker pull redis:7.2-alpine

Watchtower 本质:把"定期升级"做成可调度、可过滤、可观测的 cron 任务。

When to use

  • 测试 / 预发环境镜像更新(强烈推荐
  • 内部工具的镜像(推荐
  • 生产环境(谨慎使用,建议灰度 + 人工 confirm

不适用:需要特殊启动参数 / 一次性初始化(如数据库迁移)的镜像——升级可能丢数据。

二、最小化启动

1
2
3
4
5
6
docker run -d \
  --name watchtower \
  --restart always \
  -v /var/run/docker.sock:/var/run/docker.sock \
  containrrr/watchtower \
  -c

参数说明:

  • -v /var/run/docker.sock:必须挂载,Watchtower 通过 Docker API 操作其他容器
  • -c / --cleanup:升级后自动删除旧镜像——避免磁盘满

启动后 Watchtower 默认 每 5 分钟轮询一次所有运行容器的镜像,看是否有新版本。

三、选择性更新

只升级特定容器,避免 Watchtower 动到不该动的:

1
2
3
4
5
6
7
docker run -d \
  --name watchtower \
  --restart always \
  -v /var/run/docker.sock:/var/run/docker.sock \
  containrrr/watchtower \
  -c \
  nginx redis mysql

nginx redis mysql容器名列表——只升级这几个,其他的不动。

3.1 通过文件列表更新

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# 1. 创建列表
cat > ~/.watchtower.list <<'EOF'
aria2-pro
unlockmusic
mtg
nginx
EOF

# 2. 启动
docker run -d \
  --name watchtower \
  --restart always \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v /root/.watchtower.list:/root/.watchtower.list:ro \
  containrrr/watchtower \
  -c \
  $(cat ~/.watchtower.list)

好处:增删容器名只改文件,不用重启 Watchtower

四、排除特定容器

给容器加 label com.centurylinklabs.watchtower.enable=falseWatchtower 会自动跳过

1
2
3
4
5
6
7
8
docker run -d \
  --name openwrt-mini \
  --restart always \
  --network openwrt \
  --privileged \
  --label com.centurylinklabs.watchtower.enable=false \
  p3terx/openwrt-mini \
  /sbin/init

反向操作watchtower 只升级显式标记的容器,其他一律不管。

启动 Watchtower 时加 --label-enable

1
2
3
4
5
6
docker run -d \
  --name watchtower \
  --restart unless-stopped \
  -v /var/run/docker.sock:/var/run/docker.sock \
  containrrr/watchtower -c \
  --label-enable

启动容器时加 --label com.centurylinklabs.watchtower.enable=true

1
2
3
4
5
docker run -d \
  --name my-critical-app \
  --restart always \
  --label com.centurylinklabs.watchtower.enable=true \
  myapp:latest

五、调度频率

5.1 间隔模式

1
2
3
4
5
6
7
# 每 3600 秒(1 小时)检查一次
docker run -d \
  --name watchtower \
  --restart unless-stopped \
  -v /var/run/docker.sock:/var/run/docker.sock \
  containrrr/watchtower -c \
  --interval 3600

5.2 Cron 模式

1
2
3
4
5
6
7
# 每天凌晨 2 点检查
docker run -d \
  --name watchtower \
  --restart unless-stopped \
  -v /var/run/docker.sock:/var/run/docker.sock \
  containrrr/watchtower -c \
  --schedule "0 0 2 * * *"

注意:Watchtower 的 cron 是 6 位(秒 分 时 日 月 周),比传统 cron 多 1 位秒。

六、私有 Registry 认证

公司用 Harbor 时,Watchtower 升级私有镜像需要认证:

1
2
3
4
5
6
7
8
9
docker run -d \
  --name watchtower \
  --restart always \
  -e REPO_USER=admin \
  -e REPO_PASS={{REDACTED}} \
  -v /var/run/docker.sock:/var/run/docker.sock \
  containrrr/watchtower \
  -c \
  base

环境变量 REPO_USER / REPO_PASS 会被 Watchtower 用于 docker login Harbor。

更安全的做法:用 ~/.docker/config.json 挂载:

1
2
3
4
5
6
7
8
docker run -d \
  --name watchtower \
  --restart always \
  -v /root/.docker/config.json:/config.json \
  -e REPO_USER=$(jq -r .auths."dockerhub.example.com".auth /config.json | base64 -d | cut -d: -f1) \
  -e REPO_PASS=$(jq -r .auths."dockerhub.example.com".auth /config.json | base64 -d | cut -d: -f2) \
  -v /var/run/docker.sock:/var/run/docker.sock \
  containrrr/watchtower

七、远程主机升级

通过 DOCKER_HOST 环境变量,让本机 Watchtower 升级远程机器的容器

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
docker run -d \
  --name watchtower \
  --restart always \
  -e REPO_USER=admin \
  -e REPO_PASS={{REDACTED}} \
  -e DOCKER_HOST="tcp://203.0.113.10:2375" \
  -v /var/run/docker.sock:/var/run/docker.sock \
  containrrr/watchtower \
  -c \
  base

前提:远程机器的 Docker daemon 监听 0.0.0.0:2375

生产强烈推荐 TLStcp://host:2376 + CA 证书),避免 2375 明文端口被扫到。

八、手动触发更新

偶尔需要立即升级某容器(不等调度),用 --run-once

1
2
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
  containrrr/watchtower -cR aria2-pro

参数:

  • -c:清理旧镜像
  • -R / --run-once:跑一次就退出,不进入常驻守护模式

适用场景

  • CVE 紧急升级
  • 调试时反复升级测
  • 一次性升级指定容器

九、监控与日志

9.1 看实时日志

1
docker logs -f watchtower

输出示例:

1
2
3
4
time="2024-06-15T03:00:00Z" level=info msg="Found new redis:7.2-alpine image"
time="2024-06-15T03:00:01Z" level=info msg="Stopping /running redis (abc123)"
time="2024-06-15T03:00:05Z" level=info msg="Creating /running redis (def456)"
time="2024-06-15T03:00:10Z" level=info msg="Updated container redis"

9.2 接入 Loki / ELK

1
2
3
4
5
6
7
8
9
docker run -d \
  --name watchtower \
  --restart always \
  -v /var/run/docker.sock:/var/run/docker.sock \
  --log-driver=loki \
  --log-opt loki-url="http://internal.example.com:3100/loki/api/v1/push" \
  --log-opt max-size="50m" \
  --log-opt max-file="10" \
  containrrr/watchtower

告警规则(Loki + Grafana):

1
2
# 升级失败告警
{job="watchtower"} |= "level=error"

9.3 Prometheus 指标(通过 cAdvisor)

Watchtower 自身不暴露 metrics 端点,但它操作的容器被 cAdvisor 抓取——可以看每个容器重启次数。

十、典型坑位

10.1 Watchtower 自己被升级

症状:Watchtower 升级自己时失败,陷入死循环

修复:给 Watchtower 加 disable label:

1
2
3
4
5
6
7
docker run -d \
  --name watchtower \
  --restart always \
  --label com.centurylinklabs.watchtower.enable=false \
  -v /var/run/docker.sock:/var/run/docker.sock \
  containrrr/watchtower \
  -c

或者用 --label-enable 模式 + 给所有其他容器显式 enable。

10.2 升级后容器启动失败

症状:Watchtower 升级 redis:7.2 → 7.4,但 7.4 改了配置文件路径,容器启动失败

修复:升级前在测试环境验证;或者用 --no-restart 模式只下载不重启:

1
2
3
4
5
docker run -d \
  --name watchtower \
  -v /var/run/docker.sock:/var/run/docker.sock \
  containrrr/watchtower \
  --no-restart

人工 确认升级后的容器能起来,再 docker restart

10.3 数据库容器被升级丢数据

症状:MySQL 8.0.36 → 8.4 自动升级,数据文件结构不兼容,启动报错

修复永远别让 Watchtower 自动升级数据库——给 mysql / postgres / mongodb 容器加 disable label。

10.4 大量容器同时升级

症状:20 个容器同时重启,业务抖动 30 秒

修复:分批升级——用容器名列表分多个 Watchtower:

1
2
3
4
5
6
7
# Watchtower 1:升级 nginx / redis
docker run -d --name wt-frontend -v /var/run/docker.sock:/var/run/docker.sock \
  containrrr/watchtower -c nginx redis --schedule "0 0 2 * * *"

# Watchtower 2:升级 backend
docker run -d --name wt-backend -v /var/run/docker.sock:/var/run/docker.sock \
  containrrr/watchtower -c api worker scheduler --schedule "0 30 2 * * *"

十一、生产级建议

11.1 灰度升级

1
2
3
4
5
6
7
8
# 1. 升级 1 台机器(canary)
DOCKER_HOST=tcp://host-1:2375 watchtower -cR --run-once api

# 2. 监控 5 分钟无异常
# 3. 批量升级其他机器
for host in host-2 host-3 host-4; do
    DOCKER_HOST=tcp://$host:2375 watchtower -cR --run-once api
done

11.2 升级前快照

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# 1. 升级前 commit 当前容器为镜像
docker commit api api:pre-upgrade-backup

# 2. 触发升级
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
  containrrr/watchtower -cR api

# 3. 监控 10 分钟

# 4. 出问题回滚
docker stop api
docker rm api
docker run -d --name api api:pre-upgrade-backup

11.3 通知集成

Slack 通知(用 [shouts](https://github.com/containrrr/shoutrrr) 库,Watchtower 内置支持):

1
2
3
4
5
6
docker run -d \
  --name watchtower \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -e WATCHTOWER_NOTIFICATIONS=shoutrrr \
  -e WATCHTOWER_NOTIFICATION_URL=slack://token@channel \
  containrrr/watchtower

支持的通知方式:Slack / Discord / Telegram / Email / Pushover / Gotify / Microsoft Teams / Mattermost / Smtp。

十二、完整生产配置示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
version: "3.8"
services:
  watchtower:
    image: containrrr/watchtower
    container_name: watchtower
    restart: always
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      - TZ=Asia/Shanghai
      - WATCHTOWER_LABEL_ENABLE=true        # 只升级标记的容器
      - WATCHTOWER_INCLUDE_STOPPED=false
      - WATCHTOWER_CLEANUP=true
      - WATCHTOWER_SCHEDULE=0 0 3 * * *      # 每天凌晨 3 点
      - WATCHTOWER_TIMEOUT=60s
      - REPO_USER=admin
      - REPO_PASS={{REDACTED}}
      - WATCHTOWER_NOTIFICATIONS=shoutrrr
      - WATCHTOWER_NOTIFICATION_URL=slack://{{SLACK_TOKEN}}@{{SLACK_CHANNEL}}
    labels:
      - "com.centurylinklabs.watchtower.enable=false"   # 自身不升级

业务容器(要被自动升级的):

1
2
3
4
5
services:
  nginx:
    image: nginx:1.25-alpine
    labels:
      - "com.centurylinklabs.watchtower.enable=true"

十三、Watchtower 替代方案

工具优势劣势
Watchtower简单、单二进制不支持 K8s
Diundiun只通知不升级,安全首选不自动操作
Renovate / Dependabot适合 K8s manifest 自动 PR需要 GitOps 体系
ArgoCD Image UpdaterK8s 生态完整K8s only

K8s 用户推荐 ArgoCD Image Updater + Renovate 组合——Watchtower 在 K8s 里不是最佳实践。

十四、安全加固清单

  • /var/run/docker.sock 权限最小化:默认是 root 拥有,容器挂载后相当于 root in container
  • 加 label disable:防止 Watchtower 升级自己
  • 数据库容器 disable:避免自动升级导致数据丢失
  • 审计日志:所有 Watchtower 操作都进 Loki,定期回看
  • CVE 优先级:用 Diun + Watchtower 组合,先通知再决定升不升

2024+ 视角补充

本文写于 2024-06,2024-2026 期间容器自动更新工具演进:

  • Watchtower 1.7+(2024):Podman 原生支持(除 Docker 外);OCI 1.1+ 镜像清单HTTP API(查询 / 触发 / 状态);Healthcheck endpoint
  • Diun 4.x(2024-2025):仍是 “只通知不升级” 首选——配合 Watchtower 是稳态组合
  • Renovate / Dependabot 2024+K8s manifest 自动 PR 仍是 GitOps 体系推荐
  • ArgoCD Image Updater 0.13+:K8s 自动化 + GitOps 黄金组合
  • K8s 场景新选择Keel.sh(轻量 Deployment 自动滚动);Pulumi + Kubernetes Operator
  • OrbStack / Docker Desktop 5.x:本地开发环境已经内置自动更新检测

实战建议(2025-2026 视角)

  • Docker 单机 / VM → Watchtower + Diun 仍是首选
  • K8sArgoCD Image Updater + Renovate 黄金组合
  • 企业合规 → Diun 通知 + 人工审核 + 灰度(不要让 Watchtower 自动升级生产数据库)
  • 新趋势AI 辅助 CVE 评估(如 Snyk / Aikido)——自动判断镜像升级是否引入 breaking change

下一步

使用 Hugo 构建
主题 StackJimmy 设计