【摘要】 网络/容器网络因为其本身的复杂性,以及很多侧重软件开发的童鞋并不熟悉网络,导致和网络/容器网络相关问题的定位、分析和解决都比较困难,很多时 候有无从下手之感。我对最近、再加上以前参与处理了的几个网络/容器网络相关的问题,总结如下,希望能给遇到类似的问题的童鞋一点启发。
目录
1. 目标主机上没有到源主机的路由,导致ping包没有返回 - route
2. Pod可以跨节点访问docker0 IP,但是不能跨节点访问Pod IP - ip_forward
3. Flannel隧道内部payload的源地址改变为flannel0设备地址 - iptables
4. 从Docker容器内可以ping通宿主机IP,但ping不通其它主机 - iptables
5. K8s集群掉电,导致所有的服务不能访问 - flannel & docker0
6. K8s Master主机上ping不通Node节点上的docker0和容器,但在Node上可以ping通 - iptables
网络/容器网络因为其本身的复杂性,以及很多侧重软件开发的童鞋并不熟悉网络,导致和网络/容器网络相关问题的定位、分析和解决都比较困难,很多时 候有无从下手之感。我对最近、再加上以前参与处理了的几个网络/容器网络相关的问题,总结如下,希望能给遇到类似的问题的童鞋一点启发。
总结一下,引起网络不通的主要原因有:
路由(route)问题。这类问题多如牛毛。常见的是目标主机到源主机的路由不通。
防火墙(iptables)问题。这类问题多如牛毛,但iptables比route更复杂。主要有:(1)防火墙filter表的规则阻止icmp包;(2)防火墙NAT表的SNAT/MASQUERADE。
系统内核参数问题。较少,更隐蔽,现象怪异得会让你怀疑自己的智商和人生。例如ip_forward,rp_filter等。
Linux设备参数设置。较少,更隐蔽,现象怪异得会让你怀疑自己的智商和人生。例如MTU等。 至于IP地址配置错误等问题就太明显了,不在本文讨论之列。
1. 目标主机上没有到源主机的路由,导致ping包没有返回 - route
问题现象:
某类生产环境,因为网络不通导致安装PaaS平台失败。从源主机ping目标主机,数据包没有返回。
分析排查:
思路得清楚,你需要画一张类似如下的图:
在目标主机使用route -n查看没有到源主机网络的路由。
源主机和目标主机都需要加路由。否则目标机器能收到ping包,但是返回报文会因为路由不通丢失。
route add -net 192.144.52.0/24 gw 192.144.152.253 dev eth1
route add -net 192.144.152.0/24 gw 192.144.52.253 dev eth0
2. Pod可以跨节点访问docker0 IP,但是不能跨节点访问Pod IP - ip_forward
问题现象:
某生产环境,K8s+Docker+flannel。从pod内跨节点ping另外节点的pod ip,不通;但是ping另外节点的docker0,是可以ping通的。意思是Pod可以跨节点访问docker0(证明flannel隧道是可以的),但是不能跨节点访问pod。
图示如下:
分析排查:
排查如下:
测试如下:
问题解决:
因为问题现象比较怪异,一度开始怀疑自己的智商和人生。
最后欧拉的同事发现是故障节点的内核参数ip_forward=0。
这个ip_forward是必须打开的参数,否则内核收到来自docker0的非本地地址的数据包会丢弃,而不会转发(forward)出去。
进一步分析,应该是EulerOS的security-tool启动时修改了内核参数(包括设置net.ipv4.ip_forward=0)导致的问题。
Docker engine启动的时候默认也会设置net.ipv4.ip_forward=1,但是security-tool和docker engine都是系统的service,启动时间和快慢不同,可能导致这个值被两个工具设置的先后顺序不一样,导致我们看到不一样的行为。(注:这个分析待商榷)
建议的解决办法:
设置/etc/sysconf中的net.ipv4.ip_forward=1,执行sysctl –p。但这个需要欧拉Docker确认security-tool修改的其它内核参数和安全加固等,会不会影响docker引擎。
如果还不行,就修改security-tool的配置(不推荐): 修改/etc/euleros_security/security.conf的
301@m@/etc/sysctl.conf@net.ipv4.ip_forward=@0 --->
301@m@/etc/sysctl.conf@net.ipv4.ip_forward=@1
当然我们的Salt脚本也会显式地设置这个值。
3. Flannel隧道内部payload的源地址改变为flannel0设备地址 - iptables
问题现象:
但是在抓包的过程中,发现Flannel隧道内部payload的源地址改变为发起请求的Pod所在节点的flannel0设备地址,而不是发起请求的Pod的IP地址。
关于这个问题的详细分析,请见Flannel隧道内部payload的源地址改变为flannel0设备地址的问题分析
分析排查:
首先,应该不是Kube-Proxy做的修改。因为Kube-Proxy只对访问Service(ClusterIP)的流量,做了很多iptables规则;而抓包实验中是直接使用Pod的IP地址操作的。
其次,进程flanneld应该不会修改这个源IP地址。因为flanneld在跨节点传送数据的过程中的一个主要作用是做隧道的解/封装,它没有理由去修改payload的内容(inner source ip)。
我们有理由相信,flanneld拿到这个数据包时,这个inner source ip已经被修改成发起请求的Pod所在节点的flannel0设备地址了。
通过查看iptables规则,发现有如下一条:
-A POSTROUTING -s 10.1.15.0/24 ! -o docker0 -j MASQUERADE
这是docker设置的。它的意思是:在POSTROUTING链上,如果源地址属于10.1.15.0/24,且出站网络接口不是docker0,则做地址伪装。
MASQUERADE,即IP伪装,是SNAT的一种特殊形式。这应该只用于动态分配IP地址的场景。在MASQURADE中,你不需要明确指定源地址,iptables会使用该数据包出站时的网口的IP地址。关于MASQUERADE,请参考Kubernetes和容器网络详解系列之Kubernetes iptables。
由于以上这条规则,从docker0入站的流量,最终通过flannel0出站时,会把数据包源地址修改为出站接口flannel0的地址。这个时候flanneld再做封装时,payload中的源地址(inner source ip)已经被修改成flannel0的地址了。
问题解决:
删除这条规则。
4. 从Docker容器内可以ping通宿主机IP,但ping不通其它主机IP - iptables
问题现象:
从Docker容器内可以ping通宿主机IP,但ping不通其它主机IP。
分析排查:
从现象可以看到数据包可以从Pod中经过docker0出去。检查防火墙规则的时候发现是因为没有做SNAT。这样会导致目标主机可以收到ping包,但是返回包会丢失。(没有抓码流验证)
问题解决:
给防火墙添加了如下iptables规则: iptables -t nat -A POSTROUTING -s 172.16.94.0/24 -j SNAT --to <宿主机IP>
5. K8s集群掉电,导致所有的服务不能访问 - flannel & docker0
问题现象:
K8s集群掉电后,服务不能访问。 查看主机上的flannel.1和docker0网络,发现网段不一致。
如下:
分析排查:
原因:掉电导致集群节点的docker0网络配置和flannel0的网络配置不一致。flannel的网络配置是保存在etcd集群中的,以此为准。
问题解决:
解决办法:
1、ifconfig flannel0和ifconfig docker0看到二者的网络配置不一致
2、停止docker0,并删除之:ifconfig docker0 down; brctl delbr docker0
3、修改/etc/default/docker中的--bip,和flannel0的保持一致
4、重启docker daemon,service docker restart
5、docker服务运行正常后,ifconfig docker0检查:(1)docker0是否重新创建;(2)网段配置是否和flannel0保持一致。如果提示没有docker0设备,稍等一会,或者再次重启docker服务
6、重启docker服务,会造成上面的pod运行状态错误为Error。用watch kubectl get pods监视一会,会自动恢复正常。
7、按照常规测试业务的连通性。
建议:较新版本的CCE已经对此问题有处理手段,建议升级。
6. K8s Master主机上ping不通Node节点上的docker0和容器,但在Node上可以ping通 - iptables
问题现象:
一个K8s集群,包含一个Master三个Node,Master没有Node的角色,但同时安装和启动了Flannel。 使用Flannel做Overlay,backend是VXLAN。从Master上ping其它Node的docker0/Pod IP不通,从Node节点上互ping docker0/Pod IP没有问题。 导致开放原生K8s API失败(从api server代理proxy流量到Pod失败)。
分析排查:
在目标Node节点的flannel.1上抓包,可以看到有返回包,但是收不到。 检查IP、路由、网络设备等均无发现问题。
在Master节点上,用ipables-save查看防火墙规则,发现如下两条和icmp相关的,一条在INPUT链上,一条在FORWARD链上:
-A INPUT -j REJECT --reject-with icmp-host-prohibited
... ...
-A FORWARD -j REJECT --reject-with icmp-host-prohibited
可以看出Master节点会拒绝掉icmp包,理由是主机不允许。
问题解决:
找到以上两条防火墙规则的所在行号,然后删除之:
查看INPUT链:
iptables -L INPUT --line-numbers
如下:
7 REJECT all -- anywhere anywhere reject-with icmp-host-prohibited
删除:
iptables -D INPUT 7
查看FORWARD链:
iptables -L FORWARD --line-numbers
如下:
13 REJECT all -- anywhere anywhere reject-with icmp-host-prohibited
删除:
iptables -D FORWARD 13
来源:华为云社区 作者: 乔雷