Editor : Esta publicación es parte de una serie de 10 partes:
También puede descargar el conjunto completo de blogs como un libro electrónico gratuito: Cómo llevar Kubernetes de la prueba a la producción .
A medida que más empresas ejecutan aplicaciones en contenedores en producción, Kubernetes continúa 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 porque 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 rápidamente para actualizar su infraestructura porque sus clientes están experimentando importantes cortes y sobrecargas en la red.
Para lograr el nivel de rendimiento requerido en entornos de microservicios basados en la nube, necesita un software rápido y totalmente dinámico que aproveche la escalabilidad y el rendimiento de los centros de datos de hiperescala de próxima generación. Muchas organizaciones que utilizan Kubernetes para administrar contenedores dependen de un controlador Ingress basado en NGINX para entregar sus aplicaciones a los usuarios.
En este blog informamos los resultados de nuestras pruebas de rendimiento en tres controladores NGINX Ingress en un entorno multicloud realista, midiendo la latencia de las conexiones de clientes a través de Internet:
Utilizamos el programa de generación de carga wrk2
para emular un cliente, realizando solicitudes continuas a través de HTTPS durante un período definido. El controlador de Ingress bajo prueba (el controlador de Ingress comunitario, el controlador de Ingress NGINX basado en NGINX Open Source o el controlador de Ingress NGINX basado en NGINX Plus) reenvió solicitudes a aplicações de back-end implementadas en pods de Kubernetes y devolvió la respuesta generada por las aplicações al cliente. Generamos un flujo constante de tráfico de clientes para probar el estrés de los controladores de Ingress y recopilamos las siguientes métricas de rendimiento:
Para todas las pruebas, utilizamos la utilidad wrk2
que se ejecuta en una máquina cliente en AWS para generar solicitudes. El cliente de AWS se conectó a la dirección IP externa del controlador de Ingress, que se implementó como un Kubernetes DaemonSet en GKE-node-1 en un entorno de Google Kubernetes Engine (GKE). El controlador de ingreso se configuró para la terminación SSL (haciendo referencia a un secreto de Kubernetes) y enrutamiento de capa 7, y se expuso a través de un servicio de Kubernetes de tipo
LoadBalancer
. La aplicação de backend se ejecutó como una implementación de Kubernetes en GKE-node-2 .
Para obtener detalles completos sobre los tipos de máquinas en la nube y las configuraciones de software, consulte el Apéndice .
Ejecutamos el siguiente script wrk2
(versión 4.0.0) en la máquina cliente de AWS. Genera 2 subprocesos wrk
que juntos establecen 1000 conexiones al controlador de Ingress implementado en GKE. Durante cada ejecución de prueba de 3 minutos, el script genera 30 000 solicitudes por segundo (RPS), lo que consideramos una buena simulación de la carga en un controlador Ingress en un entorno de producción.
trabajo -t2 -c1000 -d180s -L -R30000 https://app.example.com:443/
DÓNDE:
-t
– Establece el número de subprocesos (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 del percentil de latencia para exportar a herramientas de análisis-R
– Establece el número de RPS (30 000)Para el cifrado TLS, utilizamos RSA con un tamaño de clave de 2048 bits y Perfect Forward Secrecy.
Cada respuesta de la aplicação back-end (a la que se accede en https://app.ejemplo.com:443) consta de aproximadamente 1 KB de metadatos básicos del servidor, junto con la 200
DE ACUERDO
Código de estado HTTP.
Realizamos pruebas con una implementación estática y dinámica de la aplicação back-end.
En la implementación estática, había cinco réplicas de Pod y no se aplicaron cambios mediante la API de Kubernetes.
Para la implementación dinámica, utilizamos el siguiente script para escalar periódicamente la implementación de nginx de backend desde cinco réplicas de Pod hasta siete, y luego nuevamente a cinco. Esto emula un entorno dinámico de Kubernetes y prueba la eficacia con la que el controlador de Ingress se adapta a los cambios de los puntos finales.
mientras [ 1 -eq 1 ]
hacer
Implementación a escala de kubectl nginx --replicas=5
dormir 12
Implementación a escala de kubectl nginx --replicas=7
dormir 10
Listo
Como se indica en el gráfico, los tres controladores Ingress lograron un rendimiento similar con una implementación estática de la aplicação back-end. Esto tiene sentido dado que todos se basan en NGINX de código abierto y la implementación estática no requiere reconfiguración del controlador Ingress.
El gráfico muestra la latencia incurrida por cada controlador de Ingress en una implementación dinámica donde periódicamente escalamos la aplicação back-end desde cinco pods de réplica hasta siete y viceversa (consulte Implementación de aplicação back-end para obtener más detalles).
Está claro que solo el controlador Ingress basado en NGINX Plus funciona bien en este entorno, y prácticamente no sufre latencia hasta el percentil 99,99. Tanto la comunidad como los controladores Ingress basados en código abierto NGINX experimentan una latencia notable en percentiles bastante bajos, aunque con un patrón bastante diferente. Para el controlador de Ingress de la comunidad, la latencia aumenta de manera suave pero constante hasta el percentil 99, donde se estabiliza en aproximadamente 5000 ms (5 segundos). Para el controlador Ingress basado en código abierto NGINX, la latencia aumenta drásticamente a aproximadamente 32 segundos en el percentil 99, y nuevamente a 60 segundos en el percentil 99,99.
Como analizamos más detalladamente en Resultados de errores y tiempos de espera para la implementación dinámica , la latencia experimentada con los controladores de ingreso basados en código abierto NGINX y la comunidad se debe a errores y tiempos de espera que ocurren después de que se actualiza y se vuelve a cargar la configuración de NGINX en respuesta a los puntos finales cambiantes de la aplicação back-end.
Aquí se muestra una vista más detallada de los resultados para el controlador Ingress comunitario y el controlador Ingress basado en NGINX Plus en la misma condición de prueba que el gráfico anterior. El controlador Ingress basado en NGINX Plus prácticamente no introduce latencia hasta el percentil 99,99, donde comienza a subir hacia los 254 ms en el percentil 99,9999. El patrón de latencia del controlador de Ingress de la comunidad crece de manera constante hasta alcanzar una latencia de 5000 ms en el percentil 99, punto en el cual la latencia se estabiliza.
Esta tabla muestra la causa de los resultados de latencia con mayor detalle.
NGINX Open Source | COMUNIDAD | NGINX Plus | |
---|---|---|---|
Errores de conexión | 33365 | 0 | 0 |
Tiempos de espera de conexión | 309 | 8809 | 0 |
Errores de lectura | 4650 | 0 | 0 |
Con el controlador Ingress basado en código abierto NGINX, la necesidad de actualizar y recargar la configuración de NGINX para cada cambio en los puntos finales de la aplicación back-end provoca muchos errores de conexión, tiempos de espera de conexión y errores de lectura. Los errores de conexión/socket ocurren durante el breve tiempo que tarda NGINX en recargarse, cuando los clientes intentan conectarse a un socket que ya no está asignado al proceso NGINX. Los tiempos de espera de conexión se producen cuando los clientes han establecido una conexión con el controlador de Ingress, pero el punto final del backend ya no está disponible. Tanto los errores como los tiempos de espera afectan gravemente la latencia, con picos de hasta 32 segundos en el percentil 99 y nuevamente a 60 segundos en el percentil 99,99.
Con el controlador de Ingress de la comunidad, hubo 8,809 tiempos de espera de conexión debido a los cambios en los puntos finales a medida que la aplicação de back-end escalaba hacia arriba y hacia abajo. El controlador de Ingress de la comunidad utiliza código Lua para evitar recargas de configuración cuando cambian los puntos finales . Los resultados muestran que ejecutar un controlador Lua dentro de NGINX para detectar cambios en los puntos finales aborda algunas de las limitaciones de rendimiento de la versión de código abierto de NGINX, que resultan de su requisito de recargar la configuración después de cada cambio en los puntos finales. Sin embargo, todavía se producen tiempos de espera de conexión que generan una latencia significativa en percentiles más altos.
Con el controlador Ingress basado en NGINX Plus no hubo errores ni tiempos de espera: el entorno dinámico prácticamente no tuvo efecto en el rendimiento. Esto se debe a que utiliza la API NGINX Plus para actualizar dinámicamente la configuración de NGINX cuando cambian los puntos finales. Como se mencionó, la latencia más alta fue de 254 ms y ocurrió solo en el percentil 99,9999.
Los resultados de rendimiento muestran que, para eliminar por completo los tiempos de espera y los errores en un entorno de nube Kubernetes dinámico, el controlador de Ingress debe ajustarse dinámicamente a los cambios en los puntos finales del back-end sin controladores de eventos ni recargas de configuración. Basándonos en los resultados, podemos decir que la API NGINX Plus es la solución óptima para reconfigurar dinámicamente NGINX en un entorno dinámico. En nuestras pruebas, solo el controlador Ingress basado en NGINX Plus logró el rendimiento impecable en entornos de Kubernetes altamente dinámicos que necesita para mantener satisfechos a sus usuarios.
Máquina | Cloud Provider | Tipo de máquina |
---|---|---|
el cliente | AWS | m5a.4xgrande |
Nodo GKE 1 | GCP | e2-estándar-32 |
Nodo GKE 2 | GCP | e2-estándar-32 |
apiVersion: apps/v1
tipo: Conjunto de demonios
Metadatos:
Nombre: nginx-ingress
Espacio de nombres: nginx-ingress
Especificación:
Selector:
Etiquetas de coincidencia:
Aplicación: nginx-ingress
Plantilla:
Metadatos:
Etiquetas:
Aplicación: nginx-ingress
#anotaciones:
#prometheus.io/scrape: "true"
#prometheus.io/port: "9113"
especificación:
NombreDeCuentaDeServicio: nginx-ingress
SelectorDeNodo:
kubernetes.io/nombreDeHost: gke-rawdata-cluster-default-pool-3ac53622-6nzr
RedDeHost: verdadero
contenedores:
- imagen: gcr.io/nginx-demos/nap-ingress:edge
PolíticaDeExtracciónDeImagen: Siempre
nombre: nginx-plus-ingress
puertos:
-nombre: http
puertoContainer: 80
Puerto del host: 80
- nombre: https
puerto del contenedor: 443
Puerto del host: 443
- nombre: puerto de preparación
puertoContenedor: 8081
#- nombre: prometheus
#puertocontenedor: 9113
readinessProbe:
httpGet:
ruta: /nginx-ready
puerto: readiness-port
periodSeconds: 1
Contexto de seguridad:
PermitirEscalamientoDePrivilegios: verdadero
EjecutarComoUsuario: 101 #nginx
Capacidades:
Desinstalar:
- TODOS
Añadir:
- NET_BIND_SERVICE
entorno:
- nombre: POD_NAMESPACE
valorDe:
ReferenciaDeCampo:
RutaDeCampo: metadatos.espaciodenombres
- nombre: NOMBRE_POD
valorDe:
Referencia_campo:
Ruta_campo: metadatos.nombre
argumentos:
- -nginx-plus
- -nginx-configmaps=$(POD_NAMESPACE)/nginx-config
- -default-server-tls-secret=$(POD_NAMESPACE)/default-server-secret
Notas:
nginx-plus
se ajustaron según fuera necesario en la configuración para NGINX Open Source.gcr.io/nginx-demos/nap-ingress:edge
), pero estaba deshabilitado (se omitió el indicador -enable-app-protect
).amable: Mapa de configuración
Versión de API: v1
Metadatos:
Nombre: nginx-config
Espacio de nombres: nginx-ingress
Datos:
Conexiones de trabajador: "10000"
worker-rlimit-nofile: "10240"
Mantener vivo: "100"
Solicitudes de mantenimiento de conexión: "100000000"
apiVersion: apps/v1
tipo: Conjunto de demonios
Metadatos:
Etiquetas:
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/componente: controlador
nombre: ingress-nginx-controller
espacio de nombres: ingress-nginx
especificación:
selector:
matchLabels:
app.kubernetes.io/nombre: ingress-nginx
app.kubernetes.io/instancia: ingress-nginx
app.kubernetes.io/componente: controlador
plantilla:
metadatos:
etiquetas:
app.kubernetes.io/nombre: ingress-nginx
app.kubernetes.io/instancia: ingress-nginx
app.kubernetes.io/componente: controlador
especificación:
nodeSelector:
kubernetes.io/nombre de host: gke-rawdata-cluster-default-pool-3ac53622-6nzr
hostNetwork: verdadero
contenedores:
- nombre: controlador
imagen: us.gcr.io/k8s-artifacts-prod/ingress-nginx/controller:v0.34.1@sha256:0e072dddd1f7f8fc8909a2ca6f65e76c5f0d2fcfb8be47935ae3457e8bbceb20
Política de extracción de imágenes: IfNotPresent
Ciclo de vida:
PreStop:
Ejecución:
Comando:
- /wait-shutdown
Argumentos:
- /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
Contexto de seguridad:
Capacidades:
Eliminar:
- TODOS
Añadir:
- NET_BIND_SERVICE
Ejecutar como usuario: 101
AllowPrivilegeEscalation: verdadero
Env:
- Nombre: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
readinessProbe:
httpGet:
path: /healthz
port: 10254
Esquema: HTTP
periodoSegundos: 1
Puertos:
- Nombre: http
ContainerPort: 80
protocolo: TCP
- nombre: https
puertoContenedor: 443
Protocolo: TCP
- nombre: webhook
puertoContenedor: 8443
Protocolo: TCP
VolumeMounts:
- nombre: webhook-cert
MountPath: /usr/local/certificates/
ReadOnly: true
ServiceAccountName: ingress-nginx
ClosingGracePeriodSeconds: 300
Volúmenes:
- Nombre: webhook-cert
Secreto:
SecretName: ingress-nginx-admission
apiVersion: v1
tipo: Mapa de configuración
Metadatos:
Nombre: controlador Ingress-Nginx
Espacio de nombres: Ingress-Nginx
Datos:
Conexiones máximas de trabajador: "10000"
máximo de archivos abiertos por trabajador: "10204"
Conexiones de mantenimiento de la conexión ascendente: "100"
Solicitudes de mantenimiento de conexión: "100000000"
apiVersion: apps/v1
tipo: Implementación
Metadatos:
Nombre: Nginx
Especificación:
Selector:
Etiquetas de coincidencia:
Aplicación: Nginx
Plantilla:
Metadatos:
Etiquetas:
Aplicación: Nginx
Especificación:
Selector de nodo:
kubernetes.io/nombredehost: gke-rawdata-cluster-default-pool-3ac53622-t2dz
Contenedores:
Nombre: Nginx
Imagen: Nginx
Puertos:
PuertoContenedor: 8080
volumenMontajes:
- nombre: volumen-configuración-principal
Ruta-de-montaje: /etc/nginx
- nombre: volumen-configuración-aplicación
Ruta-de-montaje: /etc/nginx/conf.d
readinessProbe:
httpGet:
ruta: /healthz
puerto: 8080
periodoSegundos: 3
Volúmenes:
- Nombre: volumen-configuración-principal
MapaConfig:
Nombre: configuración-principal
- Nombre: volumen-configuración-aplicación
MapaConfig:
Nombre: configuración-aplicación
---
apiVersion: v1
tipo: Mapa de configuración
Metadatos:
Nombre: configuración principal
Espacio de nombres: predeterminado
Datos:
nginx.conf: |+
Usuario nginx;
Procesos de trabajador 16;
Límite de límite de trabajador 102400;
Afinidad de CPU de trabajador automática 111111111111111;
Registro de errores /var/log/nginx/error.log notice;
PID /var/run/nginx.pid;
Eventos {
Conexiones de trabajador 100000;
}
http {
Formato de registro principal '$dirección_remota - $usuario_remoto [$tiempo_local] "$solicitud" '
'$estado $bytes_del_cuerpo_enviados "$referencia_http" '
'"$agente_usuario_http" "$http_x_reenviado_para"';
Sendfile activado;
Tcp_nodelay activado;
Acceso_cerrado;
Incluir /etc/nginx/conf.d/*.conf;
}
---
APIVersion: v1
kind: Mapa de configuración
Metadatos:
Nombre: app-conf
Espacio de nombres: predeterminado
Datos:
app.conf: "servidor {listen 8080;ubicación / {tipo predeterminado text/plain;caduca -1;devuelve 200 'Dirección del servidor: $server_addr:$server_port\nNombre del servidor: $hostname\nFecha: $time_local\nURI: $request_uri\nID de la solicitud: $request_id\n';}ubicación /healthz {devuelve 200 'Estoy feliz y sano :)';}}"
---
apiVersion: v1
tipo: Servicio
Metadatos:
Nombre: app-svc
Especificación:
Puertos:
- Puerto: 80
Puerto de destino: 8080
protocolo: TCP
Nombre: http
Selector:
Aplicación: nginx
---
"Esta publicación de blog puede hacer referencia a productos que ya no están disponibles o que ya no reciben soporte. Para obtener la información más actualizada sobre los productos y soluciones F5 NGINX disponibles, explore nuestra familia de productos NGINX . NGINX ahora es parte de F5. Todos los enlaces anteriores de NGINX.com redirigirán a contenido similar de NGINX en F5.com.