Da el salto a Kubernetes en producción: Guía práctica para desplegar FastAPI × Kubernetes
Deployment / Service / Ingress / ConfigMap / Secret / HPA / Health Checks
Introducción: Qué serás capaz de hacer y para quién es esto
Una vez que puedes ejecutar tu aplicación FastAPI con Docker, lo siguiente que probablemente quieras es: “quiero ejecutarla en un clúster” o “quiero que escale”. En este artículo desplegaremos una app FastAPI en Kubernetes (K8s) y recorreremos una configuración casi de producción donde podrás experimentar escalado, gestión de configuración, health checks y autoescalado, usando un ejemplo concreto y explicaciones detalladas.
Lectores objetivo (muy específicos)
-
Desarrolladores individuales (estudiantes / autodidactas / profesionales)
Has usado Docker Compose, pero “Kubernetes da miedo y parece complicado”.
→ Caminaremos juntos a través de poner un único servicio FastAPI en un clúster como primer paso. -
Ingenieros en equipos pequeños (3–5 personas)
Ya ejecutas imágenes Docker en producción, pero los despliegues escalables y las actualizaciones sin cortes se están volviendo dolorosos.
→ Piensa en esto como una “plantilla” que puedes llevarte, con Deployment / Service / Ingress / ConfigMap / Secret / HPA cableados entre sí. -
Equipos de desarrollo SaaS en startups
Estás pensando en un futuro de microservicios en el que varios servicios FastAPI correrán en Kubernetes.
→ Este artículo se centra en un solo servicio, pero también comentaremos separación de manifiestos por servicio, namespaces y patrones para configuración compartida.
Notas de accesibilidad (sobre legibilidad)
- La estructura sigue un triángulo invertido: “visión general primero → recursos clave (Deployment / Service / Ingress, etc.) → gestión de configuración → escalado → consejos operativos → recap final”.
- El código y el YAML se muestran en bloques de ancho fijo, y los ejemplos muy largos se trocean por propósito.
- Los términos técnicos se explican brevemente la primera vez, y después mantenemos la terminología consistente para evitar confusiones.
- Usamos bastante espacio en blanco y encabezados para que los lectores de pantalla puedan seguir fácilmente la estructura del documento.
En conjunto, este artículo apunta a una legibilidad equivalente a WCAG AA para lectores con algo de base técnica.
1. Entender el panorama general del despliegue en Kubernetes
Primero alineemos el modelo mental de cómo se descompone una app FastAPI en recursos y cómo se ejecuta en Kubernetes.
1.1 Actores principales
-
Deployment
Define el número deseado de Pods (contenedores) y su plantilla. Es el núcleo de los rolling updates y el self-healing. -
Service
Dado que las IP de los Pods son efímeras, un Service proporciona un punto de entrada estable y con nombre a tu app. Puede ser de tipo LoadBalancer, ClusterIP, etc. -
Ingress
Enruta peticiones HTTP/HTTPS externas hacia el Service adecuado. Es la puerta de enlace entre el clúster y el exterior. -
ConfigMap / Secret
Recursos para externalizar configuración y secretos como variables de entorno, ajustes, contraseñas y tokens. -
Horizontal Pod Autoscaler (HPA)
Recurso que escala automáticamente el número de Pods en función de CPU u otras métricas. -
Liveness / Readiness Probes
Ajustes de health check para determinar si el contenedor “está vivo” y “listo para recibir tráfico”.
1.2 Montaje mínimo que construiremos esta vez
-
Usar una imagen Docker de FastAPI existente (por ejemplo,
my-fastapi:latest. -
Preparar los siguientes manifiestos de Kubernetes:
Deployment(contenedor FastAPI + probes de liveness/readiness)Service(ClusterIP / NodePort, etc.)Ingress(routing para/api)ConfigMap(configuración no sensible)Secret(contraseña de BD, etc.)HPA(autoescalado basado en CPU)
-
Ejecutar y verificar la app en un clúster local (Minikube, kind) o en un K8s gestionado (EKS/GKE/AKS, etc.).
2. Confirmar los supuestos de la imagen FastAPI
Antes de meternos en Kubernetes, confirmemos cómo está estructurada la imagen de contenedor FastAPI.
2.1 Dockerfile típico (repaso)
FROM python:3.11-slim AS base
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1
WORKDIR /app
COPY requirements.txt /app/
RUN pip install --upgrade pip && pip install -r requirements.txt
COPY app /app/app
# Ejecutar como usuario no root
RUN useradd -m appuser && chown -R appuser:appuser /app
USER appuser
ENV HOST=0.0.0.0 \
PORT=8000
EXPOSE 8000
CMD ["bash", "-lc", "exec uvicorn app.main:app --host ${HOST} --port ${PORT}"]
Aquí arrancamos con uvicorn directamente, pero también podrías usar Gunicorn + UvicornWorker (no entraremos en ese detalle aquí).
2.2 Supuestos del lado de Kubernetes
- El contenedor arranca con directorio de trabajo
/app. HOSTyPORTse pueden cambiar mediante variables de entorno.- Los endpoints de health check como
/health/livenessy/health/readinessya están implementados (como se introdujo en artículos previos).
Escribiremos los manifiestos basándonos en estos supuestos.
3. Deployment: colocar contenedores FastAPI en el clúster
3.1 Manifiesto básico de Deployment
# k8s/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: fastapi-app
labels:
app: fastapi-app
spec:
replicas: 2
selector:
matchLabels:
app: fastapi-app
template:
metadata:
labels:
app: fastapi-app
spec:
containers:
- name: fastapi
image: my-fastapi:latest
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8000
env:
- name: HOST
value: "0.0.0.0"
- name: PORT
value: "8000"
livenessProbe:
httpGet:
path: /health/liveness
port: 8000
initialDelaySeconds: 10
periodSeconds: 10
failureThreshold: 3
readinessProbe:
httpGet:
path: /health/readiness
port: 8000
initialDelaySeconds: 5
periodSeconds: 5
failureThreshold: 3
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "512Mi"
Puntos clave
-
replicas: 2mantiene siempre dos Pods (redundancia mínima). -
selector.matchLabelsytemplate.metadata.labelsdeben coincidir para vincular el Deployment con sus Pods. -
livenessProbeyreadinessProbehacen health checks de FastAPI:- Si falla
liveness→ Kubelet reinicia el contenedor. - Si falla
readiness→ el Pod se saca de los endpoints del Service y deja de recibir nuevas peticiones.
- Si falla
-
resourcesdefine peticiones y límites de recursos, lo que ayuda al comportamiento del HPA y al scheduling en los nodos.
4. Service: crear un punto de entrada estable
Un Deployment por sí solo no basta, porque no se puede acceder a los Pods de forma estable. Necesitamos un Service.
4.1 Ejemplo ClusterIP (acceso dentro del clúster)
# k8s/service.yaml
apiVersion: v1
kind: Service
metadata:
name: fastapi-service
labels:
app: fastapi-app
spec:
type: ClusterIP
selector:
app: fastapi-app
ports:
- name: http
port: 80 # Puerto del Service
targetPort: 8000 # Puerto del contenedor
selector.app: fastapi-appenruta el tráfico a los Pods creados por el Deployment.- Dentro del clúster, el Service es accesible vía el nombre DNS
fastapi-service(por ejemplo,http://fastapi-service).
4.2 Exponer el Service al exterior (NodePort / LoadBalancer)
En un Minikube local, a menudo se usa NodePort; en K8s gestionado en la nube, lo habitual es LoadBalancer.
Ejemplo: Service tipo LoadBalancer (entorno cloud)
apiVersion: v1
kind: Service
metadata:
name: fastapi-service
spec:
type: LoadBalancer
selector:
app: fastapi-app
ports:
- port: 80
targetPort: 8000
Sin embargo, para un routing HTTP flexible y terminación TLS, generalmente es mejor usar un Ingress Controller, así que veamos un ejemplo de Ingress.
5. Ingress: conectando el tráfico HTTP externo con FastAPI
Ingress define reglas para enrutar tráfico HTTP hacia múltiples Services según paths u otras condiciones. Aquí vamos a usar una configuración sencilla para un solo servicio FastAPI.
5.1 Ingress simple (ruta /api hacia FastAPI)
Un Ingress Controller común es NGINX Ingress. El ejemplo siguiente lo asume.
# k8s/ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: fastapi-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$1
spec:
ingressClassName: nginx # Cambia según tu IngressClass
rules:
- host: example.local # En producción, tu FQDN real
http:
paths:
- path: /api(/|$)(.*)
pathType: Prefix
backend:
service:
name: fastapi-service
port:
number: 80
hostpermite enrutar según el nombre de dominio.pathenvía todas las rutas bajo/apial Service de FastAPI.- En la práctica también necesitas configuración TLS (un Secret con el certificado), pero aquí nos centramos en los conceptos básicos.
6. ConfigMap y Secret: externalizar configuración y secretos
Como en artículos anteriores, usar pydantic-settings para leer config desde variables de entorno es una buena opción para FastAPI. En Kubernetes, ConfigMap y Secret son la forma estándar de aportar esas variables.
6.1 ConfigMap (configuración no sensible)
# k8s/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: fastapi-config
data:
APP_NAME: "My FastAPI on K8s"
ENV: "prod"
LOG_LEVEL: "info"
CORS_ORIGINS: "https://app.example.com"
6.2 Secret (configuración sensible)
# k8s/secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: fastapi-secret
type: Opaque
data:
SECRET_KEY: cHJvZC1zZWNyZXQ= # base64("prod-secret")
DATABASE_URL: cG9zdGdyZXNxbCtwc3ljcGc6Ly91c2VyOnBhc3NAaG9zdDoyNTQzL2FwcA==
Nota: Los valores bajo
datadeben ser cadenas en base64 (por ejemplo,echo -n "prod-secret" | base64).
6.3 Usarlos como variables de entorno en el Deployment
# k8s/deployment.yaml (extracto de la sección env)
containers:
- name: fastapi
image: my-fastapi:latest
envFrom:
- configMapRef:
name: fastapi-config
- secretRef:
name: fastapi-secret
# Además puedes definir env individuales
env:
- name: HOST
value: "0.0.0.0"
- name: PORT
value: "8000"
envFrominyecta todas las claves del ConfigMap y del Secret como variables de entorno.- En lugar de usar un
.envcon pydantic-settings, lees estas variables proporcionadas por Kubernetes.
7. Horizontal Pod Autoscaler (HPA) para autoescalado
El HPA de Kubernetes escala el número de Pods cuando el uso de CPU pasa un umbral definido.
7.1 Ejemplo simple de HPA
# k8s/hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: fastapi-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: fastapi-app
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 60
averageUtilization: 60significa “escala hacia fuera si el uso medio de CPU entre todos los Pods supera el 60 %”.- Para usar HPA, tu clúster debe tener metrics-server o un sistema equivalente de métricas (la mayoría de K8s gestionados lo traen de serie).
7.2 Cosas a tener en cuenta del lado de FastAPI
- Diseña tu app para aprovechar I/O asíncrona en vez de depender fuertemente de operaciones bloqueantes, de modo que el uso de CPU refleje mejor la carga y el escalado sea más efectivo.
- Si expones métricas en formato Prometheus, luego podrás controlar el HPA con métricas personalizadas (fuera del alcance de este artículo).
8. Flujo de despliegue de ejemplo en un clúster local (con kubectl)
Hemos visto los YAML; ahora organicemos brevemente los pasos reales para desplegarlos con kubectl.
8.1 Flujo de trabajo de ejemplo
-
Construir la imagen Docker
docker build -t my-fastapi:latest . -
Poner la imagen a disposición del clúster (para Minikube)
- Ejecuta
eval $(minikube docker-env)antes del build
o bien - Empuja la imagen a un registry (ECR/GCR/Docker Hub, etc.)
- Ejecuta
-
Aplicar los manifiestos de Kubernetes
kubectl apply -f k8s/configmap.yaml kubectl apply -f k8s/secret.yaml kubectl apply -f k8s/deployment.yaml kubectl apply -f k8s/service.yaml kubectl apply -f k8s/ingress.yaml kubectl apply -f k8s/hpa.yaml -
Comprobar estado
kubectl get pods kubectl get svc kubectl get ingress kubectl get hpa -
Ver logs de Pods
kubectl logs -f deploy/fastapi-app -
Acceder vía Ingress (ejemplo Minikube)
minikube ip # Luego visita: http://<minikube-ip>/api/meta
Los detalles varían según tu entorno local (Minikube, kind, Docker Desktop, etc.).
9. Claves operativas: rolling updates y rollbacks
9.1 Rolling updates
Cuando cambias la etiqueta de imagen o variables de entorno en el Deployment y haces kubectl apply del nuevo manifiesto, se dispara un rolling update.
Por debajo, Kubernetes:
- Arranca un nuevo Pod con la spec actualizada.
- Espera a que el
readinessProbesea exitoso. - Luego detiene un Pod antiguo.
- Repite el proceso, minimizando el downtime.
9.2 Rollbacks
Si la nueva versión da problemas, puedes volver a una revisión anterior:
kubectl rollout history deploy/fastapi-app
kubectl rollout undo deploy/fastapi-app
Del lado de FastAPI, presta mucha atención a las migraciones de base de datos. Si cambias el esquema, tendrás que decidir un orden seguro; por ejemplo, aplicar las migraciones primero y después desplegar la nueva versión de la app.
10. Logs y métricas (visión rápida)
10.1 Logs
- Como regla general, escribe logs estructurados (por ejemplo, JSON) a stdout, y revísalos mediante
kubectl logso una plataforma de agregación (Cloud Logging, Elasticsearch, Loki, etc.). - No confíes en escribir archivos de log dentro del contenedor; en su lugar, deja que Kubernetes y tu stack de logging recojan lo que sale por stdout/stderr.
10.2 Métricas y trazas
- Con Prometheus u OpenTelemetry, puedes ejecutar agentes como sidecars o Pods separados.
- Cuando tu arquitectura crezca a varios servicios, el tracing distribuido (Jaeger, Tempo, etc.) será importante para identificar qué servicio está introduciendo latencia.
Esta área es profunda, así que aquí nos quedamos en palabras clave.
11. Errores habituales y cómo evitarlos
| Síntoma | Causa probable | Contramedida |
|---|---|---|
| El Pod arranca y se para inmediatamente | Faltan variables de entorno o el nombre del Secret es incorrecto | Revisa los eventos con kubectl describe pod, inspecciona env y los logs para confirmar las claves |
| No se puede acceder a la app vía Service | Selector de labels incorrecto / puertos mal configurados | Verifica labels/selector y port/targetPort en Deployment y Service |
| Ingress devuelve 404 | IngressClass o path no coinciden | Confirma ingressClassName, la configuración del Ingress Controller y la configuración de path/regex |
| HPA no funciona | metrics-server no instalado | Configura métricas en el clúster (sigue la documentación de tu proveedor cloud) |
| Pequeños cortes durante el rolling update | Readiness probe ausente o demasiado permisiva | Revisa tu implementación de /health/readiness y los tiempos de las probes para dar tiempo suficiente de arranque |
12. Resumen de beneficios según tipo de lector
-
Desarrolladores individuales
- Las plantillas concretas para Deployment / Service / Ingress / HPA hacen que “¿por dónde empiezo con Kubernetes?” sea mucho menos intimidante.
- Practicar en un clúster local (Minikube, etc.) facilita imaginar el salto a un clúster real de producción.
-
Equipos pequeños
- Obtienes una “checklist mínima” para migrar de Docker Compose a Kubernetes.
- Combinando rolling updates y HPA puedes reducir downtime y hacer tu servicio más resistente a picos de carga.
-
Equipos SaaS en startups
- Con ConfigMap / Secret / HPA y compañía, cimentas una arquitectura que puede escalar con seguridad mientras cambias configs por entorno.
- Puedes empezar a imaginar un futuro de microservicios con un Deployment / Service / Ingress por servicio.
13. Referencias (principalmente docs oficiales)
-
Kubernetes
-
FastAPI
-
K8s local
14. Cierre: convertir FastAPI en “ciudadano de primera” en Kubernetes
Hemos recorrido los recursos y ajustes básicos necesarios para desplegar una app FastAPI en Kubernetes:
- Usar un Deployment para gestionar el número y la plantilla de Pods, con probes de liveness/readiness para health checks.
- Usar un Service como punto de entrada estable, y enlazarlo con el exterior vía Ingress si hace falta.
- Usar ConfigMap y Secret para externalizar configuración y secretos, y leerlos como variables de entorno en FastAPI.
- Usar HPA para escalar Pods automáticamente según la utilización de CPU.
- Usar rolling updates y rollbacks para actualizar versiones con seguridad y depurar problemas.
Al principio, la cantidad de YAML puede parecer abrumadora, pero una vez que entiendes el rol de cada componente, Kubernetes deja de dar tanto miedo.
Usa esta plantilla como base y ve adaptándola poco a poco a tus propios proyectos.
Que tu app FastAPI prospere como “ciudadano de primera clase” en el mundo Kubernetes ✨
