k8s网络延迟排查与优化实战分享

2024-08-30 17:11:29 浏览数 (4)

在Kubernetes环境中,网络性能和稳定性对于确保服务的正常运行至关重要。然而,conntrack问题常常被忽视,导致网络连接异常,从而影响应用的正常运行。本文通过一个实际案例,详细介绍了如何发现和解决Kubernetes环境中的conntrack问题,以引起大家对这一问题的重视。

01、背 景

这是早前的一个案例,那时候随着微服务数量的增加和请求量的上涨,我们当时从监控注意到业务高峰时线上接口的慢请求越来越多,原本毫秒级响应的接口偶尔会出现请求延迟超过1秒的现象,严重影响了用户体验。为了解决这一问题,我们在非生产环境通过压测工具进行复现,如下是具体排查和优化过程。

02、请求链路

当时采用的是nginx作为流量网关反向代理微服务网关,微服务和其网关跑在k8s集群上,微服务网关通过NodePort的形式暴露,为了方便问题复现和排查,我们把复现环境的Pod设置为单副本,访问链路大致如下。

03、排查与分析过程

1、起初怀疑是节点带宽满了,经查看监控,流量都很正常,所以暂时排除;

2、查看skywalking调用链监控,发现这些请求都有一个共同点,在nginx upstream这个环节耗时较长,难道问题在nginx?

3、看了一下nginx的各项监控指标都很正常,远远没达到其性能瓶颈,那就抓个包呗?不抓还好,一抓下一跳,有大量的 TCP Retransmission(TCP 重传) ,插播知识点:

当发送方的 TCP 在一定时间内没有收到确认(ACK)响应时,会认为数据包可能丢失或未成功传输,因此会重新发送相同的数据包,这个过程称为 TCP 重传。简单来说,TCP 重传是为了确保数据可靠传输的一种机制。 在 Wireshark 中,你可以看到标记为 "TCP Retransmission" 的事件。这表示发送方多次发送了同一个序列号的数据包,因为之前的传输没有得到确认。每次重传,发送方都会等待一段时间,称为超时重传时间(RTO),如果还没有收到 ACK,就会再次发送数据包。 通过这种机制,TCP 能够确保数据即使在网络状况不佳时,也能最终到达目的地,保证了数据传输的可靠性。

4、抽一个看了下跟踪流(TCP Stream),可以看到这个请求与上游服务重传了大约1s才建立了连接,难怪nginx upstream耗时那么长了,好的,我们在找到原因后,我们需要继续探索这些请求为什么会发生TCP重传呢?让我们继续往下看。

5、为了搞清楚TCP重传的原因,我们需要分别在nginx、pod和pod所在的node节点上进行抓包,如下图,我们在pod的抓包结果中发现,pod侧没有收到第一个syn报文 ,所以猜测包可能是在node和pod之间丢了。

6、节点连接数满了?通过下面指令查看,连接数并不是很多,很正常。

代码语言:javascript复制
netstat -n | awk '/^tcp/ {  S[$NF]} END {for(a in S) print a, S[a]}'

7、难道是conntrack表满了?之所以想起conntrack,是因为NodePort模式在将报文转发给 Pod 的过程中,报文会经历五元组(源 IP、源端口、目的 IP、目的端口、协议)的转换。由于请求量较大,可能会撑爆conntrack表,果不其然,如下图,我们捕获到conntrack丢包和插入失败的信息,罪魁祸首终于找到了。

代码语言:javascript复制
# 打印conntrack统计信息
conntrack -S
# 其实也可以通过`dmesg|grep conntrack`查看异常日志,但当时直接用的上面指令,所以没有截图

04、问题优化

好了,在确定根本问题后,优化和避免问题重复发生至关重要。以下是几种优化思路:

  1. 增加NodePort节点:在nginx的upstream中多挂几个NodePort节点分摊负载,这种方式改动最小。
  2. 修改service为LoadBalancer:将service的访问方式修改为LoadBalancer可以直接将流量引导到Pod,避免了通过NodePort和Node的转发,减少了五元组转换带来的瓶颈。
  3. 使用Ingress进行负载均衡:去掉nginx,使用Ingress进行service的负载均衡。

05、Q&A

Q1:conntrack的最大值是多少?

A1:conntrack的最大值(内核参数 net.netfilter.nf_conntrack_max )是根据kube-proxy配置的参数--conntrack-max-per-core * 节点cpu核数进行计算的,假设--conntrack-max-per-core的值设置为65536,节点有32核的CPU,则conntrack的最大值为65536 * 32 = 2097152,下图是kube-proxy官方的参数解释。

Q2:conntrack的最大值在哪里看?

A2:可以查看nf_conntrack_max文件,指令如下

代码语言:javascript复制
cat /proc/sys/net/netfilter/nf_conntrack_max

Q3:conntrack的最大值如何修改?

A3:可以修改kube-proxy的启动参数,或者修改节点的内核参数,一般推荐后者,这是修改的方法

代码语言:javascript复制
# 临时生效
sudo sysctl -w net.netfilter.nf_conntrack_max=2097152
# 永久生效
echo 'net.netfilter.nf_conntrack_max=2097152' | sudo tee -a /etc/sysctl.conf
sudo sysctl -p

06、总 结

conntrack监控真的太重要了,为了尽可能避免conntrack表满的情况,在高并发的场景建议使用ingress的方案,本期分享就到这里,谢谢!

参考链接:

https://kubernetes.io/zh-cn/docs/reference/command-line-tools-reference/kube-proxy/

1 人点赞