Moby 内置网络诊断服务器详解:调试 Overlay 与 Swarm 网络问题的完整实战指南
Moby(Docker 上游项目)在 libnetwork 中内置了一个网络诊断服务器(diagnostic server),用于检查 overlay 网络与 Swarm 服务发现数据在“网络数据库”中的实际状态。本篇以 daemon/libnetwork/cmd/diagnostic/README.md 为核心,结合 诊断服务器实现、客户端实现 和 热加载入口,完整讲解如何启用该工具、使用其 RESTful API 与 diagnosticClient 命令行工具排查集群网络故障,以及如何在排查结束后安全关闭它。
一、工具定位与风险边界:它看的是什么数据
该诊断工具自 Docker CE 17.12 引入,专门帮助定位运行在 Linux 主机上的 overlay 网络与 Swarm 服务问题。启用后,诊断服务器会在指定端口监听,对外提供诊断接口。文档明确要求:该工具只应在调试特定问题时临时启动,不应长期运行。
WARNING(原文档警示):该工具会改变 libnetwork API 的内部状态,使用必须谨慎并仔细阅读文档。误用会损坏甚至永久破坏网络配置。
理解它的前提是理解 overlay 驱动的数据模型:网络信息存储在“网络数据库”(networkdb)中,当前包含两类关键信息:
endpoint_table:服务发现(service discovery)信息,即各服务的 endpoint 记录;overlay_peer_table:overlay 转发信息,即各节点间的 peer 记录。
从源码结构看,这两张表的条目都带有 owner(属主节点) 语义:每条记录归属于插入它的节点,并在该节点退出集群前保持持久。这一点直接决定了工具的使用姿势——例如对已加载 daemon 使用 -a 标志会触发 join/leave 网络的操作,副作用是“离开网络”,从而切断该 daemon 的数据路径。诊断客户端 main.go 中对孤儿条目(orphan entry)的检测也正是基于 owner 与当前网络 peer 集合的比对。
工具提供两种形态:
- 纯客户端:
dockereng/network-diagnostic:onlyclient,用于向本地 daemon 的诊断端口发请求; - Docker-in-Docker 版本:
dockereng/network-diagnostic:17.12-dind,让诊断容器自身加入 Swarm,可诊断运行旧版引擎(早于 17.12)的集群。
二、启用诊断服务器:daemon.json 配置 + 免重启热加载
2.1 操作步骤
该工具目前仅支持运行在 Linux 上的 Docker 主机。启用步骤如下:
-
在
/etc/docker/daemon.json中把network-diagnostic-port设置为一个空闲端口:"network-diagnostic-port": <port> -
获取
dockerd进程的 PID(ps aux输出中的第二列,通常是 2~6 位数字):$ ps aux |grep dockerd | grep -v grep -
向该 PID 发送
HUP信号,在不重启 Docker 的前提下热加载配置:kill -HUP <pid-of-dockerd>如果系统使用 systemd,执行
systemctl reload docker即可达到同样效果。
成功后,Docker 主机日志中会出现类似消息:
Starting the diagnostic server listening on <port> for commands
2.2 源码级验证:配置项如何生效
该配置项是 dockerd 的一个(被标记为隐藏的)命令行/JSON 配置项,定义于 daemon/command/config.go:
flags.IntVar(&conf.NetworkDiagnosticPort, "network-diagnostic-port", 0, "TCP port number of the network diagnostic server")
_ = flags.MarkHidden("network-diagnostic-port")
HUP/systemctl reload 触发的热加载逻辑位于 daemon/reload.go 的 reloadNetworkDiagnosticPort:
- 若
network-diagnostic-port未配置或值为 0,则调用netController.StopDiagnostic()确保诊断关闭; - 若配置了有效端口,则调用
netController.StartDiagnostic(conf.NetworkDiagnosticPort)启动诊断服务器。
服务器本体在 daemon/libnetwork/diagnostic/server.go:Server 结构持有 enable 状态与 http.Server,Enable(ip, port) 启动监听并打印日志“Starting network diagnostic server listening on … for commands”(即上文日志消息的来源),Shutdown() 负责优雅停止并打印 “Network diagnostic server shutdown complete”。从 Enable 实现看,服务端设置了 5 分钟的 ReadHeaderTimeout 以缓解 Slowloris 类攻击,且源码注释标明该端口在 reload 时存在不可重配置的已知限制。
三、关闭诊断工具
对参与 Swarm 的每个节点重复执行以下操作:
-
从
/etc/docker/daemon.json中删除network-diagnostic-port键; -
再次获取
dockerd的 PID:$ ps aux |grep dockerd | grep -v grep -
发送
HUP信号热加载:kill -HUP <pid-of-dockerd>
主机日志中会出现:
Disabling the diagnostic server
这对应源码中配置未设置时走 StopDiagnostic() 分支的 reload 逻辑,实现的是调用 http.Server.Shutdown() 的优雅关闭(见 server.go 的 Shutdown 方法)。
四、访问诊断工具的 RESTful API
诊断工具暴露自己的 RESTful API:直接向监听端口发送 HTTP 请求即可。以下示例假设工具监听在 2000 端口(这也是客户端默认端口)。
4.1 获取帮助
$ curl localhost:2000/help
OK
/updateentry
/getentry
/gettable
/leavenetwork
/createentry
/help
/clusterpeers
/ready
/joinnetwork
/deleteentry
/networkpeers
/
/join
/help 会列出当前注册的全部端点(实现见 server.go 的 help handler,遍历 handlers map 输出路径)。/ready 返回 OK,供客户端做就绪探测——diagnosticClient 启动时的第一件事就是请求 http://<ip>:<port>/ready 并校验响应包含 OK。
4.2 加入或退出网络数据库集群
$ curl localhost:2000/join?members=ip1,ip2,...
$ curl localhost:2000/leave?members=ip1,ip2,...
ip1、ip2… 是 Swarm 节点 IP(通常一个即可)。
4.3 加入或离开某个网络
$ curl localhost:2000/joinnetwork?nid=<network id>
$ curl localhost:2000/leavenetwork?nid=<network id>
network id 需在 manager 上通过 docker network ls --no-trunc 获取,且必须是完整长度的标识符。客户端源码中也对这一点做了防御:当查询某网络但 peer 数为 0 时会提示“check the network ID, and verify that is the non truncated version”(见 main.go)。
4.4 列出集群 peer 与网络 peer
$ curl localhost:2000/clusterpeers
列出集群级 peer;列出连接到指定网络的节点:
$ curl localhost:2000/networkpeers?nid=<network id>
4.5 转储数据库表
两张表的含义:
overlay_peer_table:包含所有 overlay 转发信息;endpoint_table:包含所有服务发现信息。
$ curl localhost:2000/gettable?nid=<network id>&tname=<table name>
4.6 操作指定表中的条目
$ curl localhost:2000/<method>?nid=<network id>&tname=<table name>&key=<key>[&value=<value>]
其中 <method> 为 createentry、getentry、updateentry、deleteentry。
注意(原文档强调):表操作具有**节点所有权(node ownership)**语义——条目会保持持久,直到插入它的节点仍在集群中。这正是排查“孤儿条目”的理论依据,也是删除操作不可逆的原因。
4.7 输出格式控制选项
从 server.go 的 ParseHTTPFormOptions 与 types.go 的 HTTPReply 可以看到,所有端点都支持 URL 表单参数控制输出:
- 追加
&json返回 JSON 格式(application/json); - 追加
&json=pretty返回缩进美化的 JSON; - 响应统一为
HTTPResult结构:message字段取值OK/FAIL等,details承载具体内容(如TableObj的size与entries,每个条目含key、value(base64 编码值)、owner,见 types.go)。
diagnosticClient 内部请求 peers 与 table 时正是带上 &json 参数并反序列化 TablePeersResult / TableEndpointsResult(见 main.go 的 fetchNodePeers / fetchTable)。
五、使用 diagnosticClient 命令行工具
CLI 以 preview 形式提供、尚不稳定,命令和选项可能随时变化。可执行文件名为 diagnosticClient,通过独立容器提供:
docker run --net host dockereng/network-diagnostic:onlyclient -v -net <full network id> -t sd
README 给出的标志如下:
| 标志 | 说明 |
|---|---|
| -t | 表名,sd 或 overlay 之一。 |
| -ip | 要查询的 IP 地址,默认 127.0.0.1。 |
| -net | 目标网络 ID。 |
| -port | 目标端口,默认 2000。 |
| -a | join/leave 网络。 |
| -v | 启用 verbose 输出。 |
补充(来自当前源码):现在的 main.go 还实现了 README 未收录的 -r 标志(perform remediation deleting orphan entries),可在发现孤儿条目后交互式确认后通过 deleteentry 接口将其删除;删除前会明确提示“this operation is irreversible”并要求输入 Yes 才执行。
5.1 关于 -a 标志的关键注意事项(原文档 NOTE)
- 默认情况下工具不会尝试 join 网络。这符合“不改变诊断客户端运行时节点状态”的设计意图,因此对运行中的 daemon 执行
diagnosticClient是安全的——它只会转储当前状态; - 相反,在容器化版本中使用
diagnosticClient时必须传-a,否则会取回空结果; - 而对已加载的 daemon 使用
-a会产生副作用:leave network 会切断该 daemon 的数据路径。
源码中对此有硬保护:若未设置 DIND_CLIENT 环境变量却带了 -a,客户端会直接 Fatal 退出并提示移除该标志(见 main.go);Dockerfile.dind 正是通过 ENV DIND_CLIENT=true 解锁该标志,并把 daemon.json(内容为 {"debug": true, "network-diagnostic-port": 2000})拷贝为容器内的 /etc/docker/daemon.json。
5.2 典型用法示例(原文档 Examples)
记得使用完整网络 ID,可用 docker network ls --no-trunc 快速获取。
服务发现与负载均衡:
$ diagnosticClient -t sd -v -net n8a8ie6tb3wr2e260vxj8ncy4 -a
Overlay 网络:
$ diagnosticClient -port 2001 -t overlay -v -net n8a8ie6tb3wr2e260vxj8ncy4 -a
-t sd 对应转储 endpoint_table,-t overlay 对应转储 overlay_peer_table(见 main.go 的 switch 分支)。工具会解码每条记录的 base64 值(sd 表解析为 libnetwork.EndpointRecord,overlay 表解析为 overlay.PeerRecord),并把 owner 不在当前网络 peer 集合中的条目标记为孤儿发出 Warn。
六、容器化版本的完整调试流程
容器化 CLI 基于 17.12 引擎,需要以 privileged 模式运行。
NOTE(原文档强调):表操作具有 ownership 语义,因此在诊断容器处于 Swarm 期间,任何
create entry操作都会保持持久。
流程如下:
-
确保运行诊断客户端的节点不属于 Swarm,若属于则先执行
docker swarm leave -f; -
启动容器:
$ docker container run --name net-diagnostic -d --privileged --network host dockereng/network-diagnostic:17.12-dind -
通过
docker exec -it <container-ID> sh进入容器,启动其中内置的诊断服务器:$ kill -HUP 1(向 PID 1 的 dind 内 dockerd 发 HUP,触发其加载容器内的
daemon.json,从而在 2000 端口启动诊断服务器;该容器镜像定义见 Dockerfile.dind,纯客户端镜像定义见 Dockerfile.client,基于 alpine + curl,ENTRYPOINT即diagnosticClient。) -
将诊断容器加入 Swarm,然后在容器内运行诊断 CLI:
$ ./diagnosticClient <flags>... -
调试结束后,离开 Swarm 并停止容器。
七、使用建议小结
结合原文档警示与源码实现,可归纳出几条实践原则:
- 临时性:诊断端口只应在排查期间打开,排查完立即从
daemon.json移除配置并 reload; - 每节点操作:Swarm 场景下启用/关闭需要对每个参与节点执行;
- 只读优先:默认不加
-a的客户端是纯只读的,可安全地对运行中 daemon 执行;一旦使用写入类端点(createentry/updateentry/deleteentry)或-a、-r,就要意识到 ownership 持久性与删除不可逆这两点; - 完整网络 ID:所有
nid参数都必须使用非截断的完整 ID,否则查询会得到空 peer 列表; - 输出自动化:脚本化采集时优先使用
&json参数,响应结构稳定(message+details),便于程序解析。
相关源码入口:诊断服务器 daemon/libnetwork/diagnostic/server.go、响应类型 daemon/libnetwork/diagnostic/types.go、客户端 daemon/libnetwork/cmd/diagnostic/main.go、热加载逻辑 daemon/reload.go、配置项定义 daemon/command/config.go。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00