基础流程和方法
-
查询 pod 的状态,适用于 pod Pending 的场景:
1
kubectl describe <pod-name> -n <namespace>
-
获取集群中的异常事件,作为排查 pod Pending 原因的补充:
1
kubectl get events -n <namespace> --sort-by .lastTimestamp [-w]
-
获取 pod 的日志,适用于 pod Error 或者 CrashLoopBack 的场景:
1
kubectl logs <pod-name> -n <namespace> [name-of-container, if multiple] [-f]
-
如果 pod 已经处于 Running 状态,并且现有的日志未能直接指出问题,则需要进入 pod 容器进一步测试,例如验证一个正在运行的进程的状态、配置,或者检查容器的网络连接。
如何访问容器
- 通过 exec 命令:
kubectl exec -it <podName> sh
。 - 通过 ephemeral container(需要 Kubernetes v1.23 以上版本)。
- 在镜像中缺乏程序二进制的前提下,执行常用排查工具和命令:
- 获取容器在宿主机对应的 PID:
docker ps | grep k8s_<containerName>_<podName> | awk '{print $1}' | xargs docker inspect --format '{{ .State.Pid }}'
- 在容器的网络命名空间中执行安装于宿主机的工具:
nsenter -t <PID> -n bash
- 退出容器的命名空间(即退出运行于该命名空间中的 shell):
exit
- 获取容器在宿主机对应的 PID:
如何访问节点
- ssh 到节点。
- journalctl -xeu kubelet 查看 kubelet 日志,适用于节点 NotReady 的场景。
常见问题及排查方法
kubectl 执行结果异常
表现:
执行任意 kubectl 命令输出以下结果:
- Error from server (InternalError): an error on the server (“”) has prevented the request from succeeding
- etcdserver: leader changed
原因:
- kubectl 和 apiserver 认证失败
- apiserver 异常
- 通常由于 etcd 导致 apiserver 异常
- etcd 异常
- 选举节点拓扑不满足奇数导致频繁切主
- 磁盘性能太差导致延迟太高甚至频繁切主
排查方法:
-
检查 kubectl 当前所使用的配置文件是否正确:
kubectl config view
- 当前所使用的配置文件
~/.kube/config
是否由该集群生成。 - 检查配置文件中的
server
地址和访问协议是否正确。
- 当前所使用的配置文件
-
apiserver 和 etcd 均属于静态 pod,如果无法使用 kubectl 命令,可直接用容器运行时如 docker 检查容器的日志:
1 2 3 4
# 获取 apiserver 和 etcd 对应的 container_id docker ps -a | grep -e k8s_kube-apiserver -e k8s_etcd # 获取日志 docker logs <container_id> [-f]
-
测试 etcd 所使用的文件系统性能:
1
fio --rw=write --ioengine=sync --fdatasync=1 --filename=<etcd-data-dir>/iotest --size=22m --bs=2300 --name=etcdio-bench
该测试重点关注 etcd 所使用文件系统的 fsync 性能:
fsync/fdatasync/sync_file_range: sync (usec): min=534, max=15766, avg=1273.08, stdev=1084.70 sync percentiles (usec): | 1.00th=[ 553], 5.00th=[ 578], 10.00th=[ 594], 20.00th=[ 627], | 30.00th=[ 709], 40.00th=[ 750], 50.00th=[ 783], 60.00th=[ 1549], | 70.00th=[ 1729], 80.00th=[ 1991], 90.00th=[ 2180], 95.00th=[ 2278], | 99.00th=[ 2376], 99.50th=[ 9634], 99.90th=[15795], 99.95th=[15795], | 99.99th=[15795]
测试结果中,如上述 fsync 99.00th 的值大于 10000 usec(即 P99 > 10ms),通常认为不满足使用条件,建议更换磁盘硬件。
DNS 解析异常
表现:
应用无法连接到 kubernetes apiserver 或其他 DB、proxy 等依赖服务,错误信息里通常包含 53 端口访问超时、can’t resolve host 等。
原因与排查方法:
-
无法访问 kube-dns service 对应的后端 pod
-
应用容器内 dns 配置(
/etc/resolv.conf
)出错导致无法发出正常的解析请求(部分旧版本发行版和 libc 库存在该问题)检查容器内的
/etc/resolv.conf
文件是否包含正确的 nameserver 和 search path,如:1 2 3 4
$ cat /etc/resolv.conf nameserver 11.96.0.10 search qfusion-admin.svc.cluster.local svc.cluster.local cluster.local options ndots:5
-
service 的负载均衡功能异常,出站请求无法 DNAT 到实际的 coreDNS 后端 pod
-
检查 kube-proxy pod 的运行状态。
-
检查应用容器所在节点是否能正常匹配 service 并完成 DNAT。
-
检查节点路由表是否有默认路由或 service 的路由规则,否则数据包不会被 DNAT。
-
检查是否存在 service 对应的 DNAT 规则,以 iptables 为例:
1
iptables-save | grep <serviceName>
对于每个 service 的每个端口,在
KUBE-SERVICES
中应该有1条规则和一个KUBE-SVC-<hash>
链。对于每个 Pod 端点,在该
KUBE-SVC-<hash>
中应该有少量的规则,以及一个KUBE-SEP-<hash>
链,其中有少量的规则。确切的规则将根据你的具体配置(包括节点端口和负载平衡器)而变化。 -
查询当前 NAT 记录:
1 2 3
conntrack -L | grep <dns-ip> # destination conntrack -L -d 10.32.0.1
-
详见 https://kubernetes.io/docs/tasks/debug/debug-application/debug-service/
-
-
跨节点容器网络异常,请求无法到达 DNS pod 所在节点
-
检查 CNI 插件相关组件的运行状态,执行 CNI 插件自带的健康检查或状态检查命令,以 cilium 为例:
1 2
cilium status [--verbose] kubectl -n kube-system exec -it cilium-xrd4d -- cilium-health status
-
检查应用容器和 coredns 之间节点和 pod 的连通性
-
使用 nc 测试两个节点及 pod 之间的四层连通性:
1 2 3 4
# host1 nc -l 9999 # host2 nc -vz <host1> 9999
-
如果容器网络(大部分情况下)采用的是 vxlan 方案,测试 vxlan 默认使用的宿主机 8472 端口是否被阻断。
-
使用
netstat -lnup | grep 8472
或者nc -lu 8472
检查 8472 UDP 端口是否已被其他程序占用。 -
使用
tcpdump
对 8472 端口进行抓包,判断数据包能否经由网络到达。例如:1
tcpdump -i p4p1 dst port 8472 -c 1 -Xvv -nn
-
测试 8472 UDP 是否可使用:
1 2 3 4
# on the server side iperf -s -p 8472 -u # on the client side iperf -c 172.28.128.103 -u -p 8472 -b 1K
-
-
-
-
MTU 问题,网络拓扑中的某一中间设备设置了比发送端网卡更小的 MTU,导致部分大小超过的包被 drop。具体表现为能 ping 通,同一协议和端口有些请求能通,有些请求不通。
-
获取发送端网卡的 MTU:
1 2 3
ifconfig # or ip -4 -s address
-
使用 ping 命令对目标主机的 MTU 进行测试:
1
ping -M do -s 1472 [dest IP]
ICMP头部使用 8 个字节,以太网头部使用 20 个字节,ping 命令再额外发送了 1472 个字节,测试 MTU 1500 字节能否到达目标主机。
-
修改发送端网卡的 MTU
1 2
echo MTU=1450 >> /etc/sysconfig/network-scripts/ifcfg-eth0 # 文件名修改为网卡名称 systemctl restart network
-
-
-
kube-dns 对应的后端 pod 异常导致无法响应解析请求或者未包含 dns 记录
-
kube-dns service 不存在
检查 kube-system 命名空间下 service 以及 endpoint 的状态:
1 2
kubectl -n kube-system get svc | grep dns kubectl -n kube-system get ep | grep dns
-
coreDNS pod 状态异常
使用上文介绍的
kubectl describe
和kubectl logs
命令获取 coreDNS 的 pod 状态及日志。 -
coreDNS 无法响应解析或未包含指定的 dns 记录
进入应用容器以及另一任意容器,测试 coreDNS 能否完成其他容器、节点及域名的解析
- 请求 DNS server 的 53 端口,判断能够访问 DNS server 及其是否正在监听:
nc -vz <ip-of-dns> 53
- 测试域名解析:
dig example.com @<ip-of-dns> +trace
(+trace 输出追踪信息以区分是否使用缓存) - 其他域名解析工具:
nslookup
,host
- 请求 DNS server 的 53 端口,判断能够访问 DNS server 及其是否正在监听:
-
更多排查 dns 解析有关的资料详见 https://kubernetes.io/docs/tasks/administer-cluster/dns-debugging-resolution/
TLS 证书异常
tls 证书的关键概念是多个证书形成一个有效的信任链,从主机上的服务器/叶子证书到多个中间证书,最后到根/CA证书。正是通过形成的信任链,客户端和服务器之间才能开始加密通信会话。
表现:因为 ssl/tls 原因无法完成预期的请求,错误信息通常包含 ssl/tls handshake error
原因:
- 未将根/CA 证书添加到可信任证书中
- 服务端/CA 证书已过期或未到开始生效时间(通常是因为系统时间不一致导致生成了还未到生效时间的证书)
排查方法:
-
使用
openssl
获取指定服务的证书信息:1
openssl s_client -connect <fqdn/ip>:<port>
-
使用
openssl
获取指定服务(以[localhost:6443](http://localhost:6443)
为例)的服务端证书并打印成可读格式:1
openssl s_client -showcerts -connect localhost:6443 </dev/null 2>/dev/null | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | openssl x509 -text
通常可检查输出中的 Issuer(签发者)、Validity(生效时间)和 Subject(CN)。
-
如果遇到
x509: certificate signed by unknown authority
(如 docker CLI 在访问内部的 https 镜像仓库时会抛出以上错误),说明目标服务器使用了不受信任的签发证书(如自签发证书),需要按照应用程序文档安装 CA 证书或变更与信任证书有关的配置。
路由和内核参数配置错误
表现:
无法完成对集群内部或外部某些状态正常服务的访问,报错信息中通常包含 Unreachable 或者 Connection timeout。
原因:
- 应用所在的 pod 或主机的路由表中无访问地址对应的路由项,且没有配置默认路由。
- 与路由有关的内核参数未正确配置或在安装 kubernetes 时开启之后被其他服务重置。
排查方法:
-
使用
netcat
或者curl
测试服务端是否可通过四层网络访问:1 2
nc -vz <ip/fqdn> <port> curl -kL telnet://<ip/fqdn>:<port> -vvv
-
使用
curl
简单测试网络延迟:1
curl -s -w 'Total time: %{time_total}s\n' http://example.com
-
打印系统路由表,注意检查是否存在默认路由:
1 2 3
route -n # or ip route show
-
查询指定 IP 的路由规则:
1
ip route get 10.101.203.141
-
跟踪请求的路由路径:
1
traceroute 10.101.203.141
traceroute 默认使用 ICMP 协议的 ECHO 包,部分设备可能不会响应。
-
net.ipv4.ip_forward
:允许 Linux 在不同网络接口间转发流量的核心参数,绝大部分 CNI 要求启用此参数才能实现 pod 间访问。1 2 3 4 5
sysctl net.ipv4.ip_forward # this will turn things back on a live server sysctl -w net.ipv4.ip_forward=1 # on Centos this will make the setting apply after reboot echo net.ipv4.ip_forward=1 >> /etc/sysconf.d/10-ipv4-forwarding-on.conf
-
net.bridge.bridge-nf-call-iptables
:允许对 Linux Bridge 设备使用 iptables 规则,部分基于 Linux Bridge 的 CNI 需要启用此参数实现容器请求外部网络的 NAT。1 2 3 4 5
sysctl net.bridge.bridge-nf-call-iptables modprobe br_netfilter # turn the iptables setting on sysctl -w net.bridge.bridge-nf-call-iptables=1 echo net.bridge.bridge-nf-call-iptables=1 >> /etc/sysconf.d/10-bridge-nf-call-iptables.conf
-
net.ipv4.conf.all.rp_filter
:部分 CNI (如 Cilium)要求启用此参数实现 pod 间访问,但一些发行版版本的 systemd 可能会覆盖此参数。1 2 3 4 5 6
cat <<EOF > /etc/sysctl.d/99-override_cilium_rp_filter.conf net.ipv4.conf.all.rp_filter = 0 net.ipv4.conf.default.rp_filter = 0 net.ipv4.conf.lxc*.rp_filter = 0 EOF systemctl restart systemd-sysctl
防火墙
表现:
请求被某些 4 层或者 7 层防火墙规则拦截,通常表现为可以访问节点但无法访问部分端口;部分请求无法完成,报错信息中通常包含 Connection Reset。
原因:
- 4 层防火墙禁止访问某些端口或者特定协议。
- 7 层防火墙审计请求的目标路径和请求体内容。
排查方法:
-
nc -vz
可测试四层连通性,curl -kL
可测试七层连通性,如果一个服务四层通但是七层不通,表明可能有七层防火墙。 -
使用
tcpdump
进行抓包分析,重点关注 ACK 为 RST 或者 Reject 的 TCP 包。 -
检查 iptables 是否有 reject 规则,
-reject-with icmp-host-unreachable
也可能表现为有路由规则但是请求返回no route to host
。1
iptables -nvL | grep -i reject
配置错误和程序 BUG
表现:
部分应用无法正常启动或按照预定配置运行,错误信息通常包含 syntax error
原因:
空格或者 tab 导致配置文件格式出现错误
如果以上排查步骤均未发现问题,且在应用容器的日志中发现了可疑的信息,可直接向开发者报告或讨论该问题,但注意不要急于给出结论。
Further reading
- https://tanzu.vmware.com/developer/learningpaths/effective-efficient-kubernetes-debugging/ 通用的 kubernetes 问题排查流程和方法
- https://kubernetes.io/docs/tasks/debug/debug-application/ Debugging Pod、Service、StatefulSet
- https://kubernetes.io/docs/tasks/administer-cluster/dns-debugging-resolution/ Debugging DNS Resolution
- https://goteleport.com/blog/troubleshooting-kubernetes-networking/