Cómo simplificar la gestión del tráfico de entrada y salida en Kubernetes

NGINX | 28 de junio de 2021

Una de las formas en que una malla de servicios puede complicar la gestión de un entorno de Kubernetes es cuando debe configurarse por separado del controlador de Ingress. Además, las configuraciones independientes no solo requieren mucho tiempo, sino que también aumentan la probabilidad de que se produzcan errores de configuración que impidan enrutar correctamente el tráfico e incluso provoquen vulnerabilidades de seguridad (por ejemplo, que agentes maliciosos accedan a aplicaciones restringidas) y malas experiencias (por ejemplo, que los clientes no puedan acceder a aplicaciones para las que tienen autorización). Aparte del tiempo que se tarda en realizar configuraciones independientes, se acaba dedicando más tiempo a solucionar errores.

Puede evitar estos problemas —y ahorrar tiempo— integrando el controlador NGINX Ingress basado en NGINX Plus con NGINX Service Mesh para controlar el tráfico mTLS tanto de entrada como de salida. En esta demostración en vídeo, explicamos todos los pasos.

La documentación de apoyo se menciona en las siguientes secciones:

Requisitos previos (0:18)

Antes de comenzar la demostración propiamente dicha, llevamos a cabo estos pasos previos:

  1. Instalamos el plano de control de NGINX Server Mesh en el clúster de Kubernetes y configuramos mTLS y la política strict para la malla de servicios.
  2. Se instaló NGINX Ingress Controller basado en NGINX Plus como Deployment (en lugar de como DaemonSet) en el clúster de Kubernetes, se habilitó el tráfico de salida, y se expuso como un servicio de tipo LoadBalancer.Nota: La demostración no funciona con NGINX Ingress Controller basado en NGINX Open Source. Para facilitar la lectura, en el resto de este blog nos referiremos a NGINX Ingress Controller basado en NGINX Plus simplemente como «NGINX Ingress Controller».
  3. Seguimos nuestras instrucciones para descargar la aplicación de ejemplo bookinfo, inyectar el sidecar de NGINX Service Mesh e implementar la aplicación.

Como resultado de la política strict creada en el paso 1, el sidecar rechaza las solicitudes a la aplicación bookinfo procedentes de clientes externos a la malla. Para mostrarlo en la demostración, primero ejecutamos el siguiente comando para configurar el reenvío de puertos:

> kubectl port-forward svc/product-page 9080Forwarding from 127.0.0.1:9080 -> 9080
Forwarding from [::1]:9080 -> 9080
Handling connection for 9080

Cuando intentamos acceder a la aplicación, obtenemos el código de estado 503 porque nuestro equipo local no forma parte de la malla de servicios:

> curl localhost:9080503

Implementación de NGINX Ingress Controller con NGINX Service Mesh (1:50)

La primera fase del proceso para exponer una aplicación consiste en implementar una instancia de NGINX Ingress Controller. Encontrará las instrucciones correspondientes en nuestro tutorial, «Implementación con NGINX Plus Ingress Controller para Kubernetes».

NGINX proporciona manifiestos tanto de Deployment como de DaemonSet para este fin. En la demostración, utilizamos el manifiesto de Deployment, nginx-plus-ingress.yaml. Este incluye anotaciones para enrutar tanto el tráfico de entrada como el de salida a través de la misma instancia de NGINX Ingress Controller:

Cargando ejemplo de código…

El manifiesto permite integrar directamente NGINX Ingress Controller con Spire, la autoridad de certificación (CA) de NGINX Service Mesh, lo que elimina la necesidad de inyectar el sidecar de NGINX Service Mesh en NGINX Ingress Controller. En su lugar, NGINX Ingress Controller obtiene los certificados y las claves directamente de la CA de Spire para usarlos en las conexiones mTLS con los pods de la malla. El manifiesto especifica la dirección del agente de Spire:

Cargando ejemplo de código…

y monta el socket UNIX del agente de Spire en el pod de NGINX Ingress Controller:

Cargando ejemplo de código…

Lo último que cabe destacar del manifiesto es el argumento de la CLI -enable-internal-routes, que nos permite enrutar el tráfico hacia los servicios de salida:

Cargando ejemplo de código…

Antes de comenzar la demostración, ejecutamos el comando kubectl apply -f nginx-plus-ingress.yaml para instalar NGINX Ingress Controller, y ahora examinamos el despliegue en el espacio de nombres nginx-ingress. Como se muestra en la columna READY de la siguiente salida, solo hay un contenedor en el pod de NGINX Ingress Controller, porque no le hemos inyectado un sidecar de NGINX Service Mesh.

También hemos desplegado un servicio de tipo LoadBalancer para exponer fuera del clúster la dirección IP externa de NGINX Ingress Controller (en este caso, 35.233.133.188). Accederemos a la aplicación de ejemplo bookinfo mediante esa dirección IP.

> kubectl get pods --namespace=nginx-ingressNAME                                 READY   STATUS    RESTARTS   AGE
pod/nginx-ingress-867f954b8f0fzdrm   1/1     Running   0          3d3h

NAME                    TYPE           CLUSTER-IP      EXTERNAL-IP      ...
service-nginx-ingress   LoadBalancer   10.31.245.207   35.233.133.188   ...

      ... PORT(S)                      AGE
      ... 80:31469/TCP,443:32481/TCP   4d2h
...

Uso de un recurso Ingress estándar de Kubernetes para exponer la aplicación (3:55)

Ahora exponemos la aplicación bookinfo en la malla mediante un recurso Ingress estándar de Kubernetes, tal como se define en bookinfo-ingress.yaml. Encontrará las instrucciones correspondientes en nuestro tutorial Exponer una aplicación con NGINX Plus Ingress Controller.

Cargando ejemplo de código…

El recurso hace referencia a un Secret de Kubernetes para la aplicación bookinfo en la línea 10 e incluye una regla de enrutamiento que especifica que las solicitudes a bookinfo.example.com se envían al servicio productpage (líneas 11–18). El Secret se define en bookinfo-secret.yaml:

Cargando ejemplo de código…

Ejecutamos este comando para cargar la clave y el certificado, que en la demostración es autofirmado:

> kubectl apply -f bookinfo-secret.yamlsecret/bookinfo-secret unchanged

Activamos el recurso Ingress:

> kubectl apply -f bookinfo-ingress.yamlingress.networking.k8s.io/bookinfo-ingress deleted

y compruebe que Ingress Controller haya añadido la ruta definida en el recurso, tal como confirma el evento que aparece al final de la salida:

> kubectl describe ingress bookinfo-ingress...
Events:
  Type    Reason          Age   From                      Message
  ----    ------          ----  ----                      -------
  Normal  AddedOrUpdated  5s    nginx-ingress-controller  Configuration for ...
        ...default/bookinfo-ingress was added or updated

En la demostración, ahora usamos un navegador para acceder a la aplicación bookinfo en https://bookinfo.example.com/. (Previamente, hemos añadido una correspondencia en el archivo local /etc/hosts entre la dirección IP del servicio del controlador de Ingress —35.233.133.188 en la demostración, como se indicó anteriormente— y bookinfo.example.com. Para obtener instrucciones, consulte la documentación.) La información de la sección Reseñas de libros de la página cambia periódicamente a medida que las solicitudes se distribuyen entre las tres versiones del servicio reviews definidas en bookinfo.yaml (descargar).

A continuación, inspeccionamos el tráfico de entrada en los clústeres. Ejecutamos el script generate-traffic.sh para realizar solicitudes al servicio productpage a través de la dirección IP pública del controlador de entrada NGINX y, después, ejecutamos el comando nginx-meshctl top para supervisar el tráfico:

> nginxmesh-ctl top deploy/productpage-v1Deployment       Direction  Resource       Success Rate  P99    P90    P50   ...
productpage-v1  
                 To         details-v1     100.00%       3ms    3ms    2ms   
                 To         reviews-v2     100.00%       99ms   90ms   20ms
                 To         reviews-v3     100.00%       99ms   85ms   18ms
                 To         reviews-v1     100.00%       20ms   17ms   9ms
                 From       nginx-ingress  100.00%       192ms  120ms  38ms

      ... NumRequests 
      ... 14
      ... 5
      ... 5
      ... 12

Uso de un recurso VirtualServer de NGINX para exponer la aplicación (6:45)

A continuación, mostramos una forma alternativa de exponer una aplicación mediante un recurso VirtualServer de NGINX. Se trata de un recurso personalizado de NGINX Ingress Controller que permite gestionar el tráfico de formas más complejas, como la división del tráfico y el enrutamiento basado en el contenido.

Primero, eliminamos el recurso Ingress estándar:

> kubectl delete -f bookinfo-ingress.yamlingress.networking.k8s.io "bookinfo-ingress" deleted

Nuestro archivo bookinfo-vs.yaml configura mTLS con el mismo Secret que en bookinfo-ingress.yaml (líneas 7–8). Las líneas 9–12 definen el servicio productpage como upstream, y las líneas 13–24, una ruta que envía todas las solicitudes GET realizadas a bookinfo.example.com a dicho upstream. Para los métodos HTTP distintos de GET, devuelve el código de estado 405.

Cargando ejemplo de código…

Aplicamos el recurso:

> kubectl apply -f bookinfo-vs.yamlvirtualserver.kubernetes.nginx.org/bookinfo-vs created

A continuación, seguimos los mismos pasos que con el recurso Ingress: ejecutamos el comando «kubectl describe» para confirmar que el despliegue es correcto y accedemos a la aplicación desde un navegador. Otra forma de confirmar que la aplicación funciona correctamente es comprobar que rechaza el método «POST»:

> curl -k -X POST https://bookinfo.example.com/Method not allowed

Configuración de una ruta de salida segura con NGINX Ingress Controller (8:44)

Ahora mostramos cómo enrutar el tráfico de salida a través de NGINX Ingress Controller. Nuestro tutorial «Configurar una ruta de salida segura con NGINX Plus Ingress Controller» explica el proceso utilizando distintas aplicaciones de ejemplo.

Ya hemos definido un pod sencillo de bash en bash.yaml y lo hemos desplegado en el espacio de nombres predeterminado, desde el que enviamos solicitudes. Como se muestra en la columna READY de esta salida, se le ha inyectado el sidecar de NGINX Service Mesh.

> kubectl get allNAME                        READY  STATUS    RESTARTS   AGE
pod/bash-6ccb678958-zsgm7   2/2    Running   0          77s

NAME                 TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE
service/kubernetes   ClusterIP   10.31.240.1   <none>        443/TCP   4d2h
...

Hay varios casos de uso en los que puede ser conveniente habilitar solicitudes desde el pod a un servicio de salida, es decir, cualquier entidad que no forme parte de NGINX Service Mesh. Algunos ejemplos son los servicios desplegados:

  • Fuera del clúster
  • En otro clúster
  • En el mismo clúster, pero sin el sidecar de NGINX Service Mesh inyectado

En la demostración, vamos a analizar el último caso de uso. Tenemos una aplicación desplegada en el espacio de nombres legacy, que no está controlado por NGINX Service Mesh y en el que la inyección automática del sidecar de NGINX Service Mesh está deshabilitada. Solo hay un pod en ejecución para la aplicación.

> kubectl get all --namespaces=legacyNAME                          READY  STATUS    RESTARTS   AGE
pod/target-5f7bcb96c6-km9lz   1/1    Running   0          27m

NAME                 TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
service/target-svc   ClusterIP   10.31.245.213   <none>        80/TCP,443/TCP   27m
...

Recuerda que hemos configurado una política mTLS strict para NGINX Service Mesh; por tanto, no podemos enviar solicitudes directamente desde el pod bash al servicio de destino, ya que ambos no pueden autenticarse entre sí. Cuando lo intentamos, obtenemos el código de estado 503, como se muestra aquí:

> kubectl exec -it bash-6ccb678958-zsgm7 -c bash -- curl target-svc.legacycurl: (56) Recv failure: connection reset by peer
503command terminated with exit code 56

La solución consiste en permitir que el pod bash envíe tráfico saliente a través de NGINX Ingress Controller. Descomentamos la anotación de las líneas 14 y 15 de bash.yaml:

Cargando ejemplo de código…

A continuación, aplicamos la nueva configuración:

> kubectl apply -f bash.yamldeployment.apps/bash configured

y comprueba que se haya iniciado un nuevo pod de «bash»:

> kubectl get podsNAME                    READY  STATUS        RESTARTS   AGE
bash-678c8b4579-7sfml   2/2    Running       0          6s
bash-6ccb678958-zsgm7   2/2    Terminating   0          3m28s

Ahora, cuando ejecutamos el mismo comando kubectl exec que antes para enviar una solicitud desde el pod bash al servicio de destino, obtenemos el código de estado 404 en lugar de 503. Esto indica que el pod bash ha enviado correctamente la solicitud a NGINX Ingress Controller, pero este no sabe adónde reenviarla porque no hay ninguna ruta definida.

Creamos la ruta necesaria con la siguiente definición de recurso Ingress en legacy-route.yaml. La anotación internal-route de la línea 7 indica que el servicio de destino no está expuesto a Internet, sino únicamente a las cargas de trabajo de NGINX Service Mesh.

Cargando ejemplo de código…

Activamos el nuevo recurso y confirmamos que NGINX Ingress Controller ha añadido la ruta definida en él:

> kubectl apply -f legacy-route.yamlingress.networking.k8s.io/target-internal-route created
> kubectl describe ingress target-internal-route -n legacy
...
Events:
  Type    Reason          Age   From                      Message
  ----    ------          ----  ----                      -------
  Normal  AddedOrUpdated  6s    nginx-ingress-controller  Configuration for ...
        ...legacy/target-internal-route was added or updated

Ahora, cuando ejecutamos el comando kubectl exec, llegamos al servicio de destino:

{"req": {"method": "GET"                "url": "/",
                "host": "target-svc.legacy",
                "remoteAddr": "10.28.2.76:56086"}}

Una ventaja de enrutar el tráfico saliente a través de NGINX Ingress Controller es que permite controlar exactamente a qué servicios externos se puede acceder desde el clúster: únicamente aquellos para los que se haya definido una ruta.

Por último, en la demostración mostramos cómo supervisar el tráfico de salida. Ejecutamos el comando kubectl exec para enviar varias solicitudes y, a continuación, ejecutamos este comando:

> nginxmesh-ctl top deploy/nginx-ingress -n nginx-ingressDeployment      Direction  Resource  Success Rate  P99  P90  P50  NumRequests
nginx-ingress  
                To         target    100.00%       1ms  1ms  1ms  9
                From       bash      100.00%       0ms  0ms  0ms  9

Diga «no» a la latencia: pruebe NGINX Service Mesh con NGINX Ingress Controller

Muchas mallas de servicios ofrecen opciones de puerta de enlace de entrada y salida, pero creemos que valorará una ventaja adicional de la integración con NGINX: una menor latencia. La mayoría de las mallas requieren que se inyecte un sidecar en el controlador de entrada, lo que obliga al tráfico a realizar un salto adicional de camino a sus aplicaciones. Cada segundo cuenta, y ese salto adicional, que ralentiza sus experiencias digitales, podría hacer que los clientes optaran por otras alternativas. NGINX Service Mesh no añade latencia innecesaria porque no inyecta un sidecar en NGINX Ingress Controller. En su lugar, al integrarse directamente con Spire, la autoridad de certificación de la malla, NGINX Ingress Controller pasa a formar parte de NGINX Service Mesh. NGINX Ingress Controller simplemente obtiene los certificados y las claves del agente de Spire y los utiliza para participar en el intercambio de certificados mTLS con los pods de la malla.

Hay dos versiones de NGINX Ingress Controller para Kubernetes: NGINX Open Source y NGINX Plus. Para implementar NGINX Ingress Controller con NGINX Service Mesh tal como se describe en este blog, debe utilizar la versión NGINX Plus, que está disponible con una prueba gratuita de 30 días.

NGINX Service Mesh es totalmente gratuito, está disponible para su descarga inmediata y se puede implementar en menos de 10 minutos. Para empezar, consulta la documentación y cuéntanos qué tal te va a través de GitHub.


Compartir
Etiquetas: 2021, F5 NGINX, Tech

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.