多服务器 Docker 管理方案:镜像缓存 + 更新监测 + 统一面板

多服务器 Docker 管理方案:镜像缓存 + 更新监测 + 统一面板

适用场景:多台 VPS + 本地 PVE + KVM 虚拟机,需要统一管理 Docker 容器、缓存镜像、监测更新。


目录

  1. 总览:三个工具解决三个问题
  2. 位置选择:本地 vs 云端
  3. 前提:Tailscale 打通内网
  4. 镜像缓存:docker-registry-proxy
  5. 多机管理:Komodo
  6. 更新监测:WUD
  7. 最终拓扑图

总览:三个工具解决三个问题

痛点工具一句话
每台服务器重复拉镜像,浪费带宽还触发 Docker Hub 限速docker-registry-proxy内网缓存代理,拉一次,全节点共享
不知道哪些容器有新版本,半夜偷偷更新把服务炸了WUD(What’s Up Docker)扫描所有主机,有更新推送到手机,手动决定升不升
多台机器东一个西一个,切来切去管理Komodo一个 Web 面板管所有 Docker 主机,支持 Git 自动部署

三件套全部 Docker 化,自身也跑在 Docker 里,不冲突,可共存。


位置选择:本地 vs 云端

先回答最重要的部署位置问题。

docker-registry-proxy(镜像缓存)

瓶颈是物理距离。 Docker 镜像动辄几百 MB,缓存的意义就是” 离得近”。

  • 放本地 → 本地 PVE / KVM 秒取;荷兰 VPS 走国际链路,慢
  • 放荷兰 VPS → VPS 秒取;本地走国际链路,慢

结论:双边部署,一边一个。 缓存代理几乎不耗 CPU,内存几十 MB,磁盘看你给多少(建议 20-50GB)。每台服务器指向离自己最近的那个 proxy。

WUD(更新监测)

WUD 需要连接 Docker socket 扫描容器。Docker socket 绝不能暴露在公网上(等同于 root 权限外泄)。

结论:部署在本地。 远程 VPS 通过 Tailscale 内网暴露 Docker API 给它。

Komodo(多机管理面板)

Komodo 架构是 Core(Web 面板)+ Periphery Agent(每台被管机器装一个)。Agent 主动连 Core,Core 不需要去连 Agent—— 天然穿越 NAT。

结论:放本地,24 小时开机的机器上。 你的 NUC9(fnOS)就是最佳位置。


前提:Tailscale 打通内网

所有服务间的通信都走 Tailscale 内网,不暴露任何端口到公网。

Terminal window
# 每一台机器执行
curl -fsSL https://tailscale.com/install.sh | sh
tailscale up

装完后每台机器获得 100.x.x.x 的内网 IP,后续所有配置用这个 IP。

如果已有 WireGuard 或其他 VPN,可以直接复用,原理一样。


镜像缓存:docker-registry-proxy

基于 nginx 的 MITM 缓存代理。工作方式:代理拦截 Docker 的 HTTPS 请求,把下载过的 layer / blob 缓存到本地磁盘。第二次拉同一层,直接从磁盘返回。

部署(本地 NUC9 上一份)

Terminal window
mkdir -p /opt/docker-registry-proxy

/opt/docker-registry-proxy/docker-compose.yml

services:
registry-proxy:
image: rpardini/docker-registry-proxy:0.6.5
container_name: docker-proxy
restart: unless-stopped
ports:
- "3128:3128"
environment:
# 开启 manifest 缓存(防 Docker Hub 限速的关键)
ENABLE_MANIFEST_CACHE: "true"
# 磁盘缓存上限
CACHE_MAX_SIZE: "50g"
# 额外要缓存的 registry(Docker Hub 默认包含,无需额外声明)
REGISTRIES: "ghcr.io quay.io registry.k8s.io mcr.microsoft.com"
# Docker Hub 认证 —— 用自己的账号,彻底告别限速
10 collapsed lines
# username 用 Docker ID(不是邮箱)
# password 建议用 Access Token(Docker Hub → Account Settings → Security)
AUTH_REGISTRIES: "auth.docker.io:你的DockerHub用户名:你的AccessToken"
DISABLE_IPV6: "true"
# nginx 超时适当放大,应对大镜像
PROXY_READ_TIMEOUT: "600"
PROXY_SEND_TIMEOUT: "600"
volumes:
- ./cache:/docker_mirror_cache
- ./certs:/ca
Terminal window
cd /opt/docker-registry-proxy && docker compose up -d

部署(荷兰 VPS 上同样一份)

直接 scp -r /opt/docker-registry-proxy 你的VPS:/opt/,端口冲突就改一下映射。

让其他服务器使用代理

在每台机器上编辑 /etc/docker/daemon.json

{
"registry-mirrors": ["https://100.x.x.x:3128"]
}

100.x.x.x 是运行 proxy 那台机器的 Tailscale IP。本地机器指向本地 proxy,荷兰 VPS 指向 VPS 上那个 proxy。

Terminal window
systemctl restart docker

验证:

Terminal window
docker pull alpine
# 第一次正常拉取
# 再拉一次,观察 proxy 日志:docker logs -f docker-proxy
# 应该看到 "HIT" 字样,表示命中缓存

注意事项

  • 证书:proxy 自签 CA,首次连接需要把证书加入信任。重启 docker daemon 时会自动处理。
  • 私有 registry:如果你的项目用到私有 registry(如 Harbor、GitLab Registry),在 AUTH_REGISTRIES 里加上对应凭据即可。
  • 磁盘清理:缓存目录会持续增长,可配合 cron 定期清理(proxy 自身会根据 CACHE_MAX_SIZE 淘汰旧数据,一般不用手动管)。

多机管理:Komodo

Komodo 是近两年快速崛起的开源 Docker 管理面板。相比 Portainer:

  • 完全开源(Apache 2.0),没有商业版功能锁
  • 内置 CI / CD(监听 Git 仓库自动部署)
  • 原生多服务器支持
  • 现代 UI

架构

┌─────────────────┐ WebSocket ┌─────────────────┐
│ Komodo Core │◄──────────────────────►│ Periphery Agent │
│ (Web面板+DB) │ 加密长连接 │ (每台被管机器) │
│ 放 NUC9 │ │ │ 独立部署 │
└─────────────────┘ │ └─────────────────┘
┌──────┴──────┐
│ Periphery │
│ Agent │ ... 每台一个
└─────────────┘

Agent 主动连 Core,所以:

  • Core 不需要公网 IP,NAT 后面也 OK
  • Agent 不需要开放端口
  • 通信全走 Tailscale 内网,安全

部署 Core(NUC9)

Terminal window
mkdir -p /opt/komodo

/opt/komodo/docker-compose.yml

services:
komodo-core:
image: ghcr.io/mbecker20/komodo:latest
container_name: komodo-core
restart: unless-stopped
ports:
- "9120:9120"
environment:
# 替换为你的随机长字符串(两处不要相同)
KOMODO_PASSKEY: "生成一个32位以上的随机字符串"
KOMODO_JWT_SECRET: "生成另一个32位以上的随机字符串"
# 面板标题
KOMODO_TITLE: "我的HomeLab"
KOMODO_DATA_PATH: "/data"
KOMODO_DATABASE_ADDRESS: "mongo:27017"
13 collapsed lines
volumes:
- ./data:/data
# 挂载本机 Docker socket —— 可以顺便管 NUC9 自己的容器
- /var/run/docker.sock:/var/run/docker.sock
depends_on:
- mongo
mongo:
image: mongo:7
container_name: komodo-mongo
restart: unless-stopped
volumes:
- ./mongo-data:/data/db

生成随机密钥:

Terminal window
openssl rand -hex 32
Terminal window
cd /opt/komodo && docker compose up -d

访问 http://NUC9的Tailscale_IP:9120。首次打开会提示创建管理员账号。

部署 Periphery Agent(每台被管机器)

在 Komodo Web UI → ServersAdd Server,会生成一段命令。到对应的机器上执行即可。

本质上是:

Terminal window
docker run -d \
--name komodo-periphery \
--restart unless-stopped \
--network host \
-v /var/run/docker.sock:/var/run/docker.sock \
-e PERIPHERY_PASSKEY="你的KOMODO_PASSKEY" \
-e PERIPHERY_CORE_ADDRESS="http://NUC9的Tailscale_IP:9120" \
ghcr.io/mbecker20/periphery:latest

--network host 是因为 Agent 需要访问 Docker socket 和 Core,host 模式最省事。

Komodo 日常用法

  • Stack 管理:直接编辑 docker-compose.yml,点 Deploy
  • Git 自动部署:配好 Git 仓库 → 推代码自动构建部署
  • 容器终端:面板里点一下直接进容器 Shell
  • 日志查看:实时流,不卡
  • 资源监控:CPU/内存/磁盘图表

更新监测:WUD

WUD 的工作流程:

Watcher 扫描 Docker 主机 → 查 Registry 是否有新版本 → 有更新 → Trigger 通知你

不自动更新。 这是刻意的设计 —— 更新决策应该由人来做,尤其是在生产环境。

部署(NUC9)

Terminal window
mkdir -p /opt/wud

/opt/wud/docker-compose.yml

services:
wud:
image: getwud/wud:latest
container_name: wud
restart: unless-stopped
ports:
- "3000:3000"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./data:/data
environment:
# ===== Watcher 配置 =====
# 1. 扫描本机(NUC9 自己)
WUD_WATCHER_LOCAL_HOST: "/var/run/docker.sock"
WUD_WATCHER_LOCAL_CRON: "0 0 * * * *" # 每小时查一次
38 collapsed lines
# 2. 扫描远程 VPS(通过 Tailscale)
# 远程 Docker 需要开启 TCP API —— 见下方说明
WUD_WATCHER_VPS1_TYPE: "tcp"
WUD_WATCHER_VPS1_HOST: "tcp://100.xxx.xxx.xxx:2375" # VPS 的 Tailscale IP
WUD_WATCHER_VPS1_CRON: "0 0 * * * *"
# 3. 第二台 VPS(如果有)
WUD_WATCHER_VPS2_TYPE: "tcp"
WUD_WATCHER_VPS2_HOST: "tcp://100.xxx.xxx.xxx:2375"
WUD_WATCHER_VPS2_CRON: "0 0 * * * *"
# 4. 本地 PVE(同上)
WUD_WATCHER_PVE_TYPE: "tcp"
WUD_WATCHER_PVE_HOST: "tcp://100.xxx.xxx.xxx:2375"
WUD_WATCHER_PVE_CRON: "0 0 * * * *"
# ===== 通知方式(选一个) =====
# 方案 A:ntfy(推荐)—— 免费,手机上装 ntfy app,订阅同一个频道
WUD_TRIGGER_NTFY_DEFAULT_URL: "https://ntfy.sh/你的私有频道名"
WUD_TRIGGER_NTFY_DEFAULT_PRIORITY: "default"
WUD_TRIGGER_NTFY_DEFAULT_TEMPLATE: |
📦 **{{name}}** 有新版本
当前: `{{current}}` → 最新: `{{latest}}`
主机: {{host}}
# 方案 B:Gotify —— 自建推送服务
# WUD_TRIGGER_GOTIFY_DEFAULT_URL: "http://你的gotify地址"
# WUD_TRIGGER_GOTIFY_DEFAULT_TOKEN: "你的gotify token"
# ===== 排除规则(可选) =====
# 某些容器不希望监测(比如自己 build 的、开发中的)
# WUD_WATCHER_LOCAL_WATCHER.EXCLUDE: "dev-*,test-*"
# ===== Web UI 访问控制 =====
WUD_AUTH_GITEA_CLIENTID: "false"
WUD_UI_BASICAUTH_USER: "admin"
WUD_UI_BASICAUTH_PASSWORD: "改个强密码"
Terminal window
cd /opt/wud && docker compose up -d

访问 http://NUC9的Tailscale_IP:3000,登录后能看到所有主机上哪些容器需要更新。

关于 cron 表达式"0 0 * * * *" 是每小时整点扫描。WUD 用的 cron 是 6 位(多了秒位)。想改成每天凌晨 3 点:"0 0 3 * * *"

远程 Docker 开启 TCP API(安全做法)

关键:只绑定 Tailscale 内网 IP,不绑定公网 IP。

VPS 上编辑 /etc/docker/daemon.json

{
"hosts": [
"unix:///var/run/docker.sock",
"tcp://100.x.x.x:2375"
]
}

注意:100.x.x.x 是这台 VPS 自己的 Tailscale IP。公网上根本无法路由到这个地址。

如果 Docker 是 systemd 管理的,直接改 hosts 可能会和 systemd 的 socket 冲突。需要 override:

Terminal window
mkdir -p /etc/systemd/system/docker.service.d
cat > /etc/systemd/system/docker.service.d/override.conf << 'EOF'
[Service]
ExecStart=
ExecStart=/usr/bin/dockerd
EOF
systemctl daemon-reload
systemctl restart docker

验证:

Terminal window
# 从 NUC9 上测试连通性(用 Tailscale IP)
curl http://100.x.x.x:2375/version
# 返回 JSON 说明成功

通知效果

手机上装 ntfy app,订阅你的频道名。当 WUD 发现有容器可以更新时,你会收到:

📦 nginx 有新版本
当前: 1.25.3 → 最新: 1.27.0
主机: vps-hh

然后你自己判断要不要升、什么时候升。


最终拓扑图

荷兰 VPS ×2 本地局域网
┌──────────────────┐ ┌──────────────────────────┐
│ Periphery Agent │ │ NUC9 (fnOS) - 24h 常开 │
│ docker-registry- │ │ ┌─────────────────────┐ │
│ proxy │ │ │ Komodo Core (9120) │ │
│ Docker TCP API │ │ │ WUD (3000) │ │
└────────┬─────────┘ │ │ docker-registry- │ │
│ │ │ proxy (3128) │ │
│ Tailscale 内网 │ └──────────┬──────────┘ │
├────────────────────────┤ │ │
│ 加密 WireGuard │ ┌──────┴──────┐ │
│ │ │ PVE (KVM) │ │
┌────────┴─────────┐ │ │ Agent+Proxy │ │
│ 第二台 VPS │ │ └─────────────┘ │
│ Agent+Proxy │ └──────────────────────────┘
13 collapsed lines
└──────────────────┘
───── 镜像拉取路径 ─────
本地机器 → 本地 proxy (3128) → 命中缓存,秒取
VPS → VPS proxy (3128) → 命中缓存,秒取
───── 管理通信路径 ─────
Komodo Agent ──WebSocket──▶ Komodo Core (一切走 Tailscale 内网)
WUD Watcher ──TCP:2375──▶ 远程 Docker API
───── 更新通知路径 ─────
WUD 发现更新 → ntfy.sh → 手机推送

快速部署清单

按顺序执行:

步骤操作在哪台机器
1装 Tailscale 并加入网络所有机器
2部署 docker-registry-proxyNUC9 + 荷兰 VPS
3配置 daemon.json 指向 proxy所有机器(各自指向最近的 proxy)
4部署 Komodo Core + MongoNUC9
5部署 Komodo Periphery Agent每台被管机器
6配置远程 Docker TCP API(仅 Tailscale IP)需要被 WUD 扫描的远程机器
7部署 WUDNUC9
8手机上装 ntfy手机

日常运维流程

部署完成后,日常的 Docker 管理就变成:

  1. 部署新服务:打开 Komodo → 写 compose → 点 Deploy
  2. 看日志:Komodo → 点容器 → Logs
  3. 进容器调试:Komodo → 点容器 → Terminal
  4. 收到更新通知:手机弹出 ntfy → 看一眼 changelog → 决定更不更
  5. 拉镜像:自动走 proxy 缓存,不用管
  6. 新增机器:装 Tailscale + Komodo Agent + 配 daemon.json,五分钟加入管理

相关链接

工具地址
docker-registry-proxyhttps://github.com/rpardini/docker-registry-proxy
Komodohttps://komo.do
WUDhttps://getwud.github.io/wud
ntfyhttps://ntfy.sh
Tailscalehttps://tailscale.com