1、Pod 网络异常
-
网络不可达:主要现象为 ping 不通,其可能原因为: -
源端和目的端防火墙(iptables、selinux)限制 -
网络路由配置不正确 -
源端和目的端的系统负载过高,网络连接数满,网卡队列满 -
网络链路故障 -
端口不可达:主要现象为可以 ping 通,但 telnet 端口不通,其可能原因为: -
源端和目的端防火墙限制 -
源端和目的端的系统负载过高,网络连接数满,网卡队列满,端口耗尽 -
目的端应用未正常监听导致(应用未启动,或监听为 127.0.0.1 等) -
DNS 解析异常:主要现象为基础网络可以连通,访问域名报错无法解析,访问 IP 可以正常连通。其可能原因为: -
Pod 的 DNS 配置不正确 -
DNS 服务异常 -
Pod 与 DNS 服务通讯异常 -
大数据包丢包:主要现象为基础网络和端口均可以连通,小数据包收发无异常,大数据包丢包。可能原因为: -
可使用 ping -s
指定数据包大小进行测试 -
数据包的大小超过了 Docker、CNI 插件、或者宿主机网卡的 MTU 值。 -
CNI 异常:主要现象为 Node 可以通,但 Pod 无法访问集群地址,可能原因有: -
kube-proxy 服务异常,没有生成 iptables 策略或者 ipvs 规则导致无法访问 -
CIDR 耗尽,无法为 Node 注入 PodCIDR 导致 CNI 插件异常 -
其他 CNI 插件问题
那么整个 Pod 网络异常分类可以如下图所示:
总结一下,Pod 最常见的网络故障有,网络不可达(ping 不通);端口不可达(telnet 不通);DNS 解析异常(域名不通)与大数据包丢失(大包不通)。
2、常用网络排查工具
在了解到常见的网络异常后,在排查时就需要使用到一些网络工具才可以很有效的定位到网络故障原因,下面会介绍一些网络排查工具。
tcpdump
tcpdump 网络嗅探器,将强大和简单结合到一个单一的命令行界面中,能够将网络中的报文抓取,输出到屏幕或者记录到文件中。
各系统下的安装:
-
Ubuntu/Debian: tcpdump;apt-get install -y tcpdump -
Centos/Fedora: tcpdump;yum install -y tcpdump -
Apline:tcpdump ;apk add tcpdump --no-cache
查看指定接口上的所有通讯。
语法:
捕获所有网络接口:
tcpdump -D
按 IP 查找流量:最常见的查询之一 host,可以看到来往于 1.1.1.1 的流量。
tcpdump host 1.1.1.1
按源 / 目的 地址过滤:如果只想查看来自 / 向某方向流量,可以使用 src 和 dst。
tcpdump src|dst 1.1.1.1
通过网络查找数据包:
使用 net 选项,来要查找出 / 入某个网络或子网的数据包。
tcpdump net 1.2.3.0/24
使用十六进制输出数据包内容:
hex 可以以 16 进制输出包的内容
tcpdump -c 1 -X icmp
查看特定端口的流量:
使用 port 选项来查找特定的端口流量。
tcpdump port 3389
tcpdump src port 1025
查找端口范围的流量:
tcpdump portrange 21-23
过滤包的大小:
如果需要查找特定大小的数据包,可以使用以下选项。你可以使用 less,greater。
tcpdump less 32
tcpdump greater 64
tcpdump <= 128
捕获流量输出为文件:
-w
可以将数据包捕获保存到一个文件中以便将来进行分析。这些文件称为 PCAP(PEE-cap)文件,它们可以由不同的工具处理,包括 Wireshark 。
tcpdump port 80 -w capture_file
组合条件:
tcpdump 也可以结合逻辑运算符进行组合条件查询:
-
ANDand or && -
ORor or || -
EXCEPTnot or !
tcpdump -i eth0 -nn host 220.181.57.216 and 10.0.0.1
# 主机之间的通讯
tcpdump -i eth0 -nn host 220.181.57.216 or 10.0.0.1
# 获取10.0.0.1与 10.0.0.9或 10.0.0.1 与10.0.0.3之间的通讯
tcpdump -i eth0 -nn host 10.0.0.1 and \(10.0.0.9 or 10.0.0.3\)
原始输出:
并显示人类可读的内容进行输出包(不包含内容)。
tcpdump -ttnnvvS -i eth0
tcpdump -ttnnvvS -i eth0
IP 到端口:
让我们查找从某个 IP 到端口任何主机的某个端口所有流量。
tcpdump -nnvvS src 10.5.2.3 and dst port 3389
去除特定流量:
可以将指定的流量排除,如这显示所有到 192.168.0.2 的 非 ICMP 的流量。
tcpdump dst 192.168.0.2 and src net and not icmp
来自非指定端口的流量,如,显示来自不是 SSH 流量的主机的所有流量。
tcpdump -vv src mars and not dst port 22
选项分组:
在构建复杂查询时,必须使用单引号 '。单引号用于忽略特殊符号 () ,以便于使用其他表达式(如 host, port, net 等)进行分组。
tcpdump
'src 10.0.2.4 and (dst port 3389 or 22)'
过滤 TCP 标记位。
TCP RST:
下面的过滤器可以找到这些不同的数据包,因为tcp[13]看的是TCP头中的偏移量13,数字代表字节内的位置,而!=0意味着相关的标志被设置为1,即它是打开的。
tcpdump
'tcp[13] & 4!=0'
tcpdump
'tcp[tcpflags] == tcp-rst'
TCP SYN:
tcpdump
'tcp[13] & 2!=0'
tcpdump
'tcp[tcpflags] == tcp-syn'
同时忽略 SYN 和 ACK 标志的数据包。
tcpdump
'tcp[13]=18'
TCP URG:
tcpdump
'tcp[13] & 32!=0'
tcpdump
'tcp[tcpflags] == tcp-urg'
TCP ACK:
tcpdump
'tcp[13] & 16!=0'
tcpdump
'tcp[tcpflags] == tcp-ack'
TCP PSH:
tcpdump
'tcp[13] & 8!=0'
tcpdump
'tcp[tcpflags] == tcp-push'
TCP FIN:
tcpdump
'tcp[13] & 1!=0'
tcpdump
'tcp[tcpflags] == tcp-fin'
查找 http 包。
查找 user-agent 信息:
tcpdump -vvAls0 | grep
'User-Agent:'
查找只是 GET 请求的流量:
tcpdump -vvAls0 | grep
'GET'
查找 http 客户端 IP:
tcpdump -vvAls0 | grep
'Host:'
查询客户端 cookie:
tcpdump -vvAls0 | grep
'Set-Cookie|Host:|Cookie:'
查找 DNS 流量:
tcpdump -vvAs0 port 53
查找对应流量的明文密码:
tcpdump port http or port ftp or port smtp or port imap or port pop3 or port telnet -lA | egrep -i -B5
'pass=|pwd=|log=|login=|user=|username=|pw=|passw=|passwd= |password=|pass:|user:|username:|password:|login:|pass |user '
wireshark 追踪流:wireshare 追踪流可以很好的了解出在一次交互过程中都发生了那些问题。
wireshare 选中包,右键选择 “追踪流“ 如果该包是允许的协议是可以打开该选项的。
关于抓包节点和抓包设备:
如何抓取有用的包,以及如何找到对应的接口,有以下建议:
-
抓包节点 :通常情况下会在源端和目的端两端同时抓包,观察数据包是否从源端正常发出,目的端是否接收到数据包并给源端回包,以及源端是否正常接收到回包。如果有丢包现象,则沿网络链路上各节点抓包排查。例如,A 节点经过 c 节点到 B 节点,先在 AB 两端同时抓包,如果 B 节点未收到 A 节点的包,则在 c 节点同时抓包。 -
抓包设备 :对于 Kubernetes 集群中的 Pod,由于容器内不便于抓包,通常视情况在 Pod 数据包经过的 veth 设备,docker0 网桥,CNI 插件设备(如 cni0,flannel.1 etc..)及 Pod 所在节点的网卡设备上指定 Pod IP 进行抓包。选取的设备根据怀疑导致网络问题的原因而定,比如范围由大缩小,从源端逐渐靠近目的端,比如怀疑是 CNI 插件导致,则在 CNI 插件设备上抓包。从 pod 发出的包逐一经过 veth 设备,cni0 设备,flannel0,宿主机网卡,到达对端,抓包时可按顺序逐一抓包,定位问题节点。
nsenter
nsenter -t pid -n <commond>
,
-t
接 进程 ID 号,
-n
表示进入名称空间内,
为执行的命令。
$ ps -ef|grep tail
root 17636 62887 0 20:19 pts/2 00:00:00 grep --color=auto tail
root 30858 30838 0 15:55 ? 00:00:01 tail -f
$ nsenter -t 30858 -n ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1480
inet 192.168.1.213 netmask 255.255.255.0 broadcast 192.168.1.255
ether 5e:d5:98:af:dc:6b txqueuelen 0 (Ethernet)
RX packets 92 bytes 9100 (8.8 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 92 bytes 8422 (8.2 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
loop txqueuelen 1000 (Local Loopback)
RX packets 5 bytes 448 (448.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 5 bytes 448 (448.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
net1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 10.1.0.201 netmask 255.255.255.0 broadcast 10.1.0.255
ether b2:79:f9:dd:2a:10 txqueuelen 0 (Ethernet)
RX packets 228 bytes 21272 (20.7 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 216 bytes 20272 (19.7 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
如何定位 Pod 名称空间:
首先需要确定 Pod 所在的节点名称。
$ kubectl get pods -owide |awk
'{print $1,$7}'
NAME NODE
netbox-85865d5556-hfg6v master-machine
netbox-85865d5556-vlgr4 node01
如果 Pod 不在当前节点还需要用 IP 登录则还需要查看 IP(可选)。
$ kubectl get pods -owide |awk
'{print $1,$6,$7}'
NAME IP NODE
netbox-85865d5556-hfg6v 192.168.1.213 master-machine
netbox-85865d5556-vlgr4 192.168.0.4 node01
接下来,登录节点,获取容器 lD,如下列所示,每个 pod 默认有一个 pause 容器,其他为用户 yaml 文件中定义的容器,理论上所有容器共享相同的网络命名空间,排查时可任选一个容器。
$ docker ps |grep netbox-85865d5556-hfg6v
6f8c58377aae f78dd05f11ff
"tail -f" 45 hours ago Up 45 hours k8s_netbox_netbox-85865d5556-hfg6v_default_4a8e2da8-05d1-4c81-97a7-3d76343a323a_0
b9c732ee457e registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.1
"/pause" 45 hours ago Up 45 hours k8s_POD_netbox-85865d5556-hfg6v_default_4a8e2da8-05d1-4c81-97a7-3d76343a323a_0
接下来获得获取容器在节点系统中对应的进程号,如下所示:
$ docker inspect --format
"{{ .State.Pid }}" 6f8c58377aae
30858
最后就可以通过 nsenter 进入容器网络空间执行命令了。
paping
paping 命令可对目标地址指定端口以 TCP 协议进行连续 ping,通过这种特性可以弥补 ping ICMP 协议,以及 nmap、telnet 只能进行一次操作的的不足;通常情况下会用于测试端口连通性和丢包率。
paping download[2]:
paping 还需要安装以下依赖,这取决于你安装的 paping 版本
-
RedHat/CentOS:yum install -y libstdc++.i686 glibc.i686 -
Ubuntu/Debian:最小化安装无需依赖
$ paping -h
paping v1.5.5 - Copyright (c) 2011 Mike Lovell
Syntax: paping [options] destination
Options:
-?, --
help display usage
-p, --port N
set TCP port N (required)
--nocolor Disable color output
-t, --timeout timeout
in milliseconds (default 1000)
-c, --count N
set number of checks to N
mtr
mtr 是一个跨平台的网络诊断工具,将 traceroute 和 ping 的功能结合到一个工具。与 traceroute 不同的是 mtr 显示的信息比起 traceroute 更加丰富:通过 mtr 可以确定网络的条数,并且可以同时打印响应百分比以及网络中各跳跃点的响应时间。
简单的使用示例:
最简单的示例,就是后接域名或 IP,这将跟踪整个路由。
$ mtr google.com
Start: Thu Jun 28 12:10:13 2018
HOST: TecMint Loss% Snt Last Avg Best Wrst StDev
1.|-- 192.168.0.1 0.0% 5 0.3 0.3 0.3 0.4 0.0
2.|-- 5.5.5.211 0.0% 5 0.7 0.9 0.7 1.3 0.0
3.|-- 209.snat-111-91-120.hns.n 80.0% 5 7.1 7.1 7.1 7.1 0.0
4.|-- 72.14.194.226 0.0% 5 1.9 2.9 1.9 4.4 1.1
5.|-- 108.170.248.161 0.0% 5 2.9 3.5 2.0 4.3 0.7
6.|-- 216.239.62.237 0.0% 5 3.0 6.2 2.9 18.3 6.7
7.|-- bom05s12-in-f14.1e100.net 0.0% 5 2.1 2.4 2.0 3.8 0.5
-n
强制 mtr 打印 IP 地址而不是主机名。
$ mtr -n google.com
Start: Thu Jun 28 12:12:58 2018
HOST: TecMint Loss% Snt Last Avg Best Wrst StDev
1.|-- 192.168.0.1 0.0% 5 0.3 0.3 0.3 0.4 0.0
2.|-- 5.5.5.211 0.0% 5 0.9 0.9 0.8 1.1 0.0
3.|-- ??? 100.0 5 0.0 0.0 0.0 0.0 0.0
4.|-- 72.14.194.226 0.0% 5 2.0 2.0 1.9 2.0 0.0
5.|-- 108.170.248.161 0.0% 5 2.3 2.3 2.2 2.4 0.0
6.|-- 216.239.62.237 0.0% 5 3.0 3.2 3.0 3.3 0.0
7.|-- 172.217.160.174 0.0% 5 3.7 3.6 2.0 5.3 1.4
-b
同时显示 IP 地址与主机名。
$ mtr -b google.com
Start: Thu Jun 28 12:14:36 2018
HOST: TecMint Loss% Snt Last Avg Best Wrst StDev
1.|-- 192.168.0.1 0.0% 5 0.3 0.3 0.3 0.4 0.0
2.|-- 5.5.5.211 0.0% 5 0.7 0.8 0.6 1.0 0.0
3.|-- 209.snat-111-91-120.hns.n 0.0% 5 1.4 1.6 1.3 2.1 0.0
4.|-- 72.14.194.226 0.0% 5 1.8 2.1 1.8 2.6 0.0
5.|-- 108.170.248.209 0.0% 5 2.0 1.9 1.8 2.0 0.0
6.|-- 216.239.56.115 0.0% 5 2.4 2.7 2.4 2.9 0.0
7.|-- bom07s15-in-f14.1e100.net 0.0% 5 3.7 2.2 1.7 3.7 0.9
-c
跟一个具体的值,这将限制 mtr ping 的次数,到达次数后会退出。
$ mtr -c5 google.com
如果需要指定次数,并且在退出后保存这些数据,使用
-r flag
。
$ mtr -r -c 5 google.com > 1
$ cat 1
Start: Sun Aug 21 22:06:49 2022
HOST: xxxxx.xxxxx.xxxx.xxxx Loss% Snt Last Avg Best Wrst StDev
1.|-- gateway 0.0% 5 0.6 146.8 0.6 420.2 191.4
2.|-- 212.xx.21.241 0.0% 5 0.4 1.0 0.4 2.3 0.5
3.|-- 188.xxx.106.124 0.0% 5 0.7 1.1 0.7 2.1 0.5
4.|-- ??? 100.0 5 0.0 0.0 0.0 0.0 0.0
5.|-- 72.14.209.89 0.0% 5 43.2 43.3 43.1 43.3 0.0
6.|-- 108.xxx.250.33 0.0% 5 43.2 43.1 43.1 43.2 0.0
7.|-- 108.xxx.250.34 0.0% 5 43.7 43.6 43.5 43.7 0.0
8.|-- 142.xxx.238.82 0.0% 5 60.6 60.9 60.6 61.2 0.0
9.|-- 142.xxx.238.64 0.0% 5 59.7 67.5 59.3 89.8 13.2
10.|-- 142.xxx.37.81 0.0% 5 62.7 62.9 62.6 63.5 0.0
11.|-- 142.xxx.229.85 0.0% 5 61.0 60.9 60.7 61.3 0.0
12.|-- xx-in-f14.1e100.net 0.0% 5 59.0 58.9 58.9 59.0 0.0
默认使用的是 ICMP 协议
-i
,
可以指定 -u、-t 使用其他协议。
mtr --tcp google.com
-m 指定最大的跳数。
mtr -m 35 216.58.223.78
-s 指定包的大小。
mtr 输出的数据:
colum | describe |
---|---|
last | 最近一次的探测延迟值 |
avg | 探测延迟的平均值 |
best | 探测延迟的最小值 |
wrst | 探测延迟的最大值 |
stdev | 标准偏差。越大说明相应节点越不稳定 |
丢包判断:
任一节点的 Loss%(丢包率)如果不为零,则说明这一跳网络可能存在问题。导致相应节点丢包的原因通常有两种。
-
运营商基于安全或性能需求,人为限制了节点的 ICMP 发送速率,导致丢包。 -
节点确实存在异常,导致丢包。可以结合异常节点及其后续节点的丢包情况,来判定丢包原因。
Notes:
-
如果随后节点均没有丢包,则通常说明异常节点丢包是由于运营商策略限制所致。可以忽略相关丢包。
-
如果随后节点也出现丢包,则通常说明节点确实存在网络异常,导致丢包。对于这种情况,如果异常节点及其后续节点连续出现丢包,而且各节点的丢包率不同,则通常以最后几跳的丢包率为准。如链路测试在第 5、6、7 跳均出现了丢包。最终丢包情况以第 7 跳作为参考。
延迟判断:
由于链路抖动或其它因素的影响,节点的 Best 和 Worst 值可能相差很大。而 Avg(平均值)统计了自链路测试以来所有探测的平均值,所以能更好的反应出相应节点的网络质量。
而 StDev(标准偏差值)越高,则说明数据包在相应节点的延时值越不相同(越离散)。所以标准偏差值可用于协助判断 Avg 是否真实反应了相应节点的网络质量。
例如,如果标准偏差很大,说明数据包的延迟是不确定的。可能某些数据包延迟很小(例如:25ms),而另一些延迟却很大(例如:350ms),但最终得到的平均延迟反而可能是正常的。所以此时 Avg 并不能很好的反应出实际的网络质量情况。
这就需要结合如下情况进行判断:
-
如果 StDev 很高,则同步观察相应节点的 Best 和 wrst,来判断相应节点是否存在异常。 -
如果 StDev 不高,则通过 Avg 来判断相应节点是否存在异常。
Tips:对于更多的网络工具的使用可以参考这篇文章[3]。
3、Pod 网络排查流程
Pod网络异常时排查思路,可以按照下图所示:
Pod network troubleshooting idea
4、案例学习
扩容节点访问 service 地址不通
测试环境 Kubernetes 节点扩容后无法访问集群 clusterlP 类型的 registry 服务。
环境信息:
IP | Hostname | role |
---|---|---|
10.153.204.15 | yq01-aip-aikefu12 | worknode 节点(本次扩容的问题节点) |
10.153.203.14 | yq01-aip-aikefu31 | master 节点 |
10.61.187.42 | yq01-aip-aikefu2746f8e9 | master 节点 |
10.61.187.48 | yq01-aip-aikefu30b61e25 | master 节点(本次 registry 服务 Pod 所在 节点) |
-
CNI 插件:flannel vxlan -
kube-proxy 工作模式为 iptables -
Registry 服务 -
单实例部署在 10.61.187.48:5000 -
Pod IP:10.233.65.46, -
Cluster IP:10.233.0.100
现象:
-
所有节点之间的 pod 通信正常 -
任意节点和 Pod curl registry 的 Pod 的 IP:5000 均可以连通 -
新扩容节点 10.153.204.15 curl registry 服务的 Cluster lP 10.233.0.100:5000 不通,其他节点 curl 均可以连通
分析思路:
-
根据现象 1 可以初步判断 CNI 插件无异常 -
根据现象 2 可以判断 registry 的 Pod 无异常 -
根据现象 3 可以判断 registry 的 service 异常的可能性不大,可能是新扩容节点访问 registry 的 service 存在异常
怀疑方向:
-
问题节点的 kube-proxy 存在异常 -
问题节点的 iptables 规则存在异常 -
问题节点到 service 的网络层面存在异常
排查过程:
排查问题节点的 kube-proxy
执行
kubectl get pod -owide -nkube-system l grep kube-proxy
查看
kube-proxy Pod
的状态,问题节点上的 kube-proxy Pod 为 running 状态
执行
kubecti logs <nodename> <kube-proxy pod name> -nkube-system
查看问题节点 kube-proxy 的 Pod 日志,没有异常报错
在问题节点操作系统上执行
iptables -S -t nat
查看 iptables 规则
排查过程:
10.233.65.46 路由
查看对端的回程路由:
回程路由
以上排查证明问题原因不是 CNI 插件或者 kube-proxy 异常导致,因此需要在访问链路上抓包,判断问题原因、问题节点执行
curl 10.233.0.100:5000
,
在问题节点和后端 pod 所在节点的 flannel.1 上同时抓包发包节点一直在重传,Cluster lP 已 DNAT 转换为后端 Pod IP,如图所示:
抓包过程,发送端
抓包过程,服务端
图片包传送过程,发送端
收包端收到了包,但未回包,如图所示:
图片包传送过程,服务端
由此可以知道,NAT 的动作已经完成,而只是后端 Pod(Registry 服务)没有回包,接下来在问题节点执行 curl10.233.65.46:5000,在问题节点和后端( registry 服务)Pod 所在节点的 flannel.1 上同时抓包,两节点收发正常,发包如图所示:
正常包发送端
正常包接收端
接下来在两端物理机网卡接口抓包,因为数据包通过物理机网卡会进行 vxlan 封装,需要抓 vxlan 设备的 8472 端口,发包端如图所示:
问题节点物理机网卡接口抓包
对端节点物理机网卡接口抓包
问题节点 IP
问题节点网卡配置
集群外云主机调用集群内应用超时
-
问题现象 :Kubernetes 集群外云主机以 http post 方式访问 Kubernetes 集群应用接口超时。
-
环境信息 :Kubernetes 集群:calicoIP-IP 模式,应用接口以 nodeport 方式对外提供服务。
-
客户端 :Kubernetes 集群之外的云主机。
-
在云主机 telnet 应用接口地址和端口,可以连通,证明网络连通正常,如图所示 -
云主机上调用接口不通,在云主机和 Pod 所在 Kubernetes 节点同时抓包,使用 wireshark 分析数据包
wireshark 分析
集群 Pod 访问对象存储超时
-
使用 nsenter 工具进入 pod 容器网络命名空间测试,ping 对象存储域名不通,报错 unknown server name,ping 对象存储 lP 可以连通。 -
telnet 对象存储 80/443 端口可以连通。 -
paping 对象存储 80/443 端口无丢包。 -
为了验证 Pod 创建好以后的初始阶段网络连通性,将以上测试动作写入 dockerfile,重新生成容器镜像并创 pod,测试结果一致。
-
集群 DNS 服务存在异常 -
上游 DNS 服务存在异常 -
集群 DNS 服务与上游 DNS 通讯异常 -
Pod 访问集群 DNS 服务异常