KubeCon 18会议分享 -- k8s网络复杂性和诊断

在刚刚过去的北美KubeCon 18中,有一位来自Redhat的老哥分享了k8s网络的诊断的方法,阿里云容器服务在客户问题中有挺多是网络的问题,这个分享可以帮助在使用k8s网络遇到问题时的诊断流程。

为啥k8s网络那么复杂

首先介绍了k8s网络为啥这么复杂,都有哪些考虑到的地方,这部分介绍的很多内容也是我们团队在做网络时遇到的一些坑。

k8s网络方案设计需要考虑很多方面:

image.png | left | 747x402

  • 宿主机间的网络:是采用纯硬件的网络还是虚拟的网络(例如openstack)的vxlan,关系到宿主机的性能以及容器网络可以选择的方案,比如宿主机是虚拟网络的话容器网络一般只能采用overlay的方案。
  • 容器间网络:采用Overlay(封包成宿主机的包)的网络还是 Non-overlay的网络以及各自的性能和限制,容器的IP地址管理,容器的网络访问控制(Network Policy), 网络带宽管理(Qos/Traffic sharping)
  • K8S本身的网络结构适配:负载均衡(kube-proxy...), K8S的Network Policy, Ingress服务等等。

需要协调很多层次的网络组件:

  • 容器网络的包会被各个层次去修改,在容器中会有istio等service mesh组件, 在宿主机上也会有kube-proxy/kubelet会修改包的源/目的地址等等
  • 需要考虑到底层网络的一些限制,比如带宽限制,比如arp劫持/防欺骗等等

image.png | left | 747x420

k8s网络配置过程以及诊断:

上面讲了很多网络的复杂度,下面讲师以kubeadm为例介绍了网络的配置过程,针对配置的过程以及每一步做的事情和完成的原因可以了解网络初始化的过程,方便出现问题时排查:

大体上的k8s网络从集群初始化到ready是这样的流程:

image.png | left | 747x405

  1. 先是集群创建出来,节点加入到集群中,这个时候每个节点(kubelet)都通过宿主机网络连通到master节点的管控的组件(apiserver等)上。这个时候在集群中能看到节点列表(kubectl get node),但是每个节点都是NotReady的状态,这个时候非Host网络的Pod都会在Pending状态,因为没有节点网络是Ready的,如下图Coredns就是Pending的,说明宿主机的网络已经联通了,但是容器网络还没有好:

image.png | left | 747x397

  1. 然后每个节点再配置CNI的plugin和配置,比如最常见的我们通过flannel的模板部署flannel的插件,节点在监听到插件和配置存在时会上报状态,这个时候整个节点的网络才ready,这时候k8s会将需要容器网络的容器调度到ready的节点上,Pending状态就会变成Creating状态,然后由节点上的kubelet再调用CNI插件去配置容器的网络,容器就能变成Running状态,这个时候容器的网络也就Ready了:

image.png | left | 719x390

容器网络配置这一步会有哪些问题呢?

在CNI插件和配置文件安装到节点上后,节点就会变成ready了,就会有容器调度到节点上,但不一定容器能正确运行:

  • CNI插件执行失败了,容器一直在Creating,通过kubelet日志或者容器事件可以看到cni的报错
  • CNI插件执行没问题,容器也能变成Running,但是容器网络通信不行(容器间,容器和宿主机(健康检查等)

对于第一种情况:

我们能看到的现象是这样的:容器一直在Creating,通过kubelet日志或者容器事件可以看到cni的报错

image.png | left | 747x327

image.png | left | 747x327

对于第二种情况:

通常可以通过健康检查日志或者容器本身的网络请求的日志看到容器网络是不连通的:

image.png | left | 747x325

如何排查这些问题:

对于容器网络不通的情况,我们要如何排查呢,这里讲师介绍了一些简单的容器虚拟化网络和排查的知识,可以从中入手:

如何区分容器的网卡

ip -d -d link show dev {网卡名} 这个命令可以看到网卡的类型以及详细的一些信息,比如

image.png | left | 747x264

cni0是一个bridge设备,可以比作物理网络中的交换机

image.png | left | 747x326

这个截图是在容器内部执行的,容器的eth0网卡,是一个veth类型的设备,veth是用来联通不同的namespace的,通常用来联通容器和宿主机的网桥,后面的if33代表它联通的另外一端的网卡的编号,到宿主机的namespace中执行ip -d link show我们就能看到这个网卡编号对应的网卡

如果有的容器没有ip 命令,那我们要怎么进到容器中排查呢?可以通过nsenter的命令进入到容器的网络命名空间操作,例如:

image.png | left | 747x352

其中的pid就是容器进程的ID,PID可以通过ps -ef | grep <容器进程>,也可以通过kubectl get pod -o yaml | grep containerID , 然后docker inspect 来找到对应的pid。

排查Iptables:

很多时候遇到网络配置成功,但是容器网络不通的问题都是iptables在从中作梗,比如增加了什么防火墙规则,Chain的Policy被改掉啦之类的。

iptables工作的地方:

image.png | left | 747x318

容器进程访问网络时,会首先经过容器内部的iptables,然后到宿主机上面,在宿主机上面会通过宿主机的iptables和routing最终决定包的取舍和去向。

那么谁会添加iptables规则呢?

答案是谁都会。。。

image.png | left | 747x334

iptables添加为了几个功能: 负载均衡(在PREROUTING中去做DNAT+RAMDOM), 访问控制(在FORWARD/INPUT/OUTPUT)中去做限制, 源地址装换(POSTROUTING中转换成宿主机的IP地址)等等

最后介绍抓包大法:

上面的介绍可见网络的过程和复杂度,但是通常排查时没办法一条一条规则的顺着去排查,何况规则还是由那么多组件操控随时会变化,这时候可以使用抓包工具排查在哪一层包被丢掉了来一一排除的解决问题:
我们可以在不同层次的网卡上抓包来看到底包丢在什么地方,或者被改成了啥熊样。

image.png | left | 747x310

在容器服务这边,通常情况下,我们采用tcpdump的方式通过容器IP等过滤条件抓包排查,如果需要详细的包的解析,可以tcpdump -w写到文件中,然后通过wireshark去解析,这里讲师展示了另外一个工具:kokotap 可以创建一个拷贝的接口,将容器的流量也导入到这个接口上方便wireshark直接在宿主机上也能抓到容器的包。

总结

整个分享就是这些,这个分享中前面介绍了很多k8s网络的复杂度,这块介绍的比较全面,我们在做网络插件时在那些点上也都会遇到一些坑,在后面的介绍中主要是网络排查的入门以及工具,通过这些工具可以上手排查问题了,但排查到现象后具体的原因没有详细介绍,可能因为遇到的问题场景就太多了,大家可以在排查的过程中多多增加经验。

容器服务在k8s网络上有深入的实践,另外有开源的网络插件Terway,覆盖了k8s网络的方方面面,这是之前的介绍文章https://www.atatech.org/articles/102813

猜你喜欢

转载自yq.aliyun.com/articles/679865