您的位置:首页 > 理论基础 > 计算机网络

几个容器网络相关问题的分析和解决总结

2019-07-11 11:56 1746 查看

【摘要】 网络/容器网络因为其本身的复杂性,以及很多侧重软件开发的童鞋并不熟悉网络,导致和网络/容器网络相关问题的定位、分析和解决都比较困难,很多时 候有无从下手之感。我对最近、再加上以前参与处理了的几个网络/容器网络相关的问题,总结如下,希望能给遇到类似的问题的童鞋一点启发。

目录

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

来源:华为云社区  作者: 乔雷

 

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: