Este tutorial es uno de los cuatro que ponen en práctica los conceptos de Microservicios de marzo de 2022: Redes de Kubernetes :
¿Quieres orientación detallada sobre el uso de NGINX para aún más casos de uso de redes Kubernetes? Descargue nuestro libro electrónico gratuito, Gestión del tráfico de Kubernetes con NGINX: Una guía práctica .
Su organización acaba de lanzar su primera aplicación y API en Kubernetes. Le han dicho que puede esperar altos volúmenes de tráfico (y ya implementó el escalamiento automático para garantizar que NGINX Ingress Controller pueda enrutar rápidamente el tráfico), pero existe la preocupación de que la API pueda ser el objetivo de un ataque malicioso. Si la API recibe un gran volumen de solicitudes HTTP (algo que puede ocurrir en casos de adivinación de contraseñas por fuerza bruta o ataques DDoS), tanto la API como la aplicación podrían verse sobrecargadas o incluso bloquearse.
Pero ¡estás de suerte! La técnica de control de tráfico denominada limitación de velocidad es un caso de uso de API Gateway que limita la tasa de solicitudes entrantes a un valor típico para usuarios reales. Configura NGINX Ingress Controller para implementar una política de limitación de velocidad, que evita que la aplicación y la API se vean abrumadas por demasiadas solicitudes. ¡Buen trabajo!
Este blog acompaña al laboratorio de la Unidad 2 de Microservicios de marzo de 2022: Exposición de API en Kubernetes , y demuestra cómo combinar múltiples controladores de ingreso NGINX con limitación de velocidad para evitar que las aplicaciones y las API se saturen.
Para ejecutar el tutorial, necesitas una máquina con:
Para aprovechar al máximo el laboratorio y el tutorial, le recomendamos que antes de comenzar:
Vea la grabación de la descripción general conceptual transmitida en vivo
Revise los blogs de fondo, el seminario web y el video.
Vea el resumen en video del laboratorio de 18 minutos:
Este tutorial utiliza estas tecnologías:
Las instrucciones para cada desafío incluyen el texto completo de los archivos YAML utilizados para configurar las aplicaciones. También puedes copiar el texto de nuestro repositorio de GitHub . Se proporciona un enlace a GitHub junto con el texto de cada archivo YAML.
Este tutorial incluye tres desafíos:
En este desafío, implementarás un clúster de Minikube e instalarás Podinfo como aplicación y API de ejemplo. Luego, implementarás el controlador de Ingress de NGINX , configurarás el enrutamiento del tráfico y probarás la configuración de Ingress .
Crear un clúster de minikube . Después de unos segundos, un mensaje confirma que la implementación fue exitosa.
$ minikube start 🏄 ¡Listo! kubectl ahora está configurado para usar el clúster "minikube" y el espacio de nombres "default" de forma predeterminada.
Podinfo es una “ aplicação web creada con Go que muestra las mejores prácticas para ejecutar microservicios en Kubernetes”. Lo usamos como aplicación de muestra y API debido a su pequeño tamaño.
Usando el editor de texto de su elección, cree un archivo YAML llamado 1-apps.yaml con el siguiente contenido (o cópielo desde GitHub ). Define un Despliegue que incluye:
apiVersion: apps/v1
tipo: Implementación
Metadatos:
Nombre: API
Especificación:
Selector:
Etiquetas de coincidencia:
Aplicación: API
Plantilla:
Metadatos:
Etiquetas:
Aplicación: API
Especificación:
Contenedores:
- Nombre: API
Imagen: stefanprodan/podinfo
Puertos:
- PuertoContenedor: 9898
---
Versión de API: v1
tipo: Servicio
Metadatos:
Nombre: API
Especificaciones:
Puertos:
- Puerto: 80
Puerto de destino: 9898
nodePort: 30001
Selector:
Aplicación: API
Tipo: Balanceador de carga
---
Versión de API: apps/v1
tipo: Implementación
Metadatos:
Nombre: Frontend
Especificación:
Selector:
Etiquetas de coincidencia:
Aplicación: Frontend
Plantilla:
Metadatos:
Etiquetas:
Aplicación: Frontend
Especificación:
Contenedores:
- Nombre: Frontend
Imagen: stefanprodan/podinfo
Puertos:
- PuertoContenedor: 9898
---
Versión de API: v1
tipo: Servicio
Metadatos:
Nombre: Interfaz
Especificaciones:
Puertos:
- Puerto: 80
Puerto de destino: 9898
nodePort: 30002
Selector:
Aplicación: interfaz
Tipo: Balanceador de carga
Implementar la aplicación y la API:
$ kubectl apply -f 1-apps.yaml despliegue.apps/api creado servicio/api creado despliegue.apps/frontend creado servicio/frontend creado
Confirme que los pods para Podinfo API y Podinfo Frontend se implementaron correctamente, como lo indica el valor En ejecución
en la columna ESTADO
.
$ kubectl get pods NOMBRE LISTO ESTADO REINICIO EDAD api-7574cf7568-c6tr6 1/1 Ejecutando 0 87s frontend-6688d86fc6-78qn7 1/1 Ejecutando 0 87s
La forma más rápida de instalar NGINX Ingress Controller es con Helm .
Instale el controlador de ingreso NGINX en un espacio de nombres separado ( nginx ) usando Helm.
Crear el espacio de nombres:
$ kubectl crear espacio de nombres nginx
Agregue el repositorio NGINX a Helm:
$ helm repo agregar nginx-stable https://helm.nginx.com/stable
Descargue e instale NGINX Ingress Controller en su clúster:
$ helm install main nginx-stable/nginx-ingress \ --set controller.watchIngressWithoutClass=true \ --set controller.ingressClass=nginx \ --set controller.service.type=NodePort \ --set controller.service.httpPort.nodePort=30010 \ --set controller.enablePreviewPolicies=true \ --namespace nginx
Confirme que el pod del controlador de ingreso NGINX se haya implementado, como lo indica el valor En ejecución
en la columna ESTADO
(para facilitar la lectura, la salida se distribuye en dos líneas).
$ kubectl get pods -namespace nginx NOMBRE LISTO ESTADO ... main-nginx-ingress-779b74bb8b-d4qtc 1/1 En ejecución ... ... REINICIO EDAD... 0 92s
apiVersion: networking.k8s.io/v1
tipo: Entrada
Metadatos:
Nombre: first
Especificación:
IngressClassName: nginx
Reglas:
- Host: "example.com"
http:
Rutas:
- Backend:
Servicio:
Nombre: Frontend
Puerto:
Número: 80
ruta: /
tipoDeRuta: Prefijo
- host: "api.example.com"
http:
rutas:
- backend:
servicio:
nombre: api
puerto:
número: 80
ruta: /
tipoDeRuta: Prefijo
$ kubectl apply -f 2-ingress.yaml ingress.networking.k8s.io/first creado
Para garantizar que su configuración de Ingress funcione como se espera, pruébela utilizando un pod temporal. Inicie un pod BusyBox desechable en el clúster:
$ kubectl run -ti --rm=true busybox --image=busybox Si no ve un símbolo del sistema, intente presionar Enter. / #
Pruebe la API Podinfo emitiendo una solicitud al pod del controlador de ingreso NGINX con el nombre de host api.example.com . La salida mostrada indica que la API está recibiendo tráfico.
/ # wget --header="Host: api.example.com" -qO- main-nginx-ingress.nginx { "nombre de host": "api-687fd448f8-t7hqk", "versión": "6.0.3", "revisión": "", "color": "#34577c", "logotipo": "https://raw.githubusercontent.com/stefanprodan/podinfo/gh-pages/cuddle_clap.gif", "mensaje": "Saludos desde podinfo v6.0.3", "goos": "linux", "goarch": "arm64", "tiempo de ejecución": "go1.16.9", "num_goroutine": "6", "núm_cpu": "4" }
/ # wget --header="Host: ejemplo.com" --header="Agente de usuario: Mozilla" -qO- main-nginx-ingress.nginx <!DOCTYPE html>
<html>
<head>
<title>frontend-596d5c9ff4-xkbdc</title>
# ...
En otra terminal, abra Podinfo en un navegador. Los saludos de la página podinfo indican que Podinfo se está ejecutando.
$ minikube servicio podinfo
¡Enhorabuena! El controlador de ingreso NGINX recibe solicitudes y las envía a la aplicación y la API.
En la terminal original, finalice la sesión de BusyBox:
/ # salir $
En este desafío, instala Locust , una herramienta de generación de carga de código abierto, y la usa para simular un aumento de tráfico que sobrecarga la API y provoca que la aplicación se bloquee.
Usando el editor de texto de su elección, cree un archivo YAML llamado 3-locust.yaml con el siguiente contenido (o cópielo desde GitHub ).
El objeto ConfigMap
define un script llamado locustfile.py que genera solicitudes que se enviarán al pod, completas con los encabezados correctos. El tráfico no se distribuye de manera uniforme entre la aplicación y la API: las solicitudes se desvían hacia Podinfo API , y solo 1 de cada 5 solicitudes se dirige a Podinfo Frontend .
Los objetos Implementación
y Servicio
definen el pod Locust.
apiVersion: v1
tipo: Mapa de configuración
Metadatos:
Nombre: script de Locust
Datos:
ArchivoLocust.py: |-
De Locust: importar UsuarioHttp, tarea, entre
Clase UsuarioRápido(UsuarioHttp):
Tiempo_de_espera = entre(0.7, 1.3)
@tarea(1)
Def: visitar_sitio_web(self):
Con self.client.get("/", encabezados={"Host": "ejemplo.com", "Agente-Usuario": "Mozilla"}, tiempo de espera=0.2, catch_response=True) como respuesta:
si response.request_meta["response_time"] > 200:
respuesta.failure("Frontend falló")
de lo contrario:
respuesta.success()
@task(5)
def visit_api(self):
con self.client.get("/", headers={"Host": "api.example.com"}, tiempo de espera=0.2) como respuesta:
si response.request_meta["response_time"] > 200:
respuesta.failure("API falló")
de lo contrario:
respuesta.success()
---
apiVersion: apps/v1
tipo: Implementación
Metadatos:
Nombre: Locust
Especificación:
Selector:
Etiquetas de coincidencia:
Aplicación: Locust
Plantilla:
Metadatos:
Etiquetas:
Aplicación: Locust
Especificación:
Contenedores:
- Nombre: Locust
Imagen: Locustio/Locust
Puertos:
- PuertoContenedor: 8089
volumenMontajes:
- Ruta de montaje: /home/locust
nombre: locust-script
volumenes:
- nombre: locust-script
mapa de configuración:
nombre: locust-script
---
Versión de API: v1
tipo: Servicio
Metadatos:
Nombre: Locust
Especificación:
Puertos:
- Puerto: 8089
Puerto de destino: 8089
NodePort: 30015
Selector:
Aplicación: Locust
Tipo: Balanceador de carga
Desplegar Locust:
$ kubectl apply -f 3-locust.yaml configmap/locust-script created despliegue.apps/locust created servicio/locust created
kubectl
apply
y, por lo tanto, la instalación aún está en progreso, como lo indica el valor ContainerCreating
para el pod Locust en el campo ESTADO
. Espere hasta que el valor esté en ejecución
antes de continuar con la siguiente sección. (La salida se distribuye en dos líneas para facilitar su legibilidad).$ kubectl get pods NOMBRE LISTO ESTADO ... api-7574cf7568-c6tr6 1/1 Ejecutando ... frontend-6688d86fc6-78qn7 1/1 Ejecutando ... locust-77c699c94d-hc76t 0/1 Creando contenedor ... ... REINICIO EDAD... 0 33m ... 0 33m ... 0 4s
Abra Locust en un navegador.
$ minikube servicio locust
Introduzca los siguientes valores en los campos:
Haga clic en el botón Iniciar enjambre para enviar tráfico a la API de Podinfo y al frontend de Podinfo . Observe los patrones de tráfico en las pestañas Gráficos y Fallas de Locust:
Esto es problemático porque un solo actor malicioso que use la API puede inutilizar no solo la API, sino también todas las aplicaciones servidas por NGINX Ingress Controller.
En el desafío final, implementa dos controladores de ingreso NGINX para eliminar las limitaciones de la implementación anterior, crea un espacio de nombres separado para cada uno, instala instancias separadas de controlador de ingreso NGINX para Podinfo Frontend y Podinfo API , reconfigura Locust para dirigir el tráfico de la aplicación y la API a sus respectivos controladores de ingreso NGINX y verifica que la limitación de velocidad sea efectiva . Primero, veamos cómo abordar el problema arquitectónico. En el desafío anterior, sobrecargaste el controlador de Ingress de NGINX con solicitudes de API, lo que también afectó a la aplicación. Esto ocurrió porque un único controlador de Ingress era responsable de enrutar el tráfico tanto a la aplicación web ( Podinfo Frontend ) como a la API ( Podinfo API ).
Ejecutar un pod de controlador de ingreso NGINX independiente para cada uno de sus servicios evita que su aplicación se vea afectada por demasiadas solicitudes de API. Esto no es necesariamente necesario para todos los casos de uso, pero en nuestra simulación es fácil ver los beneficios de ejecutar múltiples controladores de ingreso NGINX.
La segunda parte de la solución, que evita que la API de Podinfo se sature, es implementar una limitación de velocidad mediante el uso del controlador de ingreso NGINX como puerta de enlace de API.
La limitación de velocidad restringe la cantidad de solicitudes que un usuario puede realizar en un período de tiempo determinado. Para mitigar un ataque DDoS, por ejemplo, puede utilizar la limitación de velocidad para limitar la tasa de solicitudes entrantes a un valor típico para usuarios reales. Al implementar la limitación de velocidad con NGINX, los clientes que envían demasiadas solicitudes son redirigidos a una página de error para que no afecten negativamente a la API. Descubra cómo funciona esto en la documentación del controlador de ingreso de NGINX .
Una puerta de enlace API dirige las solicitudes de API de los clientes a los servicios apropiados. Una gran interpretación errónea de esta simple definición es que una puerta de enlace API es una pieza única de tecnología. Que no es. En realidad, “API gateway” describe un conjunto de casos de uso que pueden implementarse a través de distintos tipos de servidores proxy: más comúnmente, un ADC o un balanceador de carga y un proxy inverso, y cada vez más, un controlador de Ingress o una malla de servicios. La limitación de velocidad es un caso de uso común para implementar una puerta de enlace API. Obtenga más información sobre los casos de uso de API Gateway en Kubernetes en ¿Cómo elijo? Puerta de enlace API frente a. Controlador de ingreso vs. Service Mesh en nuestro blog.
Antes de poder implementar la nueva arquitectura y limitación de velocidad, debe eliminar la configuración anterior del controlador de ingreso NGINX.
$ kubectl delete -f 2-ingress.yaml ingress.networking.k8s.io "primero" eliminado
Cree un espacio de nombres llamado nginx‑web para el frontend de Podinfo :
$ kubectl crear espacio de nombres nginx-web espacio de nombres/nginx-web creado
Cree un espacio de nombres llamado nginx‑api para la API de Podinfo :
$ kubectl create namespace nginx-api namespace/nginx-api created
Instalar el controlador de ingreso NGINX:
$ helm install web nginx-stable/nginx-ingress --set controller.ingressClass=nginx-web \ --set controller.service.type=NodePort \ --set controller.service.httpPort.nodePort=30020 \ --namespace nginx-web
Cree un manifiesto de Ingress llamado 4-ingress-web.yaml para Podinfo Frontend (o cópielo desde GitHub ).
apiVersion: k8s.nginx.org/v1 tipo: Metadatos de la política: nombre: política-de-límite-de-tasa especificación: rateLimit: tasa: Tecla de 10 r/s:${binary_remote_addr} Tamaño de la zona: 10M --- apiVersion: k8s.nginx.org/v1 tipo: Metadatos de VirtualServer: nombre: api-vs especificación: ingressClassName: nginx-api host: api.example.com políticas: - nombre: rate-limit-policy upstreams: - nombre: api servicio: api puerto: 80 rutas: - ruta: / acción: contraseña: api
Implementar el nuevo manifiesto:
$ kubectl apply -f 4-ingress-web.yaml ingress.networking.k8s.io/frontend creado
Ahora, reconfigure Locust y verifique que:
Realice estos pasos:
Cambia el script de Locust para que:
Dado que Locust solo admite una única URL en el panel, codifique el valor en el script de Python usando el archivo YAML 6-locust.yaml con el siguiente contenido (o cópielo desde GitHub ). Tome nota de las URL en cada tarea
.
Versión de API: v1
Tipo: Mapa de configuración
Metadatos:
Nombre: script de Locust
Datos:
ArchivoLocust.py: |-
De Locust: import UsuarioHttp, tarea, entre
Clase UsuarioRápido(UsuarioHttp):
Tiempo_de_espera = entre(0.7, 1.3)
@tarea(1)
Def: visite_sitio_web(self):
Con self.client.get("http://web-nginx-ingress.nginx-web/", encabezados={"Host": "ejemplo.com", "Agente-Usuario": "Mozilla"}, tiempo de espera=0.2, catch_response=True) como respuesta:
si response.request_meta["response_time"] > 200:
respuesta.failure("Frontend falló")
de lo contrario:
respuesta.success()
@task(5)
def visit_api(self):
con self.client.get("http://api-nginx-ingress.nginx-api/", headers={"Host": "api.example.com"}, tiempo de espera=0.2) como respuesta:
si response.request_meta["response_time"] > 200:
respuesta.failure("API falló")
de lo contrario:
respuesta.success()
---
apiVersion: apps/v1
tipo: Implementación
Metadatos:
Nombre: Locust
Especificación:
Selector:
Etiquetas de coincidencia:
Aplicación: Locust
Plantilla:
Metadatos:
Etiquetas:
Aplicación: Locust
Especificación:
Contenedores:
- Nombre: Locust
Imagen: Locustio/Locust
Puertos:
- PuertoContenedor: 8089
volumenMontajes:
- Ruta de montaje: /home/locust
nombre: locust-script
volumenes:
- nombre: locust-script
mapa de configuración:
nombre: locust-script
---
Versión de API: v1
tipo: Servicio
Metadatos:
Nombre: Locust
Especificación:
Puertos:
- Puerto: 8089
Puerto de destino: 8089
NodePort: 30015
Selector:
Aplicación: Locust
Tipo: Balanceador de carga
Implementar la nueva configuración de Locust. La salida confirma que el script cambió pero los demás elementos permanecen sin cambios.
$ kubectl apply -f 6-locust.yaml configmap/locust-script configuradoployment.apps/locust sin cambios service/locust sin cambios
Elimina el pod Locust para forzar la recarga del nuevo ConfigMap. Para identificar el pod a eliminar, el argumento del comando kubectl
delete
pod
se expresa como comandos canalizados que seleccionan el pod Locust de la lista de todos los pods.
$ kubectl eliminar pod `kubectl obtener pods | grep locust | awk {'imprimir $1'}`
Verifique que Locust se haya recargado (el valor del pod Locust en la columna EDAD
es de solo unos segundos).
$ kubectl get pods NOMBRE LISTO ESTADO ... api-7574cf7568-jrlvd 1/1 En ejecución ... frontend-6688d86fc6-vd856 1/1 En ejecución ... locust-77c699c94d-6chsg 0/1 En ejecución ... ... REINICIO EDAD... 0 9m57s... 0 9m57s... 0 6 segundos
Regrese a Locust y cambie los parámetros en estos campos:
Haga clic en el botón Iniciar enjambre para enviar tráfico a la API de Podinfo y al frontend de Podinfo .
En la barra de título de Locust en la parte superior izquierda, observe cómo a medida que aumenta el número de usuarios en la columna ESTADO , también lo hace el valor en la columna FALLAS . Sin embargo, los errores ya no provienen de Podinfo Frontend sino de Podinfo API porque el límite de velocidad establecido para la API significa que se rechazan solicitudes excesivas. En el rastro de la parte inferior derecha se puede ver que NGINX está devolviendo el mensaje.503
Servicio
temporalmente
no disponible
, que es parte de la función de limitación de velocidad y se puede personalizar. La API tiene una tasa limitada y la aplicação web siempre está disponible. ¡Bien hecho!
En el mundo real, limitar la velocidad por sí solo no es suficiente para proteger sus aplicaciones y API de actores maliciosos. Debe implementar al menos uno o dos de los siguientes métodos para proteger las aplicaciones, las API y la infraestructura de Kubernetes:
Cubrimos estos temas y más en la Unidad 3 de Microservicios de marzo de 2022: Patrón de seguridad de microservicios en Kubernetes . Para probar NGINX Ingress Controller para Kubernetes con NGINX Plus y NGINX App Protect, comience hoy su prueba gratuita de 30 días o contáctenos para analizar sus casos de uso . Para probar NGINX Ingress Controller con NGINX Open Source, puede obtener el código fuente de la versión o descargar un contenedor prediseñado desde DockerHub .
"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.