Docker Swarm Overlay 跨节点不通,问题卡在哪?

问题背景

公司微服务部署在 Docker Swarm 集群(1 Master + 5 Worker),使用 overlay 网络 swarm-net 实现跨节点容器互访。集群运行稳定超过一年,所有服务通过 Docker Service 部署,容器 IP 由 overlay 网络自动分配。

故障现象

6 月 8 日晚 9:00,运维收到大量服务异常告警:API Gateway 返回 502 Bad Gateway,所有后端服务的健康检查失败。进一步排查发现一个关键模式:同一节点上的容器之间通信正常,跨节点的容器通信全部失败

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# 在 node-01 上
$ docker exec api-gateway curl http://user-service:8080/health
curl: (7) Failed to connect to user-service port 8080: Connection refused

$ docker exec api-gateway curl http://node-03:8080/health  # user-service在node-03上
curl: (28) Connection timed out after 5000ms

# 但node-03上本地访问正常
$ docker exec user-service curl http://localhost:8080/health
{"status":"UP"}    # 本地ok

所有 overlay 跨节点通信全部中断。

排查过程

第一步:Swarm 控制面状态

1
2
3
4
5
6
7
8
$ docker node ls
ID                   HOSTNAME   STATUS  AVAILABILITY  MANAGER STATUS
abc123def456 *       node-01    Ready   Active        Leader
def456ghi789         node-02    Ready   Active
ghi789jkl012         node-03    Ready   Active
jkl012mno345         node-04    Ready   Active
mno345pqr678         node-05    Ready   Active
pqr678stu901         node-06    Ready   Active

所有节点状态正常,Leader 可达。

第二步:overlay 网络状态

1
2
3
4
5
6
7
8
9
$ docker network inspect swarm-net | jq '.[].Peers'
[
  {"Name": "a1b2c3d4e5f6", "IP": "10.10.0.2"},
  {"Name": "a1b2c3d4e5f7", "IP": "10.10.0.3"},
  {"Name": "a1b2c3d4e5f8", "IP": "10.10.0.4"},
  {"Name": "a1b2c3d4e5f9", "IP": "10.10.0.5"},
  {"Name": "a1b2c3d4e5fa", "IP": "10.10.0.6"},   # node-06 缺失!
  {"Name": "a1b2c3d4e5fb", "IP": "10.10.0.7"},
]

node-06 上的 peer 条目还在,但实际通信中 node-06 处于孤立状态。这说明 control plane 的 gossip 协议分发正常,但 data plane(VXLAN 隧道)出了问题。

第三步:检查 VXLAN 端口

Docker overlay 网络使用 VXLAN(端口 4789/UDP)在节点之间建立隧道:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# node-06 上
$ ss -tunlp | grep 4789
# 空——VXLAN 端口未监听!

# 检查防火墙规则
$ iptables -L INPUT -n --line-numbers | grep 4789
# 无结果

$ iptables -L INPUT -n | tail -5
REJECT   all  --  0.0.0.0/0   0.0.0.0/0   reject-with icmp-host-prohibited

节点 6 的 iptables 有一条全局 REJECT 规则在末尾,且没有 4789/udp 的允许规则——所有 VXLAN 流量被防火墙拒绝。

第四步:追溯防火墙变更

1
2
3
$ cat /etc/sysconfig/iptables | grep -C3 "May 25"
# May 25 某运维执行了 "iptables-save > /etc/sysconfig/iptables" 
# 覆盖了原有规则文件,但原文件中有VXLAN允许规则,新保存的版本没有

检查 /var/log/secure:当天下午,有人在 node-06 上执行了 systemctl restart iptables,导致运行时 iptables 规则被重新加载为文件中的版本——而文件中恰好缺失了 Docker 自动添加的 VXLAN 规则。

Docker daemon 在启动时会在 iptables 中自动添加 overlay 网络所需的规则,但 Docker 不会将这些规则持久化到配置文件中。一旦 iptables 服务被重启,Docker 自动添加的规则就会丢失。

解决方案

  1. 紧急恢复:重启 node-06 上的 Docker daemon
1
systemctl restart docker

Docker 重启后自动重新添加了 iptables 规则,VXLAN 端口恢复监听。

  1. 固化 Docker 防火墙规则
1
2
# 重启docker后立即固化规则
iptables-save > /etc/sysconfig/iptables
  1. 配置 iptables 持久化服务:安装 iptables-services,确保每次 Docker 启动后自动持久化规则。

根因分析

直接原因:node-06 的 iptables 被重启后丢失了 Docker overlay 网络的 VXLAN 规则,导致跨节点容器通信中断。Swarm 集群中只要有一个节点 VXLAN 不通,所有指向该节点的流量都会超时。

深层原因:

  • Docker 自动生成的 iptables 规则不会自动持久化
  • 缺少"防火墙重启后自动恢复 Docker 规则"的机制
  • 集群缺少 overlay 网络健康检测

预防措施

  1. iptables 持久化策略:所有 Docker 节点配置 iptables-services 并在 /etc/docker/daemon.json 中设置 "iptables": true
  2. 防火墙变更流程:修改 iptables 配置后必须验证 Docker overlay 网络连通性
  3. overlay 网络心跳监控:定期从每个节点 ping overlay 网络中的固定测试容器
  4. Swarm 节点健康检查:监控每个节点的 gossip 协议心跳和 VXLAN 隧道状态
  5. 配置管理:iptables 规则文件纳入版本管理,变更可审计

总结

Docker overlay 网络的脆弱点在于它依赖 iptables 的动态规则,而这些规则不在任何配置文件中。一旦有人重启了防火墙服务,Docker 不会自动感知并重建规则。这个设计意味着:在 Docker 集群中,操作 iptables 服务就等同于操作网络基础设施。如果你不知道 Docker 在背后往 iptables 里写了什么,那就别碰 iptables。

使用 Hugo 构建
主题 StackJimmy 设计