Ubuntu上怎么用Watchtower管理Docker容器镜像?

文章导读
Watchtower 是一个容器,它监控您正在运行的 Docker containers,并在它们的 registry 中有更新的 image 可用时更新它们。它连接到 Docker socket,以可配置的间隔轮询 image registry,当检测到 digest 更改时,它会拉取新 image,停止运行中的 container,并使用更新的 image 启动一个替换容器。这种自动化对于 h
📋 目录
  1. A 介绍
  2. B 先决条件
  3. C 关键要点
  4. D 了解 Watchtower 的工作原理
  5. E 步骤 1 - 使用 Docker Run 运行 Watchtower
  6. F 步骤 2 - 使用标签监控特定容器
  7. G 步骤 3 - 使用 Docker Compose 运行 Watchtower
  8. H 步骤 4 - 与私有镜像仓库认证
  9. I 步骤 5 - 配置通知 (Email、Slack、Webhook)
  10. J 步骤 6 - 使用 Watchtower HTTP API 进行按需更新
A A

介绍

Watchtower 是一个容器,它监控您正在运行的 Docker containers,并在它们的 registry 中有更新的 image 可用时更新它们。它连接到 Docker socket,以可配置的间隔轮询 image registry,当检测到 digest 更改时,它会拉取新 image,停止运行中的 container,并使用更新的 image 启动一个替换容器。这种自动化对于 homelabs、开发环境以及低关键性工作负载非常有用,在这些场景中,您希望 containers 保持最新而无需手动拉取和重启。

Watchtower 项目于 2025 年 12 月被所有者归档,现在处于只读状态。仓库仍然可用,但 issues 和 discussions 已锁定,最后一个稳定版本是 v1.7.1。社区讨论(包括 r/docker)经常询问 Watchtower 是否已被弃用或停止维护。截至本文撰写,该 image 仍在 Docker Hub 和 GHCR 上可用,许多用户仍在非关键环境中运行它。对于新部署或具有回滚要求的 production pipelines,请考虑使用 Renovate 或 CI/CD 驱动的更新等替代方案,这些将在本教程后面介绍。

当您希望在单个主机或小型集群上实现无手动干预的 image 更新,并带有可选通知和最小配置时,Watchtower 是一个不错的选择。它不太适合需要变更审批、回滚或零停机编排的生产环境;在这些情况下,集成 CI/CD 的方法更合适。

先决条件

要完成本教程,您需要:

  • 一台 Ubuntu 服务器(22.04 或更高版本),按照 Ubuntu 初始服务器设置指南配置,具有具有 sudo 权限的非 root 用户并启用防火墙。
  • 在服务器上安装 Docker,按照 Ubuntu 22.04 上安装和使用 Docker 的步骤 1 和 2 操作。
    • 如果您计划使用自己的 Docker Hub image 测试更新,则需要一个 Docker Hub 账户并熟悉推送 image。您可以按照同一 Docker 教程的步骤 5 到 8 创建自定义 image。
  • 安装 Docker Compose。在当前 Ubuntu 版本中,Docker Compose v2 是默认版本(以 docker compose 调用,不带连字符)。如有需要,请按照 Ubuntu 22.04 上安装和使用 Docker Compose 的步骤 1 操作。
  • 对于步骤 6(Watchtower HTTP API),安装 curl 以便触发按需更新(如果不存在,请运行 sudo apt install curl)。
  • 如果您计划通过 Gmail 设置电子邮件通知,则需要一个启用 2-Step Verification 的 Gmail 账户和应用专用 App Password。Google 已弃用 SMTP 的纯密码认证;App Passwords 仍是支持的方法。

关键要点

  • Watchtower 以 container 的形式运行,它挂载 Docker socket 并以固定间隔轮询 image registries;当远程 digest 与运行中的 image 不同时,它会拉取新 image,停止 container,并使用更新后的 image 启动一个新的 container。
  • 您可以通过将 container 名称作为参数传递给 Watchtower 或使用 labels 来限制哪些 container 被更新(com.centurylinklabs.watchtower.enable=true 配合 --label-enable,或 com.centurylinklabs.watchtower.enable=false 来从 watch-all 实例中排除)。
  • 支持通过 REPO_USERREPO_PASS(Docker Hub、GHCR)或 registry 特定的凭证助手(例如 AWS ECR)进行私有 registry 认证;切勿在提交到版本控制的 Compose 文件中硬编码凭证。
  • 可以通过 email(SMTP)、Slack webhook 或通用 webhook(shoutrrr)发送通知,以便在检测到更新或应用更新时收到通知。
  • Watchtower HTTP API(使用 --http-api-update 启用,并由 WATCHTOWER_HTTP_API_TOKEN 保护)允许按需触发更新;未经认证和防火墙规则的情况下,不要公开暴露 API 端口。
  • 仅监控模式(--monitor-onlyWATCHTOWER_MONITOR_ONLY=true)会检查新 image 并发送通知,而不重启 container;您可以随后手动应用更新,或通过一次性 --run-once 运行来应用。
  • 对于需要回滚、审批或零停机部署的生产管道,优先选择集成 CI/CD 的工具(例如 Renovate)而非 Watchtower;Watchtower 非常适合 homelab、开发环境和低关键性生产环境。

了解 Watchtower 的工作原理

Watchtower 在 Docker container 内运行,通过将 Docker daemon socket(/var/run/docker.sock)挂载到 Watchtower container 中,从而控制宿主机的 containers。它不会在主机上安装除运行 Docker 所需之外的任何软件。Watchtower 需要 Docker socket 挂载,因为它直接与 Docker daemon API 通信,以列出 containers、检查它们的 image 元数据、拉取新 images,并发出停止和启动命令,这些操作与您手动执行 docker pulldocker run 时相同。

更新生命周期是一个轮询循环。在每个周期中,Watchtower 检查其配置监控的 containers(所有运行中的 containers,或仅匹配名称或标签的那些)。对于每个 container,它查询 image registry(Docker Hub、GHCR、ECR 等)以获取正在使用的 image tag 的 digest。然后,它将该 digest 与运行中的 container 创建时所用 image 的 digest 进行比较。(Digest 是一个唯一的 SHA256 哈希值,用于标识确切的 image 构建。如果 image 被重建并再次推送,即使具有相同的 tag,两个 image 也可能具有不同的 digest。)如果它们匹配,则不采取任何行动,Watchtower 会休眠直到下一次轮询。如果它们不同,Watchtower 会拉取新 image、停止运行中的 container,并使用相同的 image 名称和 tag 以及 container 最初启动时相同的参数(包括环境变量、volumes、网络、端口绑定和重启策略)从更新后的 image 启动一个新 container。更新过程中不会丢失任何配置。可选地,Watchtower 可以在成功更新后删除旧 image(--cleanup),以避免积累未使用的 images。有关清理 images 和 containers 的更多信息,请参阅如何移除 Docker images、containers 和 volumes。

您可以控制循环运行的频率。使用基于时间的轮询间隔时,默认值为 86400 秒(24 小时)。使用 --schedule 的 cron 风格调度时,您可以精确定义检查运行的时间(例如,每天凌晨 4:00)。您不能同时使用 --interval--schedule。典型的 Watchtower container 使用大约 10-15 MB 内存。

生命周期的纯文本视图:

  +------------------+
  | Container Running|
  +--------+---------+
           |
           v
  +------------------+
  |  Poll Registry   |
  |  (digest check)  |
  +--------+---------+
           |
           v
  +------------------+
  | Compare Digest   |
  +--------+---------+
           |
     +-----+-----+
     |           |
     v           v
 No Change    Change Detected
     |           |
     v           v
 +-------+   +--------+
 | Sleep |   | Pull   |
 | until |   | then   |
 | next  |   | Stop   |
 | poll  |   | then   |
 +-------+   |Restart |
 +-------+   +--------+

步骤 1 - 使用 Docker Run 运行 Watchtower

你需要至少有一个运行中的 container 供 Watchtower 监控。以下示例从官方 Ubuntu image 创建一个长期运行的 container,以便你稍后观察 Watchtower 更新它。

创建一个持续运行的 container:

docker run -d \
  --name ubuntu-container \
  ubuntu \
  sleep infinity

启动 Watchtower 并指向该 container。为了可重现性,使用固定的 image tag;当前稳定版本是 1.7.1:

docker run -d \
  --name watchtower \
  -v /var/run/docker.sock:/var/run/docker.sock \
  containrrr/watchtower:1.7.1 \
  ubuntu-container
  • -d:以 detached 模式运行。
  • --name watchtower:用于管理的 container 名称。
  • -v /var/run/docker.sock:/var/run/docker.sock:必需,用于让 Watchtower 与 Docker daemon 通信。
  • containrrr/watchtower:1.7.1:固定的 image;如果有更新的 tag,请替换。
  • ubuntu-container:仅监控此 container;省略此参数将监控所有运行中的 container。

如果 image 不存在,Docker 将拉取它。示例输出:

# 输出
Unable to find image 'containrrr/watchtower:1.7.1' locally
1.7.1: Pulling from containrrr/watchtower
...
Status: Downloaded newer image for containrrr/watchtower:1.7.1
container_id

验证两个 container 是否都在运行:

docker ps
# 输出
CONTAINER ID   IMAGE                        COMMAND                  CREATED         STATUS         PORTS      NAMES
watchtower_id   containrrr/watchtower:1.7.1   "/watchtower ubuntu-…"   N seconds ago   Up N seconds   8080/tcp   watchtower
ubuntu_id   ubuntu                      "sleep infinity"         N seconds ago   Up N seconds              ubuntu-container

当你的 container 使用的 tag 有更新的 image 被推送时,Watchtower 将在下一次 poll 时检测到它,拉取 image,停止 ubuntu-container,并使用更新后的 image 启动一个新的 container。默认 poll 间隔为 86400 秒(24 小时),除非你使用 --interval 覆盖它。

通过检查其日志验证 Watchtower 是否正确启动并正在监控你的 container:

docker logs watchtower

在干净启动时,你应该看到类似以下的输出:

# 输出
time="2024-01-15T10:00:00Z" level=info msg="Starting Watchtower and scheduling first run: 2024-01-15 10:00:00 +0000 UTC"
time="2024-01-15T10:00:00Z" level=info msg="Checking all containers (except explicitly disabled)"
time="2024-01-15T10:00:00Z" level=info msg="Found new containrrr/watchtower:1.7.1 image (sha256:...)"

如果你看到 level=error msg="Could not connect to Docker daemon",则 socket 挂载缺失或不正确。停止 container,验证 -v /var/run/docker.sock:/var/run/docker.sock 参数,然后重新启动。

如果你看到 level=info msg="Scheduling first run" 后没有进一步输出,则 Watchtower 正常运行并等待下一次 poll 间隔。只有在 poll 周期运行或检测到更新时才会看到额外的日志行。

注意: Watchtower 在应用更新时会自动重启 container。在生产环境中,在启用自动更新前,请测试重启行为和依赖关系。

停止并移除演示 container:

docker stop watchtower ubuntu-container
docker rm watchtower ubuntu-container

常见错误及其含义

Cannot connect to the Docker daemon

-v /var/run/docker.sock:/var/run/docker.sock 挂载缺失或 socket 路径错误。在主机上使用 ls -la /var/run/docker.sock 验证。如果你的 Docker 安装使用不同的 socket 路径,请相应更新 volume 挂载。

Error response from daemon: manifest unknown

Watchtower 检查的 image tag 在 registry 中不再存在,或 registry 返回 404。使用 docker inspect <container_name> --format '{{.Config.Image}}' 验证 image 名称和 tag。

Error response from daemon: toomanyrequests

Docker Hub 速率限制已被触发(每个 IP 每 6 小时 100 次未认证拉取)。使用 REPO_USERREPO_PASS(在步骤 4 中介绍)对 Watchtower 进行 Docker Hub 认证,将免费账户的限制提高到每 6 小时 200 次,或使用付费 Docker Hub 订阅以获得更高限制。

Container 重启但更新后应用不可达

更新后的 image 可能更改了端口、entrypoint 或环境变量。更新后检查 docker logs <container_name> 中的启动错误。Watchtower 不执行健康检查;如果需要失败时回滚更新,请改用 CI/CD pipeline。

步骤 2 - 使用标签监控特定容器

您可以通过两种方式控制 Watchtower 更新哪些容器:通过将容器名称作为参数传递(如同步骤 1 中所示)或使用标签。基于标签的过滤在您有许多容器且希望无需列出名称即可选择性包含或排除时非常有用。

选择加入:仅更新带有启用标签的容器

为希望 Watchtower 更新的容器添加标签 com.centurylinklabs.watchtower.enable=true。使用 --label-enable 运行 Watchtower,这样它将 监控那些将此标签设置为 true 的容器。

示例:创建两个容器,但仅为一个附加标签:

# Watchtower 将更新的容器(具有 enable=true)
docker run -d \
  --name app-container \
  --label com.centurylinklabs.watchtower.enable=true \
  nginx:alpine

# Watchtower 将忽略的容器(无标签)
docker run -d \
  --name other-container \
  nginx:alpine

使用基于标签的过滤启动 Watchtower(不传递容器名称作为参数):

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

app-container 将被考虑进行更新。确认标签是否设置在正确的容器上:

docker inspect app-container --format '{{json .Config.Labels}}'
# 输出
{"com.centurylinklabs.watchtower.enable":"true"}
docker inspect other-container --format '{{json .Config.Labels}}'
# 输出
{}

选择退出:监控所有容器,除带有禁用标签的容器

如果您 使用 --label-enable,Watchtower 默认会监控所有运行中的容器。要排除特定容器,请在这些容器上设置 com.centurylinklabs.watchtower.enable=false

docker run -d \
  --name no-update-container \
  --label com.centurylinklabs.watchtower.enable=false \
  nginx:alpine

当 Watchtower 在不使用 --label-enable 且不提供容器名称列表的情况下运行时,它将监控所有容器,除那些具有 com.centurylinklabs.watchtower.enable=false 的容器。

以监控所有模式运行 Watchtower,监控除明确排除的所有容器:

docker run -d \
  --name watchtower \
  -v /var/run/docker.sock:/var/run/docker.sock \
  containrrr/watchtower:1.7.1
# 无 --label-enable 标志且无容器名称参数。
# Watchtower 监控所有运行中的容器,除那些标记为 enable=false 的容器。

通过检查启动后的日志确认 Watchtower 忽略了哪些容器:

docker logs watchtower

带有 com.centurylinklabs.watchtower.enable=false 的容器将在每次轮询周期中出现在日志中,被标记为已跳过。

Docker Compose 中标签的示例

选择加入模式:当 Watchtower 使用 --label-enable 时,仅监控带有标签的服务:

services:
  app:
    image: nginx:alpine
    container_name: app-container
    labels:
      - com.centurylinklabs.watchtower.enable=true

  other:
    image: nginx:alpine
    container_name: other-container
    # 无标签 — Watchtower 将忽略此容器。

  watchtower:
    image: containrrr/watchtower:1.7.1
    container_name: watchtower
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    command: --label-enable --interval 300

选择退出模式:监控所有服务,除带有 enable=false 的服务:

services:
  app:
    image: nginx:alpine
    container_name: app-container
    # 无标签 — Watchtower 将更新此容器。

  no-update:
    image: nginx:alpine
    container_name: no-update-container
    labels:
      - com.centurylinklabs.watchtower.enable=false
    # 明确排除更新。

  watchtower:
    image: containrrr/watchtower:1.7.1
    container_name: watchtower
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    command: --interval 300
    # 无 --label-enable 标志:监控所有容器,除那些 enable=false 的容器。

步骤 3 - 使用 Docker Compose 运行 Watchtower

您可以从单个 Compose 文件运行 Watchtower 和您的应用容器。以下示例使用基于标签的 opt-in 模式,并设置 30 秒的轮询间隔用于测试。Docker Compose v2 不需要顶层的 version 键;请省略它。

创建一个项目目录和 Compose 文件:

mkdir -p ~/watchtower
cd ~/watchtower
nano docker-compose.yml

粘贴以下内容(如果使用私有镜像,请将 sammy 替换为您的 Docker Hub 用户名):

services:
  ubuntu:
    image: ubuntu
    container_name: ubuntu-container
    command: sleep infinity
    labels:
      - com.centurylinklabs.watchtower.enable=true

  custom-test:
    image: sammy/ubuntu-nodejs
    container_name: test-container
    command: sleep infinity
    labels:
      - com.centurylinklabs.watchtower.enable=true

  watchtower:
    image: containrrr/watchtower:1.7.1
    container_name: watchtower
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    command: --interval 30 --label-enable --cleanup

启动 stack:

docker compose up -d

验证 Watchtower 已启动并正在监控正确的容器:

docker compose logs watchtower

在成功启动且标签过滤激活的情况下,您将看到类似以下的行:

# 输出
watchtower  | time="..." level=info msg="Starting Watchtower and scheduling first run: ..."
watchtower  | time="..." level=info msg="Checking containers with enable label"
watchtower  | time="..." level=info msg="ubuntu-container (ubuntu): up to date"
watchtower  | time="..." level=info msg="test-container (sammy/ubuntu-nodejs): up to date"

没有 com.centurylinklabs.watchtower.enable=true 标签的容器不会出现在这些日志行中,这确认过滤器正在工作。

在更改 Compose 文件后(例如,添加服务或更改 Watchtower 命令),重新创建容器:

docker compose up -d --force-recreate

仅具有 com.centurylinklabs.watchtower.enable=true 的服务会被监控。--cleanup 标志会在成功更新后删除旧镜像,以限制磁盘使用。有关保持镜像精简的更多信息,请参阅如何优化 Docker 镜像以用于生产环境。

步骤 4 - 与私有镜像仓库认证

Watchtower 可以从私有镜像仓库拉取镜像。您可以通过环境变量或 Docker 配置提供凭证;具体方法取决于镜像仓库。

Docker Hub (私有仓库)

设置 REPO_USERREPO_PASS(Docker Hub 用户名和密码或访问令牌)。在 Compose 中:

services:
  watchtower:
    image: containrrr/watchtower:1.7.1
    container_name: watchtower
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      REPO_USER: your_dockerhub_username
      REPO_PASS: your_dockerhub_password_or_token
    command: --interval 3600 --label-enable

AWS ECR

Watchtower 可以使用 amazon-ecr-credential-helper 向 AWS ECR 进行认证。凭证助手必须安装在主机上并在 Docker 的凭证存储中配置。然后,Watchtower 通过将其挂载到 container 中来使用主机的 Docker 配置。

在主机上安装凭证助手:

sudo apt install amazon-ecr-credential-helper

将助手添加到 Docker 的凭证配置中。创建或编辑 ~/.docker/config.json

{
  "credHelpers": {
    "your_account_id.dkr.ecr.us-east-1.amazonaws.com": "ecr-login"
  }
}

将 Docker 配置挂载到 Watchtower container 中,以便它可以使用凭证助手,并通过环境变量提供 AWS 凭证:

services:
  watchtower:
    image: containrrr/watchtower:1.7.1
    container_name: watchtower
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - $HOME/.docker/config.json:/config/config.json:ro
    environment:
      DOCKER_CONFIG: /config
      AWS_ACCESS_KEY_ID: your_access_key
      AWS_SECRET_ACCESS_KEY: your_secret_key
      AWS_REGION: us-east-1
    command: --interval 3600 --label-enable

注意: IAM 用户或角色至少必须具有 ecr:GetAuthorizationTokenecr:BatchGetImageecr:GetDownloadUrlForLayer 权限。没有这些权限,Watchtower 将记录认证错误并跳过拉取。

GitHub Container Registry (GHCR)

使用 GitHub 用户名和具有 read:packages 权限的 Personal Access Token (PAT) 作为 REPO_USERREPO_PASS

services:
  watchtower:
    image: containrrr/watchtower:1.7.1
    container_name: watchtower
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      REPO_USER: your_github_username
      REPO_PASS: your_github_pat
    command: --interval 3600 --label-enable

警告: 不要在提交到版本控制的 docker-compose.yml 中硬编码凭证。使用 Docker secrets 或列在 .gitignore 中的 .env 文件,并使用 env_file 引用它。

使用 .env 文件和 Compose 的示例:

.env(添加到 .gitignore):

REPO_USER=your_username
REPO_PASS=your_secret

docker-compose.yml

services:
  watchtower:
    image: containrrr/watchtower:1.7.1
    container_name: watchtower
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    env_file:
      - .env
    command: --interval 3600 --label-enable

步骤 5 - 配置通知 (Email、Slack、Webhook)

Watchtower 可以在启动时、发现更新时以及应用更新时发送通知。它使用 shoutrrr 支持多种后端。通过环境变量配置一种或多种通知类型。

Email (Gmail SMTP)

使用 Gmail 并搭配 App Password(Google 已弃用纯密码认证)。将通知类型设置为 email,并设置 SMTP 变量:

services:
  watchtower:
    image: containrrr/watchtower:1.7.1
    container_name: watchtower
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      WATCHTOWER_NOTIFICATIONS: email
      WATCHTOWER_NOTIFICATION_EMAIL_FROM: your_gmail@gmail.com
      WATCHTOWER_NOTIFICATION_EMAIL_TO: recipient@example.com
      WATCHTOWER_NOTIFICATION_EMAIL_SERVER: smtp.gmail.com
      WATCHTOWER_NOTIFICATION_EMAIL_SERVER_PORT: 587
      WATCHTOWER_NOTIFICATION_EMAIL_SERVER_USER: your_gmail@gmail.com
      WATCHTOWER_NOTIFICATION_EMAIL_SERVER_PASSWORD: gmail_app_password
    command: --interval 300 --label-enable

Slack

使用 Slack Incoming Webhook URL:

services:
  watchtower:
    image: containrrr/watchtower:1.7.1
    container_name: watchtower
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      WATCHTOWER_NOTIFICATIONS: slack
      WATCHTOWER_NOTIFICATION_SLACK_HOOK_URL: https://hooks.slack.com/services/YOUR/WEBHOOK/PATH
    command: --interval 300 --label-enable

通用 Webhook (shoutrrr)

使用通用 webhook URL(例如用于 Discord、Telegram 或自定义端点)。格式取决于服务,请参阅 shoutrrr 文档:

services:
  watchtower:
    image: containrrr/watchtower:1.7.1
    container_name: watchtower
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      WATCHTOWER_NOTIFICATIONS: shoutrrr
      WATCHTOWER_NOTIFICATION_URL: generic+https://your-webhook-endpoint.example.com/notify
    command: --interval 300 --label-enable

Watchtower 通知的样子

Watchtower 在每个至少有一个 container 被更新的更新周期中发送一条通知。Slack 消息将类似于:

Watchtower Updates on hostname

Updated containers:
- ubuntu-container (ubuntu:latest): updated from sha256:abc123 to sha256:def456
- test-container (sammy/ubuntu-nodejs:latest): updated from sha256:111aaa to sha256:222bbb

Watchtower v1.7.1

如果在一次轮询周期中没有 container 被更新,则不会发送通知。通知仅在检测到更新并应用(或在仅监控模式下检测到)时触发。如果推送后未收到通知,请检查 docker logs watchtower 中的 SMTP 认证或 webhook 发送相关的错误。

仅监控模式下带通知

要仅收到可用更新的通知而不应用它们,请添加:

environment:
  WATCHTOWER_MONITOR_ONLY: "true"

然后在您选择的时间手动应用更新(例如,使用一次性 docker run ... --run-once 或通过步骤 6 中的 HTTP API)。

步骤 6 - 使用 Watchtower HTTP API 进行按需更新

Watchtower 可以暴露一个 HTTP API,让你按需触发更新周期,而不是等待下一次轮询。这对于 CI/CD 或脚本非常有用。

使用 --http-api-update 启用 API,并设置用于认证的 token:

docker run -d \
  --name watchtower \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -p 8080:8080 \
  -e WATCHTOWER_HTTP_API_UPDATE=true \
  -e WATCHTOWER_HTTP_API_TOKEN=your_secret_token \
  containrrr/watchtower:1.7.1

要在 Compose 文件中启用 HTTP API,请将端口映射和环境变量添加到 Watchtower 服务中:

services:
  watchtower:
    image: containrrr/watchtower:1.7.1
    container_name: watchtower
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    ports:
      - "127.0.0.1:8080:8080"
    environment:
      WATCHTOWER_HTTP_API_UPDATE: "true"
      WATCHTOWER_HTTP_API_TOKEN: your_secret_token
    command: --label-enable

注意: 将端口绑定到 127.0.0.1:8080 而不是 0.0.0.0:8080,可以将 API 限制在仅 localhost 访问,这是在单主机设置中最安全的默认配置。如果需要从其他主机调用 API,请将其置于带有 TLS 的反向代理后面,而不是直接暴露 8080 端口。

使用 curl 触发更新:

curl -H "Authorization: Bearer your_secret_token" \
  http://localhost:8080/v1/update

示例响应:

# 输出
{"updated":["ubuntu-container"],"fresh":[],"failed":[],"skipped":[]}

如果没有容器需要更新:

# 输出
{"updated":[],"fresh":["ubuntu-container"],"failed":[],"skipped":[]}

示例:在推送镜像后从 CI/CD 流水线触发 Watchtower

在 GitHub Actions 工作流中,你可以在推送新镜像后立即触发 Watchtower,而不是等待下一次轮询:

# .github/workflows/deploy.yml (仅相关步骤)
- name: 触发 Watchtower 更新
  run: |
    curl -f -H "Authorization: Bearer ${{ secrets.WATCHTOWER_TOKEN }}" \
      https://your-host/v1/update

WATCHTOWER_TOKEN 存储为 GitHub Actions 的 secret。-f 标志会让 curl 在 HTTP 响应为错误时以非零状态码退出,因此如果触发未到达 Watchtower,该工作流步骤会明显失败。

在正常守护进程模式下,Watchtower 会持续运行并按间隔(或计划)轮询。使用 --http-api-update 时,如果你还设置了 --http-api-periodic-polls,它仍然可以定期运行;否则,API 是触发检查的唯一方式。--run-once 标志不同:它运行一次更新周期后容器就会退出(适用于一次性任务或 cron)。

注意: HTTP API 默认监听 8080 端口。没有认证(token)和防火墙规则的情况下,不要公开暴露此端口;否则任何能访问该端口的人都可以触发更新。

Watchtower 配置参考

常用标志和环境变量:

标志 环境变量 默认值 描述
--interval WATCHTOWER_POLL_INTERVAL 86400 轮询间隔(秒)。不能与 --schedule 一起使用。
--schedule WATCHTOWER_SCHEDULE - Cron 表达式(6 个字段),指定检查时间。不能与 --interval 一起使用。
--cleanup WATCHTOWER_CLEANUP false 更新后删除旧镜像。
--label-enable WATCHTOWER_LABEL_ENABLE false 仅监控带有 com.centurylinklabs.watchtower.enable=true 的容器。
--monitor-only WATCHTOWER_MONITOR_ONLY false 仅检查并通知,不重启容器。
--run-once WATCHTOWER_RUN_ONCE false 运行一次更新周期后退出。
--http-api-update WATCHTOWER_HTTP_API_UPDATE false 启用 HTTP API 以触发更新。
--http-api-periodic-polls WATCHTOWER_HTTP_API_PERIODIC_POLLS false 使用 HTTP API 时,继续按配置的间隔或计划进行轮询。没有此标志时,仅运行 API 触发的检查。
- WATCHTOWER_HTTP_API_TOKEN - HTTP API 认证令牌。
- WATCHTOWER_NOTIFICATIONS - 通知类型:emailslackshoutrrr 等。
- REPO_USER - 镜像仓库认证用户名(例如 Docker Hub、GHCR)。
- REPO_PASS - 镜像仓库认证密码或令牌。

当同时设置标志(例如在 command: 中)和环境变量时,标志优先。

Watchtower 与替代方案对比

工具 更新方式 镜像仓库支持 通知 最适合
Watchtower 轮询主机 Docker,拉取并重启 Docker Hub、ECR、GHCR 等 Email、Slack、webhook (shoutrrr) Homelab、开发、低关键性生产环境
What’s Up Docker (WUD) 轮询镜像仓库,可报告或触发 多个镜像仓库 Webhooks、Slack 等 可见性和报告;可触发更新
Renovate 基于 PR,在仓库中更新 仓库使用的任意镜像仓库 GitHub/GitLab 通知 CI/CD、生产环境、回滚和审批流程
Portainer UI 驱动,手动或定时 Docker 支持的任意镜像仓库 内置和 webhook 偏好 UI 而非 CLI/Compose 的团队

Watchtower

与容器运行在同一主机上,使用 Docker socket,当检测到新镜像 digest 时重启容器。部署简单,非常适合 homelab 和开发环境。它不与 Git 或 CI/CD 集成,因此没有内置的回滚或审批步骤。

What’s Up Docker (WUD)

专注于检测可用更新并通知你(或调用 webhook)。它可以与 Watchtower 或其他 runner 集成来执行实际更新,因此常用于可见性和控制,而不是直接替代。

Renovate

通过打开 pull request(或 merge request)来更新仓库中的镜像引用。合并后,你的 pipeline 构建并部署。这提供了审查、通过 Git 回滚,并适合具有变更控制的生产管道。

Portainer

提供 Web UI 来管理容器和镜像。它可以从 UI 或按计划更新容器。当运维人员偏好 GUI 并已使用 Portainer 进行日常管理时,这是一个很好的选择。

Watchtower 非常适合 homelab、开发和低关键性生产环境,在这些环境中自动重启是可以接受的。对于需要回滚、审批或零停机部署的生产管道,集成 CI/CD 的工具如 Renovate 更合适。

FAQ

问:Docker Watchtower 是否已被弃用?
答: 上游 containrrr/watchtower 仓库于 2025 年 12 月被所有者归档,现为只读状态。该项目不再积极维护。Docker image 仍然可用,许多用户仍在非关键工作负载上运行它。对于新部署或生产关键部署,请考虑使用积极维护的替代方案,这些方案支持回滚和审批工作流。

问:Watchtower Docker 项目发生了什么?
答: 维护者归档了 GitHub 仓库;不再计划新版本发布。最后稳定标签为 v1.7.1。社区讨论(例如在 r/docker 上)反映了其在 homelab 和开发环境中的持续使用,以及对生产环境 forks 或替代方案的兴趣。

问:Watchtower 如何与 Docker 协作?
答: Watchtower 作为一个 container 运行,它挂载了宿主机的 Docker socket。它会定期查询 image registry,获取每个被监控 container 使用的 image tag 的 digest。如果远程 digest 与运行中 container 使用的不同,Watchtower 会拉取新 image,停止 container,并从更新后的 image 启动一个新 container。有关管理 container 的更多信息,请参阅 Docker containers 的工作方式。

问:Watchtower 可以更新来自私有 registry 的 container 吗?
答: 可以。对于 Docker Hub 和 GitHub Container Registry,使用 REPO_USERREPO_PASS。对于 AWS ECR,配置 ECR credential helper 或向 Watchtower container 提供 AWS 凭证。切勿将凭证存储在版本控制中的 Compose 文件中;请使用 secrets 或未提交的 env_file

问:如何阻止 Watchtower 更新特定 container?
答: 如果向 Watchtower 传递 container 名称列表,则仅监控这些 container。要在 Watchtower 监控所有 container 时排除某个 container,请为该 container 添加标签 com.centurylinklabs.watchtower.enable=false。要仅更新选定 container,请使用 --label-enable 并仅为要更新的 container 添加 com.centurylinklabs.watchtower.enable=true

问:在生产环境中运行 Watchtower 安全吗?
答: Watchtower 在应用更新时会自动重启 container,这会导致每个 container 短暂 downtime。它不执行健康检查或回滚。仅在自动重启可接受的低关键性工作负载的生产环境中使用它。对于需要变更控制、回滚或零 downtime 的生产环境,请改用 CI/CD 驱动的方法(例如 Renovate 加上您的 pipeline)。

结论

Watchtower 提供了一种简单有效的方式来自动化 Ubuntu 22.04 上的 Docker container image 更新,支持常见 registry、私有认证和灵活的通知选项。它非常适合 homelab、开发环境以及将最小化维护作为优先级且偶尔重启可接受的工作负载。虽然该项目不再维护,但它继续为许多用户良好运行,只需注意安全和运营权衡。

对于需要零 downtime、变更控制、回滚或与 CI/CD 集成的更大规模或生产部署,请考虑 Renovate 或托管部署 pipeline 等替代方案。无论选择何种方案,都要始终保护您的 secrets 并监控 container,以确保环境保持安全和最新。