DryDock 分布式容器更新监控部署实录:一屏管五台机器
事情是这样的。我有 5 台机器跑着 Docker,分布在 NAS、内网几台小主机、还有一台荷兰 VPS。每次更新镜像要 SSH 进去逐个 docker compose pull,漏了哪台全靠运气发现。
调研了一圈镜像更新监控工具,Watchtower 太粗暴(动不动自动重启),Diun 只通知不管理。最后选了 DryDock—— 一个 Controller 管所有 Agent,Web UI 统一查看,支持 23 个镜像注册表,20+ 通知渠道。
环境:fnOS NAS(Controller)、AI 主机(.216)、n8n 工具机(.181)、Bandwagon VPS(云主机)。Frigate NVR(.160)暂缓。
架构
┌─────────────────────────────────────┐│ fnOS (<NAS_IP>) ││ DryDock Controller :24300 ││ 统一面板 · 通知 · 自动更新 ││ agent.env ← 所有 Agent 连接配置 │└──┬──────────┬──────────┬────────────┘ │ │ │ ▼ ▼ ▼ Agent Agent Agent :3000 :3000 :34300 AI主机 n8n Bandwagon .216 .181 云主机 ✅ ✅ ✅通信方式:Controller 主动连 Agent(SSE over HTTP)。Agent 只需要 Docker socket 访问,不暴露给外部 —— 通过 docker-socket-proxy 做一层隔离。
Controller 配置(fnOS)
Controller 跑在 NAS 上,核心是两个容器:DryDock 本体 + docker-socket-proxy(用于监控本机容器)。
services: drydock: image: codeswhat/drydock container_name: drydock restart: unless-stopped env_file: - agent.env # ← Agent 连接独立文件 depends_on: socket-proxy: condition: service_healthy environment: - DD_WATCHER_LOCAL_HOST=socket-proxy - DD_WATCHER_LOCAL_PORT=2375 - DD_AUTH_BASIC_ADMIN_USER=*** - "DD_AUTH_BASIC_ADMIN_HASH=***"35 collapsed lines
ports: - 24300:3000 deploy: resources: limits: memory: 512M cpus: "1.0" logging: driver: json-file options: max-size: "20m" max-file: "3" security_opt: - no-new-privileges:true
socket-proxy: image: tecnativa/docker-socket-proxy container_name: socket-proxy restart: unless-stopped volumes: - /var/run/docker.sock:/var/run/docker.sock environment: - CONTAINERS=1 - IMAGES=1 - EVENTS=1 - SERVICES=1 - POST=1 - NETWORKS=1 - INFO=1 healthcheck: test: wget --spider http://localhost:2375/version || exit 1 interval: 5s timeout: 3s retries: 3 start_period: 5sagent.env 设计
Agent 连接配置不写在 docker-compose.yml 的 environment 里,而是拆到独立的 agent.env:
# === AI 主机 (<AI_HOST_IP>) ===DD_AGENT_AI_HOST=<AI_HOST_IP>DD_AGENT_AI_SECRET=***
# === n8n 工具机 (<N8N_HOST_IP>) ===DD_AGENT_N8N_HOST=<N8N_HOST_IP>DD_AGENT_N8N_SECRET=***
# === Bandwagonhost VPS (<VPS_IP>) ===DD_AGENT_VPSNL2_HOST=<VPS_IP>DD_AGENT_VPSNL2_PORT=34300DD_AGENT_VPSNL2_SECRET=***
DD_AGENT_ALLOW_INSECURE_SECRET=*** # LAN 明文 HTTPAgent 部署模板
每台目标机器上跑两个容器:docker-socket-proxy + drydock-agent(command: --agent)。
services: socket-proxy: image: tecnativa/docker-socket-proxy container_name: drydock-socket-proxy restart: unless-stopped volumes: - /var/run/docker.sock:/var/run/docker.sock environment: - CONTAINERS=1 - IMAGES=1 - EVENTS=1 - SERVICES=1 - POST=1 healthcheck: test: wget --spider http://localhost:2375/version || exit 118 collapsed lines
interval: 5s timeout: 3s retries: 3
drydock-agent: image: codeswhat/drydock command: --agent container_name: drydock-agent restart: unless-stopped depends_on: socket-proxy: condition: service_healthy ports: - "3000:3000" environment: - DD_AGENT_SECRET=*** # 和 Controller agent.env 一致 - DD_WATCHER_LOCAL_HOST=socket-proxy - DD_WATCHER_LOCAL_PORT=2375已部署的 Agent 位置:
| 机器 | 路径 |
|---|---|
| AI 主机 (.216) | /opt/drydock-agent/ |
| n8n (.181) | /opt/drydock-agent/ |
| Bandwagon VPS(云主机) | 用户自建,端口 34300 |
新机器加入流程
- SSH 到目标机器
- 创建
/opt/drydock-agent/docker-compose.yml(复制上方模板) - 确认
DD_AGENT_SECRET与 Controller 的agent.env一致 docker compose up -d- 验证:
curl -s -o /dev/null -w "%{http_code}" http://localhost:3000→ 应返回 401(正常,没有认证头) - 在 Controller 的
agent.env追加对应行(端口非 3000 时加_PORT=xxxx) - Controller 重建:
docker compose up -d - 打开
http://<NAS_IP>:24300,左侧栏应出现新 Agent
通知配置
DryDock 支持 20+ 通知渠道(ntfy、Telegram、Lark、Discord 等)。当前配的是自建邮箱 <mail.your-domain.com>,汇总模式(batch),所有容器更新合成一封邮件。
DD_NOTIFICATION_SMTP_MAIN_HOST=<mail.your-domain.com>DD_NOTIFICATION_SMTP_MAIN_PORT=465DD_NOTIFICATION_SMTP_MAIN_FROM=<your@email.com>DD_NOTIFICATION_SMTP_MAIN_TO=<your@email.com>DD_NOTIFICATION_SMTP_MAIN_USER=<your@email.com>DD_NOTIFICATION_SMTP_MAIN_PASS=***DD_NOTIFICATION_SMTP_MAIN_MODE=batch三种通知模式:
| 模式 | 效果 |
|---|---|
simple | 每个容器一条(刷屏) |
batch | 全部汇总一条 ✅ |
digest | 每天定时摘要 |
踩坑记录
env_file 中的 $ 被 shell 吃掉
YAML compose 文件通过 SSH + heredoc 传到远程时,$argon... 变成了 argon...(丢了一个 $)。SSH + shell 对 $ 做了二次解释。解决:用 Python yaml.dump 写到远程,完全绕过 shell。
Agent 端口不匹配
VPS 上 Agent 映射了 34300:3000,但 Controller 的 agent.env 没指定端口,默认连 3000 → 连接失败。解决:加 DD_AGENT_{name}_PORT=34300。
Agent 返回 401 是正常的
Agent 收到无认证的请求返回 401。Controller 通过 DD_AGENT_{name}_SECRET 认证后正常通信。用 curl 测试看到 401 说明 Agent 在跑,不是挂了。
与 1Panel 的关系
DryDock 和 1Panel 互补,不是替代:
| 功能 | DryDock | 1Panel |
|---|---|---|
| 镜像更新检测 | ✅ 23 个注册表 | ❌ |
| 更新通知推送 | ✅ 20+ 通道 | ❌ |
| 容器操作(启停) | ✅ | ✅ |
| 网站 / SSL 管理 | ❌ | ✅ |
| 数据库管理 | ❌ | ✅ |
| Docker Compose GUI | ❌ | ✅ |
有 1Panel 的机器两者都跑;没 1Panel 的机器 DryDock Agent 就够了。
文件清单
fnOS:/vol1/1000/docker/drydock/├── docker-compose.yml # Controller + socket-proxy├── agent.env # Agent 连接列表└── docker-compose.yml.bak
各 Agent 机器:/opt/drydock-agent/└── docker-compose.yml # 标准模板总结
DryDock 填补了 Watchtower(太自动)和 Diun(只通知)之间的空白。Controller-Agent 架构对于多机器场景很顺手:加一台机器就是复制一份 compose + 加三行 env,5 分钟的事。
目前 4 台已接入,Frigate NVR 暂缓(Dpanel 托管路径要适配)。后续打算把通知切到 ntfy,邮件通知容易被 Gmail 归到垃圾箱。