从一次IPVS故障开始的Kubernetes容器网络排障

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 up
Host is up (0.000032s latency).
Host is up (0.000033s latency).
Host is up (0.00063s latency).
Host is up (0.000038s latency).
Nmap done: 4 IP addresses (4 hosts up) scanned in 0.22 seconds

运行的结果是排除了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    <none>        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的测试集群都非常的短命,也就没有机会等到这个问题出现。

文章已创建 23

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部