Watchtower 完全指南:用 Go 实现 Docker 容器基础镜像自动化更新的实用方案

  • 运维
  • DevOps
  • 云原生
  • 容器运行时

【免费下载链接】watchtower

A process for automating Docker container base image updates.

项目地址: https://gitcode.com/gh_mirrors/wa/watchtower
点击查看 免费下载

Watchtower 是一个用于自动化 Docker 容器基础镜像更新的进程:当你在 Docker Hub 或自建镜像仓库中推送新镜像后,Watchtower 会自动拉取新镜像、优雅地关闭旧容器,并以最初部署时相同的参数重启容器。本文以仓库 README.md 为骨架,结合 docs 目录下的完整文档与 Go 源码实现,系统讲解 Watchtower 的安装部署、工作原理、容器筛选、全部命令行参数与高级运行模式,帮助你在一台 Docker 主机上搭建起一套"推送即更新"的镜像自动更新链路。

一、项目定位与适用场景

Watchtower 的自我定位非常明确(见 README.md):

A process for automating Docker container base image updates.

它是一个自动化 Docker 容器基础镜像更新的进程,设计目标是让"运行中的容器应用保持最新"这件事变得几乎零成本:你只需要把新镜像推送到 Docker Hub 或自己的镜像仓库,Watchtower 就会完成后续的拉取、停机与重启。

典型工作流:一次完整的更新循环

以仓库 docs/introduction.md 中的示例为例,假设你同时运行了 centurylink/wetty-cli 与 containrrr/watchtower 两个容器:

$ docker ps
CONTAINER ID   IMAGE                   STATUS          PORTS                    NAMES
967848166a45   centurylink/wetty-cli   Up 10 minutes   0.0.0.0:8080->3000/tcp   wetty
6cc4d2a9d1a5   containrrr/watchtower   Up 15 minutes                            watchtower

Watchtower 每天会拉取最新的 centurylink/wetty-cli 镜像,并与其启动容器时所用的镜像进行比较。一旦发现镜像发生变化,它就会:

  1. 停止并移除旧的 wetty 容器;
  2. 使用新镜像,以及当初启动容器时所用的同一条 docker run 参数(例如这里的 -p 8080:3000 端口映射)重新创建并启动容器。

这套"记住原容器启动配置、按原配置重建"的机制,正是 Watchtower 的核心价值所在——它保证了更新前后的容器行为完全一致。

官方推荐与不推荐的使用环境

README 中对适用环境给出了明确边界,这也是部署前必须了解的事实:

  • 推荐使用场景:家庭实验室(homelab)、媒体中心、本地开发环境等非关键环境;
  • 不推荐场景:商业或生产环境。官方明确表示 不推荐在商业或生产环境中使用 Watchtower;
  • 生产环境的替代方向:官方建议生产环境应转向 Kubernetes;如果觉得直接上 K8s 步子太大,可以关注 MicroK8s、k3s 这类大幅降低集群运维负担的单机 Kubernetes 发行版。

二、快速开始:60 秒跑起第一个 Watchtower

README 的 Quick Start 给出了最小可用的启动命令:

$ docker run --detach \
    --name watchtower \
    --volume /var/run/docker.sock:/var/run/docker.sock \
    containrrr/watchtower

这条命令做了三件事:

  1. --detach:以守护进程方式在后台运行;
  2. --name watchtower:为容器命名;
  3. --volume /var/run/docker.sock:/var/run/docker.sock:把宿主机的 Docker 套接字挂载进容器——这是 Watchtower 能够工作最关键的一步。

为什么必须挂载 docker.sock?

Watchtower 本质上是 Docker API 的一个客户端:它通过 Docker API 列出容器、拉取镜像、停止和重启容器。挂载 /var/run/docker.sock 之后,容器内的进程才能直接与宿主机的 Docker 守护进程对话(详见 docs/usage-overview.md)。不挂载这个套接字,Watchtower 将无法监控任何容器。

从源码也可以印证这一点:默认的 Docker 连接地址就是 unix:///var/run/docker.sock(见 docs/arguments.md 中 --host 参数的默认值),而程序的入口 cmd/root.go 在启动时通过 container.NewClient 构建 Docker 客户端,并通过 pkg/container/client.go 中的客户端实现完成与守护进程的通信。

三、安装与运行方式详解

Watchtower 本身被打包为 Docker 镜像(containrrr/watchtower),安装即拉取镜像。

基础运行

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

私有镜像仓库认证

如果要从私有 Docker 仓库拉取镜像,有两种认证方式:

方式一:环境变量认证(见 docs/usage-overview.md)

通过 REPO_USER 与 REPO_PASS 两个环境变量传入仓库凭据:

docker run -d \
  --name watchtower \
  -e REPO_USER=username \
  -e REPO_PASS=password \
  -v /var/run/docker.sock:/var/run/docker.sock \
  containrrr/watchtower container_to_watch --debug

注意:如果 Docker Hub 开启了 2FA 双重认证,仅靠用户名密码是不够的。

方式二:挂载 Docker 配置文件(推荐,支持 2FA)

先在宿主机执行 docker login,将凭据保存到 $HOME/.docker/config.json,然后把这个配置文件挂载进 Watchtower 容器(挂载到容器根目录 / 下的 config.json):

docker run -d \
  --name watchtower \
  -v $HOME/.docker/config.json:/config.json \
  -v /var/run/docker.sock:/var/run/docker.sock \
  containrrr/watchtower container_to_watch --debug

⚠️ 关于 config.json 的热更新陷阱(docs/usage-overview.md 有专门说明): 按上述方式挂载 config.json 时,宿主机上的改动通常不会传播到运行中的容器。原因是 Docker 对单个文件的挂载是绑定挂载(bind mount),其机制基于 inode。而大多数应用程序(包括 docker login 和 vim)在保存文件时并不是直接编辑原文件,而是"复制一份再替换原文件",这会生成新的 inode,从而破坏绑定挂载。

解决办法:先为 config.json 创建符号链接,再挂载这个符号链接。符号链接本身的 inode 始终不变,绑定挂载因此保持有效,无论源文件的 inode 如何变化,改动都能同步到容器中。

docker-compose 完整示例

挂载配置文件方式下,还需要在启动被监控镜像时带上仓库 URL 前缀(可省略 https://)。下面是一个完整的 docker-compose.yml:同时启动一个来自 GitHub Registry 私有仓库的容器与负责监控它的 Watchtower,并将轮询间隔从默认的 24 小时调整为 30 秒(示例来自 docs/usage-overview.md):

version: "3"
services:
  cavo:
    image: ghcr.io/<org>/<image>:<tag>
    ports:
      - "443:3443"
      - "80:3080"
  watchtower:
    image: containrrr/watchtower
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - /root/.docker/config.json:/config.json
    command: --interval 30

仓库根目录的 docker-compose.yml 还提供了一个更丰富的开发环境示例:它同时编排了 watchtower、Prometheus、Grafana,以及一对具有 depends-on 依赖关系的 parent/child 容器,并展示了 --interval 10 --http-api-metrics --http-api-token demotoken --debug 等参数的组合用法,可作为多服务场景的参考。

四、容器筛选:让 Watchtower 只更新你想更新的容器

默认情况下,Watchtower 会监控 Docker 守护进程内的全部运行中容器(见 docs/arguments.md)。但实际部署中,往往只希望对部分容器进行自动更新。仓库提供了两层控制手段,详见 docs/container-selection.md:

  • 完全排除(Full Exclude):让某些容器彻底不参与监控;
  • 仅监控(Monitor Only):Watchtower 检查更新、发送通知、调用 pre-check/post-check 钩子,但不执行更新。

通过容器名称指定监控目标

可以在启动命令的末尾追加容器名作为参数,只监控这些容器:

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

以上命令中,Watchtower 只监控名为 nginx 和 redis 的容器,其他容器一律忽略。不指定任何名称时,则监控全部运行中容器。

通过标签控制(核心机制)

Watchtower 使用 com.centurylinklabs.watchtower.enable 标签进行容器级别的开关控制。注意:这个标签要打在业务容器上,而不是 Watchtower 自己身上。

排除特定容器(三种方式等价):

# dockerfile 中
LABEL com.centurylinklabs.watchtower.enable="false"
# docker run 命令行
docker run -d --label=com.centurylinklabs.watchtower.enable=false someimage
# docker-compose
version: "3"
services:
  someimage:
    container_name: someimage
    labels:
      - "com.centurylinklabs.watchtower.enable=false"

白名单模式(只更新打标容器):给 Watchtower 传入 --label-enable(或环境变量 WATCHTOWER_LABEL_ENABLE),同时给想监控的容器打上值为 true 的 enable 标签:

# watchtower 启动参数
docker run -d --label=com.centurylinklabs.watchtower.enable=true someimage
# 或在 docker-compose 中
labels:
  - "com.centurylinklabs.watchtower.enable=true"

标签过滤的判定逻辑

Watchtower 会对每个运行中的容器逐一测试各项筛选条件,所有条件同时满足才会被监控(见 docs/container-selection.md):

  • 如果容器名在监控名单中(--name 参数非空),但其 enable 标签为 false,则不会被监控;
  • 如果容器名不在监控名单中,即使它打了 enable=true 且 Watchtower 开了 --label-enable,也不会被监控。

单容器"仅监控"模式

给某个容器打上 com.centurylinklabs.watchtower.monitor-only=true 标签,效果等同于对该容器单独设置了 WATCHTOWER_MONITOR_ONLY,即只检查更新、不执行更新:

LABEL com.centurylinklabs.watchtower.monitor-only="true"
docker run -d --label=com.centurylinklabs.watchtower.monitor-only=true someimage

五、命令行参数全景:从轮询间隔到优雅停机

Watchtower 的全部参数定义在 docs/arguments.md,它们既可以作为 CLI 参数传入,也可以作为 WATCHTOWER_ 前缀的环境变量传入。下面按功能分组完整列出。

5.1 时间与调度

参数环境变量类型 / 默认值说明
--interval, -iWATCHTOWER_POLL_INTERVALInteger / 86400(24 小时)轮询间隔(秒)。与 --schedule 二选一
--schedule, -sWATCHTOWER_SCHEDULEString / -6 字段 Cron 表达式(比传统 5 字段多一个秒字段),如 --schedule "0 0 4 * * *" 表示每天凌晨 4 点检查
TZTZString / "UTC"日志与 --schedule 使用的时区,如 Europe/Rome;也可通过 -v /etc/localtime:/etc/localtime:ro 挂载宿主机时区文件
--stop-timeoutWATCHTOWER_TIMEOUTDuration / 10s容器被强制停止前的等待超时,如 --stop-timeout 30s

调度与轮询的实际执行在 cmd/root.go 中完成:runUpgradesOnSchedule 使用 robfig/cron 调度器注册任务,并配合一个容量为 1 的 channel 作为互斥锁——同一时刻只允许一次更新运行,若上次更新尚未结束则跳过本轮(源码中会输出 "Skipped another update already running." 的调试日志)。--interval 最终也会被转换为 cron 表达式执行。

5.2 运行模式

参数环境变量类型 / 默认值说明
--run-once, -RWATCHTOWER_RUN_ONCEBoolean / false对容器名单立即执行一次更新尝试后退出
--monitor-onlyWATCHTOWER_MONITOR_ONLYBoolean / false只监控、发通知、调用钩子,不更新容器。注意:由于 Docker API 限制,最新镜像仍会被拉取(HEAD digest 检查可跳过无变化时的拉取)
--no-restartWATCHTOWER_NO_RESTARTBoolean / false更新后不重启容器,适用于由 systemd 等外部系统管理容器启动的场景
--no-pullWATCHTOWER_NO_PULLBoolean / false不拉取新镜像,仅监控本地镜像缓存变化,适合直接在宿主机构建镜像而不推送仓库的场景。也可用 com.centurylinklabs.watchtower.no-pull 标签按容器设置
--no-startup-messageWATCHTOWER_NO_STARTUP_MESSAGEBoolean / false启动时不发送通知消息
--rolling-restartWATCHTOWER_ROLLING_RESTARTBoolean / false一次只重启一个容器(而非全部一起停止再启动),配合生命周期钩子可实现零停机部署
--health-check--返回成功退出码,供 Docker HEALTHCHECK 使用;绝不能放在主容器可执行命令行上
--porcelain, -PWATCHTOWER_PORCELAIN取值 v1 / -以稳定、机器可读的格式向 STDOUT 输出会话结果,等价于 --notification-url logger:// + --notification-log-stdout + --notification-report + --notification-template porcelain.VERSION.summary-no-log

--run-once 是交互式手动更新最常用的模式(见 docs/arguments.md):它会启用调试输出显示所有执行动作,完成后容器退出;配合 --rm 标志容器会被自动删除:

$ docker run --rm \
    -v /var/run/docker.sock:/var/run/docker.sock \
    containrrr/watchtower \
    --run-once \
    nginx redis

上面的命令会立即对 nginx 与 redis 执行一次更新尝试。

5.3 更新行为与清理

参数环境变量类型 / 默认值说明
--cleanupWATCHTOWER_CLEANUPBoolean / false更新并重启容器后删除旧镜像,防止孤立镜像堆积
--remove-volumesWATCHTOWER_REMOVE_VOLUMESBoolean / false更新后移除容器中的匿名卷(命名卷不会被移除)
--include-restartingWATCHTOWER_INCLUDE_RESTARTINGBoolean / false将"重启中"的容器也纳入监控
--include-stopped, -SWATCHTOWER_INCLUDE_STOPPEDBoolean / false将"已创建"和"已退出"的容器也纳入监控
--revive-stoppedWATCHTOWER_REVIVE_STOPPEDBoolean / false镜像更新后启动已停止的容器(仅与 --include-stopped 搭配使用)

5.4 容器筛选

参数环境变量类型 / 默认值说明
--label-enableWATCHTOWER_LABEL_ENABLEBoolean / false只监控打了 com.centurylinklabs.watchtower.enable=true 标签的容器
--disable-containers, -xWATCHTOWER_DISABLE_CONTAINERS逗号或空格分隔的字符串列表 / ""排除指定名称的容器;即使这些容器 enable 标签为 true 也会被排除
--scopeWATCHTOWER_SCOPEString / -只更新 com.centurylinklabs.watchtower.scope 标签值与参数相同的容器,用于支持多实例部署;设为 none 时其他实例会忽略这些带 scope 的容器;省略时更新所有容器(无论 scope)
--label-take-precedenceWATCHTOWER_LABEL_TAKE_PRECEDENCEBoolean / false让标签优先于参数:默认参数优先(如 --monitor-only 会覆盖容器上的 monitor-only=false 标签),开启后则以容器标签为准

5.5 日志与连接

参数环境变量类型 / 默认值说明
--debug, -dWATCHTOWER_DEBUGBoolean / false调试模式,详细日志;是 --log-level debug 的别名,作为参数时不接收值(--debug true 无效)
--traceWATCHTOWER_TRACEBoolean / false极详细日志;是 --log-level trace 的别名。注意:会暴露凭据!
--log-levelWATCHTOWER_LOG_LEVELpanic/fatal/error/warn/info/debug/trace / info写入 STDERR 的最大日志级别(容器内即 docker log 可见内容)
--log-format, -lWATCHTOWER_LOG_FORMATAuto/LogFmt/Pretty/JSON / Auto控制台日志输出格式
--no-colorNO_COLORBoolean / false禁用日志中的 ANSI 颜色转义码
--host, -HDOCKER_HOSTString / unix:///var/run/docker.sockDocker 守护进程套接字;可填 tcp://hostname:port 连接远程 Docker
--api-version, -aDOCKER_API_VERSIONString / 1.24Docker API 版本,最低支持 1.24
--tlsverifyDOCKER_TLS_VERIFYBoolean / false连接 Docker 套接字时使用 TLS 并校验服务器证书
--warn-on-head-failureWATCHTOWER_WARN_ON_HEAD_FAILUREalways/auto/never / autoHEAD 拉取失败时的告警策略;auto 表示仅在已知会处理请求且可能限流拉取的仓库(主要是 docker.io)发出告警

5.6 HTTP API 模式

参数环境变量类型 / 默认值说明
--http-api-updateWATCHTOWER_HTTP_API_UPDATEBoolean / false以 HTTP API 模式运行,仅允许通过 HTTP 请求触发镜像更新
--http-api-tokenWATCHTOWER_HTTP_API_TOKENString / -HTTP API 请求的认证令牌;可引用文件,此时使用文件内容作为值
--http-api-periodic-pollsWATCHTOWER_HTTP_API_PERIODIC_POLLSBoolean / falseHTTP API 模式下仍保持周期轮询(否则 API 模式会阻止周期轮询)
--http-api-metricsWATCHTOWER_HTTP_API_METRICSBoolean / false启用 Prometheus 指标端点

关于 HTTP API 模式的完整用法(端点、请求格式、令牌认证),请参阅仓库文档 docs/http-api-mode.md;指标端点细节见 docs/metrics.md。HTTP API 服务器的初始化与路由注册在 cmd/root.go 的 Run 函数中完成(api.New(apiToken)、更新处理器与指标处理器通过 RegisterFunc / RegisterHandler 挂载)。

5.7 帮助信息与配置文件取密

  • --help:显示所有支持的标志说明。
  • Secrets/Files:部分参数支持"值来自文件",以避免把机密写进配置文件或命令行。以下参数及其对应 WATCHTOWER_ 环境变量均支持此特性:notification-url、notification-email-server-password、notification-slack-hook-url、notification-msteams-hook、notification-gotify-token、http-api-token。

docker-compose 中通过 Docker Secrets 传递令牌的示例(见 docs/arguments.md):

secrets:
  access_token:
    file: access_token

services:
  watchtower:
    secrets:
      - access_token
    environment:
      - WATCHTOWER_HTTP_API_TOKEN=/run/secrets/access_token

六、更新流程的源码级拆解:拉取 → 停止 → 重启

README 只描述了"拉取新镜像、优雅停机、用相同参数重启"这层表象,其底层实现位于 internal/actions/update.go 的 Update 函数,一次完整会话包含以下阶段:

  1. 生命周期钩子前置检查:若启用了 lifecycle hooks(--enable-lifecycle-hooks),先执行 lifecycle.ExecutePreChecks;
  2. 列出候选容器:通过 client.ListContainers(params.Filter) 按筛选条件获取容器列表;
  3. 逐个判定是否过期:对每个容器调用 client.IsContainerStale,判断"是否过期 + 是否需要更新"(stale && !NoRestart && !MonitorOnly),并尝试 VerifyConfiguration() 校验重建容器所需的完整信息——如果镜像信息不完整且开了 trace 日志,会输出容器与镜像的详细诊断信息;
  4. 依赖排序:通过 pkg/sorter/sort.go 的 SortByDependencies 按依赖关系排序,并由 UpdateImplicitRestart 将"链接到待重启容器"的容器也标记为需要重启(这保证了依赖容器的正确更新顺序);
  5. 执行更新:
    • 非滚动模式:先按逆序停止所有待更新容器(stopContainersInReversedOrder),再按依赖排序结果重启(restartContainersInSortedOrder);
    • 滚动模式(--rolling-restart):调用 performRollingRestart,一次只处理一个容器,停止成功后立即重启,实现逐个滚动;
  6. 清理旧镜像:若开启 --cleanup,通过 cleanupImages 删除已更新容器对应的旧镜像;
  7. 生命周期钩子后置检查:lifecycle.ExecutePostChecks。

值得注意的细节是 internal/actions/check.go 中的两重前置检查:

  • CheckForSanity:滚动重启模式下,若发现任何容器依赖其他容器(Links() 非空)会直接报错——滚动重启与容器链接不兼容;
  • CheckForMultipleWatchtowerInstances:启动时检测是否存在多个 Watchtower 实例,若存在(且未设置 scope),会停止并移除除最近启动实例外的其他实例,从而避免多个实例互相打架。

而针对 Watchtower 容器自身的更新,restartStaleContainer 中有专门处理:由于无法立即关闭自身,Watchtower 会先把当前实例重命名(client.RenameContainer(container, util.RandName())),让新实例接管旧名字,实现自我更新。

七、常见问题与使用注意事项

  1. 生产环境请慎重:README 明确不推荐商业/生产环境使用,建议生产场景评估 Kubernetes(或 MicroK8s、k3s 等轻量发行版)。
  2. --monitor-only 与 --no-pull 同时使用可能毫无动作:源码 cmd/root.go 中对此会输出警告日志:"Using WATCHTOWER_NO_PULL and WATCHTOWER_MONITOR_ONLY simultaneously might lead to no action being taken at all"。
  3. --rolling-restart 与全局 monitor-only 不兼容:二者同时设置会直接导致进程退出(log.Fatal)。
  4. 健康检查标志的误用风险:--health-check 只能用于 Docker HEALTHCHECK 指令,放在主进程命令行会导致错误退出(源码中通过 os.Getpid() == 1 检测并拒绝)。
  5. 时区默认 UTC:日志时间与 cron 调度默认使用 UTC,可通过 TZ 环境变量或挂载 /etc/localtime 调整。
  6. 认证信息更新的 inode 陷阱:见第三节中关于 config.json 绑定挂载与符号链接的说明。

八、延伸阅读与源码地图

Watchtower 的完整文档目录位于 docs,按需取用:

关键源码位置:

通过本文,你应该已经掌握了 Watchtower 的完整部署路径:从一条 docker run 快速拉起监控、通过标签与参数精确控制监控范围、按需组合轮询/Cron/单次/HTTP API 等运行模式,并理解了其"拉取—停旧—按原配置启新"的更新内核。对于家庭实验室与本地开发环境,这足以构成一套低成本、可维护的镜像自动更新基础设施。

  • 运维
  • DevOps
  • 云原生
  • 容器运行时

【免费下载链接】watchtower

A process for automating Docker container base image updates.

项目地址: https://gitcode.com/gh_mirrors/wa/watchtower
点击查看 免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值