在运维自建 Docker 服务器时,很多小伙伴都会使用 Watchtower 来实现容器镜像的自动化更新。然而,近期在查看 Portainer 或 docker ps 容器列表时,突然发现了几个名称极其诡异的容器:
eager_borg(或者各种形如“形容词+人名”的随机组合)
watchtower-old-1a4e98628895
最初以为是服务器遭受了恶意入侵或黑客挂马,但深入分析 Watchtower 的运行日志后,发现这其实是一起典型的 Watchtower 自动更新遇到系统卡顿/超时导致的“半路遗弃”事件。
本文将还原整个故障现场,剖析底层原因,并给出彻底杜绝该问题的终极 docker-compose.yml 配置。
一、 故障现场与日志排查
观察 Watchtower 的运行日志,发现了以下几组关键的 context deadline exceeded(请求超时)错误:
# 1. 尝试连接 Docker 引擎时出现响应超时
level=warning msg="Ping failed during initialization..." error="Get \"http://%2Fvar%2Frun%2Fdocker.sock/_ping\": context deadline exceeded"
# 2. 停止旧容器后,创建/启动新容器遭遇超时卡死
level=info msg="Stopping container" container=tailscale-nginx id=7536f65b7f30
level=warning msg="Updated container state to failed" error="failed to create container: Post \"http://%2Fvar%2Frun%2Fdocker.sock/v1.55/containers/create\": context deadline exceeded" name=tailscale-nginx
level=warning msg="Updated container state to failed" error="failed to create container... context deadline exceeded" name=watchtower二、 底层原理:乱码容器是如何诞生的?
Docker 引擎在正常处理容器更新时是一个原子化链条,但在系统 I/O 爆表、CPU 满载或 Docker Daemon 卡顿时,这个链条会在中途断裂。
1. 为什么会产生 watchtower-old-xxxx 孤儿容器?
Watchtower 自我更新的逻辑非常特殊:
它先把当前的自身容器重命名为 watchtower-old-xxxxx,然后拉起一个新的 Watchtower。
新 Watchtower 确认正常运行后,清理并删除这个旧的 watchtower-old-xxxxx。
如果在第 1 步重命名完成后,服务器出现 API 响应超时,新容器没能及时拉起,旧容器就会以 watchtower-old-xxxxx 的名字永久残留下来。
2. 为什么原本叫 tailscale-nginx 的容器变成了 eager_borg?
标准更新流程为:停止旧容器 → 删除旧容器 → 带上原配置与 –name 参数创建新容器。
当更新过程因超时被中断后,如果后续通过手动或脚本重新拉起该容器,但启动命令中丢失了 –name 自定义名称参数,Docker 引擎就会自动调用内置的“随机命名生成器”,随机分配一个由“形容词 + 人名”组成的组合(如 eager_borg),导致容器名称看起来像被入侵了一样。
三、 临时清理方案
如果你当前容器列表中已经存在这些孤儿或乱名容器,可以在 docker-compose.yml 所在的目录下执行:
# 彻底清理不在当前 YAML 定义中的孤儿容器,并按正确配置重建
docker-compose up -d --remove-orphans
四、 彻底解决:防卡顿、防孤儿的 Watchtower 终极配置
要从源头上杜绝此类问题,我们需要在 Watchtower 的配置中做到三点:
延长超时时间:防止服务器卡顿时 Watchtower 过早放弃。
禁止自我更新:彻底杜绝 watchtower-old-xxx 的产生。
开启自动清理:及时清理更新遗留的废弃镜像与匿名数据卷。
修改后的 docker-compose.yml 如下:
YAML
version: '3'
services:
watchtower:
image: nickfedor/watchtower:latest
container_name: watchtower
restart: unless-stopped
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /etc/localtime:/etc/localtime:ro
environment:
- TZ=Asia/Shanghai
- WATCHTOWER_UPDATE_ON_START=true # 启动即检查更新
- WATCHTOWER_POLL_INTERVAL=86400 # 每 24 小时检查一次
- WATCHTOWER_CLEANUP=true # 自动清理旧镜像
- WATCHTOWER_REMOVE_VOLUMES=true # 自动清理孤儿数据卷
- WATCHTOWER_INCLUDE_STOPPED=false # 忽略已停止的容器
- WATCHTOWER_TIMEOUT=60s # 关键:超时时间延长至 60s(防止 API 超时报错)
labels:
# 核心防线:禁止 Watchtower 自我更新,彻底杜绝产生 watchtower-old-xxx 乱码容器
- "com.centurylinklabs.watchtower.enable=false"
五、 总结
容器列表里出现的“乱码名称”并非黑客入侵,而是自动化工具在遭遇系统性能瓶颈时的边缘异常表现。
通过给 Watchtower 加上 WATCHTOWER_TIMEOUT=60s 缓解 API 压力,并添加 enable=false 标签禁止其自我更新,即可在享受无感自动更新的同时,保持 Docker 容器列表的绝对整洁与稳定。
