您现在的位置是:首页 >技术交流 >解开 Kubernetes 中 Pod 健康检查失败之谜网站首页技术交流
解开 Kubernetes 中 Pod 健康检查失败之谜
Pipedrive Infra 在不同的云中(主要是 AWS 和本地 OpenStack)运营自管理的 Kubernetes 集群。
截至撰写本文时,我们管理着 20 多个不同的集群,规模大小不一,有些非常具体,有些则较小。
Pod 健康检查失败的历史
我们很久以前就注意到,有时 Pod 健康检查会无缘无故地失败,然后几乎立即恢复。但是,由于这种情况很少发生且不会影响任何事情,所以没有人认为这是一个问题。
但是,随后它变得更加频繁,这意味着开发人员开始更频繁地收到有关其部署健康状况的警报。当这种情况发生时,通常会首先询问基础设施团队,但没有任何报告:一切看起来都很健康。
我们决定找出这个问题的根本原因。起初,这种情况非常罕见,很难捕捉到事件。
步骤1:日志
-
Kubernetes 工作节点的 syslog — 没有任何信息。
-
Kubelet 日志 — 没有任何信息。
-
Containerd 日志 — 没有任何信息。
-
CNI 日志 — 没有任何信息。
-
最近失败检查的 Pod 中的日志 — 没有任何信息:它们似乎很正常。
-
失败的 Pod “朋友”的日志 — 没有任何信息:它们似乎没有检测到任何友好服务的停机时间。
但是健康检查仍然失败,而且发生得更加频繁,出现在更多的地方!
需要注意的是:
-
它与云没有关系:我们在 AWS ec2 和本地虚拟机中都经历了相同的失败节奏。
-
与 CNI 无关:不同的云使用不同的 CNI。我们在本地使用 calico,在 AWS 中使用 AWS CNI。
-
与 Containerd 或 Kubernetes 版本无关:它在任何地方都失败了。
-
不依赖于集群负载:它发生在测试环境中,在高峰期和夜间。
由于看起来是一个“网络问题”,所以这个问题被分配给了网络团队,他们想要查看实际的流量,并使用 tcpdump 进行了调查。
步骤2:tcpdump
在流量捕获中,我们注意到当 Kubelet 向 Pod 发送 TCP SYN 时,Pod 会回复 TCP SYN-ACK,但是 Kubelet 没有跟随 TCP ACK。经过一些重试后,Kubelet 建立了一个没有问题的 TCP 会话 — 这是一个完全随机的故障。
为了确保,我们检查了失败的 TCP 流中的 seq 和 ack 数字,一切都非常正常。
我们开始怀疑工作节点上的源进程:如果 Kubelet 发生了什么事情,它不想继续怎么办?
步骤3:ss
我们每秒钟检查一次“ss -natp”的输出。这应该显示一些东西 — 至少每个进程的连接数。
我们很快发现,失败的连接被卡在 SYN-SENT 中。这与我们在 tcpdump 中看到的不符,应该是 SYN-RECV。
步骤4:conntrack
在 Conntrack 中,损坏的连接被卡在 SYN-RECV 中,这至少是可以预期的。
在通过防火墙后将返回流量返回给 Kubelet 时可能会发生什么?什么可以防止 TCP SYN-ACK 到达 Kubelet 打开的套接字?
此时,我们已经没有更多的想法了,但我们注意到卡在 SYN-SENT 或 SYN-RECV 中的连接并不完全是随机的,因为所有源端口似乎都相似。
我们允许使用广泛的端口作为源:
net.ipv4.ip_local_port_range=12000 65001
有问题的端口看起来像是 30XXX 或 31XXX,这个范围看起来非常熟悉。
步骤5:ipvs
我们使用 ipvsadm 检查了我们的 ipvs 配置,并发现所有卡在连接中的端口都被 Kubernetes nodeport 保留了。
发现根本原因是这样的:Kubelet使用一个随机源端口(例如31055)向pod发起TCP会话。TCP SYN到达pod,pod回复TCP SYN-ACK到端口31055。回复命中IPVS,我们在Kubernetes服务上使用nodeport 31055的负载均衡器。TCP SYN-ACK被重定向到服务端点(其他pod)。结果是可预测的:没有人回答。
解决健康检查失败的方法是禁止使用nodeport范围作为TCP会话的源端口。
幸运的是,只需要一行代码:
net.ipv4.ip_local_reserved_ports=”30000–32768"
在所有节点上实施后,问题立即得到解决。具有讽刺意味的是,几个小时的故障排除只产生了一行代码。
作者:Roman Kuchin
更多技术干货请关注公号“云原生数据库”