网络异常排错
本章主要介绍各种常见的网络问题以及排错方法,包括 Pod 访问异常、Service 访问异常以及网络安全策略异常等。
说到 Kubernetes 的网络,其实无非就是以下三种情况之一
- Pod 访问 Node
- Node 访问 Pod
- Pod 访问 Pod
当然,以上每种情况还都分别包括本地访问和跨主机访问两种场景,并且一般情况下都是通过 Service 间接访问 Pod。
排查网络问题基本上也是从这几种情况出发,定位出具体的网络异常点,再进而寻找解决方法。网络异常可能的原因比较多,常见的有
- CNI 网络插件配置错误,导致多主机网络不通,比如
- IP 网段与现有网络冲突
- 插件使用了底层网络不支持的协议
- 忘记开启 IP 转发等
sysctl net.ipv4.ip_forward
sysctl net.bridge.bridge-nf-call-iptables
- Pod 网络路由丢失,比如
- kubenet 要求网络中有 podCIDR 到主机 IP 地址的路由,这些路由如果没有正确配置会导致 Pod 网络通信等问题
- 在公有云平台上,kube-controller-manager 会自动为所有 Node 配置路由,但如果配置不当(如认证授权失败、超出配额等),也有可能导致无法配置路由
- 主机内或者云平台的安全组、防火墙或者安全策略等阻止了 Pod 网络,比如
- 非 Kubernetes 管理的 iptables 规则禁止了 Pod 网络
- 公有云平台的安全组禁止了 Pod 网络(注意 Pod 网络有可能与 Node 网络不在同一个网段)
- 交换机或者路由器的 ACL 禁止了 Pod 网络
Pod 无法解析 DNS,也无法访问外网
如果 Node 上安装的 Docker 版本大于 1.12,那么 Docker 会把默认的 iptables FORWARD 策略改为 DROP。这会引发 Pod 网络访问的问题。解决方法则在每个 Node 上面运行 iptables -P FORWARD ACCEPT
,比如
echo "ExecStartPost=/sbin/iptables -P FORWARD ACCEPT" >> /etc/systemd/system/docker.service.d/exec_start.conf
systemctl daemon-reload
systemctl restart docker
如果使用了 flannel/weave 网络插件,更新为最新版本也可以解决这个问题。
DNS 无法解析也有可能是 kube-dns 服务异常导致的,排查方法可以参考 Kube-dns/Dashboard CrashLoopBackOff 排错 。
Service 无法访问
访问 Service ClusterIP 失败时,可以首先确认是否有对应的 Endpoints
kubectl get endpoints <service-name>
如果该列表为空,则有可能是该 Service 的 LabelSelector 配置错误,可以用下面的方法确认一下
# 查询 Service 的 LabelSelector
kubectl get svc <service-name> -o jsonpath='{.spec.selector}'
# 查询匹配 LabelSelector 的 Pod
kubectl get pods -l key1=value1,key2=value2
如果 Endpoints 正常,可以进一步检查
- Pod 的 containerPort 与 Service 的 containerPort 是否对应
- 直接访问 podIP:containerPort 是否正常
再进一步,即使上述配置都正确无误,还有其他的原因会导致 Service 无法访问,比如
- Pod 内的容器有可能未正常运行或者没有监听在指定的 containerPort 上
- CNI 网络或主机路由异常也会导致类似的问题
- kube-proxy 服务有可能未启动或者未正确配置相应的 iptables 规则,比如正常情况下名为
hostnames
的服务会配置以下 iptables 规则
$ iptables-save | grep hostnames
-A KUBE-SEP-57KPRZ3JQVENLNBR -s 10.244.3.6/32 -m comment --comment "default/hostnames:" -j MARK --set-xmark 0x00004000/0x00004000
-A KUBE-SEP-57KPRZ3JQVENLNBR -p tcp -m comment --comment "default/hostnames:" -m tcp -j DNAT --to-destination 10.244.3.6:9376
-A KUBE-SEP-WNBA2IHDGP2BOBGZ -s 10.244.1.7/32 -m comment --comment "default/hostnames:" -j MARK --set-xmark 0x00004000/0x00004000
-A KUBE-SEP-WNBA2IHDGP2BOBGZ -p tcp -m comment --comment "default/hostnames:" -m tcp -j DNAT --to-destination 10.244.1.7:9376
-A KUBE-SEP-X3P2623AGDH6CDF3 -s 10.244.2.3/32 -m comment --comment "default/hostnames:" -j MARK --set-xmark 0x00004000/0x00004000
-A KUBE-SEP-X3P2623AGDH6CDF3 -p tcp -m comment --comment "default/hostnames:" -m tcp -j DNAT --to-destination 10.244.2.3:9376
-A KUBE-SERVICES -d 10.0.1.175/32 -p tcp -m comment --comment "default/hostnames: cluster IP" -m tcp --dport 80 -j KUBE-SVC-NWV5X2332I4OT4T3
-A KUBE-SVC-NWV5X2332I4OT4T3 -m comment --comment "default/hostnames:" -m statistic --mode random --probability 0.33332999982 -j KUBE-SEP-WNBA2IHDGP2BOBGZ
-A KUBE-SVC-NWV5X2332I4OT4T3 -m comment --comment "default/hostnames:" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-X3P2623AGDH6CDF3
-A KUBE-SVC-NWV5X2332I4OT4T3 -m comment --comment "default/hostnames:" -j KUBE-SEP-57KPRZ3JQVENLNBR
Pod 无法通过 Service 访问自己
这通常是 hairpin 配置错误导致的,可以通过 Kubelet 的 --hairpin-mode
选项配置,可选参数包括 "promiscuous-bridge"、"hairpin-veth" 和 "none"(默认为"promiscuous-bridge")。
对于 hairpin-veth 模式,可以通过以下命令来确认是否生效
$ for intf in /sys/devices/virtual/net/cbr0/brif/*; do cat $intf/hairpin_mode; done
1
1
1
1
而对于 promiscuous-bridge 模式,可以通过以下命令来确认是否生效
$ ifconfig cbr0 |grep PROMISC
UP BROADCAST RUNNING PROMISC MULTICAST MTU:1460 Metric:1