环境说明:
- K8s集群内节点通过公网IP互通(关键点)
- 域名解析正常
- 集群使用Flannel网络插件
- 使用 VXLAN (udp 8472端口)
- 采用默认配置
转载说明:
- 原创内容,请注明出处
1. 排查思路
1.1 控制平面节点抓包
bash
sudo tcpdump -i eth0 -n port 8472
抓包日志如下
bash
# Overlay 通信, pod间通信
IP 10.244.0.0.47464 > 10.244.1.72.10250: Flags [S], seq 2153322784, win 64240, options [mss 1460,sackOK,TS val 1895790326 ecr 0,nop,wscale 7], length 0
# Underlay 传输,将pod间通信信息通过节点传输
00:54:02.148025 IP 49.232.87.190.33673 > 82.156.88.229.8472: OTV, flags [I] (0x08), overlay 0, instance 1
---
IP 10.244.0.0.14642 > 10.244.1.72.10250: Flags [S], seq 3355553120, win 64240, options [mss 1460,sackOK,TS val 1895790740 ecr 0,nop,wscale 7], length 0
00:54:02.561412 IP 49.232.87.190.49894 > 82.156.88.229.8472: OTV, flags [I] (0x08), overlay 0, instance 1
这些流量是K8s中Flannel从控制平面节点发往工作节点的流量,从length 0
明显可以看出没有收到来自工作节点的响应,但理论上如果两个节点连通是可以收到响应的
停掉如上抓包进程,我们进行更精确的抓包
bash
sudo tcpdump -i eth0 -n port 8472 | grep -3 -C 3 invalid
1.2 工作节点抓包
bash
sudo tcpdump -i any -n udp port 8472
可以发现工作节点没有接收到任何流量,但实际上控制平面节点却不断往工作节点发送流量
维持抓包进程
1.3 控制平面节点手动往工作节点发送一条消息
bash
# 一条"invalid"的信息,方便控制平面精确抓包
echo "VXLAN" | nc -u 82.156.88.229 8472
1.4 查看控制平面节点的抓包日志
bash
IP 10.244.0.0.19215 > 10.244.1.72.10250: Flags [S], seq 1293368681, win 64240, options [mss 1460,sackOK,TS val 1897566360 ecr 0,nop,wscale 7], length 0
01:23:38.181413 IP 49.232.87.190.42458 > 82.156.88.229.8472: OTV, flags [I] (0x08), overlay 0, instance 1
----重点信息 START
01:23:38.537738 IP 10.2.24.12.50511 > 82.156.88.229.8472: OTV, [length 6 < 8] (invalid)
--- 重点信息 END
IP 10.244.0.0.9238 > 10.244.1.72.10250: Flags [S], seq 3574053469, win 64240, options [mss 1460,sackOK,TS val 1897567380 ecr 0,nop,wscale 7], length 0
01:23:39.201415 IP 49.232.87.190.48421 > 82.156.88.229.8472: OTV, flags [I] (0x08), overlay 0, instance 1
我们可以看到刚才发送消息对应的流量,它是从控制平面节点内网IP到工作节点公网IP,此处虽然length 0
,但那是因为我们没有在工作节点发送响应
1.5 查看工作节点的抓包日志
bash
01:23:38.537383 eth0 In IP 49.232.87.190.50511 > 10.2.24.3.8472: OTV, [length 6 < 8] (invalid)
工作节点接收到了流量,而且流量的 src 变成了控制平面节点的公网IP, 由此我们猜想 : 只有 src 为控制平面节点内网IP
的流量从网卡出来,才能被工作节点接收到, 下一步我们进行猜想的验证
1.6 控制平面节点手动往工作节点发送一条消息
指定消息请求的 src 为控制平面节点公网IP
bash
echo "VXLAN" | nc -u -s 49.232.87.190 82.156.88.229 8472
1.7 查看控制平面节点的抓包日志
bash
IP 10.244.0.0.36977 > 10.244.1.72.10250: Flags [S], seq 4056525145, win 64240, options [mss 1460,sackOK,TS val 1898115736 ecr 0,nop,wscale 7], length 0
01:32:50.369407 IP 49.232.87.190.40861 > 82.156.88.229.8472: OTV, flags [I] (0x08), overlay 0, instance 1
----重点信息 START
01:32:53.778916 IP 49.232.87.190.42542 > 82.156.88.229.8472: OTV, [length 6 < 8] (invalid)
----重点信息 END
IP 10.244.0.0.39586 > 10.244.1.72.10250: Flags [S], seq 140640535, win 64240, options [mss 1460,sackOK,TS val 1898125336 ecr 0,nop,wscale 7], length 0
01:32:57.157869 IP 49.232.87.190.34355 > 82.156.88.229.8472: OTV, flags [I] (0x08), overlay 0, instance 1
可以看到,流量的 src 确实是控制平面节点公网IP
,下一步去看工作节点
的抓包日志
1.8 查看工作节点的抓包日志
没有任何信息输出,这证明了我们的猜想,
结论 : Pod无法跨节点访问Service是因为我们经过Flannel的网络请求的 src 是控制平面公网IP
, 这样的流量是无法被云厂商转发的;
因为我们使用的轻量应用服务器,网卡和内网IP都是虚拟的,任何从机器的内网IP流出的流量,都会先到达云厂商的网关,云厂商会将内网IP伪装成对应的公网IP, 发送到目标公网IP对应的机器, 机器将响应数据再通过请求方的公网IP回传,这样才能达成通信的完整链路;
如果网络请求的 src 是公网IP,一是云厂商的网关不允许这样的流量,会抛弃掉这样的流量,第二就是,就算请求能到达目标机器,目标机器响应的时候在网络空间根本无法路由到请求方的 内网IP , 也就无法达成通信的完整链路。
2. 解决思路
在K8s集群节点通过公网IP互通的环境下,节点网络请求从网卡出的时候,保证 src 为内网IP
比如我们使用 Flannel 作为网络插件的话,安装 Flannel 可以采用如下配置:
控制平面
机器上执行
bash
# 下载Flannel的Manifest文件
# 如果网络原因阻碍,可以本地下载文件后上传到服务器使用
sudo wget -O kube-flannel.yml https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml
# 修改 kube-flannel.yml
## 第一处修改点
containers:
- args:
- --public-ip=$(PUBLIC_IP) # 新增行
- --iface-regex=10\.2\.\d+\.\d+ # 新增行,正则匹配你所有机器内网IP, 因为每个节点都会有一个flannel的Pod
- --ip-masq=true
- --kube-subnet-mgr
command:
- /opt/bin/flanneld
## 第二处修改点
env:
- name: PUBLIC_IP # 新增环境变量
valueFrom:
fieldRef:
fieldPath: status.podIP
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
# 部署 Flannel
kubectl apply -f kube-flannel.yml
创作不易,希望大家多多支持,文章持续更新,我们下期见.
程序员白话 | [原创]
点关注不迷路
可以抖音搜索「程序员白话」,大家有任何问题都可以私聊我,知无不言~