导读:本站Compose篇教过"固定版本号"的稳妥哲学,Docker Registry篇讲过镜像的搬运与治理——但有个日常烦恼一直没解决:自托管应用三天两头出更新,每次都要ssh上去、改tag、pull、up -d,五六个服务轮一遍半小时没了,不更新吧,安全补丁又拖着。Watchtower就是干这个的:它盯着你跑着的容器,发现镜像有新版本就自动拉取、自动重建、自动恢复运行。这篇讲透:Watchtower的适用边界(哪些容器该自动更新、哪些千万不能)、最小配置、通知接入、半自动的notify模式、以及和站内"固定版本号"哲学如何调和。读完这篇,你的小鸡应用栈从此自己保持新鲜。
一、先划红线:哪些容器能托管给Watchtower,哪些不能
自动更新听着美好,但容器世界里"新版"不等于"更好"——major版本跳变、breaking change、镜像仓库被投毒,任何一样都能让你的服务一夜之间挂掉。所以第一课是分类:你的容器分三种。数据库类(MySQL、PostgreSQL、Redis)绝对不能自动更新——数据是有状态的,major升级往往涉及数据迁移,自动化的后果是数据损坏,这类升级必须人工盯着、按官方迁移文档走、升级前先备份(备份篇的规矩一条不能省)。基础设施类(Nginx、frp、WireGuard)谨慎自动——挂了影响面大,但更新频率低、兼容性稳定,可以开自动但通知必须配。纯应用类(Umami、哪吒、Uptime Kuma这类无状态应用)放心自动——数据在卷里、挂了重启就好、更新基本向后兼容,这是Watchtower的主战场。先把你Compose栈里的服务按这三类过一遍,再往下配。
二、Watchtower怎么盯的:原理五分钟讲清
Watchtower本身也是个容器,和你的应用栈跑在同一台机器上(共享/var/run/docker.sock,这让它能看到并操作兄弟容器——权限极大,所以镜像务必用官方源,别来路不明)。它的工作循环很简单:按设定的间隔,把每个运行中容器的镜像名和tag拿去Registry查digest(本站Registry篇讲过"镜像=清单+层",digest就是那份清单的指纹),远端digest和本地不一致说明有新版本,于是pull新镜像、停旧容器、用完全相同的参数(端口、卷、环境变量)创建新容器、清理旧镜像。整个过程应用只中断重启那几秒。理解这个原理有两个实际意义:一是它更新的是"同tag滚动"(1.22的latest或固定minor滚动),不是改tag升级major——所以你在compose里写死nginx:1.27,Watchtower只会拉1.27.x的新patch,不会给你跳到1.28,这个边界对第一节的红线策略是天然保护;二是它逐字复用原容器参数,你手工docker run加过的参数它都记得,但compose栈的改动(比如你改了卷映射)还是要up -d重建一次让它重新"认识"最新参数。
三、最小配置:一条Compose服务搞定
把Watchtower加进你现有的docker-compose.yml(Compose篇的应用栈理念,它就是栈里新的一员):
services:
watchtower:
image: containrrr/watchtower
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /root/.docker/config.json:/config.json:ro
environment:
- WATCHTOWER_CLEANUP=true
- WATCHTOWER_POLL_INTERVAL=86400
restart: unless-stopped
逐行拆解:挂载docker.sock是它的眼睛和手;config.json挂载是给私有仓库准备的——如果你的镜像在自建Registry(Registry篇的成果)需要认证,把宿主机的docker登录凭据只读挂进去,它才能pull;WATCHTOWER_POLL_INTERVAL=86400是检查间隔,单位秒,86400即每天查一次——个人自托管没必要更频繁,更新不是越快越好;WATCHTOWER_CLEANUP=true让它在更新后自动清掉旧镜像层,磁盘不被一轮轮更新撑爆(日志治理篇的"有退出机制"原则,在镜像层同样成立)。
四、通知配置:更新了什么,挂了没有,必须知道
自动更新最怕"静默失败":容器更新失败、或者更新完起不来,你半个月后才发现。所以通知不是可选项,是必选项。Watchtower原生支持多种通知渠道,其中最通用的是shoutrrr协议格式的邮件:
- WATCHTOWER_NOTIFICATIONS=email - WATCHTOWER_NOTIFICATION_EMAIL_FROM=watchtower@example.com - WATCHTOWER_NOTIFICATION_EMAIL_TO=you@example.com - WATCHTOWER_NOTIFICATION_EMAIL_SERVER=mail.example.com - WATCHTOWER_NOTIFICATION_EMAIL_SERVER_PORT=587 - WATCHTOWER_NOTIFICATION_EMAIL_SERVER_USER=watchtower - WATCHTOWER_NOTIFICATION_EMAIL_SERVER_PASSWORD=YourPass
发信服务器直接用Postfix那套(发信篇的成果在此复用),每次更新动作(更新了谁、成功失败)都会发邮件汇报。Telegram用户把notifications换成telegram并配bot token,一条环境变量的事。还有个对排障更友好的参数:WATCHTOWER_LOG_EVENTS=true,把事件日志也喂给docker logs,journalctl或docker logs一查,哪天哪个容器更新了什么一目了然——配合cron篇讲的日志归档思路,更新历史可回溯。
五、半自动模式:通知但不更新,把决定权留给自己
对"谨慎类"容器,Watchtower有个特别合适的姿势:只检查、只发通知"某某有新版本了"、不动手——notify-only模式。更精细的控制靠label:默认WATCHTOWER_LABEL_ENABLE=false时Watchtower忽略一切,只更新打了label的容器:
labels: - com.centurylinklabs.watchtower.enable=true
或者反过来全局自动、个别排除:
labels: - com.centurylinklabs.watchtower.enable=false
两套策略对应两种心态:白名单(只更新我确认过安全的)适合刚起步,黑名单(除数据库外全自动)适合跑稳之后。而notify-only更进一步:只检查、只发通知、绝不动手。这是完美的过渡形态——头一个月开notify-only,观察通知里哪些应用的更新历史稳定可靠,再把它们逐步转成全自动,数据库永远留在notify-only。自动化不是开关,是旋钮,一点一点拧到位。
六、更新失败的善后:回滚与排障
Watchtower更新一个容器失败(新镜像拉不动、新容器起来就退),它不会神奇地回滚——旧镜像如果还在本地,你可以docker compose up -d --force-recreate降回旧tag应急;所以重要应用在compose里保留tag的可回退性很关键(这又回到Compose篇固定版本号的智慧:latest标签的容器更新失败后你可能都不知道该回到哪个版本)。排障三板斧:docker logs watchtower看它自己的日志,更新动作全在里面;docker ps -a看目标容器是不是反复重启(systemd篇讲的Restart策略对容器同样适用,compose里restart: unless-stopped就是容器的"崩了自己站起来");哪吒探针(监控篇)盯着服务端口,更新挂了第一时间有告警。把这三层叠起来,"自动更新"才配得上"自动化"三个字——自动化的前提是失败也能被自动发现。
七、和"固定版本号"哲学的和解:一稳一鲜的双轨制
读到这你可能发现矛盾:Compose篇说"生产环境永远写死具体版本号",这篇又说自动更新,到底听谁的?答案是双轨制,两者本来就不冲突:compose文件里的tag是你允许它滚动的上限——写nginx:1.27意味着"1.27.x内随便更,1.28别碰",这是固定版本的收紧面;Watchtower在这个上限内自动滚动,这是自动化的放松面。收紧面保证major跳变永远需要你人工点头(改tag那一刻就是你review变更的机会),放松面保证安全patch当天就打上。再配一层:每月人工巡检一次docker compose pull的输出和Watchtower的月度更新汇总,确认没有异常的大动作。一稳一鲜,各管一段,这就是个人自托管在"省心"和"可控"之间的甜点位。
结语
Watchtower解决的是自托管玩家的"更新疲劳":十几个容器的手动维护成本,换成一封每日更新报告邮件。配好分类红线(数据库绝不自动)、通知必开、从notify-only起步渐进放权,这套自动化就是安全的。它和站内已有的体系严丝合缝:Registry篇管镜像从哪来,Compose篇管应用怎么跑,日志篇管磁盘不膨胀,监控篇管挂了有人喊,Watchtower管这一切保持新鲜——五块拼图凑齐,你的小鸡就是一个自己会保养的私人数据中心。今晚加进compose栈,明早收第一封更新报告,感觉会很奇妙。


