nginx-ingress性能优化
性能优化ingress-nginx 集群作为 kubernetes 集群内外通信的流量网关,需要优化性能满足业务需求,我们在 nginx 和内核配置层面做了相应的优化工作。宿主机中断优化ingress-nginx 物理机执行 top 命令发现每个 CPU 的 si 指标不均衡,针对此问题 我们开启了网卡多队列机制 以及中断优化。开启网卡多队列:ethtool -l eth0// 查看网卡可以支持的
性能优化
ingress-nginx 集群作为 kubernetes 集群内外通信的流量网关,需要优化性能满足业务需求,我们在 nginx 和内核配置层面做了相应的优化工作。
宿主机中断优化
ingress-nginx 物理机执行 top 命令发现每个 CPU 的 si 指标不均衡,针对此问题 我们开启了网卡多队列机制 以及中断优化。
开启网卡多队列:
ethtool -l eth0 // 查看网卡可以支持的多队列配置
ethtool -L eth0 combined 8 // 开启网卡多队列机制
中断打散优化:
service irqbalance stop // 首先关闭irqbalance系统服务
sh set_irq_affinity -X all eth0 // Intel提供中断打散脚本:
https://github.com/majek/ixgbe/blob/master/scripts/set_irq_affinity
我们针对内核参数也做了优化工作以提升 nginx 性能,主要基于 nginx 官方提供的性能优化方案:https://www.nginx.com/blog/tuning-nginx/
(1)调整连接队列大小
nginx 进程监听 socket 套接字的 连接队列大小默认为 511 ,在高并发场景下 默认的队列大小不足以快速处理业务流量洪峰,连接队列过小会造成队列溢出、部分请求无法建立 TCP 连接,因此我们调整了 nginx 进程连接队列大小。
sysctl -w net.core.somaxconn=32768
nginx 进程充当反向代理时 会作为客户端与 upstream 服务端建立 TCP 连接,此时会占用临时端口,Linux 默认的端口使用范围是 32768-60999,在高并发场景下,默认的源端口过少会造成端口资源耗尽,nginx 无法与 upstream 服务端建立连接,因此我们调整了默认端口使用范围。
sysctl -w net.ipv4.ip_local_port_range="1024 65000"
nginx 进程充当反向代理时 会作为服务端与接入层 nginx 建立 TCP 连接,同时作为客户端与 upstream 服务端建立 TCP 连接,即 1 个 HTTP 请求在 nginx 侧会耗用 2 条连接,也就占用 2 个文件描述符。在高并发场景下,为了同时处理海量请求,我们调整了最大文件描述符数限制。
sysctl -w fs.file-max=1048576
nginx 进程充当反向代理时 会作为客户端与 upstream 服务端建立 TCP 连接,连接会超时回收和主动释放,nginx 侧作为 TCP 连接释放的发起方,会存在 TIME_WAIT 状态的 TCP 连接,这种状态的 TCP 连接会长时间 (2MSL 时长) 占用端口资源,当 TIME_WAIT 连接过多时 会造成 nginx 无法与 upstream 建立连接。
处理 TIME_WAIT 连接通常有 2 种解决办法:
net.ipv4.tcp_tw_reuse:复用TIME_WAIT状态的socket用于新建连接
net.ipv4.tcp_tw_recycle:快速回收TIME_WAIT状态连接
由于 tcp_tw_recycle 的高危性,4.12 内核已经废弃此选项,tcp_tw_reuse 则相对安全,nginx 作为 TCP 连接的发起方,因此启用此选项。
sysctl -w net.ipv4.tcp_tw_reuse=1
以上内核参数都使用 kubernetes 提供的 initContainer 机制进行设置。
initContainers:
initContainers:
- name: sysctl
image: alpine:3.10
securityContext:
privileged: true
command:
- sh
- -c
- sysctl -w net.core.somaxconn=32768; sysctl -w net.ipv4.ip_local_port_range='1024 65000'; sysctl -w fs.file-max=1048576; sysctl -w net.ipv4.tcp_tw_reuse=1
nginx连接数
nginx 作为服务端与接入层 nginx 建立 TCP 连接,同时作为客户端与 upstream 服务端建立 TCP 连接,由此两个方向的 TCP 连接数都需要调优。
(1)nginx 充当服务端,调整 keep-alive 连接超时和最大请求数
ingress-nginx 使用 keep-alive 选项设置 接入层 nginx 和 ingress nginx 之间的连接超时时间(默认超时时间为 75s)。
使用 keep-alive-requests 选项设置 接入层 nginx 和 ingress nginx 之间 单个连接可承载的最大请求数(默认情况下单连接处理 100 个请求之后就会断开释放)。
在高并发场景下,我们调整了这两个选项值,对应到 ingress-nginx 全局 configmap 配置。
keep-alive: "75"
keep-alive-requests: "10000"
(2)nginx 充当客户端,调整 upstream-keepalive 连接超时和最大空闲连接数
ingress-nginx 使用 upstream-keepalive-connections 选项 设置 ingress nginx 和 upstream pod 之间 最大空闲连接缓存数(默认情况下最多缓存 32 个空闲连接)。
使用 upstream-keepalive-timeout 选项 设置 ingress nginx 和 upstream pod 之间的连接超时时间(默认超时时间为 60s)。
使用 upstream-keepalive-requests 选项 设置 ingress nginx 和 upstream pod 之间 单个连接可承载的最大请求数(默认情况下单连接处理 100 个请求之后就会断开释放)。
在高并发场景下,我们也调整了这 3 个选项值,使得 nginx 尽可能快速处理 HTTP 请求(尽量少释放并重建 TCP 连接),同时控制 nginx 内存使用量。
upstream-keepalive-connections: "200"
upstream-keepalive-requests: "10000"
upstream-keepalive-timeout: "100"
网关超时
ingress nginx 与 upstream pod 建立 TCP 连接并进行通信,其中涉及 3 个超时配置,我们也相应进行调优。
proxy-connect-timeout 选项 设置 nginx 与 upstream pod 连接建立的超时时间,ingress nginx 默认设置为 5s,由于在 nginx 和业务均在内网同机房通信,我们将此超时时间缩短到 1s。
proxy-read-timeout 选项 设置 nginx 与 upstream pod 之间读操作的超时时间,ingress nginx 默认设置为 60s,当业务方服务异常导致响应耗时飙涨时,异常请求会长时间夯住 ingress 网关,我们在拉取所有服务正常请求的 P99.99 耗时之后,将网关与 upstream pod 之间读写超时均缩短到 3s,使得 nginx 可以及时掐断异常请求,避免长时间被夯住。
proxy-connect-timeout: "1"
proxy-read-timeout: "3"
proxy-send-timeout: "3"
如果某个业务需要单独调整读写超时,可以设置
ingress annotation(nginx.ingress.kubernetes.io/proxy-read-timeout 和 nginx.ingress.kubernetes.io/proxy-send-timeout)进行调整。
4稳定性建设
ingress-nginx 作为 kubernetes 集群内外通信的流量网关,而且多服务共享 ingress 集群,因此 ingress 集群必须保证极致的高可用性,我们在稳定性建设方面做了大量工作,未来会持续提升 ingress 集群稳定性。
健康检查
接入层 nginx 将 ingress nginx worker IP 作为 upstream,早期接入层 nginx 使用默认的被动健康检查机制,当 kubernetes 集群某个服务异常时,这种健康检查机制会影响其他正常业务请求。
例如:接入层 nginx 将请求转发到 ingress nginx 集群的 2 个实例。
upstream ingress-backend {
server 10.192.168.1 max_fails=3 fail_timeout=30s;
server 10.192.168.2 max_fails=3 fail_timeout=30s;
}
如果某个业务 A 出现大量 HTTP error,接入层 nginx 在默认的健康检查机制之下会将 ingress nginx 实例屏蔽,但是此时业务 B 的请求是正常的,理应被 ingress nginx 正常转发。
针对此问题,我们配合接入层 nginx 使用 nginx_upstream_check_module 模块来做主动健康检查,使用 /healthz 接口来反馈 ingress-nginx 运行的健康状况,这种情况下接入层 nginx 不依赖于实际业务请求做 upstream ip 健康检查,因此 kubernetes 集群每个业务请求彼此独立,不会互相影响。
upstream ingress-backend {
server 10.192.168.1 max_fails=0 fail_timeout=10s;
server 10.192.168.2 max_fails=0 fail_timeout=10s;
check interval=1000 rise=2 fall=2 timeout=1000 type=http default_down=false;
check_keepalive_requests 1;
check_http_send "GET /healthz HTTP/1.0\r\n\r\n";
check_http_expect_alive http_2xx;
zone ingress-backend 1M;
}
优化重试机制
nginx 提供了默认的 upstream 请求重试机制,默认情况下,当 upstream 服务返回 error 或者超时,nginx 会自动重试异常请求,并且没有重试次数限制。由于接入层 nginx 和 ingress nginx 本质都是 nginx,两层 nginx 都启用了默认的重试机制,异常请求时会出现大量重试,最差情况下会导致集群网关雪崩。我们和接入层 nginx 一起解决了这个问题:接入层 nginx 必须使用 proxy_next_upstream_tries 严格限制重试次数,ingress nginx 则使用 proxy-next-upstream="off"直接关闭默认的重试机制。
更多推荐
所有评论(0)