k8s修改pod的内核参数以优化服务网络性能

1、面对高并发场景:

TIME_WAIT 连接复用

如果短连接并发量较高,它所在 netns 中 TIME_WAIT 状态的连接就比较多,而 TIME_WAIT 连接默认要等 2MSL 时长才释放,长时间占用源端口,当这种状态连接数量累积到超过一定量之后可能会导致无法新建连接。
所以建议开启 TIME_WAIT 复用,即允许将 TIME_WAIT 连接重新用于新的 TCP 连接:

net.ipv4.tcp_tw_reuse=1
在高版本内核中,net.ipv4.tcp_tw_reuse 默认值为 2,仅为回环地址开启复用,基本可以认为没开启复用。

扩大源端口范围

高并发场景,对于 client 来说会使用大量源端口,源端口范围从 net.ipv4.ip_local_port_range 这个内核参数中定义的区间随机选取,在高并发环境下,端口范围小容易导致源端口耗尽,使得部分连接异常。通常 Pod 源端口范围默认是 32768-60999,建议将其扩大,调整为:1024-65535:
sysctl -w net.ipv4.ip_local_port_range=“1024 65535”。

调大最大文件句柄数

在 linux 中,每个连接都会占用一个文件句柄,所以句柄数量限制同样也会限制最大连接数, 对于像 Nginx 这样的反向代理,对于每个请求,它会与 client 和 upstream server 分别建立一个连接,即占据两个文件句柄,所以理论上来说 Nginx 能同时处理的连接数最多是系统最大文件句柄数限制的一半。

系统最大文件句柄数由 fs.file-max 这个内核参数来控制,一些环境默认值可能为 838860,建议调大:

fs.file-max=1048576

调大全连接连接队列的大小

TCP 全连接队列的长度如果过小,在高并发环境可能导致队列溢出,使得部分连接无法建立。

如果因全连接队列溢出导致了丢包,从统计的计数上是可以看出来的:

# 用 netstat 查看统计
$ netstat -s | grep -E 'overflow|drop'
    12178939 times the listen queue of a socket overflowed
    12247395 SYNs to LISTEN sockets dropped
    
# 也可以用 nstat 查看计数器
$ nstat -az | grep -E 'TcpExtListenOverflows|TcpExtListenDrops'
TcpExtListenOverflows           12178939              0.0
TcpExtListenDrops               12247395              0.0

全连接队列的大小取决于 net.core.somaxconn 内核参数以及业务进程调用 listen 时传入的
 backlog 参数,取两者中的较小值(min(backlog,somaxconn)),一些编程语言通常是默认取
  net.core.somaxconn 参数的值作为 backlog 参数传入 listen 系统调用(比如Go语言)。

高并发环境可以考虑将其改到 65535:
sysctl -w net.core.somaxconn=65535

如何查看队列大小来验证是否成功调整队列大小?可以执行 ss -lntp 看 Send-Q 的值。

$ ss -lntp
State        Recv-Q       Send-Q             Local Address:Port             Peer Address:Port      Process
LISTEN       0            65535                    0.0.0.0:80                    0.0.0.0:*          users:(("nginx",pid=347916,fd=6),("nginx",pid=347915,fd=6),("nginx",pid=347887,fd=6))

ss 用 -l 查看 LISTEN 状态连接时,Recv-Q 表示的当前已建连但还未被服务端调用 accept()
 取走的连接数量,即全连接队列中的连接数;Send-Q 表示的则是最大的 listen backlog 数值,
 即全连接队列大小。如果 Recv-Q 大小接近 Send-Q 的大小时,说明连接队列可能溢出。

需要注意的是,Nginx 在 listen 时并没有读取 somaxconn 作为 backlog 参数传入,
而是在 nginx 配置文件中有自己单独的参数配置:

server {
    listen  80  backlog=1024;
    ...
如果不设置,backlog 在 linux 上默认为 511:
backlog=number
sets the backlog parameter in the listen() call that limits the maximum
length for the queue of pending connections. By default, backlog is
set to -1 on FreeBSD, DragonFly BSD, and macOS, and to 511 on other platforms.
也就是说,即便你的 somaxconn 配的很高,nginx 所监听端口的连接队列最大却也只有 511,
高并发场景下还是可能导致连接队列溢出,所以建议配置下 nginx 的 backlog 参数。

2、面对高吞吐场景

调大 TCP 缓冲区

TCP socket 的发送和接收缓冲区也是有上限的,不过对于发送缓冲区,即便满了也是不会丢包的,只是会让程序发送数据包时卡住,等待缓冲区有足够空间释放出来,所以一般不需要优化发送缓冲区。

对于接收缓冲区,在高并发环境如果较小,可能导致缓冲区满而丢包,从网络计数可以看出来:

$ nstat -az | grep TcpExtTCPRcvQDrop
TcpExtTCPRcvQDrop               264324                  0.0

还可以使用 ss -ntmp 查看当前缓冲区情况:

$ ss -ntmp
ESTAB        0             0                    [::ffff:109.244.190.163]:9988                       [::ffff:10.10.4.26]:54440         users:(("xray",pid=3603,fd=20))
     skmem:(r0,rb12582912,t0,tb12582912,f0,w0,o0,bl0,d0)

rb12582912 表示 TCP 接收缓冲区大小是 12582912 字节,tb12582912 表示 UDP 发送缓存区大小是 12582912 字节。
Recv-Q 和 Send-Q 分别表示当前接收和发送缓冲区中的数据包字节数。
如果存在 net.ipv4.tcp_rmem 这个参数,对于 TCP 而言,会覆盖 net.core.rmem_default 和 net.core.rmem_max 的值。这个参数网络命名空间隔离的,而在容器网络命名空间中,一般默认是有配置的,所以如果要调整 TCP 接收缓冲区,需要显式在 Pod 级别配置下内核参数:

net.ipv4.tcp_rmem="4096 26214400 26214400"
单位是字节,分别是 min, default, max。

如果程序没用 setsockopt 更改 buffer 长度,就会使用 default 作为初始 buffer 长度(覆盖 net.core.rmem_default),然后根据内存压力在 min 和 max 之间自动调整。
如果程序使用了 setsockopt 更改 buffer 长度,则使用传入的长度 (仍然受限于 net.core.rmem_max)。

总结:内核参数调优配置示例

调整 Pod 内核参数:
      initContainers:
      - name: setsysctl
        image: busybox
        securityContext:
          privileged: true
        command:
        - sh
        - -c
        - |
          sysctl -w net.core.somaxconn=65535
          sysctl -w net.ipv4.ip_local_port_range="1024 65535"
          sysctl -w net.ipv4.tcp_tw_reuse=1
          sysctl -w fs.file-max=1048576
          sysctl -w net.ipv4.tcp_rmem="4096 26214400 26214400"
调整节点内核参数(修改 /etc/sysctl.conf 并执行 sysctl -p):
net.core.rmem_default=26214400
net.core.wmem_default=26214400
net.core.rmem_max=26214400
net.core.wmem_max=26214400
Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐