📚 DDNS-GO 状态监控完整部署教程(Webhook + Caddy 静态服务版)
本教程旨在通过 Webhook 接收 DDNS-GO 的更新请求,将状态写入 JSON 文件,再利用 Caddy 提供静态文件服务(经 Traefik 反向代理实现 HTTPS),最终由 Homepage 的 Custom API Widget 进行定时轮询展示。整个流程实现了从 IP 变更检测到可视化监控的闭环。
🏗️ 一、整体架构与原理
在开始之前,请先理解本方案的数据流转逻辑,这有助于排查后续可能出现的问题:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| DDNS-GO (IP 变化时触发) │ ▼ POST 请求 (https://webhook.your-domain.com/hooks/ddns-update) Traefik 反向代理 │ ▼ 转发到 Webhook 容器 (9000 端口) Webhook 容器 (adnanh/webhook) │ ▼ 验证签名后,执行脚本,写入 JSON 文件 容器内路径: /deploy/api/ddns/ddns-status.json │ ▼ 映射到宿主机 宿主机路径: ~/docker/caddy/data/www/api/ddns/ddns-status.json │ ▼ 映射到 Caddy 容器 Caddy 容器路径: /opt/www/api/ddns/ddns-status.json │ ▼ Caddy 提供 HTTP 服务 http://api.your-domain.com/ddns/ddns-status.json │ ▼ Traefik 反向代理为 HTTPS https://api.your-domain.com/ddns/ddns-status.json │ ▼ 定时轮询 (60秒) Homepage Custom API Widget │ ▼ 显示在仪表盘 IPv4/IPv6 地址、状态、域名等信息
|
数据流详解:
- **触发层 (DDNS-GO)**:当公网 IP 发生变化时,DDNS-GO 向指定 Webhook 地址发送 POST 请求。
- **接收层 (Webhook)**:容器接收请求,验证签名后执行 Shell 脚本,提取参数并生成
ddns-status.json。
- **存储层 (Caddy)**:利用 Caddy 的静态文件服务功能,将 JSON 文件暴露为 Web 接口。
- **展示层 (Homepage)**:仪表盘通过 Custom API 组件定时请求该 JSON 接口,解析并渲染数据。
🛠️ 二、环境准备与前置条件
在部署前,请确保您的服务器环境已满足以下基础条件:
| 类别 |
必备组件 |
状态要求 |
| 基础环境 |
Docker & Docker Compose |
已安装并可正常运行 |
| 核心服务 |
Traefik |
已部署,且配置了 webhook.your-domain.com 和 api.your-domain.com 的路由规则 |
| 依赖服务 |
DDNS-GO |
已部署,建议使用 host 网络模式以准确获取 IP |
| 展示服务 |
Homepage |
已部署,用于最终可视化展示 |
| 网络配置 |
域名解析 |
webhook.your-domain.com 和 api.your-domain.com 已解析至服务器公网 IP |
| 防火墙 |
端口开放 |
确保 80/TCP 和 443/TCP 端口处于开放状态 |
📂 三、项目目录结构规划
统一的目录结构是维护复杂 Docker 项目的关键。建议在宿主机创建以下目录层级:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| ~/docker/ ├── webhook/ │ ├── docker-compose.yml │ ├── data/ │ │ ├── webhook/ │ │ ├── scripts/ │ │ │ └── ddns/ │ │ │ └── ddns-update.sh │ │ └── logs/ ├── caddy/ │ ├── docker-compose.yml │ └── data/ │ ├── conf/ │ │ └── Caddyfile │ ├── data/ │ └── www/ │ └── api/ │ └── ddns/ │ └── ddns-status.json
|
⚙️ 四、核心组件配置详解
本章节包含所有关键配置文件的编写,请严格按照路径创建并修改。
4.1 Webhook 处理脚本 (ddns-update.sh)
文件路径:~/docker/webhook/data/scripts/ddns/ddns-update.sh
功能说明:该脚本负责接收 DDNS-GO 传来的参数,并将其格式化为 JSON 文件。脚本中包含了参数验证和日志记录功能。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| #!/bin/sh
IPV4_ADDR="$1" IPV4_RESULT="$2" IPV4_DOMAINS="$3" IPV6_ADDR="$4" IPV6_RESULT="$5" IPV6_DOMAINS="$6" TIMESTAMP="$7"
if [ -z "$IPV4_ADDR" ] && [ -z "$IPV6_ADDR" ]; then echo "Error: No IP address provided" >&2 exit 1 fi
echo "ipv4_addr=$IPV4_ADDR" echo "ipv4_result=$IPV4_RESULT" echo "ipv4_domains=$IPV4_DOMAINS" echo "ipv6_addr=$IPV6_ADDR" echo "ipv6_result=$IPV6_RESULT" echo "ipv6_domains=$IPV6_DOMAINS" echo "timestamp=$TIMESTAMP"
cat > /deploy/api/ddns/ddns-status.json << EOF { "ipv4_addr": "$IPV4_ADDR", "ipv4_result": "$IPV4_RESULT", "ipv4_domains": "$IPV4_DOMAINS", "ipv6_addr": "$IPV6_ADDR", "ipv6_result": "$IPV6_RESULT", "ipv6_domains": "$IPV6_DOMAINS", "timestamp": "$TIMESTAMP" } EOF
logger "DDNS updated: ipv4_addr=$IPV4_ADDR"
|
💡 权限设置
脚本创建后,必须赋予执行权限,否则 Webhook 容器无法运行它。
1
| chmod +x ~/docker/webhook/data/scripts/ddns/ddns-update.sh
|
4.2 Webhook 配置文件 (hooks.json)
文件路径:~/docker/webhook/data/webhook/hooks.json
配置要点:
id: 对应访问 URL 的路径后缀。
trigger-rule: 安全机制,通过 Header 中的签名密钥验证请求合法性。
pass-arguments-to-command: 定义了从 HTTP 请求体中提取哪些字段传递给脚本。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| [ { "id": "ddns-update", "execute-command": "/scripts/ddns/ddns-update.sh", "command-working-directory": "/scripts/ddns", "response-message": "DDNS updated.", "pass-arguments-to-command": [ { "source": "payload", "name": "ipv4_addr" }, { "source": "payload", "name": "ipv4_result" }, { "source": "payload", "name": "ipv4_domains" }, { "source": "payload", "name": "ipv6_addr" }, { "source": "payload", "name": "ipv6_result" }, { "source": "payload", "name": "ipv6_domains" }, { "source": "payload", "name": "timestamp" } ], "trigger-rule": { "match": { "type": "value", "value": "your-secret-key", "parameter": { "source": "header", "name": "X-DDNS-Signature" } } } } ]
|
🔐 密钥生成建议
请在终端执行 openssl rand -base64 32 生成一个高强度的随机密钥,并替换配置中的 your-secret-key。该密钥需同时配置在 DDNS-GO 的 Webhook 设置中。
4.3 Caddy 配置文件 (Caddyfile)
文件路径:~/docker/caddy/data/conf/Caddyfile
原理说明:Caddy 在容器内监听 80 端口提供 HTTP 服务,实际对外由 Traefik 提供 HTTPS 加密。此配置指定了静态文件的根目录。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
http://api.your-domain.com:80 { root * /opt/www/api
log { output file /var/log/caddy/api_access.log { roll_size 10mb roll_keep 5 } format console } }
|
4.4 Docker Compose 编排
1. Webhook 服务编排
文件路径:~/docker/webhook/docker-compose.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| version: '3' services: webhook: image: my-webhook:alpine container_name: webhook volumes: - ./data/webhook:/etc/webhook - ./data/scripts:/scripts:rw - ../caddy/data/www/api/ddns:/deploy/api/ddns command: -hooks /etc/webhook/hooks.json -port 9000 -verbose restart: unless-stopped networks: - dual-stack-bridge labels: - "traefik.enable=true" - "traefik.http.routers.webhook.rule=Host(`webhook.your-domain.com`)" - "traefik.http.routers.webhook.tls=true" - "traefik.http.routers.webhook.tls.certresolver=myresolver" - "traefik.http.services.webhook.loadbalancer.server.port=9000"
networks: dual-stack-bridge: external: true
|
2. Caddy 服务编排
文件路径:~/docker/caddy/docker-compose.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| version: '3' services: caddy: image: caddy:alpine container_name: caddy ports: - "80:80" - "443:443" volumes: - ./data/www/api:/opt/www/api - ./data/data:/data - ./data/conf/Caddyfile:/etc/caddy/Caddyfile:ro restart: unless-stopped networks: - dual-stack-bridge
networks: dual-stack-bridge: external: true
|
🖥️ 五、客户端与仪表盘配置
5.1 DDNS-GO 端配置
- 登录 DDNS-GO 管理界面(默认端口
9876)。
- 找到 Webhook 配置模块。
- URL:
https://webhook.your-domain.com/hooks/ddns-update
- 请求头:
X-DDNS-Signature: your-secret-key (需与 hooks.json 中一致)
- 请求体 (JSON): 复制以下内容,注意使用 DDNS-GO 的模板变量:
1 2 3 4 5 6 7 8 9
| { "ipv4_addr": "#{ipv4Addr}", "ipv4_result": "#{ipv4Result}", "ipv4_domains": "#{ipv4Domains}", "ipv6_addr": "#{ipv6Addr}", "ipv6_result": "#{ipv6Result}", "ipv6_domains": "#{ipv6Domains}", "timestamp": "#{timestamp}" }
|
5.2 Homepage 仪表盘配置
在 services.yaml 中添加以下卡片配置,即可在首页看到实时状态。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| - 动态DNS监控: - DDNS-GO 状态: icon: /icons/ddns-go.png href: http://your-ddns-go-ip:9876/ description: "公网 IP 变更实时监控" siteMonitor: http://your-ddns-go-ip:9876/ widget: type: customapi url: https://api.your-domain.com/ddns/ddns-status.json refreshInterval: 60000 mappings: - field: ipv4_addr label: IPv4 地址 - field: ipv4_result label: IPv4 状态 - field: ipv4_domains label: IPv4 域名 - field: ipv6_addr label: IPv6 地址 - field: ipv6_result label: IPv6 状态 - field: ipv6_domains label: IPv6 域名
|
🛡️ 六、安全建议与故障排查
安全加固
- 密钥管理:定期更换 Webhook 的签名密钥,防止恶意伪造请求。
- 网络隔离:在防火墙上限制
webhook.your-domain.com 的访问源,仅允许 DDNS-GO 所在 IP 访问。
- 只读挂载:Caddyfile 配置文件建议以只读(
:ro)方式挂载进容器。
常见问题排查表
| 故障现象 |
可能原因 |
解决方案 |
| Webhook 返回 401/403 |
签名密钥不匹配 |
核对 hooks.json 中的 value 与 DDNS-GO 请求头中的密钥是否一致 |
| JSON 文件未生成 |
路径映射错误 |
检查 Webhook 容器内的 /deploy/api/ddns 是否正确映射到 Caddy 的静态目录 |
| Homepage 显示空白 |
跨域或 URL 错误 |
浏览器直接访问 https://api.your-domain.com/ddns/ddns-status.json 查看是否能获取数据 |
| Caddy 404 错误 |
文件权限 |
确认宿主机目录权限为 755,文件可被 Caddy 进程读取 |
| 无法触发更新 |
网络连通性 |
确保服务器能解析 webhook.your-domain.com 且能访问 Traefik 端口 |
🎉 七、总结
通过以上步骤,您已成功构建了一套高可用的 DDNS 状态监控系统。该方案利用 Webhook 实现了事件驱动,利用 Caddy 实现了极简的静态 API 服务,最终通过 Homepage 实现了可视化展示。整个流程不仅解决了 DDNS 状态不可视的问题,也为后续扩展其他服务监控提供了标准范例。