首页
/ Moby 源码实战:用 Swarm Service Driller(ssd)排查 Docker Swarm 网络的控制面与内核数据面不一致

Moby 源码实战:用 Swarm Service Driller(ssd)排查 Docker Swarm 网络的控制面与内核数据面不一致

2026-09-04 22:07:51作者:温艾琴Wonderful

ssd(Swarm Service Driller)是 Moby 仓库内置的 Swarm 网络故障排查工具,用于核对 Docker 守护进程内存中的网络控制面状态与内核中实际编程的数据面(IPVS 负载均衡、iptables 转发规则)是否一致,以及各节点通过 gossip 协议同步出的控制面状态是否收敛。本文基于 READMEssd.py 源码,完整讲解两类检查的原理、操作命令和输出判读方法,帮助你在出现"网络 inspect 正常但流量不通"这类疑难问题时快速定位责任层。

ssd 的定位:控制面与数据面之间的"一致性探针"

在 Docker Swarm 网络中,服务发现与负载均衡由两部分协作完成:

  • 控制面:由 Docker 守护进程(daemon/libnetwork 下的集群 agent)维护,记录每个服务的 VIP、本地负载均衡索引(LocalLBIndex)与后端任务列表;
  • 数据面:由内核实际编程,overlay 网络在每个容器沙箱内用 IPVS(以 fwmark 标识服务)做本地负载均衡,ingress 网络则用 iptables 的 DNAT 规则把发布端口转发到 ingress 沙箱。

ssd 的核心职责就是把这两份"账本"逐条比对,确认没有漂移。它当前只覆盖负载均衡(IPVS 实现)的一致性检查,这一点在 README 中明确说明。

运行方式与依赖

ssd 以容器形式运行,通过挂载 Docker socket 和 netns 目录获得对 daemon API 与内核网络命名空间的访问权。README 给出的标准命令为:

docker run -v /var/run/docker.sock:/var/run/docker.sock \
  -v /var/run/docker/netns:/var/run/docker/netns \
  --privileged --net=host sanimej/ssd ov2

从源码看,两个挂载点各有用途:

  • /var/run/docker.sock:ssd 通过 docker-py 客户端(docker.APIClient(base_url='unix://var/run/docker.sock'),见 ssd.py L141)拉取 network inspect --verbose 的控制面数据;
  • /var/run/docker/netns:ssd 用 nsenter --net=<namespace> 进入每个容器的网络沙箱执行 ipvsadm -ln 读取内核中真实的 IPVS 表(ssd.py L94),因此必须 --privileged

命令的第一个参数是网络名;第二个可选参数为子命令 gossip-consistency,默认值为 default(即一致性检查,ssd.py L136-L145)。如果第一个参数是 ingress,则只检查 ingress 网络。

仓库提供了现成的 Dockerfile,基于 Alpine,预装了检查所需的工具链:python2iproute2ipvsadmbashnsenter(util-linux)以及网络诊断工具 straceiperfethtool 等,并将 ssd.py 设为入口 python /ssd.py。你可以用它自行构建镜像,替代 README 中引用的外部 sanimej/ssd 镜像,例如:

docker build -t local/ssd daemon/libnetwork/cmd/ssd

检查一:节点内的控制面 / 数据面一致性(IPVS 编程核对)

default 模式下,ssd 对指定网络执行 check_network,流程如下(见 check_network):

  1. 拉取控制面真值:调用 network inspect --verbose,从返回的 Services 字段提取每个服务的 LocalLBIndex(即 IPVS 中使用的 fwmark)和任务列表的 EndpointIP,构建"fwmark → 后端 IP 集合"的期望映射;
  2. 读取内核真值:对网络上每个容器(沙箱键来自 NetworkSettings.SandboxKey),nsenter 进其网络命名空间执行 ipvsadm -ln,解析输出中以 FWM 开头行的标记值和 -> 开头的后端条目;
  3. 双向比对
    • IPVS 中有、daemon 中没有的 LB Index → 报 LB Index X present in IPVS but missing in docker daemon(内核残留);
    • daemon 中有、IPVS 中没有的 → 报 present in docker daemon but missing in IPVS(编程丢失);
    • 两边都有但后端集合不一致 → 报 Incorrect LB Programming for service ...,并分别打印控制面与内核 IPVS 的后端列表,方便肉眼定位多出来的或少掉的实例。

agent.go 中可以看到 ServiceInfo 结构的定义:VIPLocalLBIndexTasksPorts——这正是 ssd 比对所依赖的 network inspect --verbose 输出的数据来源。

README 中一个三节点集群(网络 ov2,3 个服务各 3 副本)的正常输出如下,每个沙箱下逐一核对所有服务后输出 OK,随后自动追加对 ingress 网络的检查:

$ docker run -v /var/run/docker.sock:/var/run/docker.sock \
    -v /var/run/docker/netns:/var/run/docker/netns \
    --privileged --net=host sanimej/ssd ov2
Verifying LB programming for containers on network ov2
Verifying container /s2.3.ltrdwef0iqf90rqauw3ehcs56...
service s2... OK
service s3... OK
service s1... OK
Verifying container /s3.3.nyhwvdvnocb4wftyhb8dr4fj8...
service s2... OK
service s3... OK
service s1... OK
Verifying container /s1.3.wwx5tuxhnvoz5vrb8ohphby0r...
service s2... OK
service s3... OK
service s1... OK
Verifying LB programming for containers on network ingress
Verifying container Ingress...
service web... OK

ingress 网络:iptables 发布端口 DNAT 规则核对

ingress 网络(或 default 模式自动追加的 ingress 检查),ssd 额外调用 check_iptablesssd.py L25-L45):

  • nsenter 进入 ingress_sbox 网络命名空间,读取 eth1 的 IP(ingress 沙箱在 docker_gwbridge 网络上的地址,发布端口最终会被 DNAT 到该 IP);
  • 对服务 Ports 中的每个 Target: x, Publish: y 条目,用 iptables -t nat -C DOCKER-INGRESS -p tcp --dport <publish> -j DNAT --to <ingressIP>:<publish> 探测 nat 表 DOCKER-INGRESS 链中是否存在对应规则;
  • 缺失时输出:Service <name>: host iptables DNAT rule for port <x> -> ingress sandbox <ip>:<x> missing

这与数据面实现相互印证:libnetwork 的 bridge 驱动内部在主机命名空间的 nat 表 PREROUTING 中挂接 DOCKER-INGRESS 链(见 iptabler/network.go L408),服务流量经 DNAT 进入 ingress 沙箱后再由 IPVS 分发到后端。

检查二:跨节点控制面一致性(gossip-consistency)

Swarm 网络状态通过 gossip 协议在集群内同步。gossip-consistency 子命令验证所有节点对该网络的控制面状态是否一致(README):

docker run -v /var/run/docker.sock:/var/run/docker.sock \
  -v /var/run/docker/netns:/var/run/docker/netns \
  --privileged sanimej/ssd ov2 gossip-consistency

三节点集群、ov2 网络上的正常输出——三行 hash 完全相同即表示已收敛:

Node id: sjfp0ca8f43rvnab6v7f21gq0 gossip hash c57d89094dbb574a37930393278dc282

Node id: bg228r3q9095grj4wxkqs80oe gossip hash c57d89094dbb574a37930393278dc282

Node id: 6jylcraipcv2pxdricqe77j5q gossip hash c57d89094dbb574a37930393278dc282

源码级实现:全局服务 + 每节点 hash

该子命令的机制比表面更巧妙(ssd.py L147-L165):

  1. ssd 在本地节点通过 daemon API 创建一个名为 gossip-hash 的 global 服务,其任务镜像为 docker/ssd,参数为 <网络名> gossip-hash,并把 /var/run/docker.sock 挂载进每个任务;
  2. global 服务保证集群中每个节点各运行一个任务,每个任务执行 gossip-hash 分支:inspect_network --verbose 后,把该节点视角下所有服务的 VIP 及任务明细字段排序,逐条喂给 MD5,打印节点 ID 与摘要,然后 signal.pause() 挂起等待被收集;
  3. 创建服务后短暂等待,用 service logs gossip-hash 收集全部任务的输出(即每节点一行 hash),最后调用 remove_service 清理。

因此输出的每行 Node id: <node-id> gossip hash <md5> 对应集群中的一个节点。hash 相同说明各节点经 gossip 同步出的网络控制面状态一致;如果出现不一致,按 README 的建议,在各节点上执行 docker network inspect --verbose 逐个对比输出,即可定位具体差异字段(通常是某个服务的任务列表或端点 IP 未同步到位)。值得注意的是:hash 是对"排序后的状态字段"计算的,与任务的枚举顺序无关,可以消除字段顺序造成的假阳性。

故障排查建议路径

结合两类检查,可以形成一条从现象到结论的定位路径:

  1. 服务 VIP 可达性异常、个别后端失联 → 先跑 default 模式的一致性检查。若报 missing in IPVS / Incorrect LB Programming,问题在本机内核数据面编程,可进一步用 ipvsadm -ln(经 nsenter 到对应沙箱)与 docker network inspect --verbose 手工复核;
  2. ingress 发布端口不通、容器内直连正常 → 关注 iptables DNAT 规则缺失输出,检查 iptables -t nat -L DOCKER-INGRESS -n 中发布端口规则;
  3. 部分节点行为与主节点不一致(如新加入节点上服务不可见) → 跑 gossip-consistency。若 hash 不一致,逐节点 docker network inspect --verbose diff 出未收敛的节点与字段。

适用前提与注意事项

  • 工具依赖 Linux 内核特性(IPVS、iptables、netns + nsenter),在 Linux 节点上运行才有效;
  • ssd.py 使用 Python 2 语法(print 语句),Dockerfile 中显式安装 python2 并建立 python 软链,自行移植到 Python 3 环境需做语法迁移;
  • 一致性检查当前仅覆盖 IPVS 负载均衡这一项数据面内容(README 明示 "currently the tool checks only for the consistency of the Load balancer"),其他数据面要素(如路由、VXLAN 配置)不在核对范围内;
  • gossip-consistency 会在集群中短暂创建并删除一个 global 服务,请确认当前节点具有 swarm 管理权限,且集群允许创建临时服务。

延伸阅读(仓库内相关路径)

  • README:ssd 工具官方说明与示例输出;
  • ssd.py:检查逻辑全部实现,约 194 行,可读性高,适合作为理解 Swarm 网络数据面编程的"活文档";
  • agent.goServiceInfo(VIP / LocalLBIndex / Tasks / Ports)结构定义,即 ssd 比对的控制面数据来源;
  • service_linux.go:daemon 侧通过 moby/ipvs 库在沙箱内创建/删除 IPVS 服务与后端的实现,与 ssd 读取的内核状态一一对应;
  • networkdb:gossip 同步的集群数据库层,gossip-consistency 校验的对象即其收敛结果。
登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
527
590
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
904
1.82 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
889
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.52 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.33 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
982
502
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384