Sometimes people don't need advice, they just need someone to listen and care.
Toggle navigation
Home
Archives
Tags
About
Kubernetes 踩坑记之 集群node无法访问service
2019-09-24 06:36:18
3138
0
0
william
## 现象 Kubernetes集群版本1.15.2,使用了kube-proxy,使用的 iptables 模式。 在kubernetes的一个node节点上直接访问kubernetes中的service不通,注意是访问服务不通,不是ping不通。 以apiserver对应的kubernetes service为例: ```bash $ kubectl get service -o wide NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR kubernetes ClusterIP 172.254.0.1 <none> 443/TCP 1y <none> ``` 直接在node上发起访问是不通的,如下: ```bash $ telnet 172.254.0.1 443 Trying 172.254.0.1... ``` 注意只是从node上、在使用了host网络模式的pod中,无法访问kubernetes中的service,在非host网络模式的pod中可以访问service, 从物理机也无法访问。 ## 原因排查 先抓个包: ```bash [root@10.18.161.10]# tcpdump -i eth1 host 10.18.161.8 and port 6443 -nn tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on eth1, link-type EN10MB (Ethernet), capture size 262144 bytes 10:45:14.566057 IP xx.xx.xx.xx.55644 > 10.18.161.8.6443: Flags [S], seq 1192507649, win 29200, options [mss 1460,sackOK,TS val 3973848524 ecr 0,nop,wscale 7], length 0 10:45:15.577405 IP xx.xx.xx.xx.55644 > 10.18.161.8.6443: Flags [S], seq 1192507649, win 29200, options [mss 1460,sackOK,TS val 3973849536 ecr 0,nop,wscale 7], length 0 10:45:16.601403 IP xx.xx.xx.xx.55644 > 10.18.161.8.6443: Flags [S], seq 1192507649, win 29200, options [mss 1460,sackOK,TS val 3973850560 ecr 0,nop,wscale 7], length 0 10:45:17.625379 IP xx.xx.xx.xx.55644 > 10.18.161.8.6443: Flags [S], seq 1192507649, win 29200, options [mss 1460,sackOK,TS val 3973851584 ecr 0,nop,wscale 7], length 0 10:45:18.649399 IP xx.xx.xx.xx.55644 > 10.18.161.8.6443: Flags [S], seq 1192507649, win 29200, options [mss 1460,sackOK,TS val 3973852608 ecr 0,nop,wscale 7], length 0 10:45:19.673406 IP xx.xx.xx.xx.55644 > 10.18.161.8.6443: Flags [S], seq 1192507649, win 29200, options [mss 1460,sackOK,TS val 3973853632 ecr 0,nop,wscale 7], length 0 ``` > xx.xx.xx.xx 为物理机的公网IP 目标地址10.18.161.8是apiserver的真实IP,目标IP被转换成了真实IP,符合预期。 `源地址`使用了物理机的公网IP,导致通过 eth1网卡无法回包。 这里说明一下我的物理机的网络模型: - eth0 链接在公网交换机上 - eth1 链接在内网交换机上 - 两台交换机互不相同 那么问题来了,为什么我的 Local IP 默认选择物理机的公网IP呢,使用 eth0 与内网通信肯定不通啊~~~~ ### 问题排查 查看 K8S 对应的 iptables 规则 ```bash -A PREROUTING -m comment --comment "kubernetes service portals" -j KUBE-SERVICES -A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER -A OUTPUT -m comment --comment "kubernetes service portals" -j KUBE-SERVICES -A OUTPUT ! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER -A POSTROUTING -m comment --comment "kubernetes postrouting rules" -j KUBE-POSTROUTING -A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE -A DOCKER -i docker0 -j RETURN -A KUBE-MARK-DROP -j MARK --set-xmark 0x8000/0x8000 -A KUBE-MARK-MASQ -j MARK --set-xmark 0x4000/0x4000 -A KUBE-POSTROUTING -m comment --comment "kubernetes service traffic requiring SNAT" -m mark --mark 0x4000/0x4000 -j MASQUERADE -A KUBE-SEP-DWJFKQDV6SFQDO7C -s 10.18.161.8/32 -j KUBE-MARK-MASQ -A KUBE-SEP-DWJFKQDV6SFQDO7C -p tcp -m tcp -j DNAT --to-destination 10.18.161.8:6443 -A KUBE-SERVICES -d 172.254.0.1/32 -p tcp -m comment --comment "default/kubernetes:https cluster IP" -m tcp --dport 443 -j KUBE-SVC-NPX46M4PTMTKRN6Y -A KUBE-SERVICES -m comment --comment "kubernetes service nodeports; NOTE: this must be the last rule in this chain" -m addrtype --dst-type LOCAL -j KUBE-NODEPORTS -A KUBE-SVC-NPX46M4PTMTKRN6Y -j KUBE-SEP-DWJFKQDV6SFQDO7C ``` `-A KUBE-SERVICES -d 172.254.0.1/32 -p tcp -m comment --comment "default/kubernetes:https cluster IP" -m tcp --dport 443 -j KUBE-SVC-NPX46M4PTMTKRN6Y` 发现这条规则确实是做了 DNAT。 而我物理机上的路由是这样的: ```bash Destination Gateway Genmask Flags Metric Ref Use Iface 0.0.0.0 xx.xx.xx.xx 0.0.0.0 UG 0 0 0 eth0 10.18.161.0 0.0.0.0 255.255.255.0 U 0 0 0 eth1 ``` > xx.xx.xx.xx 为公网网关 这就奇怪了,为什么第二条路由没有生效呢? 问题排查陷入僵局.... ### 柳暗花明 查看 iptables 工作原理:  发现 tcp 链接选择路由在 DNAT 之前,DNAT 之前是目的IP 是 `172.254.0.1`, 所以走了第一条路由,因为LOCAL IP选择了 eth0 所在网卡的 IP 知道了根本原因,如果解决呢? 在源码没有秘密! 在 Kubernetes 代码 `kubernetes/pkg/proxy/iptables/proxier.go` 文件中,发现了相关代码 ```go if proxier.masqueradeAll { writeLine(proxier.natRules, append(args, "-j", string(KubeMarkMasqChain))...) } else if len(proxier.clusterCIDR) > 0 { // This masquerades off-cluster traffic to a service VIP. The idea // is that you can establish a static route for your Service range, // routing to any node, and that node will bridge into the Service // for you. Since that might bounce off-node, we masquerade here. // If/when we support "Local" policy for VIPs, we should update this. writeLine(proxier.natRules, append(args, "! -s", proxier.clusterCIDR, "-j", string(KubeMarkMasqChain))...) } writeLine(proxier.natRules, append(args, "-j", string(svcChain))...) ``` else if 中的逻辑是,如果配置了 POD 的 CIDR 则会添加一条 MASQ 的规则,MASQ 实际是做SNAT ,不了解 MASQ的同学可以自行度娘。 查看 kube-proxy 相关配置,发现有这样一个配置参数 cluster-cidr,配置上之后会增加一条iptables规则。 ```bash -A KUBE-SERVICES ! -s 172.20.0.0/16 -d 172.254.0.1/32 -p tcp -m comment --comment "default/kubernetes:https cluster IP" -m tcp --dport 443 -j KUBE-MARK-MASQ ``` 上面三条规则的意思是:当源IP不是-s 172.20.0.0/16,进入-j KUBE-MARK-MASQ,打上标记0x4000/0x4000,在随后的POSTROUTING阶段,带有0x4000/0x4000标记的报文会被SNAT(-j MASQUERADE)。 有了这条iptables规则后问题就消失了,结合起来考虑可以断定原因就是缺少了这条iptables规则。 再次抓包: ```bash [root@10.18.161.10]# tcpdump -i eth1 host 10.18.161.8 and port 6443 -nn tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on eth1, link-type EN10MB (Ethernet), capture size 262144 bytes 15:14:13.935952 IP 10.18.161.10.31518 > 10.18.161.8.6443: Flags [S], seq 607612491, win 29200, options [mss 1460,sackOK,TS val 11759890 ecr 0,nop,wscale 7], length 0 15:14:13.936030 IP 10.18.161.8.6443 > 10.18.161.10.31518: Flags [S.], seq 4183602742, ack 607612492, win 28960, options [mss 1460,sackOK,TS val 3990047849 ecr 11759890,nop,wscale 7], length 0 15:14:13.936043 IP 10.18.161.10.31518 > 10.18.161.8.6443: Flags [.], ack 1, win 229, options [nop,nop,TS val 11759890 ecr 3990047849], length 0 ``` ### 总结 在有多块网卡的机器上,尤其是两块网卡所在的网络不通时,一定要记得在 kube-proxy 添加 --cluster-cidr 参数,否则可能会出现问题(取决于默认路由是那块网卡)
Pre:
LVS 负载均衡模式
Next:
Golang 踩坑记之 Http Client 连接未释放
0
likes
3138
Weibo
Wechat
Tencent Weibo
QQ Zone
RenRen
Please enable JavaScript to view the
comments powered by Disqus.
comments powered by
Disqus
Table of content