Pruebas de rendimiento de los controladores de Ingress de NGINX en un entorno dinámico de Kubernetes en la nube

NGINX | 22 de septiembre de 2020

Editor – Esta publicación forma parte de una serie de 10 entregas:

  1. Reduzca la complejidad con Kubernetes preparado para producción
  2. Cómo mejorar la resiliencia en Kubernetes con una gestión avanzada del tráfico
  3. Cómo mejorar la visibilidad en Kubernetes
  4. Seis formas de proteger Kubernetes mediante herramientas de gestión del tráfico
  5. Guía para elegir un controlador de Ingress, parte 1: identifique sus requisitos
  6. Guía para elegir un controlador de Ingress, parte 2: riesgos y preparación para el futuro
  7. Guía para elegir un controlador de Ingress, parte 3: código abierto frente a opción predeterminada frente a solución comercial
  8. Guía para elegir un controlador de Ingress, parte 4: opciones de controladores de Ingress de NGINX
  9. Cómo elegir una malla de servicios
  10. Pruebas de rendimiento de los controladores Ingress de NGINX en un entorno dinámico de Kubernetes en la nube (esta publicación)

También puede descargar la colección completa de artículos del blog como libro electrónico gratuito – Cómo llevar Kubernetes de las pruebas a producción.

A medida que cada vez más empresas ejecutan aplicaciones en contenedores en entornos de producción, Kubernetes sigue consolidando su posición como la herramienta estándar para la orquestación de contenedores. Al mismo tiempo, la demanda de computación en la nube se ha adelantado un par de años, ya que las iniciativas de trabajo desde casa impulsadas por la pandemia de COVID‑19 han acelerado el crecimiento del tráfico de Internet. Las empresas están trabajando con rapidez para actualizar su infraestructura porque sus clientes están sufriendo importantes interrupciones y sobrecargas de la red.

Para alcanzar el nivel de rendimiento necesario en entornos de microservicios basados en la nube, se necesita un software rápido y totalmente dinámico que aproveche la escalabilidad y el rendimiento de los centros de datos hiperescalables de última generación. Muchas organizaciones que utilizan Kubernetes para gestionar contenedores dependen de un controlador Ingress basado en NGINX para poner sus aplicaciones a disposición de los usuarios.

En este blog presentamos los resultados de nuestras pruebas de rendimiento de tres controladores Ingress de NGINX en un entorno multinube realista, en las que medimos la latencia de las conexiones de los clientes a través de Internet:

Protocolo de pruebas y métricas recopiladas

Utilizamos el programa de generación de carga wrk2 para emular un cliente que realizaba solicitudes continuas a través de HTTPS durante un periodo definido. El controlador Ingress sometido a prueba —el controlador Ingress de la comunidad, NGINX Ingress Controller basado en NGINX Open Source o NGINX Ingress Controller basado en NGINX Plus— reenviaba las solicitudes a las aplicaciones de backend desplegadas en pods de Kubernetes y devolvía al cliente la respuesta generada por las aplicaciones. Generamos un flujo constante de tráfico de clientes para someter a los controladores Ingress a pruebas de estrés y recopilamos las siguientes métricas de rendimiento:

  • Latencia: tiempo transcurrido entre el momento en que el cliente genera una solicitud y recibe la respuesta. Las latencias se presentan mediante una distribución por percentiles. Por ejemplo, si hay 100 muestras de pruebas de latencia, el valor del percentil 99 corresponde a la segunda latencia de respuesta más alta de las 100 ejecuciones de prueba.
  • Tiempos de espera de conexión – Conexiones TCP que se interrumpen o descartan silenciosamente porque el controlador de Ingress no responde a las solicitudes dentro de un plazo determinado.
  • Errores de lectura – Intentos de lectura en una conexión que fallan porque se ha cerrado un socket del controlador de Ingress.
  • Errores de conexión – Conexiones TCP entre el cliente y el controlador de Ingress que no llegan a establecerse.

Topología

Para todas las pruebas, utilizamos la utilidad wrk2 ejecutada en una máquina cliente de AWS para generar solicitudes. El cliente de AWS se conectó a la dirección IP externa del controlador Ingress, que se implementó como un DaemonSet de Kubernetes en GKE-node-1 dentro de un entorno de Google Kubernetes Engine (GKE). El controlador Ingress se configuró para la terminación SSL (con referencia a un Secret de Kubernetes) y el enrutamiento de capa 7, y se expuso mediante un Service de Kubernetes de Type LoadBalancer. La aplicación de backend se ejecutó como un Deployment de Kubernetes en GKE-node-2.

Para obtener información detallada sobre los tipos de máquinas en la nube y las configuraciones de software, consulte el Apéndice.

Metodología de las pruebas

Implementación del cliente

Ejecutamos el siguiente script de wrk2 (versión 4.0.0) en la máquina cliente de AWS. Este inicia 2 hilos de wrk que, en conjunto, establecen 1000 conexiones con el controlador de Ingress desplegado en GKE. Durante cada prueba de 3 minutos, el script genera 30 000 solicitudes por segundo (RPS), lo que consideramos una buena simulación de la carga que soporta un controlador de Ingress en un entorno de producción.

wrk -t2 -c1000 -d180s -L -R30000 https://app.example.com:443/

donde:

  • -t – Establece el número de hilos (2)
  • -c – Establece el número de conexiones TCP (1000)
  • -d – Establece la duración de la ejecución de la prueba en segundos (180, o 3 minutos)
  • -L – Genera información detallada sobre los percentiles de latencia para exportarla a herramientas de análisis
  • -R – Establece el número de solicitudes por segundo (30 000)

Para el cifrado TLS, utilizamos RSA con una clave de 2048 bits y secreto perfecto hacia adelante.

Cada respuesta de la aplicación de back-end (a la que se accede en https://app.example.com:443) consta de aproximadamente 1 KB de metadatos básicos del servidor, junto con el código de estado HTTP 200 OK.

Despliegue de la aplicación de back-end

Realizamos pruebas tanto con un despliegue estático como con uno dinámico de la aplicación de back-end.

En el despliegue estático, había cinco réplicas de Pods y no se aplicó ningún cambio mediante la API de Kubernetes.

Para el despliegue dinámico, utilizamos el siguiente script para escalar periódicamente el despliegue de backend nginx de cinco réplicas de Pod a siete y, después, volver a reducirlo a cinco. Esto simula un entorno dinámico de Kubernetes y permite comprobar la eficacia con la que el controlador Ingress se adapta a los cambios en los endpoints.

while [ 1 -eq 1 ]
do
  kubectl scale deployment nginx --replicas=5
  sleep 12
  kubectl scale deployment nginx --replicas=7
  sleep 10
done

Resultados de rendimiento

Resultados de latencia del despliegue estático

Como se muestra en el gráfico, los tres controladores de Ingress lograron un rendimiento similar con un despliegue estático de la aplicación de backend. Esto resulta lógico, dado que todos se basan en NGINX Open Source y el despliegue estático no requiere que el controlador de Ingress se reconfigure.

Resultados de latencia de la implementación dinámica

El gráfico muestra la latencia generada por cada controlador Ingress en un despliegue dinámico en el que escalamos periódicamente la aplicación de back-end de cinco Pods de réplica a siete y viceversa (consulte Despliegue de la aplicación de back-end para obtener más información).

Está claro que solo el controlador Ingress basado en NGINX Plus ofrece un buen rendimiento en este entorno, prácticamente sin latencia hasta el percentil 99,99. Tanto el controlador Ingress de la comunidad como el basado en NGINX Open Source presentan una latencia apreciable en percentiles bastante bajos, aunque con patrones muy diferentes. En el caso del controlador Ingress de la comunidad, la latencia aumenta de forma gradual pero constante hasta el percentil 99, donde se estabiliza en torno a los 5000 ms (5 segundos). En el caso del controlador Ingress basado en NGINX Open Source, la latencia se dispara hasta unos 32 segundos en el percentil 99 y vuelve a aumentar hasta los 60 segundos en el percentil 99,99.

Como explicamos con más detalle en Resultados de tiempos de espera y errores de la implementación dinámica, la latencia que experimentan los controladores Ingress de la comunidad y los basados en NGINX Open Source se debe a los errores y tiempos de espera que se producen después de actualizar y volver a cargar la configuración de NGINX en respuesta a los cambios en los endpoints de la aplicación de backend.

A continuación, se muestra una vista más detallada de los resultados del controlador Ingress de la comunidad y del controlador Ingress basado en NGINX Plus en las mismas condiciones de prueba que en el gráfico anterior. El controlador Ingress basado en NGINX Plus prácticamente no introduce latencia hasta el percentil 99,99, momento en el que empieza a aumentar hasta alcanzar los 254 ms en el percentil 99,9999. El patrón de latencia del controlador Ingress de la comunidad aumenta de forma constante hasta alcanzar una latencia de 5000 ms en el percentil 99, punto a partir del cual se estabiliza.

Resultados de tiempos de espera y errores del despliegue dinámico

Esta tabla muestra con mayor detalle la causa de los resultados de latencia.

NGINX Open Source Comunidad NGINX PlusErrores de conexión 33365 0 0Tiempos de espera de conexión 309 8809 0Errores de lectura 4650 0 0

Con el controlador Ingress basado en NGINX Open Source, la necesidad de actualizar y recargar la configuración de NGINX cada vez que cambian los endpoints de la aplicación de backend provoca numerosos errores de conexión, tiempos de espera de conexión agotados y errores de lectura. Los errores de conexión/socket se producen durante el breve periodo que tarda NGINX en recargarse, cuando los clientes intentan conectarse a un socket que ya no está asignado al proceso de NGINX. Los tiempos de espera de conexión se agotan cuando los clientes han establecido una conexión con el controlador Ingress, pero el endpoint de backend ya no está disponible. Tanto los errores como los tiempos de espera agotados afectan gravemente a la latencia, con picos de 32 segundos en el percentil 99 y de hasta 60 segundos en el percentil 99,99.

Con el controlador Ingress de la comunidad, se produjeron 8.809 tiempos de espera de conexión debido a los cambios en los endpoints a medida que la aplicación de backend aumentaba y reducía su escala. El controlador Ingress de la comunidad utiliza código Lua para evitar recargar la configuración cuando cambian los endpoints. Los resultados muestran que ejecutar un controlador Lua dentro de NGINX para detectar cambios en los endpoints soluciona algunas de las limitaciones de rendimiento de la versión basada en NGINX Open Source, derivadas de la necesidad de recargar la configuración después de cada cambio en los endpoints. No obstante, siguen produciéndose tiempos de espera de conexión, lo que genera una latencia considerable en los percentiles más altos.

Con el controlador Ingress basado en NGINX Plus no se produjeron errores ni tiempos de espera agotados: el entorno dinámico prácticamente no afectó al rendimiento. Esto se debe a que utiliza la API de NGINX Plus para actualizar dinámicamente la configuración de NGINX cuando cambian los endpoints. Como se ha mencionado, la latencia máxima fue de 254 ms y solo se produjo en el percentil 99,9999.

Conclusión

Los resultados de rendimiento demuestran que, para eliminar por completo los tiempos de espera agotados y los errores en un entorno dinámico de nube de Kubernetes, el controlador Ingress debe adaptarse dinámicamente a los cambios en los endpoints de backend sin controladores de eventos ni recargas de configuración. A partir de los resultados, podemos afirmar que la API de NGINX Plus es la solución óptima para reconfigurar NGINX dinámicamente en un entorno dinámico. En nuestras pruebas, solo el controlador Ingress basado en NGINX Plus logró el rendimiento impecable que necesita en entornos de Kubernetes altamente dinámicos para mantener satisfechos a sus usuarios.

Apéndice

Especificaciones de la máquina en la nube

Máquina Proveedor de nube Tipo de máquinaCliente AWS m5a.4xlargeGKE-node-1 GCP e2-standard-32GKE-node-2 GCP e2-standard-32

Configuración de los controladores de Ingress NGINX Open Source y NGINX Plus

Configuración de Kubernetes

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: nginx-ingress
  namespace: nginx-ingress
spec:
  selector:
    matchLabels:
      app: nginx-ingress
  template:
    metadata:
      labels:
        app: nginx-ingress
     #annotations:
       #prometheus.io/scrape: "true"
       #prometheus.io/port: "9113"
    spec:
      serviceAccountName: nginx-ingress
      nodeSelector:
        kubernetes.io/hostname: gke-rawdata-cluster-default-pool-3ac53622-6nzr
      hostNetwork: true    
      containers:
      - image: gcr.io/nginx-demos/nap-ingress:edge
        imagePullPolicy: Always
        name: nginx-plus-ingress
        ports:
        - name: http
          containerPort: 80
          hostPort: 80
        - name: https
          containerPort: 443
          hostPort: 443
        - name: readiness-port
          containerPort: 8081
       #- name: prometheus
         #containerPort: 9113
        readinessProbe:
          httpGet:
            path: /nginx-ready
            port: readiness-port
          periodSeconds: 1
        securityContext:
          allowPrivilegeEscalation: true
          runAsUser: 101 #nginx
          capabilities:
            drop:
            - ALL
            add:
            - NET_BIND_SERVICE
        env:
        - name: POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        - name: POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        args:
          - -nginx-plus
          - -nginx-configmaps=$(POD_NAMESPACE)/nginx-config
          - -default-server-tls-secret=$(POD_NAMESPACE)/default-server-secret

Notas:

  • Esta configuración es para NGINX Plus. Las referencias a nginx‑plus se adaptaron según fue necesario en la configuración para NGINX Open Source.
  • NGINX App Protect está incluido en la imagen (gcr.io/nginx-demos/nap-ingress:edge), pero se deshabilitó (se omitió la opción -enable-app-protect).

ConfigMap

kind: ConfigMap
apiVersion: v1
metadata:
  name: nginx-config
  namespace: nginx-ingress
data:
  worker-connections: "10000"
  worker-rlimit-nofile: "10240"
  keepalive: "100"
  keepalive-requests: "100000000"

Configuración del controlador de Ingress NGINX de la comunidad

Configuración de Kubernetes

apiVersion: apps/v1
kind: DaemonSet
metadata:
  labels:
    helm.sh/chart: ingress-nginx-2.11.1
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/version: 0.34.1
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/component: controller
  name: ingress-nginx-controller
  namespace: ingress-nginx
spec:
  selector:
    matchLabels:
      app.kubernetes.io/name: ingress-nginx
      app.kubernetes.io/instance: ingress-nginx
      app.kubernetes.io/component: controller
  template:
    metadata:
      labels:
        app.kubernetes.io/name: ingress-nginx
        app.kubernetes.io/instance: ingress-nginx
        app.kubernetes.io/component: controller
    spec:
      nodeSelector: 
        kubernetes.io/hostname: gke-rawdata-cluster-default-pool-3ac53622-6nzr
      hostNetwork: true
      containers:
        - name: controller
          image: us.gcr.io/k8s-artifacts-prod/ingress-nginx/controller:v0.34.1@sha256:0e072dddd1f7f8fc8909a2ca6f65e76c5f0d2fcfb8be47935ae3457e8bbceb20
          imagePullPolicy: IfNotPresent
          lifecycle:
            preStop:
              exec:
                command:
                  - /wait-shutdown
          args:
            - /nginx-ingress-controller
            - --election-id=ingress-controller-leader
            - --ingress-class=nginx
            - --configmap=$(POD_NAMESPACE)/ingress-nginx-controller
            - --validating-webhook=:8443
            - --validating-webhook-certificate=/usr/local/certificates/cert
            - --validating-webhook-key=/usr/local/certificates/key
          securityContext:
            capabilities:
              drop:
                - ALL
              add:
                - NET_BIND_SERVICE
            runAsUser: 101
            allowPrivilegeEscalation: true
          env:
            - name: POD_NAME
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
            - name: POD_NAMESPACE
              valueFrom:
                fieldRef:
                  fieldPath: metadata.namespace
          readinessProbe:
            httpGet:
              path: /healthz
              port: 10254
              scheme: HTTP
            periodSeconds: 1
          ports:
            - name: http
              containerPort: 80
              protocol: TCP
            - name: https
              containerPort: 443
              protocol: TCP
            - name: webhook
              containerPort: 8443
              protocol: TCP
          volumeMounts:
            - name: webhook-cert
              mountPath: /usr/local/certificates/
              readOnly: true
      serviceAccountName: ingress-nginx
      terminationGracePeriodSeconds: 300
      volumes:
        - name: webhook-cert
          secret:
            secretName: ingress-nginx-admission

ConfigMap

apiVersion: v1
kind: ConfigMap
metadata:
  name: ingress-nginx-controller
  namespace: ingress-nginx
data:
  max-worker-connections: "10000"
  max-worker-open-files: "10204"
  upstream-keepalive-connections: "100"
  keep-alive-requests: "100000000"

Configuración de la aplicación back-end

Configuración de Kubernetes

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
spec:
  selector:
    matchLabels:
      app: nginx 
  template:
    metadata:
      labels:
        app: nginx
    spec:
      nodeSelector:
        kubernetes.io/hostname: gke-rawdata-cluster-default-pool-3ac53622-t2dz 
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 8080
        volumeMounts: 
        - name: main-config-volume
          mountPath: /etc/nginx
        - name: app-config-volume
          mountPath: /etc/nginx/conf.d
        readinessProbe:
          httpGet:
            path: /healthz
            port: 8080
          periodSeconds: 3
      volumes: 
      - name: main-config-volume
        configMap:
          name: main-conf
      - name: app-config-volume
        configMap: 
          name: app-conf
---

ConfigMaps

apiVersion: v1
kind: ConfigMap
metadata:
  name: main-conf
  namespace: default
data:
  nginx.conf: |+
    user nginx;
    worker_processes 16;
    worker_rlimit_nofile 102400;
    worker_cpu_affinity auto 1111111111111111;
    error_log  /var/log/nginx/error.log notice;
    pid        /var/run/nginx.pid;
 
    events {
        worker_connections  100000;
    }
 
    http {
 
        log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                          '$status $body_bytes_sent "$http_referer" '
                          '"$http_user_agent" "$http_x_forwarded_for"';
 
        sendfile        on;
        tcp_nodelay on;
 
        access_log off;
 
        include /etc/nginx/conf.d/*.conf;
    }
 
---
 
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-conf
  namespace: default
data:
  app.conf: "server {listen 8080;location / {default_type text/plain;expires -1;return 200 'Server address: $server_addr:$server_portnServer name:$hostnamenDate: $time_localnURI: $request_urinRequest ID: $request_idn';}location /healthz {return 200 'I am happy and healthy :)';}}"
---

Servicio

apiVersion: v1
kind: Service
metadata:
  name: app-svc
spec:
  ports:
  - port: 80
    targetPort: 8080
    protocol: TCP
    name: http
  selector:
    app: nginx
---


Compartir

Sobre el autor

Artículos relacionados del blog

Únete a F5 NGINX en AppWorld 2024
NGINX | 04/01/2024

Únete a F5 NGINX en AppWorld 2024

Descubre las numerosas razones para asistir al evento insignia de F5, que incluirá un emocionante anuncio sobre la cartera de productos NGINX.

Rotación de certificados SSL/TLS sin reinicios en NGINX Open Source
NGINX | 26/09/2023

Rotación de certificados SSL/TLS sin reinicios en NGINX Open Source

La función de diccionario compartido permite configurar NGINX sin necesidad de reiniciarlo al renovar los certificados SSL/TLS.

Exposición de APIs en Kubernetes
NGINX | 10/03/2022

Exposición de APIs en Kubernetes

La unidad 2 de Microservices March 2022 se centra en cómo exponer tus API en Kubernetes. Aquí tienes enlaces a los recursos que necesitas: la retransmisión en directo del 14 de marzo, artículos de blog y otros recursos de preparación, y el laboratorio práctico «Protege las API de Kubernetes mediante la limitación de solicitudes».

NGINX Sprint 2.0: visión clara, código renovado y nuevos compromisos con el código abierto
NGINX | 23/08/2021

NGINX Sprint 2.0: visión clara, código renovado y nuevos compromisos con el código abierto

Hoy, en NGINX Sprint 2.0, anunciamos cómo respaldaremos nuestra visión del futuro de las aplicaciones modernas con una mayor presencia en la comunidad de código abierto, incluida la participación en el proyecto comunitario Kubernetes, nuevos proyectos de software de código abierto y la arquitectura de referencia para aplicaciones modernas.

Presentamos NGINX Plus R22
NGINX | 09/06/2020

Presentamos NGINX Plus R22

NGINX Plus R22 incorpora nuevas funcionalidades, como la compatibilidad con OCSP para verificar la revocación de certificados en TLS mutuo, el uso de varios proveedores de identidad OIDC en una configuración, el seguimiento en tiempo real de la limitación de solicitudes y conexiones en el panel de NGINX Plus y mejoras en NGINX JavaScript.

Presentamos NGINX Plus R17
NGINX | 11/12/2018

Presentamos NGINX Plus R17

NGINX Plus R17 incorpora compatibilidad con la limitación de velocidad en dos fases y con TLS 1.3, la versión más reciente del protocolo Transport Layer Security. La configuración de OpenID Connect es más sencilla y NGINX ModSecurity WAF es el doble de rápido que antes. También se ha actualizado el módulo JavaScript de NGINX.