Moby 内置网络诊断服务器详解:调试 Overlay 与 Swarm 网络问题的完整实战指南
2026/9/5 22:49:40 网站建设 项目流程

Moby 内置网络诊断服务器详解:调试 Overlay 与 Swarm 网络问题的完整实战指南

【免费下载链接】mobyThe Moby Project - a collaborative project for the container ecosystem to assemble container-based systems项目地址: https://gitcode.com/GitHub_Trending/mo/moby

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 集合的比对。

工具提供两种形态:

  1. 纯客户端dockereng/network-diagnostic:onlyclient,用于向本地 daemon 的诊断端口发请求;
  2. Docker-in-Docker 版本dockereng/network-diagnostic:17.12-dind,让诊断容器自身加入 Swarm,可诊断运行旧版引擎(早于 17.12)的集群。

二、启用诊断服务器:daemon.json 配置 + 免重启热加载

2.1 操作步骤

该工具目前仅支持运行在 Linux 上的 Docker 主机。启用步骤如下:

  1. /etc/docker/daemon.json中把network-diagnostic-port设置为一个空闲端口:

    "network-diagnostic-port": <port>
  2. 获取dockerd进程的 PID(ps aux输出中的第二列,通常是 2~6 位数字):

    $ ps aux |grep dockerd | grep -v grep
  3. 向该 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.ServerEnable(ip, port)启动监听并打印日志“Starting network diagnostic server listening on … for commands”(即上文日志消息的来源),Shutdown()负责优雅停止并打印 “Network diagnostic server shutdown complete”。从Enable实现看,服务端设置了 5 分钟的ReadHeaderTimeout以缓解 Slowloris 类攻击,且源码注释标明该端口在 reload 时存在不可重配置的已知限制。

三、关闭诊断工具

对参与 Swarm 的每个节点重复执行以下操作:

  1. /etc/docker/daemon.json中删除network-diagnostic-port键;

  2. 再次获取dockerd的 PID:

    $ ps aux |grep dockerd | grep -v grep
  3. 发送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,遍历handlersmap 输出路径)。/ready返回OK,供客户端做就绪探测——diagnosticClient启动时的第一件事就是请求http://<ip>:<port>/ready并校验响应包含OK

4.2 加入或退出网络数据库集群

$ curl localhost:2000/join?members=ip1,ip2,...
$ curl localhost:2000/leave?members=ip1,ip2,...

ip1ip2… 是 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>createentrygetentryupdateentrydeleteentry

注意(原文档强调):表操作具有**节点所有权(node ownership)**语义——条目会保持持久,直到插入它的节点仍在集群中。这正是排查“孤儿条目”的理论依据,也是删除操作不可逆的原因。

4.7 输出格式控制选项

从 server.go 的ParseHTTPFormOptions与 types.go 的HTTPReply可以看到,所有端点都支持 URL 表单参数控制输出:

  • 追加&json返回 JSON 格式(application/json);
  • 追加&json=pretty返回缩进美化的 JSON;
  • 响应统一为HTTPResult结构:message字段取值OK/FAIL等,details承载具体内容(如TableObjsizeentries,每个条目含keyvalue(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表名,sdoverlay之一。
-ip要查询的 IP 地址,默认 127.0.0.1。
-net目标网络 ID。
-port目标端口,默认 2000。
-ajoin/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.EndpointRecordoverlay表解析为overlay.PeerRecord),并把owner不在当前网络 peer 集合中的条目标记为孤儿发出 Warn。

六、容器化版本的完整调试流程

容器化 CLI 基于 17.12 引擎,需要以 privileged 模式运行。

NOTE(原文档强调):表操作具有 ownership 语义,因此在诊断容器处于 Swarm 期间,任何create entry操作都会保持持久。

流程如下:

  1. 确保运行诊断客户端的节点不属于 Swarm,若属于则先执行docker swarm leave -f

  2. 启动容器:

    $ docker container run --name net-diagnostic -d --privileged --network host dockereng/network-diagnostic:17.12-dind
  3. 通过docker exec -it <container-ID> sh进入容器,启动其中内置的诊断服务器:

    $ kill -HUP 1

    (向 PID 1 的 dind 内 dockerd 发 HUP,触发其加载容器内的daemon.json,从而在 2000 端口启动诊断服务器;该容器镜像定义见 Dockerfile.dind,纯客户端镜像定义见 Dockerfile.client,基于 alpine + curl,ENTRYPOINTdiagnosticClient。)

  4. 将诊断容器加入 Swarm,然后在容器内运行诊断 CLI:

    $ ./diagnosticClient <flags>...
  5. 调试结束后,离开 Swarm 并停止容器。

七、使用建议小结

结合原文档警示与源码实现,可归纳出几条实践原则:

  1. 临时性:诊断端口只应在排查期间打开,排查完立即从daemon.json移除配置并 reload;
  2. 每节点操作:Swarm 场景下启用/关闭需要对每个参与节点执行;
  3. 只读优先:默认不加-a的客户端是纯只读的,可安全地对运行中 daemon 执行;一旦使用写入类端点(createentry/updateentry/deleteentry)或-a-r,就要意识到 ownership 持久性与删除不可逆这两点;
  4. 完整网络 ID:所有nid参数都必须使用非截断的完整 ID,否则查询会得到空 peer 列表;
  5. 输出自动化:脚本化采集时优先使用&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。

【免费下载链接】mobyThe Moby Project - a collaborative project for the container ecosystem to assemble container-based systems项目地址: https://gitcode.com/GitHub_Trending/mo/moby

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询