Capítulo 23: Deployment en Kubernetes
Capítulo 23: Deployment en Kubernetes
“Kubernetes: orquestando servicios que orquestan sagas”
Introduccion
Hemos construido sagas con persistencia, mensajeria, frontend y observabilidad. Ahora necesitamos desplegar todo esto en produccion. Kubernetes (K8s) es la plataforma de orquestacion de contenedores mas popular, ideal para microservicios.
Kubernetes proporciona:
- Scheduling: Decide donde ejecutar cada contenedor
- Self-healing: Reinicia contenedores fallidos automaticamente
- Scaling: Escala servicios horizontal y verticalmente
- Service Discovery: Los servicios se encuentran por nombre
- Rolling Updates: Actualiza sin downtime
Este capitulo cubre la configuracion de Kubernetes para nuestro sistema de sagas.
Arquitectura en Kubernetes
El diagrama muestra los componentes principales. El Ingress es el punto de entrada; los Services exponen pods; las bases de datos corren dentro del cluster o como servicios externos.
graph TB
subgraph "Kubernetes Cluster"
ING[Ingress] --> API[API Gateway]
API --> OS[Order Service]
API --> IS[Inventory Service]
API --> PS[Payment Service]
OS --> RMQ[RabbitMQ]
IS --> RMQ
PS --> RMQ
OS --> PG[(PostgreSQL)]
IS --> PG
PS --> PG
OS --> RD[(Redis)]
end
Deployment de Order Service
Un Deployment define como desplegar y mantener una aplicacion:
- replicas: Numero de instancias a ejecutar
- selector: Identifica los pods que pertenecen a este deployment
- template: Plantilla para crear pods
- containers: Definicion del contenedor (imagen, puertos, env vars)
- resources: Limites de CPU y memoria
- probes: Health checks para saber si el pod esta listo
Las probes son cruciales:
- livenessProbe: Si falla, Kubernetes reinicia el pod
- readinessProbe: Si falla, Kubernetes deja de enviarle trafico
# k8s/order-service/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
labels:
app: order-service
spec:
replicas: 3
selector:
matchLabels:
app: order-service
template:
metadata:
labels:
app: order-service
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "9090"
spec:
containers:
- name: order-service
image: orderflow/order-service:latest
ports:
- containerPort: 3000
- containerPort: 9090
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: order-secrets
key: database-url
- name: RABBITMQ_URL
valueFrom:
secretKeyRef:
name: order-secrets
key: rabbitmq-url
- name: REDIS_URL
valueFrom:
configMapKeyRef:
name: order-config
key: redis-url
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /health/live
port: 3000
initialDelaySeconds: 10
periodSeconds: 10
readinessProbe:
httpGet:
path: /health/ready
port: 3000
initialDelaySeconds: 5
periodSeconds: 5
Service y ConfigMap
Un Service expone pods a otros componentes del cluster:
- Proporciona una IP estable (los pods tienen IPs efimeras)
- Balancea carga entre multiples replicas
- Permite acceso por nombre DNS (
order-service.default.svc.cluster.local)
Un ConfigMap almacena configuracion no sensible como pares clave-valor. Los pods pueden montar ConfigMaps como variables de entorno o archivos.
La separacion de configuracion permite cambiarla sin reconstruir la imagen del contenedor.
# k8s/order-service/service.yaml
apiVersion: v1
kind: Service
metadata:
name: order-service
spec:
selector:
app: order-service
ports:
- name: http
port: 80
targetPort: 3000
- name: metrics
port: 9090
targetPort: 9090
---
# k8s/order-service/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: order-config
data:
redis-url: "redis://redis:6379"
log-level: "info"
saga-timeout: "300"
Secrets
Un Secret almacena datos sensibles (passwords, tokens, certificados) de forma segura. A diferencia de ConfigMaps:
- Los valores se almacenan codificados en base64
- Kubernetes puede encriptarlos en reposo
- Tienen permisos de acceso mas restrictivos
En produccion, considera usar soluciones externas como HashiCorp Vault o AWS Secrets Manager para mayor seguridad.
Nota: El ejemplo usa stringData para facilidad; en produccion, evita commitear secrets al repositorio.
# k8s/order-service/secrets.yaml
apiVersion: v1
kind: Secret
metadata:
name: order-secrets
type: Opaque
stringData:
database-url: "postgresql://user:pass@postgres:5432/orders"
rabbitmq-url: "amqp://user:pass@rabbitmq:5672"
HorizontalPodAutoscaler
El HPA (HorizontalPodAutoscaler) escala automaticamente el numero de replicas basandose en metricas:
- minReplicas/maxReplicas: Limites de escalado
- metrics: Metricas que disparan el escalado (CPU, memoria, custom)
- target: Umbral que activa el escalado
El ejemplo escala basandose en:
- CPU > 70%: Indica que los pods necesitan ayuda
saga_active_count> 50: Metrica custom de Prometheus
Cuando las metricas exceden los umbrales, HPA crea nuevos pods. Cuando bajan, los elimina gradualmente.
# k8s/order-service/hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: order-service-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: order-service
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Pods
pods:
metric:
name: saga_active_count
target:
type: AverageValue
averageValue: "50"
RabbitMQ con Operator
Un Operator es un patron de Kubernetes que extiende la API para manejar aplicaciones complejas. El RabbitMQ Operator:
- Crea y configura clusters de RabbitMQ automaticamente
- Maneja replicacion, failover y recuperacion
- Simplifica operaciones dia-a-dia (backups, upgrades)
La configuracion define:
- replicas: 3: Cluster de 3 nodos para alta disponibilidad
- persistence: Almacenamiento duradero para mensajes
- resources: Limites de CPU/memoria por nodo
Sin el operator, configurar un cluster RabbitMQ requeriria decenas de manifiestos YAML.
# k8s/rabbitmq/cluster.yaml
apiVersion: rabbitmq.com/v1beta1
kind: RabbitmqCluster
metadata:
name: rabbitmq
spec:
replicas: 3
resources:
requests:
cpu: "500m"
memory: "1Gi"
limits:
cpu: "1"
memory: "2Gi"
rabbitmq:
additionalConfig: |
vm_memory_high_watermark.relative = 0.8
persistence:
storageClassName: standard
storage: "10Gi"
PostgreSQL con CloudNativePG
CloudNativePG es un operator para PostgreSQL disenado para Kubernetes. Proporciona:
- Replicacion con failover automatico
- Backups continuos a object storage
- Connection pooling integrado
- Actualizaciones con zero downtime
La configuracion instances: 3 crea un cluster con 1 primary y 2 replicas. Si el primary falla, una replica se promueve automaticamente.
bootstrap.initdb configura la base de datos inicial que se crea al arrancar el cluster.
# k8s/postgres/cluster.yaml
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: postgres-cluster
spec:
instances: 3
storage:
size: 20Gi
postgresql:
parameters:
max_connections: "200"
shared_buffers: "256MB"
bootstrap:
initdb:
database: orderflow
owner: orderflow
Helm Chart
Helm es el gestor de paquetes de Kubernetes. Un Chart empaqueta todos los manifiestos necesarios para una aplicacion, con valores configurables.
Beneficios de Helm:
- Templating: Un chart, multiples ambientes (dev, staging, prod)
- Releases: Versionado y rollback de deployments
- Dependencias: Un chart puede depender de otros (PostgreSQL, Redis)
- values.yaml: Centraliza toda la configuracion
El archivo values.yaml define valores por defecto que se pueden sobrescribir por ambiente.
# helm/orderflow/values.yaml
global:
environment: production
orderService:
replicaCount: 3
image:
repository: orderflow/order-service
tag: "1.0.0"
resources:
requests:
memory: "256Mi"
cpu: "250m"
autoscaling:
enabled: true
minReplicas: 2
maxReplicas: 10
inventoryService:
replicaCount: 2
image:
repository: orderflow/inventory-service
tag: "1.0.0"
paymentService:
replicaCount: 2
image:
repository: orderflow/payment-service
tag: "1.0.0"
rabbitmq:
enabled: true
replicas: 3
postgresql:
enabled: true
replicas: 3
redis:
enabled: true
sentinel:
enabled: true
Ingress
El Ingress expone servicios HTTP/HTTPS al exterior del cluster:
- host: Dominio que maneja (api.orderflow.com)
- paths: Rutas y a que servicio dirigirlas
- tls: Configuracion de HTTPS con certificados
Las annotations configuran el Ingress Controller (nginx en este caso):
rewrite-target: Reescribe URLs antes de enviarlas al serviciocluster-issuer: Usa cert-manager para obtener certificados Let’s Encrypt automaticamente
Sin Ingress, cada servicio necesitaria su propio LoadBalancer externo (costoso).
# k8s/ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: orderflow-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
tls:
- hosts:
- api.orderflow.com
secretName: orderflow-tls
rules:
- host: api.orderflow.com
http:
paths:
- path: /orders
pathType: Prefix
backend:
service:
name: order-service
port:
number: 80
- path: /inventory
pathType: Prefix
backend:
service:
name: inventory-service
port:
number: 80
CI/CD con GitHub Actions
CI/CD (Continuous Integration/Continuous Deployment) automatiza el proceso de construir, probar y desplegar codigo.
El workflow de GitHub Actions:
- Trigger: Se ejecuta en cada push a main
- Build and push: Construye imagen Docker y la sube al registry
- Deploy: Aplica los manifiestos de Kubernetes con la nueva imagen
El uso del SHA del commit (${{ github.sha }}) como tag de imagen garantiza que cada despliegue usa una imagen unica y trazable.
En produccion, considera agregar:
- Tests antes de build
- Aprobacion manual para produccion
- Notificaciones (Slack, email)
# .github/workflows/deploy.yml
name: Deploy to Kubernetes
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build and push
uses: docker/build-push-action@v5
with:
push: true
tags: orderflow/order-service:${{ github.sha }}
- name: Deploy to K8s
uses: azure/k8s-deploy@v4
with:
manifests: k8s/
images: orderflow/order-service:${{ github.sha }}
Resumen
- Deployments con health checks y recursos definidos
- HPA escala segun CPU y metricas custom
- RabbitMQ Operator para mensajeria resiliente
- CloudNativePG para PostgreSQL de alta disponibilidad
- Helm para gestion de releases
- CI/CD automatizado con GitHub Actions
Glosario
Kubernetes (K8s)
Definicion: Plataforma open-source de orquestacion de contenedores que automatiza el despliegue, escalado y operacion de aplicaciones containerizadas.
Por que es importante: Es el estandar de facto para ejecutar microservicios en produccion, proporcionando abstracciones para networking, storage, scheduling y self-healing.
Ejemplo practico: Desplegamos 3 replicas del order-service. Kubernetes distribuye los pods entre nodos, reinicia los que fallan, y balancea trafico automaticamente.
Pod
Definicion: Unidad mas pequena en Kubernetes, consistente en uno o mas contenedores que comparten networking y storage.
Por que es importante: Los pods son efimeros - Kubernetes los crea y destruye constantemente. Los Deployments abstraen esta complejidad manteniendo el numero deseado de replicas.
Ejemplo practico: Un pod order-service-abc123 ejecuta el contenedor de la aplicacion. Si el nodo falla, Kubernetes crea un nuevo pod en otro nodo.
Deployment
Definicion: Recurso de Kubernetes que define el estado deseado de una aplicacion (imagen, replicas, configuracion) y lo mantiene automaticamente.
Por que es importante: Proporciona actualizaciones declarativas - describes el estado final y Kubernetes hace la transicion gradualmente, con rollback automatico si falla.
Ejemplo practico: Cambias la imagen de v1.0 a v1.1. Kubernetes crea nuevos pods con v1.1, verifica que estan sanos, y luego termina los pods v1.0.
Service (Kubernetes)
Definicion: Abstraccion que define un conjunto logico de pods y una politica de acceso a ellos, proporcionando una IP estable y balanceo de carga.
Por que es importante: Los pods tienen IPs efimeras que cambian al reiniciarse. El Service proporciona un punto de acceso estable por nombre DNS.
Ejemplo practico: order-service siempre resuelve a la IP del Service. Este distribuye trafico entre todos los pods sanos, sin importar cuantos hay o sus IPs.
ConfigMap
Definicion: Recurso de Kubernetes para almacenar configuracion no sensible como pares clave-valor, que los pods pueden consumir como variables de entorno o archivos.
Por que es importante: Separa la configuracion del codigo, permitiendo cambiar comportamiento sin reconstruir imagenes.
Ejemplo practico: LOG_LEVEL=debug en desarrollo, LOG_LEVEL=info en produccion. Misma imagen, diferente ConfigMap.
Secret
Definicion: Recurso de Kubernetes para almacenar datos sensibles (passwords, tokens, claves) con mecanismos de seguridad adicionales.
Por que es importante: Evita hardcodear secretos en imagenes o codigo, y proporciona control de acceso granular sobre quien puede ver los valores.
Ejemplo practico: La password de PostgreSQL se almacena en un Secret. El pod la recibe como variable de entorno sin que aparezca en logs o manifiestos.
HorizontalPodAutoscaler (HPA)
Definicion: Controlador de Kubernetes que ajusta automaticamente el numero de replicas de un Deployment basandose en metricas observadas.
Por que es importante: Escala la aplicacion segun demanda real, ahorrando recursos en periodos tranquilos y manejando picos de trafico automaticamente.
Ejemplo practico: Durante Black Friday, CPU sube a 80%. HPA escala de 3 a 10 pods en minutos. Despues, trafico baja y HPA reduce a 3 pods.
Operator (Kubernetes)
Definicion: Patron que extiende Kubernetes con logica de dominio especifica, automatizando tareas de operacion complejas para aplicaciones estateful.
Por que es importante: Convierte conocimiento operacional (como escalar PostgreSQL, hacer backups) en codigo que Kubernetes ejecuta automaticamente.
Ejemplo practico: El CloudNativePG Operator detecta que el primary PostgreSQL fallo, promueve una replica, y actualiza las conexiones - todo automatico.
Helm
Definicion: Gestor de paquetes para Kubernetes que empaqueta aplicaciones como “charts” con templating y gestion de releases.
Por que es importante: Simplifica deployments complejos, permite reutilizar configuraciones, y proporciona versionado con rollback facil.
Ejemplo practico: helm upgrade orderflow ./chart -f prod-values.yaml actualiza toda la aplicacion (5 servicios, 3 DBs) con un solo comando.
Ingress
Definicion: Recurso de Kubernetes que gestiona acceso HTTP/HTTPS externo a servicios dentro del cluster, con ruteo basado en host/path.
Por que es importante: Un solo punto de entrada para multiples servicios, con TLS terminacion, reduciendo costos de LoadBalancers externos.
Ejemplo practico: api.example.com/orders va al order-service, /payments al payment-service. Un solo certificado SSL para todo el dominio.
CI/CD
Definicion: Continuous Integration (merge frecuente de codigo con tests automaticos) y Continuous Deployment (despliegue automatico a produccion tras pasar tests).
Por que es importante: Reduce tiempo entre escribir codigo y tenerlo en produccion, detecta problemas temprano, y elimina errores de despliegue manual.
Ejemplo practico: Push a main -> tests corren -> imagen se construye -> se despliega a staging -> tests E2E pasan -> se despliega a produccion. Todo en 15 minutos, sin intervencion.