
# 高负载场景下NGINX Ingress控制器的性能调优
Ingress对象为集群提供HTTP/HTTPS等七层协议的负载均衡访问方式，NGINX Ingress是社区常见的一种实现，目前CCE服务提供了一款基于社区的NGINX Ingress Controller优化的精选开源插件，提供丰富的七层负载均衡能力。而在高并发场景下，插件的CPU内存等预分配资源和网络连接数的不足会影响应用的性能。本文介绍如何通过调优NGINX Ingress控制器来支撑高负载业务。
![](https://support.huaweicloud.com/usermanual-cce/public_sys-resources/caution_3.0-zh-cn.png)
调优过程涉及NGINX Ingress容器滚动升级，建议在业务低峰期进行调优操作。
#### 前提条件
CCE集群中已经部署NGINX Ingress控制器插件。
#### 调优建议
优化高负载场景下的NGINX Ingress插件需要考虑以下几个方面。
#### 使用高性能节点
在高并发场景下，Ingress对CPU资源和网络连接数占用都非常高，所以可选增强型ECS实例。
1. 登录[CCE控制台](https://console.huaweicloud.com/cce2.0/?#/cce/cluster/list)，单击集群名称进入集群。
2. 在左侧选择"节点管理"，在右侧选择"节点池"页签。
3. 单击右上角"创建节点池"，创建新的节点池并添加2台节点，该节点池可以为Nginx Ingress独占。具体操作请参见[创建节点池](https://support.huaweicloud.com/usermanual-cce/cce_10_0012.html)。
   
   在高并发场景下，可选性能较强的增强型实例，例如通用计算增强型实例：c7.8xlarge.2（32 Core 64 GiB，30Gbit/s最大带宽，550万PPS）。
   
   
4. 在"高级配置"中为节点池配置节点标签和污点。 
   - 设置污点的键为nginx-ingress-pod-reserved，值为true，Effect为NoExecute。
   
   - 设置标签的键为nginx-ingress-pod-reserved，值为true。
   
   
   
   
5. 在左侧选择"插件管理"，编辑NGINX Ingress控制器插件配置，修改调度策略。 
   - 节点亲和：设置自定义亲和策略，亲和nginx-ingress-pod-reserved: true的标签。
   
   - 容忍策略：添加容忍策略，容忍键值为nginx-ingress-pod-reserved: true、Effect为NoExecute的污点。
   
   
   
   
 
#### 优化NGINX Ingress插件配置
在插件管理中安装或编辑NGINX Ingress控制器插件，优化以下配置：
- 调整nginx-ingress-controller容器的资源限制。如应用处理的HTTP请求的每秒请求数（RPS）为30万，可将CPU/内存的requests和limits均设置为16 Core/20 GiB。
- 设置实例数大于等于2。
 
- 关闭插件的指标采集。 如果不需要获取Metrics信息，推荐关闭指标采集，关闭后可节省Nginx容器的CPU及内存消耗。
  
- 通过以下方式对Nginx全局配置进行调优：
  - **调高keepalive连接最大请求数**
    Nginx针对client和upstream的keepalive连接，具备keepalive_requests参数来控制单个keepalive连接的最大请求数，默认值均为100。
    当一个keepalive连接中请求次数超过默认值时，将断开并重新建立连接。 如果是内网Ingress，单个client的QPS可能较大，例如达到10000QPS，Nginx将可能频繁断开跟client建立的keepalive连接，并产生大量TIME_WAIT状态连接。
    为避免产生大量的TIME_WAIT连接，建议您在高并发环境中增大Nginx与client的keepalive连接的最大请求数量，在Nginx Ingress中配置对应keep-alive-requests，可以设置为10000，详情请参见[keep-alive-requests](https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/#keep-alive-requests)。
    同样，Nginx针对upstream的keepalive连接的请求数量的配置是upstream-keepalive-requests，详情请参见[upstream-keepalive-requests](https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/#upstream-keepalive-requests)。
    ![](https://support.huaweicloud.com/usermanual-cce/public_sys-resources/caution_3.0-zh-cn.png)
    在非高并发环境，不必配置upstream-keepalive-requests参数。如果将其调高，可能导致负载不均，因Nginx与upstream保持的keepalive连接过久，导致连接发生调度的次数减少，将使流量负载不均衡。
    
  
  - **调高keepalive最大空闲连接数**
    Nginx针对upstream可配置参数keepalive。该参数为最大空闲连接数，默认值为320。
    在高并发环境下将产生大量请求和连接，而实际生产环境中请求并不是完全均匀，有些建立的连接可能会短暂空闲，在空闲连接数多了之后关闭空闲连接，将可能导致Nginx与upstream频繁断连和建连，引发TIME_WAIT飙升。在高并发环境下，建议将 keepalive 值配置为1000，详情请参见 [upstream-keepalive-connections](https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/#upstream-keepalive-connections)。
    
  
  - **调高单个worker最大连接数**
    max-worker-connections控制每个 worker 进程可以打开的最大连接数，在高并发环境下建议调高该参数值，例如配置为65536，调高该值可以让 Nginx 拥有处理更多连接的能力，详情请参见 [max-worker-connections](https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/#max-worker-connections)。
    
  
  - **调优网关超时时间**
    nginx与upstream pod建立TCP连接并进行通信，其中涉及3个超时配置。
    - proxy-connect-timeout：设置 nginx 与 upstream pod 连接建立的超时时间，ingress nginx 默认设置为 5s，由于在nginx 和业务均在内网同机房通信，可将此超时时间缩短一些，比如3秒。详情请参见[proxy-connect-timeout](https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/#proxy-connect-timeout)。
    
    - proxy-read-timeout 、proxy-send-timeout：设置nginx与upstream pod之间读写操作的超时时间，ingress nginx 默认设置为 60s，当业务方服务异常导致响应耗时飙涨时，异常请求会长时间夯住 ingress 网关，在拉取所有服务正常请求的 P99.99 耗时之后，将网关与 upstream pod 之间读写超时均缩短到适当数值如30s，使得 nginx 可以及时掐断异常请求，避免长时间被夯住。详情请参见[proxy-read-timeout](https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/#proxy-read-timeout)及[proxy-send-timeout](https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/#proxy-send-timeout)。
      
   
 
#### Nginx内核参数调优
![](https://support.huaweicloud.com/usermanual-cce/public_sys-resources/caution_3.0-zh-cn.png)
如您需要自定义修改内核参数，请在修改之前，请务必确保您已完全理解该内核参数的具体含义和功能。请谨慎操作，错误的参数设置可能导致系统出现意外的错误，影响正常运行。
请特别注意以下两点：
1. 确保了解内核参数的含义：需清楚内核参数的作用和影响，这将有助于您正确设置相应的值。
2. 填写有效的内核参数值：内核参数值必须有效且符合预期要求，否则修改将不会生效。
 
您可通过以下方式对Nginx Ingress进行内核参数调优，并可使用initContainers方式设置内核参数。
CCE在2.2.75、2.6.26、3.0.1及以上版本的NGINX Ingress控制器插件中默认为您开启内核参数调优。
- **调高连接队列的大小**
  在高并发环境下，如果连接队列过小，则可能导致队列溢出，使部分连接无法建立。进程监听socket的连接队列大小受限于内核参数 net.core.somaxconn，调整somaxconn内核参数的值即可增加Nginx Ingress连接队列。
  进程调用listen系统监听端口时会传入一个backlog参数，该参数决定socket连接队列大小，且其值不大于somaxconn取值。Go程序标准库在listen时，默认直接读取somaxconn作为队列大小，但Nginx监听socket时并不会读取somaxconn，而是读取nginx.conf。在nginx.conf中的listen端口配置项中，可以通过backlog参数配置连接队列大小，来决定Nginx listen端口的连接队列大小。配置示例如下：
  ```
  server {
      listen  80  backlog=1024;
      ...
  ```
  如果未配置backlog值，则该值默认为511。在默认配置下，即便somaxconn的值配置超过511，但Nginx所监听端口的连接队列最大只有511，因此在高并发环境下可能导致连接队列溢出。
  而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的大小，该取值在CCE中默认为4096。在高并发环境下，建议执行以下命令，将 somaxconn 设为65535：
  ```
  sysctl -w net.core.somaxconn=65535
  ```
  
- **扩大源端口范围**
  高并发环境将导致 Nginx Ingress 使用大量源端口与 upstream 建立连接，源端口范围从 net.ipv4.ip_local_port_range 内核参数中定义的区间随机选取。在高并发环境下，端口范围小容易导致源端口耗尽，使得部分连接异常。CCE环境创建的 Pod 源端口范围默认为32768 - 60999，建议执行以下命令扩大源端口范围，调整为1024 - 65535：
  ```
  sysctl -w net.ipv4.ip_local_port_range="1024 65535"
  ```
  
- **调整TIME_WAIT**
  建议执行以下命令，为Nginx Ingress开启TIME_WAIT复用，即允许将TIME_WAIT连接重新用于新的TCP连接，并且减小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
  ```
  
为Nginx Ingress Controller的Pod添加initContainers并设置上述内核参数。可参考以下代码示例：
```
...
initContainers:
  - name: setsysctl
    image: ***(cce默认使用社区的nginx-ingress镜像)
    securityContext:
      runAsUser: 0
      runAsGroup: 0
      capabilities:
        add:
          - SYS_ADMIN
        drop:
          - ALL
    command:
      - sh
      - -c
      - |
        if [ "$POD_IP" != "$HOST_IP" ]; then
           mount -o remount rw /proc/sys
           if [ $? -eq 0 ]; then
             sysctl -w net.core.somaxconn=65535
             sysctl -w net.ipv4.ip_local_port_range="1024 65535"
             sysctl -w net.ipv4.tcp_fin_timeout=15
             sysctl -w net.netfilter.nf_conntrack_tcp_timeout_time_wait=30
           else
             echo "Failed to remount /proc/sys as read-write. Skipping sysctl commands."
           fi
         fi
    env:
      - name: POD_IP
        valueFrom:
          fieldRef:
            apiVersion: v1
            fieldPath: status.podIP
      - name: HOST_IP
        valueFrom:
          fieldRef:
            apiVersion: v1
            fieldPath: status.hostIP
```
