Este tutorial es uno de los cuatro que ponen en práctica conceptos de «Microservicios, marzo de 2022: redes de Kubernetes»:
- Reduzca la latencia de Kubernetes con el escalado automático
- Protege las API de Kubernetes mediante la limitación de solicitudes (esta publicación)
- Protege las aplicaciones de Kubernetes frente a la inyección SQL
- Mejore el tiempo de actividad y la resiliencia con una implementación canary
¿Quieres una guía detallada sobre cómo usar NGINX en aún más casos de uso de redes de Kubernetes? Descarga gratis nuestro libro electrónico, «Gestión del tráfico de Kubernetes con NGINX: guía práctica».
Su organización acaba de lanzar su primera aplicación y API en Kubernetes. Le han indicado que debe esperar grandes volúmenes de tráfico (y ya ha implementado el escalado automático para garantizar que NGINX Ingress Controller pueda enrutar el tráfico rápidamente), pero existe la preocupación de que la API pueda ser objeto de un ataque malicioso. Si la API recibe un gran volumen de solicitudes HTTP —algo que puede ocurrir en ataques de adivinación de contraseñas por fuerza bruta o ataques DDoS—, tanto la API como la aplicación podrían verse saturadas o incluso dejar de funcionar.
¡Pero estás de suerte! La técnica de control del tráfico denominada «limitación de velocidad» es un caso de uso de las puertas de enlace de API que limita la tasa de solicitudes entrantes a un valor habitual para usuarios reales. Configuras NGINX Ingress Controller para implementar una política de limitación de velocidad, lo que evita que la aplicación y la API se vean desbordadas por un exceso de solicitudes. ¡Buen trabajo!
Descripción general del laboratorio y el tutorial
Este blog complementa el laboratorio de la Unidad 2 de Microservices March 2022 – Exposición de API en Kubernetes, en el que se muestra cómo combinar varios controladores de entrada NGINX con limitación de velocidad para evitar que las aplicaciones y las API se saturen.
Para seguir el tutorial, necesitas un equipo con:
- 2 CPU o más
- 2 GB de memoria libre
- 20 GB de espacio libre en disco
- Conexión a Internet
- Gestor de contenedores o máquinas virtuales, como Docker, Hyperkit, Hyper-V, KVM, Parallels, Podman, VirtualBox o VMware Fusion/Workstation
- minikube instalado
- Helm instalado
- Una configuración que permita abrir una ventana del navegador. Si no es posible, tendrás que averiguar cómo acceder a los servicios pertinentes mediante un navegador.
Para sacar el máximo partido al laboratorio y al tutorial, le recomendamos que, antes de empezar:
- Mira la grabación de la presentación conceptual retransmitida en directo
- Consulta los blogs, el seminario web y el vídeo con información de contexto
- Vea el vídeo resumen de 18 minutos del laboratorio:  
Este tutorial utiliza las siguientes tecnologías:
Las instrucciones de cada reto incluyen el texto completo de los archivos YAML utilizados para configurar las aplicaciones. También puedes copiar el texto de nuestro repositorio de GitHub. Junto al texto de cada archivo YAML se proporciona un enlace a GitHub.
Este tutorial incluye tres retos:
- Desplegar un clúster, una aplicación, una API y un controlador de entrada
- Satura tu aplicación y API
- Protege tu aplicación y tu API con controladores de entrada duales y limitación de solicitudes
Reto 1: Implementar un clúster, una aplicación, una API y un controlador de Ingress
En este reto, desplegará un clúster de minikube e instalará Podinfo como aplicación y API de ejemplo. A continuación, desplegará NGINX Ingress Controller, configurará el enrutamiento del tráfico y probará la configuración de Ingress.
Crear un clúster de Minikube
Crea un clúster de minikube. Tras unos segundos, aparecerá un mensaje que confirma que la implementación se ha realizado correctamente.
$ minikube start 🏄 Done! kubectl is now configured to use "minikube" cluster and "default" namespace by default Instala la aplicación Podinfo y la API de Podinfo
Podinfo es una «aplicación web desarrollada con Go que muestra las prácticas recomendadas para ejecutar microservicios en Kubernetes». La utilizamos como aplicación y API de ejemplo porque ocupa muy poco espacio.
- Con el editor de texto que prefieras, crea un archivo YAML llamado 1-apps.yaml con el siguiente contenido (o cópialo desde GitHub). Este archivo define un Deployment que incluye:
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
spec:
selector:
matchLabels:
app: api
template:
metadata:
labels:
app: api
spec:
containers:
- name: api
image: stefanprodan/podinfo
ports:
- containerPort: 9898
---
apiVersion: v1
kind: Service
metadata:
name: api
spec:
ports:
- port: 80
targetPort: 9898
nodePort: 30001
selector:
app: api
type: LoadBalancer
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend
spec:
selector:
matchLabels:
app: frontend
template:
metadata:
labels:
app: frontend
spec:
containers:
- name: frontend
image: stefanprodan/podinfo
ports:
- containerPort: 9898
---
apiVersion: v1
kind: Service
metadata:
name: frontend
spec:
ports:
- port: 80
targetPort: 9898
nodePort: 30002
selector:
app: frontend
type: LoadBalancer- Una aplicación web (que llamaremos «Podinfo Frontend») que renderiza una página HTML
- Una API (API de Podinfo) que devuelve una carga útil JSON
- Despliega la aplicación y la API:
$ kubectl apply -f 1-apps.yamldeployment.apps/api created
service/api created
deployment.apps/frontend created
service/frontend created - Confirma que los pods de Podinfo API y Podinfo Frontend se hayan desplegado correctamente, tal como indica el valor
Runningen la columnaSTATUS.
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
api-7574cf7568-c6tr6 1/1 Running 0 87s
frontend-6688d86fc6-78qn7 1/1 Running 0 87s
Desplegar el controlador de Ingress NGINX
La forma más rápida de instalar NGINX Ingress Controller es mediante Helm.
Instale NGINX Ingress Controller en un espacio de nombres independiente (nginx) mediante Helm.
- Crea el espacio de nombres:
$ kubectl create namespace nginx - Añade el repositorio de NGINX a Helm:
$ helm repo add nginx-stable https://helm.nginx.com/stable - Descarga e instala NGINX Ingress Controller en tu 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 de NGINX Ingress Controller se haya desplegado, tal como indica el valor «
Running» en la columna «STATUS» (para facilitar la lectura, la salida se muestra en dos líneas).
$ kubectl get pods -namespace nginx NAME READY STATUS ...
main-nginx-ingress-779b74bb8b-d4qtc 1/1 Running ...
... RESTARTS AGE
... 0 92s Dirige el tráfico a tu aplicación
- Con el editor de texto que prefieras, crea un archivo YAML llamado 2-ingress.yaml con el siguiente contenido (o cópialo desde GitHub). Este archivo define el manifiesto de Ingress necesario para dirigir el tráfico a la aplicación y a la API.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: first
spec:
ingressClassName: nginx
rules:
- host: "example.com"
http:
paths:
- backend:
service:
name: frontend
port:
number: 80
path: /
pathType: Prefix
- host: "api.example.com"
http:
paths:
- backend:
service:
name: api
port:
number: 80
path: /
pathType: Prefix- Despliega el recurso Ingress:
$ kubectl apply -f 2-ingress.yaml ingress.networking.k8s.io/first created Probar la configuración de Ingress
- Para asegurarte de que la configuración de Ingress funciona según lo previsto, pruébala con un pod temporal. Inicia un pod desechable de BusyBox en el clúster:
$ kubectl run -ti --rm=true busybox --image=busybox If you don't see a command prompt, try pressing enter.
/ #- Pruebe la API de Podinfo enviando una solicitud al pod del controlador de entrada 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 {
"hostname": "api-687fd448f8-t7hqk",
"version": "6.0.3",
"revision": "",
"color": "#34577c",
"logo": "https://raw.githubusercontent.com/stefanprodan/podinfo/gh-pages/cuddle_clap.gif",
"message": "greetings from podinfo v6.0.3",
"goos": "linux",
"goarch": "arm64",
"runtime": "go1.16.9",
"num_goroutine": "6",
"num_cpu": "4"
} - Pruebe Podinfo Frontend ejecutando el siguiente comando en el mismo pod de BusyBox para simular un navegador web y obtener la página web. La salida que se muestra es el código HTML del inicio de la página web.
/ # wget --header="Host: example.com" --header="User-Agent: Mozilla" -qO- main-nginx-ingress.nginx <!DOCTYPE html>
<html>
<head>
<title>frontend-596d5c9ff4-xkbdc</title>
# ...- En otro terminal, abre Podinfo en un navegador. La página Saludos de Podinfo indica que Podinfo está en ejecución.
$ minikube service podinfo- ¡Enhorabuena! NGINX Ingress Controller está recibiendo solicitudes y reenviándolas a la aplicación y a la API.

- En el terminal original, finaliza la sesión de BusyBox:
/ # exit
$Desafío 2: Sobrecarga tu aplicación y API
En este reto, instalarás Locust, una herramienta de código abierto para generar carga, y la utilizarás para simular un aumento repentino del tráfico que sature la API y provoque que la aplicación deje de funcionar.
Instalar Locust
- Con el editor de texto que prefieras, crea un archivo YAML llamado 3-locust.yaml con el siguiente contenido (o cópialo desde GitHub). El objeto
ConfigMapdefine un script llamado locustfile.py que genera solicitudes para enviarlas al pod con las cabeceras correctas. El tráfico no se distribuye de manera uniforme entre la aplicación y la API: las solicitudes se dirigen principalmente a Podinfo API, y solo 1 de cada 5 se envía a Podinfo Frontend. Los objetosDeploymentyServicedefinen el pod de Locust.
apiVersion: v1
kind: ConfigMap
metadata:
name: locust-script
data:
locustfile.py: |-
from locust import HttpUser, task, between
class QuickstartUser(HttpUser):
wait_time = between(0.7, 1.3)
@task(1)
def visit_website(self):
with self.client.get("/", headers={"Host": "example.com", "User-Agent": "Mozilla"}, timeout=0.2, catch_response=True) as response:
if response.request_meta["response_time"] > 200:
response.failure("Frontend failed")
else:
response.success()
@task(5)
def visit_api(self):
with self.client.get("/", headers={"Host": "api.example.com"}, timeout=0.2) as response:
if response.request_meta["response_time"] > 200:
response.failure("API failed")
else:
response.success()
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: locust
spec:
selector:
matchLabels:
app: locust
template:
metadata:
labels:
app: locust
spec:
containers:
- name: locust
image: locustio/locust
ports:
- containerPort: 8089
volumeMounts:
- mountPath: /home/locust
name: locust-script
volumes:
- name: locust-script
configMap:
name: locust-script
---
apiVersion: v1
kind: Service
metadata:
name: locust
spec:
ports:
- port: 8089
targetPort: 8089
nodePort: 30015
selector:
app: locust
type: LoadBalancer- Despliega Locust:
$ kubectl apply -f 3-locust.yaml configmap/locust-script created
deployment.apps/locust created
service/locust created - Verifique el despliegue de Locust. En el siguiente ejemplo de salida, el comando de verificación se ejecutó apenas unos segundos después del comando
kubectlapply, por lo que la instalación aún está en curso, como indica el valorContainerCreatingdel pod de Locust en el campoSTATUS. Espere hasta que el valor seaRunningantes de continuar con la siguiente sección. (La salida se muestra en dos líneas para facilitar su lectura).
$ kubectl get pods
NAME READY STATUS ... api-7574cf7568-c6tr6 1/1 Running ...
frontend-6688d86fc6-78qn7 1/1 Running ... locust-77c699c94d-hc76t 0/1 ContainerCreating ...
... RESTARTS AGE
... 0 33m
... 0 33m
... 0 4sSimular un aumento repentino del tráfico
- Abre Locust en un navegador.
$ minikube service locust-

- Introduzca los siguientes valores en los campos:
- Número de usuarios – 1000
- Tasa de generación – 30
- Host – http://main-nginx-ingress
- Haz clic en el botón «Start swarming» para enviar tráfico a Podinfo API y Podinfo Frontend. Observa los patrones de tráfico en las pestañas Charts y Failures de Locust:
- Gráfico – A medida que aumenta el número de solicitudes a la API, los tiempos de respuesta de Podinfo API empeoran.
- Errores – Dado que Podinfo API y Podinfo Frontend comparten un controlador Ingress, el creciente número de solicitudes a la API pronto hace que la aplicación web empiece a devolver errores.
Esto es problemático porque un solo actor malintencionado que utilice la API puede hacer que dejen de funcionar no solo la API, sino también todas las aplicaciones servidas por NGINX Ingress Controller.
Reto 3: Proteja su aplicación y su API con dos controladores de Ingress y limitación de solicitudes
En el desafío final, desplegará dos controladores de Ingress de NGINX para eliminar las limitaciones del despliegue anterior, creará un espacio de nombres independiente para cada uno, instalará instancias independientes del controlador de Ingress de NGINX para Podinfo Frontend y Podinfo API, reconfigurará Locust para dirigir el tráfico de la aplicación y la API a sus respectivos controladores de Ingress de NGINX y comprobará que la limitación de velocidad sea efectiva. Primero, veamos cómo abordar el problema arquitectónico. En el desafío anterior, saturó el controlador de Ingress de NGINX con solicitudes a la API, lo que también afectó a la aplicación. Esto ocurrió porque un único controlador de Ingress se encargaba de enrutar el tráfico tanto a la aplicación web (Podinfo Frontend) como a la API (Podinfo API).

Ejecutar un pod independiente de NGINX Ingress Controller para cada uno de tus servicios evita que tu aplicación se vea afectada por un exceso de solicitudes a la API. Esto no es necesariamente imprescindible en todos los casos de uso, pero en nuestra simulación resulta fácil apreciar las ventajas de ejecutar varios NGINX Ingress Controllers.

La segunda parte de la solución, que evita que la API de Podinfo se vea desbordada, consiste en implementar una limitación de solicitudes utilizando NGINX Ingress Controller como puerta de enlace de API.
¿Qué es la limitación de solicitudes?
La limitación de velocidad restringe el número de solicitudes que un usuario puede realizar en un periodo 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 habitual para usuarios reales. Cuando se implementa la limitación de velocidad con NGINX, los clientes que envían demasiadas solicitudes son redirigidos a una página de error para evitar que afecten negativamente a la API. Consulte cómo funciona en la documentación de NGINX Ingress Controller.
¿Qué es una puerta de enlace de API?
Una puerta de enlace de API dirige las solicitudes de API de los clientes a los servicios correspondientes. Una interpretación muy equivocada de esta sencilla definición es pensar que una puerta de enlace de API es una tecnología específica. No lo es. En realidad, «puerta de enlace de API» describe un conjunto de casos de uso que pueden implementarse mediante distintos tipos de proxies, normalmente un ADC o 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 la tasa de solicitudes es un caso de uso habitual para implementar una puerta de enlace de API. Obtenga más información sobre los casos de uso de las puertas de enlace de API en Kubernetes en ¿Cómo elegir? Puerta de enlace de API frente a controlador de Ingress frente a malla de servicios en nuestro blog.
Prepare su clúster
Antes de poder implementar la nueva arquitectura y la limitación de solicitudes, debes eliminar la configuración anterior de NGINX Ingress Controller.
- Elimina la configuración del controlador de Ingress NGINX:
$ kubectl delete -f 2-ingress.yaml
ingress.networking.k8s.io "first" deleted
- Crea un espacio de nombres llamado nginx‑web para Podinfo Frontend:
$ kubectl create namespace nginx-web
namespace/nginx-web created
- Crea un espacio de nombres llamado nginx‑api para Podinfo API:
$ kubectl create namespace nginx-api
namespace/nginx-api created
Instala el controlador de Ingress de NGINX para el frontend de Podinfo
- Instala el controlador de Ingress 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
- Crea un manifiesto de Ingress llamado 4-ingress-web.yaml para Podinfo Frontend (o cópialo de GitHub).
apiVersion: k8s.nginx.org/v1 kind: Policy metadata: name: rate-limit-policy spec: rateLimit: rate: 10r/s key: ${binary_remote_addr} zoneSize: 10M --- apiVersion: k8s.nginx.org/v1 kind: VirtualServer metadata: name: api-vs spec: ingressClassName: nginx-api host: api.example.com policies: - name: rate-limit-policy upstreams: - name: api service: api port: 80 routes: - path: / action: pass: api - Despliega el nuevo manifiesto:
$ kubectl apply -f 4-ingress-web.yaml
ingress.networking.k8s.io/frontend created
Reconfigurar Locust
Ahora, vuelve a configurar Locust y comprueba que:
- API de Podinfo no se sobrecarga.
- Independientemente del número de solicitudes que se envíen a Podinfo API, no habrá ningún impacto en Podinfo Frontend.
Sigue estos pasos:
- Modifica el script de Locust de la siguiente manera: dado que Locust solo admite una única URL en el panel, codifica el valor directamente en el script de Python utilizando el archivo YAML 6-locust.yaml con el siguiente contenido (o cópialo de GitHub). Presta atención a las URL de cada
tarea.
apiVersion: v1
kind: ConfigMap
metadata:
name: locust-script
data:
locustfile.py: |-
from locust import HttpUser, task, between
class QuickstartUser(HttpUser):
wait_time = between(0.7, 1.3)
@task(1)
def visit_website(self):
with self.client.get("http://web-nginx-ingress.nginx-web/", headers={"Host": "example.com", "User-Agent": "Mozilla"}, timeout=0.2, catch_response=True) as response:
if response.request_meta["response_time"] > 200:
response.failure("Frontend failed")
else:
response.success()
@task(5)
def visit_api(self):
with self.client.get("http://api-nginx-ingress.nginx-api/", headers={"Host": "api.example.com"}, timeout=0.2) as response:
if response.request_meta["response_time"] > 200:
response.failure("API failed")
else:
response.success()
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: locust
spec:
selector:
matchLabels:
app: locust
template:
metadata:
labels:
app: locust
spec:
containers:
- name: locust
image: locustio/locust
ports:
- containerPort: 8089
volumeMounts:
- mountPath: /home/locust
name: locust-script
volumes:
- name: locust-script
configMap:
name: locust-script
---
apiVersion: v1
kind: Service
metadata:
name: locust
spec:
ports:
- port: 8089
targetPort: 8089
nodePort: 30015
selector:
app: locust
type: LoadBalancer
- Todas las solicitudes a Podinfo Frontend se dirigen al controlador de entrada NGINX nginx‑web en http://web-nginx-ingress.nginx-web
- Todas las solicitudes a Podinfo API se dirigen al controlador de entrada NGINX nginx‑api en http://api-nginx-ingress.nginx-api
- Despliega la nueva configuración de Locust. La salida confirma que el script ha cambiado, pero los demás elementos permanecen sin cambios.
$ kubectl apply -f 6-locust.yaml
configmap/locust-script configured
deployment.apps/locust unchanged
service/locust unchanged
- Elimina el pod de Locust para forzar la recarga del nuevo ConfigMap. Para identificar el pod que se debe eliminar, el argumento del comando
kubectldeletepodse expresa como una serie de comandos conectados mediante tuberías que seleccionan el pod de Locust de la lista de todos los pods.
$ kubectl delete pod `kubectl get pods | grep locust | awk {'print $1'}`
- Compruebe que Locust se haya vuelto a cargar (el valor del pod de Locust en la columna
AGEes de solo unos segundos).
$ kubectl get pods
NAME READY STATUS ... api-7574cf7568-jrlvd 1/1 Running ...
frontend-6688d86fc6-vd856 1/1 Running ... locust-77c699c94d-6chsg 0/1 Running ...
... RESTARTS AGE
... 0 9m57s
... 0 9m57s
... 0 6s
Verificar la limitación de solicitudes
- Vuelve a Locust y cambia los parámetros de estos campos:
- Número de usuarios – 400
- Tasa de generación – 10
- Host – http://main-nginx-ingress
- Haz clic en el botón Start swarming para enviar tráfico a Podinfo API y Podinfo Frontend. En la barra de título de Locust, situada en la parte superior izquierda, observa que, a medida que aumenta el número de usuarios en la columna STATUS, también aumenta el valor de la columna FAILURES. Sin embargo, los errores ya no proceden de Podinfo Frontend, sino de Podinfo API, porque el límite de solicitudes establecido para la API hace que se rechacen las solicitudes excesivas. En la traza de la parte inferior derecha puedes ver que NGINX devuelve el mensaje
503Serviciotemporalmenteno disponible, que forma parte de la función de limitación de solicitudes y se puede personalizar. La API tiene un límite de solicitudes y la aplicación web está siempre disponible. ¡Bien hecho!
Próximos pasos
En la práctica, limitar la tasa de solicitudes no basta para proteger tus aplicaciones y API frente a agentes maliciosos. Debes implementar al menos uno o dos de los siguientes métodos para proteger las aplicaciones, las API y la infraestructura de Kubernetes:
- Autenticación y autorización
- Firewall de aplicaciones web y protección contra ataques DDoS
- Cifrado de extremo a extremo y confianza cero
- Cumplimiento de la normativa del sector
Abordamos estos y otros temas en la unidad 3 de Microservices March 2022: Patrón de seguridad de microservicios en Kubernetes. Para probar NGINX Ingress Controller para Kubernetes con NGINX Plus y NGINX App Protect, inicie su prueba gratuita de 30 días hoy mismo o póngase en contacto con nosotros para hablar sobre 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 precompilado de DockerHub.
Sobre el autor
Artículos relacionados del blog
Ú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
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
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
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 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 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.