解决pod健康检查问题

2023-05-27 10:25:43 浏览数 (1)

解决pod健康检查问题

引自:Solving the mystery of pods health checks failures in Kubernetes。原文中的某些描述并不清晰,本文作了调整。

很早以前,环境中的pod有时候会遇到健康检查失败的问题,但并没有什么明显表征,且几乎是立马就会恢复。由于这种情况很少发生,且不会对业务造成影响,因此起初并没有人关注该问题。

但后来发生的频率越来越高,导致开发人员频繁接收到deployment的健康告警。

第1步:查看日志

  • Kubernetes worker的系统日志 -- 无异常
  • kubelet 日志 -- 无异常
  • Containerd 日志 -- 无异常
  • CNI 日志 -- 无异常
  • 检查最近失败的pod日志 -- 无异常

通过检查相关日志,并没有发现什么异常

第2步:tcpdump

在抓取的流量中发现,当kubelet给pod发送TCP SYN之后,pod会回复SYN-ACK,但kubelet并没有发送TCP ACK。在一段时间的重试之后,Kubelet会建立起一条TCP会话,因此该问题是随机发生的。

为以防万一,我们检查了TCP中的seq和ack序列号,并没有发现问题。

此时怀疑worker可能存在问题:是不是Kubelet没有处理接收到的报文?

第3步:ss

每秒调用一次"ss -natp"来查看kubelet进程连接,此时发现失败的连接卡在了SYN-SENT阶段,说明kubelet并没有接收到pod发来的SYN-ACK报文。

第4步:conntrack

使用conntrack查看TCP网络连接跟踪,发现有的连接卡在SYN-SENT状态(kubelet侧),有的连接卡在SYN-RECV(pod侧),但连接的源端口号看起来都类似。

在我们的环境中,设定了一个较大的源端口可选范围:

代码语言:javascript复制
net.ipv4.ip_local_port_range=12000 65001

出现问题的源端口为30XXX或31XXX,非常类似。

第5步:ipvs

通过ipvsadm命令查看ipvs配置发现,所有卡住的连接都使用了Kubernetes的nodeport 保留端口

根因分析

至此,问题已经明了。当Kubelet初始化一条TCP连接时,会随机选择一个源端口号,例如31055。当TCP SYN到达pod之后,pod会向31055端口号回复一个TCP SYN-ACK报文。当该报文到达IPVS之后,由于已经存在一个端口号为31055的nodeport(Kubernetes loadbalance service),此时会将TCP SYN-ACK报文转发到对应的后端(其他pod),这样就导致Kubelet无法接收到回复的报文,无法建立连接。

解决办法

解决方式也很简单,设置如下内核参数即可,这样Kubelet在建立连接时就不会选择30000–32768的端口作为TCP源端口:

代码语言:javascript复制
net.ipv4.ip_local_reserved_ports="30000–32768"

Kubernetes的nodeport保留端口为30000-32767,因此设置的net.ipv4.ip_local_reserved_ports为30000–32768

TIPs

  • net.ipv4.ip_local_port_range的默认值为32768 60999,正好和Kubernetes的nodeport保留端口错开,本文中描述的问题的源头也是因为修改了该内核参数,因此非必要不要修改内核参数!

0 人点赞