Capítulo 23: Deployment en Kubernetes

Por: SiempreListo
sagakubernetesdeploymenthelmdevops

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:

  1. CPU > 70%: Indica que los pods necesitan ayuda
  2. 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 servicio
  • cluster-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:

  1. Trigger: Se ejecuta en cada push a main
  2. Build and push: Construye imagen Docker y la sube al registry
  3. 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.


← Capítulo 22: Observabilidad | Volver al Indice →