k8s的 Nginx Ingress 调优

简介: k8s的 Nginx Ingress 调优

概述


Nginx Ingress Controller 基于 Nginx 实现了 Kubernetes Ingress API,Nginx 是公认的高性能网关,但如果不对其进行一些参数调优,就不能充分发挥出高性能的优势。Nginx Ingress工作原理:


640.png


内核参数调优


我们先看看通过内核的哪些参数能够提高Ingress的性能。保证在高并发环境下,发挥Ingress的最大性能。


调大全连接队列的大小


TCP 全连接队列的最大值取决于 somaxconn 和 backlog 之间的最小值,也就是 min(somaxconn, backlog)。在高并发环境下,如果队列过小,可能导致队列溢出,使得连接部分连接无法建立。要调大 Nginx Ingress 的连接队列,只需要调整 somaxconn 内核参数的值即可,但我想跟你分享下这背后的相关原理。Nginx 监听 socket 时没有读取 somaxconn,而是有自己单独的参数配置。在 nginx.conf 中 listen 端口的位置,还有个叫 backlog 参数可以设置,它会决定 nginx listen 的端口的连接队列大小。


server {
    listen  80  backlog=1024;
    ...


backlog 是 listen(int sockfd, int backlog) 函数中的 backlog 大小,Nginx 默认值是 511,可以通过修改配置文件设置其长度;还有 Go 程序标准库在 listen 时,默认直接读取 somaxconn 作为队列大小。就是说,即便你的 somaxconn 配的很高,nginx 所监听端口的连接队列最大却也只有 511,高并发场景下可能导致连接队列溢出。所以在这个在 Nginx Ingress 中, Nginx Ingress Controller 会自动读取 somaxconn 的值作为 backlog 参数写到生成的 nginx.conf 中: https://github.com/kubernetes/ingress-nginx/blob/controller-v0.34.1/internal/ingress/controller/nginx.go#L592 也就是说,Nginx Ingress 的连接队列大小只取决于 somaxconn 的大小,这个值在  Nginx Ingress 默认为 4096,建议给 Nginx Ingress 设为 65535:


sysctl -w net.core.somaxconn=65535


扩大源端口范围


根据《linux中TCP三次握手与四次挥手介绍及调优》的介绍,我们知道客户端会占用端口。在高并发场景会导致 Nginx Ingress 使用大量源端口与upstream建立连接。源端口范围是在内核参数 net.ipv4.ip_local_port_range 中调整的。在高并发环境下,端口范围过小容易导致源端口耗尽,使得部分连接异常。Nginx Ingress 创建的 Pod 源端口范围默认是 32768-60999,建议将其扩大,调整为 1024-65535:


sysctl -w net.ipv4.ip_local_port_range="1024 65535"


TIME_WAIT


根据《linux中TCP三次握手与四次挥手介绍及调优》的介绍我们知道客户端会占用端口。当在 netns 中 TIME_WAIT 状态的连接就比较多的时候,源端口就会被长时间占用。因为而 TIME_WAIT 连接默认要等 2MSL 时长才释放,当这种状态连接数量累积到超过一定量之后可能会导致无法新建连接。所以建议给 Nginx Ingress 开启 TIME_WAIT 复用,即允许将 TIME_WAIT 连接重新用于新的 TCP 连接:


sysctl -w net.ipv4.tcp_tw_reuse=1


减小FIN_WAIT2状态的参数 net.ipv4.tcp_fin_timeout 的时间和减小TIME_WAIT 状态的参数net.netfilter.nf_conntrack_tcp_timeout_time_wait的时间 ,让系统尽快释放它们所占用的资源。


sysctl -w net.ipv4.tcp_fin_timeout=15
sysctl -w net.netfilter.nf_conntrack_tcp_timeout_time_wait=30


调大增大处于 TIME_WAIT 状态的连接数


Nginx一定要关注这个值,因为它对你的系统起到一个保护的作用,一旦端口全部被占用,服务就异常了。tcp_max_tw_buckets 能帮你降低这种情况的发生概率,争取补救时间。在只有 60000 多个端口可用的情况下,配置为:


sysctl -w net.ipv4.tcp_max_tw_buckets = 55000


调大最大文件句柄数


Nginx 作为反向代理,对于每个请求,它会与 client 和 upstream server 分别建立一个连接,即占据两个文件句柄,所以理论上来说 Nginx 能同时处理的连接数最多是系统最大文件句柄数限制的一半。系统最大文件句柄数由 fs.file-max 这个内核参数来控制,默认值为 838860,建议调大:


sysctl -w fs.file-max=1048576


配置示例


给 Nginx Ingress Controller 的 Pod 添加 initContainers 来设置内核参数:


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_max_tw_buckets = 55000
          sysctl -w net.ipv4.tcp_tw_reuse=1
          sysctl -w fs.file-max=1048576
          sysctl -w net.ipv4.tcp_fin_timeout=15
          sysctl -w net.netfilter.nf_conntrack_tcp_timeout_time_wait=30


应用层配置调优


除了内核参数需要调优,Nginx 本身的一些配置也需要进行调优,下面我们来详细看下。


调高 keepalive 连接最大请求数


keepalive_requests指令用于设置一个keep-alive连接上可以服务的请求的最大数量,当最大请求数量达到时,连接被关闭。默认是100。这个参数的真实含义,是指一个keep alive建立之后,nginx就会为这个连接设置一个计数器,记录这个keep alive的长连接上已经接收并处理的客户端请求的数量。如果达到这个参数设置的最大值时,则nginx会强行关闭这个长连接,逼迫客户端不得不重新建立新的长连接。


简单解释一下:QPS=10000时,客户端每秒发送10000个请求(通常建立有多个长连接),每个连接只能最多跑100次请求,意味着平均每秒钟就会有100个长连接因此被nginx关闭。同样意味着为了保持QPS,客户端不得不每秒重新新建100个连接。因此,就会发现有大量的TIME_WAIT的socket连接(即使此时keep alive已经在client和nginx之间生效)。因此对于QPS较高的场景,非常有必要加大这个参数,以避免出现大量连接被生成再抛弃的情况,减少TIME_WAIT。


如果是内网 Ingress,单个 client 的 QPS 可能较大,比如达到 10000 QPS,Nginx 就可能频繁断开跟 client 建立的 keepalive 连接,然后就会产生大量 TIME_WAIT 状态连接。我们应该尽量避免产生大量 TIME_WAIT 连接,所以,建议这种高并发场景应该增大 Nginx 与 client 的 keepalive 连接的最大请求数量,在 Nginx Ingress 的配置对应 keep-alive-requests,可以设置为 10000,参考: https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/#keep-alive-requests 同样的,Nginx 与 upstream 的 keepalive 连接的请求数量的配置是 upstream-keepalive-requests,参考: https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/#upstream-keepalive-requests


但是,一般情况应该不必配此参数,如果将其调高,可能导致负载不均,因为 Nginx 与 upstream 保持的 keepalive 连接过久,导致连接发生调度的次数就少了,连接就过于 "固化",使得流量的负载不均衡。


调高 keepalive 最大空闲连接数


Nginx 针对 upstream 有个叫 keepalive 的配置,它不是 keepalive 超时时间,也不是 keepalive 最大连接数,而是 keepalive 最大空闲连接数。当这个数量被突破时,最近使用最少的连接将被关闭。


简单解释一下:有一个HTTP服务,作为upstream服务器接收请求,响应时间为100毫秒。如果要达到10000 QPS的性能,就需要在nginx和upstream服务器之间建立大约1000条HTTP连接。nginx为此建立连接池,然后请求过来时为每个请求分配一个连接,请求结束时回收连接放入连接池中,连接的状态也就更改为idle。我们再假设这个upstream服务器的keepalive参数设置比较小,比如常见的10. A、假设请求和响应是均匀而平稳的,那么这1000条连接应该都是一放回连接池就立即被后续请求申请使用,线程池中的idle线程会非常的少,趋近于零,不会造成连接数量反复震荡。B、显示中请求和响应不可能平稳,我们以10毫秒为一个单位,来看连接的情况(注意场景是1000个线程+100毫秒响应时间,每秒有10000个请求完成),我们假设应答始终都是平稳的,只是请求不平稳,第一个10毫秒只有50,第二个10毫秒有150:


  1. 下一个10毫秒,有100个连接结束请求回收连接到连接池,但是假设此时请求不均匀10毫秒内没有预计的100个请求进来,而是只有50个请求。注意此时连接池回收了100个连接又分配出去50个连接,因此连接池内有50个空闲连接。
  2. 然后注意看keepalive=10的设置,这意味着连接池中最多容许保留有10个空闲连接。因此nginx不得不将这50个空闲连接中的40个关闭,只留下10个。
  3. 再下一个10个毫秒,有150个请求进来,有100个请求结束任务释放连接。150 - 100 = 50,空缺了50个连接,减掉前面连接池保留的10个空闲连接,nginx不得不新建40个新连接来满足要求。


C、同样,如果假设相应不均衡也会出现上面的连接数波动情况。


它的默认值为 32,在高并发下场景下会产生大量请求和连接,而现实世界中请求并不是完全均匀的,有些建立的连接可能会短暂空闲,而空闲连接数多了之后关闭空闲连接,就可能导致 Nginx 与 upstream 频繁断连和建连,引发 TIME_WAIT 飙升。在高并发场景下可以调到 1000,参考: https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/#upstream-keepalive-connections


网关超时


ingress nginx 与 upstream pod 建立 TCP 连接并进行通信,其中涉及 3 个超时配置,我们也相应进行调优。proxy-connect-timeout 选项 设置 nginx 与 upstream pod 连接建立的超时时间,ingress nginx 默认设置为 5s,由于在nginx 和业务均在内网同机房通信,我们将此超时时间缩短一些,比如3秒。参考:https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/#proxy-connect-timeout


proxy-read-timeout 选项设置 nginx 与 upstream pod 之间读操作的超时时间,ingress nginx 默认设置为 60s,当业务方服务异常导致响应耗时飙涨时,异常请求会长时间夯住 ingress 网关,我们在拉取所有服务正常请求的 P99.99 耗时之后,将网关与 upstream pod 之间读写超时均缩短到 3s,使得 nginx 可以及时掐断异常请求,避免长时间被夯住。参考:https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/#proxy-read-timeout

https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/#proxy-send-timeout


调高单个 worker 最大连接数


max-worker-connections 控制每个 worker 进程可以打开的最大连接数,默认配置是 16384。在高并发环境建议调高,比如设置到 65536,这样可以让 nginx 拥有处理更多连接的能力,参考: https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/#max-worker-connections


优化重试机制


nginx 提供了默认的 upstream 请求重试机制,默认情况下,当 upstream 服务返回 error 或者超时,nginx 会自动重试异常请求,并且没有重试次数限制。由于接入层 nginx 和 ingress nginx 本质都是 nginx,两层 nginx 都启用了默认的重试机制,异常请求时会出现大量重试,最差情况下会导致集群网关雪崩。接入层 nginx 一起解决了这个问题:接入层 nginx 必须使用 proxy_next_upstream_tries 严格限制重试次数,ingress nginx 则使用 proxy-next-upstream="off"直接关闭默认的重试机制。参考:https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/#proxy-next-upstream


开启 brotli 压缩


考: https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/#enable-brotli


压缩是时间换空间的通用方法。用cpu时间来换取大量的网络带宽,增大吞吐量。Brotli是Google开发的一种压缩方法,于2015年发布。我们常用的压缩算法是 gzip(Ingress-nginx也是默认使用gzip),据说brotli要比gzip高出20%至30%的压缩率。默认的压缩算法是gzip,压缩级别为1,如需要启用brotli,需要配置以下三个参数:


  • enable-brotli: true 或 false,是否启用brotli压缩算法
  • brotli-level: 压缩级别,范围1~11,默认为4,级别越高,越消耗CPU性能。
  • brotli-types: 由brotli即时压缩的MIME类型


配置示例


Nginx 全局配置通过 configmap 配置(Nginx Ingress Controller 会 watch 并自动 reload 配置):


apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-ingress-controller
data:
  keep-alive-requests: "10000"
  upstream-keepalive-connections: "200"
  max-worker-connections: "65536"
  proxy-connect-timeout: "3"
  proxy-read-timeout: "3"
  proxy-send-timeout: "3"
  proxy-next-upstream: "off"
  enable-brotli: "true"
  brotli-level: "6"
  brotli-types: "text/xml image/svg+xml application/x-font-ttf image/vnd.microsoft.ic
相关实践学习
通过Ingress进行灰度发布
本场景您将运行一个简单的应用,部署一个新的应用用于新的发布,并通过Ingress能力实现灰度发布。
容器应用与集群管理
欢迎来到《容器应用与集群管理》课程,本课程是“云原生容器Clouder认证“系列中的第二阶段。课程将向您介绍与容器集群相关的概念和技术,这些概念和技术可以帮助您了解阿里云容器服务ACK/ACK Serverless的使用。同时,本课程也会向您介绍可以采取的工具、方法和可操作步骤,以帮助您了解如何基于容器服务ACK Serverless构建和管理企业级应用。 学习完本课程后,您将能够: 掌握容器集群、容器编排的基本概念 掌握Kubernetes的基础概念及核心思想 掌握阿里云容器服务ACK/ACK Serverless概念及使用方法 基于容器服务ACK Serverless搭建和管理企业级网站应用
相关文章
|
2月前
|
Kubernetes 应用服务中间件 nginx
k8s学习--YAML资源清单文件托管服务nginx
k8s学习--YAML资源清单文件托管服务nginx
k8s学习--YAML资源清单文件托管服务nginx
|
2月前
|
Kubernetes 监控 测试技术
k8s学习--基于Ingress-nginx实现灰度发布系统
k8s学习--基于Ingress-nginx实现灰度发布系统
116 2
k8s学习--基于Ingress-nginx实现灰度发布系统
|
2月前
|
Kubernetes 负载均衡 应用服务中间件
k8s学习--ingress详细解释与应用(nginx ingress controller))
k8s学习--ingress详细解释与应用(nginx ingress controller))
255 0
|
2月前
|
缓存 Kubernetes 负载均衡
在K8S中,ingress 有何作用?
在K8S中,ingress 有何作用?
|
2月前
|
应用服务中间件 BI nginx
Nginx的location配置详解
【10月更文挑战第16天】Nginx的location配置详解
|
2月前
|
缓存 负载均衡 安全
Nginx常用基本配置总结:从入门到实战的全方位指南
Nginx常用基本配置总结:从入门到实战的全方位指南
296 0
|
2月前
|
应用服务中间件 Linux nginx
Jetson 环境安装(四):jetson nano配置ffmpeg和nginx(亲测)之编译错误汇总
这篇文章是关于在Jetson Nano上配置FFmpeg和Nginx时遇到的编译错误及其解决方案的汇总。
96 4
|
19天前
|
存储 负载均衡 中间件
Nginx反向代理配置详解,图文全面总结,建议收藏
Nginx 是大型架构必备中间件,也是大厂喜欢考察的内容,必知必会。本篇全面详解 Nginx 反向代理及配置,建议收藏。
Nginx反向代理配置详解,图文全面总结,建议收藏
|
1月前
|
应用服务中间件 API nginx
nginx配置反向代理404问题
【10月更文挑战第18天】本文介绍了使用Nginx进行反向代理的配置方法,解决了404错误、跨域问题和302重定向问题。关键配置包括代理路径、请求头设置、跨域头添加以及端口转发设置。通过调整`proxy_set_header`和添加必要的HTTP头,实现了稳定的服务代理和跨域访问。
163 1
nginx配置反向代理404问题
|
16天前
|
应用服务中间件 网络安全 nginx
轻松上手Nginx Proxy Manager:安装、配置与实战
Nginx Proxy Manager (NPM) 是一款基于 Nginx 的反向代理管理工具,提供直观的 Web 界面,方便用户配置和管理反向代理、SSL 证书等。本文档介绍了 NPM 的安装步骤,包括 Docker 和 Docker Compose 的安装、Docker Compose 文件的创建与配置、启动服务、访问 Web 管理界面、基本使用方法以及如何申请和配置 SSL 证书,帮助用户快速上手 NPM。
87 1