一次IPVS模式下的Kubernetes容器网络排障

2023年 7月 9日 31.4k 0

TL; DR:

问题现象是集群运行一段时间后kube-proxy停止更新IPVS规则,造成部分容器无法互通。

这个问题是由一个存在于1.11.5/1.12.2/1.13.0版本中的bug造成的。并在以下版本中得到了修复,可以查看kubernetes/kubernetes#71071了解更多信息。

  • 1.11.7
  • 1.12.5
  • 1.13.2

另一方面,也可以通过将未修复版本的kube-proxy设置为iptables模式来规避这个问题。

故事

最近有两个新平台部署,第一次在线上启用了很多新方案。包括Master高可用,LVS,持久化存储,Prometheus监控。本以为在测试环境已经正常跑了一个月的方案理当顺风顺水,结果PoC的第二天就出了一些问题。

问题最初的现象是局部网络不通,一些模块之间的连接出现了问题,exec到容器上,nmap扫描对端端口,发现竟然是filtered的,然而在对端容器里扫描自身却是Open的。显然是容器器网络出现了一些问题。

回想一下K8s中容器通信都会经过什么,在我的方案中,CNI插件采用了Calico,而kube-proxy则是开启了IPVS模式。在容器网络的构建中,CNI和kube-proxy两者都有参与。

CNI插件的任务是在容器开启时为容器分配IP,并为这个IP构建虚拟设备,另外,CNI插件也负责将容器间通信的协议包(如TCP/UDP等)从K8s集群中的任意一台机器转发到指定容器所在的机器上,再转发到指定容器上,在Calico方案中,这是通过N条主机路由(N=cali虚拟网卡数量)和M条tunl0上的直接路由(M=该K8s集群节点数量)实现的。

$ route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         10.130.29.1     0.0.0.0         UG    100    0        0 ens32
10.130.29.0     0.0.0.0         255.255.255.0   U     100    0        0 ens32
10.244.0.0      0.0.0.0         255.255.255.0   U     0      0        0 *
10.244.0.137    0.0.0.0         255.255.255.255 UH    0      0        0 calid3c6b0469a6
10.244.0.138    0.0.0.0         255.255.255.255 UH    0      0        0 calidbc2311f514
10.244.0.140    0.0.0.0         255.255.255.255 UH    0      0        0 califb4eac25ec6
10.244.1.0      10.130.29.81    255.255.255.0   UG    0      0        0 tunl0
10.244.2.0      10.130.29.82    255.255.255.0   UG    0      0        0 tunl0

首先尝试了排除CNI的问题,CNI很好排查,只需要记录各个节点上容器的IP(每个节点记一个IP即可)然后在各个节点上Ping这些IP,如果都能Ping通,那CNI插件的工作就是完全正常的。

$ kubectl get pods -o wide -n kube-system|grep 10.244|awk '{print $6}'|xargs nmap -sP|grep Host
Host is up (0.000032s latency).
Host is up (0.000033s latency).
Host is up (0.00063s latency).
Host is up (0.000038s latency).

运行的结果是排除了CNI的问题。

随后,由于第一次使用nmap扫描对端端口时, 使用的是Cluster IP,也即是Service的IP,而ClusterIP到Pod IP之间只夹着一条LVS负载均衡规则(或者如果kube-proxy使用默认配置,则是iptables规则),既然Pod IP的连通性没有问题,那大概率是IPVS规则出现了问题。查询现有的IPVS规则,就发现了只有virtual server endpoint而没有real server的IPVS规则:

ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  10.96.0.1:443 rr
  -> 10.130.29.80:6443            Masq    1      6          0         
  -> 10.130.29.81:6443            Masq    1      1          0         
  -> 10.130.29.82:6443            Masq    1      0          0         
TCP  10.96.0.10:53 rr
  -> 10.244.0.137:53              Masq    1      0          0         
  -> 10.244.0.138:53              Masq    1      0          0         
TCP  10.99.182.13:80 rr
  -> 10.130.29.80:8080            Masq    1      0          0         
  -> 10.130.29.81:8080            Masq    1      0          0         
  -> 10.130.29.82:8080            Masq    1      0          0         
TCP  10.99.216.249:443 rr
  -> 10.244.1.30:8443             Masq    1      0          0         
TCP  10.102.174.22:443 rr
  -> 10.244.0.140:443             Masq    1      2          0         
TCP  10.108.62.55:5473 rr
UDP  10.96.0.10:53 rr
  -> 10.244.0.137:53              Masq    1      0          0         
  -> 10.244.0.138:53              Masq    1      0          0    

$ kubectl get service --all-namespaces|grep 10.108.62.55 
kube-system   calico-typha           ClusterIP   10.108.62.55            5473/TCP        13d
     
$ kubectl get pods -n kube-system|grep calico
calico-node-ftrks                               2/2     Running   0          13d
calico-node-gvtsp                               2/2     Running   0          13d
calico-node-v6cx4                               2/2     Running   0          13d

至此可以确定是由于缺少IPVS规则来将到达service的流量负载均衡到Pod上导致了这个问题。而IPVS规则是由kube-proxy维护的,打开kube-proxy的日志,发现了报错:

kubectl logs -n kube-system kube-proxy-cdsgl |tail 
I0111 06:51:48.874607       1 graceful_termination.go:160] Trying to delete rs: 10.96.0.1:443/TCP/10.130.29.80:6443
I0111 06:51:48.874765       1 graceful_termination.go:160] Trying to delete rs: 10.96.0.1:443/TCP/10.130.29.81:6443
I0111 06:52:48.877668       1 graceful_termination.go:160] Trying to delete rs: 10.96.0.1:443/TCP/10.130.29.80:6443
I0111 06:52:48.877823       1 graceful_termination.go:160] Trying to delete rs: 10.96.0.1:443/TCP/10.130.29.81:6443
I0111 06:53:48.878217       1 graceful_termination.go:160] Trying to delete rs: 10.96.0.1:443/TCP/10.130.29.80:6443
I0111 06:53:48.878490       1 graceful_termination.go:160] Trying to delete rs: 10.96.0.1:443/TCP/10.130.29.81:6443
I0111 06:54:48.878874       1 graceful_termination.go:160] Trying to delete rs: 10.96.0.1:443/TCP/10.130.29.80:6443
I0111 06:54:48.879044       1 graceful_termination.go:160] Trying to delete rs: 10.96.0.1:443/TCP/10.130.29.81:6443
I0111 06:55:48.879356       1 graceful_termination.go:160] Trying to delete rs: 10.96.0.1:443/TCP/10.130.29.80:6443
I0111 06:55:48.879550       1 graceful_termination.go:160] Trying to delete rs: 10.96.0.1:443/TCP/10.130.29.81:6443

参考修复该问题的PR中的解释,原因是 kube-proxy中两个goroutine同时调用libipvs产生了死锁,新版本在对libipvs的调用上加了锁。

至于为什么直到在线上PoC部署的时候才遇到这个问题,主要原因还是在线下测试的不够多,使用量大的集群只更新了1.13.0,但从没有开启过IPVS,而同时升级了1.13.0并且开启IPVS的测试集群都非常的短命,也就没有机会等到这个问题出现。

相关文章

KubeSphere 部署向量数据库 Milvus 实战指南
探索 Kubernetes 持久化存储之 Longhorn 初窥门径
征服 Docker 镜像访问限制!KubeSphere v3.4.1 成功部署全攻略
那些年在 Terraform 上吃到的糖和踩过的坑
无需 Kubernetes 测试 Kubernetes 网络实现
Kubernetes v1.31 中的移除和主要变更

发布评论