两个站点、三个管理节点和五个工作节点,靠一条隧道相连,docker service ls 显示两边都有任务在跑,服务之间的调用却超时。Swarm 日志里看不出任何问题。这种组合是数据平面问题的典型特征:控制平面找到了节点,而封装后的帧从未真正送达。

overlay 之前必须先有底层网络

Swarm 的 overlay 直接在节点地址之间推送封装帧,这意味着每个节点都需要一个其他所有节点无需转换就能到达的地址,并且要落在隧道提供的那块接口上。只把客户端送到发布端口的隧道是不够的。底层网络必须能在两个方向上把节点之间路由起来。

动 Swarm 之前先测这一步。从每个节点检查到对端隧道地址的路由,ping 通它,再测控制端口。如果 ping 通而端口不通,说明隧道没问题,中间有过滤规则。

ip -brief address
ip route get 10.20.0.12
ping -c3 10.20.0.12
nc -vz 10.20.0.12 2377

如果路由指向物理接口而不是 tun0 或 wg0,overlay 就会封装到一个远端站点不可达的地址上,症状看起来像丢包。

检查每个节点宣告的是哪个地址

守护进程只宣告一个地址,对端拿它来做数据平面。在多网卡主机上,很容易在隧道承载另一个地址时却宣告了 LAN 或公网地址。别假设,去查;默认值不对时,在加入集群时用 --advertise-addr 固定下来。宣告了只有自己能到达的地址的节点照样能加入 swarm,故障会在之后以跨站服务无法通信的形式出现。

之后想改宣告地址不是改配置那么简单,节点必须先退出再加入。

把每个节点的宣告地址记进笔记,因为大多数非对称故障都能由它解释。

Masquerade 通常是元凶

当网关夹在节点和隧道之间时,常见错误是对离开物理接口的所有流量都做 masquerade。这会把 overlay 的内层地址也一起改写,于是每个对端看起来都来自网关,VXLAN 对端彼此认不出来。结果是封装流量丢失或单向,而普通 ping 仍然正常。

让 overlay 的网段走网关,但不要做转换。如果确实需要对另一个网段做转换,就把它限制在那里,绝不要覆盖 overlay 使用的范围。

ip route add 172.16.0.0/22 via 10.20.0.1
iptables -t nat -I POSTROUTING -o tun0 -s 172.16.0.0/22 -j ACCEPT

既要看规则的顺序,也要看规则的内容,因为链中靠前的宽泛 MASQUERADE 会压过靠后的窄例外。

VXLAN 需要包里的余量

VXLAN 会给它承载的帧加上 50 字节。1500 字节的底层承载 1500 字节的内层帧,会产生任何路径都送不到的封装包,故障看起来像间歇性的服务发现,而不是尺寸问题。要么在两端都调低隧道 MTU,要么设置 overlay 的 MTU,让驱动宣告一个装得下的负载。

docker network create --driver overlay --attachable --opt com.docker.network.driver.mtu=1400 swarm-overlay

ingress 网络是自动创建的,所以改它意味着删掉重建,而不是原地编辑。

如果只修隧道的一侧,最终起作用的仍然是较小的那个 MTU。

看数据平面,不要只看日志

在两端都抓 VXLAN 端口。帧从一侧出去、从另一侧到达,说明底层没问题,问题在 overlay 内部。帧出去了却始终不到,说明是路由或过滤。帧根本不出门,说明源地址错了。

几秒后就自行恢复的服务发现故障,通常指向包余量或抖动路径,而不是 raft 仲裁。

保留一段正常时的抓包,因为拿它和故障时的对比,比重新读配置快得多。

相关文章