部署高负载场景的Nginx Ingress Controller

Kubernetes集群中,Ingress对集群服务(Service)中外部可访问的API对象进行管理,提供七层负载均衡能力。Nginx Ingress Controller负责实现Ingress的功能,根据规则配置负载均衡并提供访问入口。在高负载场景下,CPU资源和网络连接数的不足会影响应用的性能。本文介绍如何通过部署Nginx Ingress Controller来支撑高负载应用。

前提条件

部署说明

部署高负载场景下的Nginx Ingress Controller需要考虑以下三个方面。

硬件选型

在高并发场景下,IngressCPU资源和网络连接数占用都非常高,建议您选择增强型ECS实例,例如:

  • 计算型实例:ecs.c6e.8xlarge(32 Core 64 GB,600PPS)

  • 网络型实例:ecs.g6e.8xlarge(32 Core 128 GB,600PPS)

关于ECS实例规格的更多信息,请参见实例规格族

K8s配置

  • 设置Ingress Pod独占节点资源,添加污点和节点标签:

    kubectl label nodes $node_name ingress-pod="yes"
    kubectl taint nodes $node_name ingress-pod="yes":NoExecute
  • 设置CPU Policystatic

  • 推荐调整ingress-controller service对应的SLB规格为超强型(slb.s3.large)。

  • 推荐集群使用Terway网络插件及配置独占ENI。

Ingress配置

  • 设置Ingress PodGuaranteed类型。

    • 设置nginx-ingress-controller container的资源限制requestslimits:30Core 40GiB。

    • 设置initContainer init-sysctl的资源限制requestslimits:100m 70MiB。

  • 调整Deployment Replicas数为新增节点数。

  • 设置ConfigMapworker-processes数为28(预留部分给系统使用)。

  • 调整ConfigMapkeepalive链接最大请求数。

  • 关闭日志访问记录。

步骤一:添加节点

选择指定集群,创建新的节点池并添加2台节点。

创建节点池需要配置的主要参数如下所示。具体操作,请参见创建和管理节点池

  • 选择操作系统Alibaba Cloud Linux3

  • 配置节点标签污点(Taints)

    • 设置污点(Taints)ingress-podyesEffectNoExecute

    • 设置节点标签ingress-podyes

  • 选择CPU PolicyStatic

步骤二:配置Nginx Ingress Controller

执行kubectl edit deploy nginx-ingress-controller -n kube-system命令打开Ingress Controller的配置文件,根据以下内容更新Ingress Controller的配置:

  1. 删除Pod反亲和性配置。

    podAntiAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: app
          operator: In
          values:
          - ingress-nginx
       topologyKey: kubernetes.io/hostname
  2. 配置InitContainerrequestslimits

    resources:
      limits:
        cpu: 100m
        memory: 70Mi
      requests:
        cpu: 100m
        memory: 70Mi
  3. 修改nginx-ingress-controller containerrequestslimits都为30Core 40GiB。

    resources:
      limits:
        cpu: "30"
        memory: 40Gi
      requests:
        cpu: "30"
        memory: 40Gi
  4. 设置节点亲和性和容忍性。

    nodeSelector:
      ingress-pod: "yes"
    tolerations:
    - effect: NoExecute
      key: ingress-pod
      operator: Equal
      value: "yes"
  5. 调整Replicas数为新增节点数。

  6. 关闭Metrics采集,修改启动参数,添加--enable-metrics=false

    说明

    如果不需要获取Metrics信息,建议关闭Metrics采集。对于v1.9.3以上版本的Nginx Ingress Controller,您还可以通过配置启动参数 --exclude-socket-metrics来排除相关的指标, 例如nginx_ingress_controller_ingress_upstream_latency_seconds。这样做可以帮助减轻对系统性能的影响。

    containers:
    - args:
      - /nginx-ingress-controller
      - --configmap=$(POD_NAMESPACE)/nginx-configuration
      - --tcp-services-configmap=$(POD_NAMESPACE)/tcp-services
      - --udp-services-configmap=$(POD_NAMESPACE)/udp-services
      - --annotations-prefix=nginx.ingress.kubernetes.io
      - --publish-service=$(POD_NAMESPACE)/nginx-ingress-lb
      - --enable-metrics=false
      - --v=1

步骤三:配置Nginx Ingress ConfigMap

  1. 使用kubectl edit cm -n kube-system nginx-configuration更新ConfigMap,可参考以下代码示例:

    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: nginx-configuration
      namespace: kube-system
    data:
      allow-backend-server-header: "true"
      enable-underscores-in-headers: "true"
      generate-request-id: "true"
      ignore-invalid-headers: "true"
      log-format-upstream: $remote_addr - [$remote_addr] - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" $request_length $request_time [$proxy_upstream_name] $upstream_addr $upstream_response_length $upstream_response_time $upstream_status $req_id $host [$proxy_alternative_upstream_name]
      max-worker-connections: "65536"
      proxy-body-size: 20m
      proxy-connect-timeout: "3"
      proxy-read-timeout: "5"
      proxy-send-timeout: "5"
      reuse-port: "true"
      server-tokens: "false"
      ssl-redirect: "false"
      upstream-keepalive-timeout: "900"
      worker-processes: "28" # 设置启动的Nginx进程数,请根据您的node节点规格进行实际调整,建议调整为cpu核数-2。
      worker-cpu-affinity: auto
      upstream-keepalive-connections: "300"
      upstream-keepalive-requests: "1000" # 设置单个长连接最多可以发送多个。
      keep-alive: "900"
      keep-alive-requests: "10000"
  2. 采用文件方式记录访问日志并对日志轮转。

    默认的日志访问记录输出到/dev/stdout,在高并发场景下,会占用大量CPU,故采用文件方式记录访问日志并对日志设置轮转。

    1. SSH方式登录部署ingress-controller PodECS节点。具体操作,请参见通过密钥认证登录Linux实例

    2. /etc/crontab文件结尾添加以下内容:

      */15 * * * *  root /root/nginx-log-rotate.sh
      说明

      此处以每隔15分钟对日志轮转一次为例,可根据实际需求进行调整。

    3. /root目录下添加文件nginx-log-rotate.sh

      Docker集群

      #!/bin/bash
      # 最多保留日志文件个数,可根据需求进行调整。
      keep_log_num=5
      ingress_nginx_container_ids=$(docker ps | grep nginx-ingress-controller | grep -v pause | awk '{print $1}')
      if [[ -z "$ingress_nginx_container_ids" ]]; then
       echo "error: failed to get ingress nginx container ids"
       exit 1
      fi
      # 随机睡眠5~10秒。
      sleep $(( RANDOM % (10 - 5 + 1 ) + 5 ))
      for id in $ingress_nginx_container_ids; do
       docker exec $id bash -c "cd /var/log/nginx; if [[ \$(ls access.log-* | wc -l) -gt $keep_log_num ]]; then rm -f \$(ls -t access.log-* | tail -1); fi ; mv access.log access.log-\$(date +%F:%T) ; kill -USR1 \$(cat /tmp/nginx/nginx.pid)"
      done	
      	

      containerd集群

      #!/bin/bash
      # 最多保留日志文件个数,可根据需求进行调整。
      keep_log_num=5
      ingress_nginx_container_ids=$(crictl ps | grep nginx-ingress-controller | grep -v pause | awk '{print $1}')
      if [[ -z "$ingress_nginx_container_ids" ]]; then
       echo "error: failed to get ingress nginx container ids"
       exit 1
      fi
      # 随机睡眠5~10秒。
      sleep $(( RANDOM % (10 - 5 + 1 ) + 5 ))
      for id in $ingress_nginx_container_ids; do
       crictl exec $id bash -c "cd /var/log/nginx; if [[ \$(ls access.log-* | wc -l) -gt $keep_log_num ]]; then rm -f \$(ls -t access.log-* | tail -1); fi ; mv access.log access.log-\$(date +%F:%T) ; kill -USR1 \$(cat /tmp/nginx/nginx.pid)"
      done	
      	
    4. 执行以下命令,对nginx-log-rotate.sh文件添加可执行权限。

      chmod 755 /root/nginx-log-rotate.sh

步骤四: 可选配置调优

以下为内核相关参数、应用设置、压缩功能和HTTPS性能优化的建议配置。

  • 内核相关参数调优

    在对内核参数进行调优之前,务必确保对这些参数有深刻的理解,并慎重操作。

    • 调整TIME_WAIT

      为了优化Nginx Ingress的性能,可以通过调整系统参数来开启TIME_WAIT复用,并减少FIN_WAIT2TIME_WAIT状态的超时时间。

      • net.ipv4.tcp_fin_timeout=15:减少FIN_WAIT2状态的超时时间,以更快释放连接资源。

      • net.netfilter.nf_conntrack_tcp_timeout_time_wait=30:缩短TIME_WAIT状态下的连接保持时间,加速资源释放。

    • 配置操作

      1. 通过以下命令编辑Nginx Ingress ControllerDeployment。

        kubectl edit deployments -n kube-system nginx-ingress-controller
      2. init container部分新增sysctl -w net.ipv4.tcp_fin_timeout=15sysctl -w net.netfilter.nf_conntrack_tcp_timeout_time_wait=30参数,保存并退出。

        initContainers:
              - command:   # 新增sysctl -w net.ipv4.tcp_fin_timeout=15sysctl -w net.netfilter.nf_conntrack_tcp_timeout_time_wait=30参数。
                - /bin/sh
                - -c
                - |
                  if [ "$POD_IP" != "$HOST_IP" ]; then
                  mount -o remount rw /proc/sys
                  sysctl -w net.core.somaxconn=65535
                  sysctl -w net.ipv4.ip_local_port_range="1024 65535"
                  sysctl -w kernel.core_uses_pid=0
                  sysctl -w net.ipv4.tcp_fin_timeout=15 
                  sysctl -w net.netfilter.nf_conntrack_tcp_timeout_time_wait=30   
                  fi
  • 应用层配置调优

    您可以通过以下命令和配置对业务从应用层面配置调优。

    1. 使用以下命令编辑Nginx Ingress ControllerConfigMap。

       kubectl edit cm -n kube-system nginx-configuration
    2. 根据下述配置参数,结合您的实际业务场景进行配置调优。

      配置项

      配置参数

      描述

      下游keepalive

      keep-alive: "60"

      指定keepalive连接的超时时间,单位为秒。

      keep-alive-requests: "10000"

      指定keepalive请求的最大数量。

      上游keepalive

      upstream-keepalive-connections: "1000"

      最大keepalive连接数。

      upstream-keepalive-requests: "2147483647"

      允许的最大keepalive请求数。

      upstream-keepalive-time: 1h

      上游keepalive连接的最大保持时间。

      upstream-keepalive-timeout: "150"

      指定上游keepalive连接的空闲超时时间,单位为秒。

      单个Worker最大连接

      max-worker-connections: "65536"

      单个Worker支持的最大连接数。

      超时配置

      说明

      可根据实际业务场景调整,按需配置。

      proxy-connect-timeout: "3"

      建立TCP连接的超时时间,单位为秒。

      proxy-read-timeout: "5"

      读取数据的超时时间,单位为秒。

      proxy-send-timeout: "5"

      发送数据的超时时间,单位为秒。

      优化重试机制

      说明

      当后端服务异常情况下,多级重试的过多请求可能加重后端服务的负载引发雪崩问题。更多详情请参见Ingress-nginx官方文档

      proxy-next-upstream-tries:"3"

      失败后的重试次数,默认为3次,包括1次初始请求和2次重试。

      proxy-next-upstream:"off"

      指定重试条件,设置为off则关闭重试功能。

      proxy-next-upstream-timeout

      指定请求重试的超时时间,单位为秒,根据实际应用场景进行调整。

  • 启用Brotli压缩

    压缩是一种通过牺牲CPU时间来节省大量网络带宽的通用方法,能够提升吞吐量。BrotliGoogle开发的一种压缩算法。相较于常用的gzip压缩算法(Nginx Ingress Controller默认使用gzip),Brotli的压缩率据称要高出20%~30%。Ingress-nginx中启用Brotli压缩,需要配置以下参数,更多详情请参见Ingress-nginx官方文档

    • enable-brotli: 是否启用Brotli压缩算法,取值为 true 或 false

    • brotli-level: 压缩级别,范围是111,默认是4。级别越高,CPU性能消耗越大。

    • brotli-types: 指定使用Brotli即时压缩的MIME类型。

    您可以使用以下命令启用Brotli压缩。

     kubectl edit cm -n kube-system nginx-configuration

    配置示例:

    enable-brotli: "true"
    brotli-level: "6"
    brotli-types: "text/xml image/svg+xml application/x-font-ttf image/vnd.microsoft.icon application/x-font-opentype application/json font/eot application/vnd.ms-fontobject application/javascript font/otf application/xml application/xhtml+xml text/javascript application/x-javascript text/plain application/x-font-truetype application/xml+rss image/x-icon font/opentype text/css image/x-win-bitmap"
  • HTTPS性能优化

    为了提升HTTPS的性能,可以使用kubectl edit cm -n kube-system nginx-configuration命令编辑ConfigMap,并配置以下参数:SSL 会话缓存、OCSP Stapling、TLS 1.3 早期数据以及调整密码套件优先级。

    • SSL会话缓存与超时

      设置SSL 共享会话缓存的大小以及重用缓存中存储的会话参数的时间,可以减少SSL握手的开销。

      • ConfigMap配置:

        ssl-session-cache-size:"10m"
        ssl-session-timeout:"10m"
      • 对应Nginx侧的nginx.conf配置,可以根据实际场景需求调整。

        ssl_session_cache shared:SSL:120m;   # 1m 4000个。
        ssl_session_timeout 1h;              # 会话超时时间为1小时。
    • 启用OCSP Stapling

      使用OCSP Stapling可以减少客户端证书验证的时间。

      enable-ocsp: "true"
    • 支持TLS 1.3早期数据(0-RTT)

      启用TLS 1.30-RTT功能可以允许客户端在握手完成之前就发送数据,从而减少连接建立的延迟。

      ssl-early-data: "true"
      ssl-protocols: "TLSv1.3"
    • 调整Cipher优先级(无需手动调优)

      通过调整加密套件的优先级,可以有效降低延迟。Nginx Ingress Controller的默认配置已进行了优化。

      ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384';
      ssl_prefer_server_ciphers on;    # 优先使用服务器端的Cipher配置。