Sabes usar las dos. Has levantado Aurora Libros con compose.yaml y overrides por entorno en el módulo 4, y la has desplegado en un clúster con StatefulSet, Ingress, HPA y rollback ensayado en el módulo 6. Esta lección no viene a decirte cuál gana, sino a darte los criterios para decidir cuál necesita tu proyecto, con las equivalencias exactas entre ambos formatos y una cifra honesta del coste de mantener un clúster.

Contenido

  1. No son la misma categoría de herramienta
  2. La pregunta correcta
  3. Comparativa a fondo, dimensión por dimensión
  4. Equivalencias conceptuales
  5. El mismo aurora-api, en los dos formatos
  6. Árbol de decisión
  7. El camino intermedio: Compose en producción
  8. Swarm como escalón, y los servicios que ejecutan Compose
  9. Kompose y los límites reales de la conversión
  10. El coste oculto de Kubernetes
  11. Aurora Libros a tres tamaños

  1. No son la misma categoría de herramienta

Comparar Compose con Kubernetes es como comparar una receta con una cocina industrial. Ambas cosas sirven para comer, pero una es un documento y la otra es un sistema con personal.

Docker Compose Kubernetes
Qué es Un descriptor de aplicación multicontenedor y una CLI que lo aplica Un orquestador distribuido con plano de control propio
Dónde vive En tu máquina, hablando con un daemon En un clúster de nodos, con etcd y controladores
Qué hace cuando algo cae Reinicia el contenedor según restart: Reprograma la carga en otro nodo
Modelo de ejecución Un docker compose up que ejecutas tú Bucles de reconciliación permanentes
Estado deseado Existe mientras dure el comando Persistente en etcd, vigilado siempre
Unidad mínima El contenedor El Pod (uno o más contenedores)

La diferencia estructural está en la cuarta fila. Compose ejecuta lo que le pides y termina; Kubernetes guarda tu intención y no deja de compararla con la realidad. Ese bucle es lo que te da que un nodo se caiga a las cuatro de la mañana y los Pods reaparezcan en otro sin que nadie despierte. También es lo que te obliga a mantener un plano de control.

  1. La pregunta correcta

No es «¿cuál es mejor?». Es una batería de preguntas concretas:

  1. ¿Puedes permitirte que la plataforma esté caída quince minutos mientras reinicias una máquina?
  2. ¿Cuántos nodos vas a tener de verdad dentro de un año: uno, tres o treinta?
  3. ¿Hay alguien en el equipo que sepa depurar un CrashLoopBackOff sin buscar en Internet?
  4. ¿Quién está de guardia a las tres de la mañana, y cobra por estarlo?
  5. ¿Tu carga es constante o tiene picos de ×8 que exigen autoescalado?
  6. ¿El presupuesto aguanta el plano de control más los nodos con margen libre?

Si has respondido «quince minutos son aceptables, un nodo, nadie, nadie, constante, no» —que es la situación de la mayoría de proyectos—, Compose es la respuesta profesional, y decirlo en voz alta te ahorrará un año de sufrimiento. Si has respondido lo contrario en tres o más preguntas, Kubernetes empieza a pagar su coste.

  1. Comparativa a fondo, dimensión por dimensión

Dimensión Docker Compose Kubernetes
Modelo mental Un fichero, servicios, up/down API declarativa, objetos, controladores, selectores
Curva de aprendizaje Horas Semanas, y meses para operarlo bien
Alcance Un host (o Swarm, con deploy:) Decenas o miles de nodos
Alta disponibilidad La del host: si cae, cae todo Reprograma en otro nodo automáticamente
Reprogramación ante caídas No existe El Deployment la garantiza
Escalado manual --scale api=4 en el mismo host kubectl scale, en todo el clúster
Autoescalado No HPA, VPA y Cluster Autoscaler
Red Bridge con DNS por nombre de servicio CNI, Service, NetworkPolicy, malla opcional
Descubrimiento DNS interno de Docker DNS del clúster + Service estables
Balanceo Round-robin del DNS o un proxy propio Service (L4) + Ingress (L7)
Almacenamiento Volúmenes locales del host PV/PVC, StorageClass, provisión dinámica
Configuración environment, env_file ConfigMap, montables o inyectables
Secretos Fichero en disco o secrets: (Swarm) Secret + integración con gestores externos
Actualizaciones Recrea el contenedor: hay corte Rolling update con maxSurge/maxUnavailable
Rollback Volver a la etiqueta anterior a mano kubectl rollout undo, con historial
Sondas healthcheck (una sola) Liveness, readiness y startup separadas
Observabilidad Logs del daemon, Prometheus si lo montas Métricas y eventos nativos, ecosistema enorme
Extensibilidad Ninguna real CRDs, operadores, webhooks
Multi-tenencia No Namespaces, RBAC, cuotas
Coste de infraestructura El de una VM Plano de control + nodos + margen libre
Coste operativo Casi cero Alto y permanente
Quién lo mantiene Cualquiera del equipo Alguien que sepa, de guardia
Madurez de equipo necesaria Un desarrollador Al menos una persona con experiencia real

Las tres últimas filas deciden más proyectos que todas las anteriores juntas, y son las que menos aparecen en las comparativas de Internet.

  1. Equivalencias conceptuales

Casi todo lo que escribiste en el compose.yaml de Aurora Libros tiene traducción. Lo que cambia no es la idea, sino cuántos objetos hacen falta para expresarla.

Compose Kubernetes Nota
services: api: Deployment + Service Dos objetos donde había uno
image: spec.containers[].image Idéntico; misma imagen OCI
ports: "8080:8080" Service (interno) o Ingress (externo) La exposición se parte en dos capas
environment: ConfigMap + envFrom La configuración sale del descriptor
secrets: / .env Secret (+ gestor externo) Secret es base64, no cifrado por defecto
volumes: (nombrado) PersistentVolumeClaim Con StorageClass y modo de acceso
volumes: (bind mount) hostPath (evítalo) o ConfigMap montado Rara vez lo que quieres
deploy.replicas spec.replicas Compose solo lo respeta en Swarm
deploy.resources.limits resources.requests / limits K8s distingue lo que pide de lo que topa
healthcheck: livenessProbe + readinessProbe + startupProbe De una sonda a tres semánticas
depends_on: condition: initContainers + readinessProbe No hay orden global; hay reintentos
restart: unless-stopped restartPolicy: Always (implícito) El Deployment ya lo garantiza
networks: NetworkPolicy En Compose aísla; en K8s hay que declararlo
profiles: Overlays de Kustomize Ambos activan subconjuntos
compose.prod.yaml (override) overlays/prod con parches Misma idea, distinta mecánica
docker compose up -d kubectl apply -k overlays/prod Uno aplica y sale; el otro deja controladores

Dos filas merecen un aviso. La de depends_on: en Kubernetes no existe el arranque ordenado global; el Pod de la API arrancará aunque PostgreSQL no esté, fallará, y CrashLoopBackOff lo reintentará hasta que la base de datos responda. Es feo de ver y es lo correcto: obliga a que la aplicación tolere que sus dependencias no estén, que es justo lo que aprendiste en 06-01. Y la de Secret: está codificado en base64, no cifrado; sin cifrado en reposo de etcd y RBAC bien puesto, no es un secreto, es un texto incómodo de leer.

  1. El mismo aurora-api, en los dos formatos

Compose, tal como quedó tras el módulo 4:

# compose.yaml (fragmento)
services:
  api:
    image: ghcr.io/auroralibros/aurora-api:2.0.0
    restart: unless-stopped
    environment:
      DB_HOST: aurora-db
      DB_NAME: aurora_libros
      DB_USER: aurora
      REDIS_URL: redis://aurora-cache:6379
      LOG_LEVEL: info
    secrets: [db_password]
    ports: ["8080:8080"]
    depends_on:
      aurora-db:   { condition: service_healthy }
      aurora-cache: { condition: service_started }
    healthcheck:
      test: ["CMD", "node", "-e", "fetch('http://localhost:8080/salud/vivo')"]
      interval: 10s
      timeout: 3s
      retries: 3
      start_period: 20s
    deploy:
      replicas: 3
      resources:
        limits: { cpus: "1.0", memory: 512M }
    read_only: true
    cap_drop: [ALL]
    security_opt: ["no-new-privileges:true"]

Veintiocho líneas. Ahora lo mismo en Kubernetes, sin recortar nada esencial:

# k8s/base/api.yaml
apiVersion: v1
kind: ConfigMap
metadata: { name: aurora-config, namespace: aurora }
data:
  DB_HOST: aurora-db
  DB_NAME: aurora_libros
  DB_USER: aurora
  REDIS_URL: redis://aurora-cache:6379
  LOG_LEVEL: info
---
apiVersion: apps/v1
kind: Deployment
metadata: { name: aurora-api, namespace: aurora }
spec:
  replicas: 3
  selector:
    matchLabels: { app: aurora-api }
  template:
    metadata:
      labels: { app: aurora-api }
    spec:
      securityContext:
        runAsNonRoot: true
        runAsUser: 10001
      containers:
        - name: api
          image: ghcr.io/auroralibros/aurora-api:2.0.0
          ports: [{ containerPort: 8080, name: http }]
          envFrom:
            - configMapRef: { name: aurora-config }
          env:
            - name: DB_PASSWORD
              valueFrom:
                secretKeyRef: { name: aurora-secrets, key: db-password }
          resources:
            requests: { cpu: "250m", memory: "256Mi" }
            limits:   { cpu: "1000m", memory: "512Mi" }
          startupProbe:
            httpGet: { path: /salud/vivo, port: http }
            failureThreshold: 30
            periodSeconds: 2
          livenessProbe:
            httpGet: { path: /salud/vivo, port: http }
            periodSeconds: 10
          readinessProbe:
            httpGet: { path: /salud/listo, port: http }
            periodSeconds: 5
          securityContext:
            readOnlyRootFilesystem: true
            allowPrivilegeEscalation: false
            capabilities: { drop: [ALL] }
---
apiVersion: v1
kind: Service
metadata: { name: aurora-api, namespace: aurora }
spec:
  selector: { app: aurora-api }
  ports: [{ port: 80, targetPort: http }]
Compose Kubernetes
Líneas para el mismo servicio 28 63
Objetos declarados 1 3 (ConfigMap, Deployment, Service)
Ficheros en un proyecto completo 1 + overrides Decenas, más los overlays
Lo que ganas — Reprogramación, sondas separadas, RBAC, HPA

La proporción de más del doble de YAML no es un detalle estético: se multiplica por cada servicio, y con cuatro servicios y tres entornos tienes un directorio que ya nadie lee entero. A cambio, cada línea de más compra algo real. La pregunta es si necesitas lo que compra.

  1. Árbol de decisión

graph TD
    A["¿Cuánto corte toleras?"] -->|"Minutos, sin drama"| B["¿Más de un nodo?"]
    A -->|"Segundos o ninguno"| E["¿Hay alguien que sepa K8s?"]
    B -->|No| C["¿Picos de carga imprevisibles?"]
    B -->|Sí| E
    C -->|No| D["**Compose en un host**<br/>bien montado"]
    C -->|Sí| F["¿Cabe en un servicio<br/>gestionado de contenedores?"]
    F -->|Sí| G["**Cloud Run / Container Apps**<br/>autoescala sin clúster"]
    F -->|No| E
    E -->|"No, y no se puede contratar"| H["**Gestionado o Compose**<br/>nunca K8s autogestionado"]
    E -->|Sí| I["¿Presupuesto para<br/>plano de control + margen?"]
    I -->|No| H
    I -->|Sí| J["**Kubernetes gestionado**<br/>módulo 6 tal cual"]

Cinco criterios y ninguno es «lo que se lleva». Fíjate en que el camino hacia Kubernetes exige dos síes seguidos —conocimiento y presupuesto— y que el nodo H existe porque la peor decisión posible es un clúster autogestionado que nadie sabe reparar.

  1. El camino intermedio: Compose en producción

Hay una idea muy extendida y falsa: que Compose «no es para producción». Compose en un host bien montado sirve a millones de peticiones diarias en empresas reales. Lo que hace falta es montarlo con criterio:

# compose.prod.yaml — lo que convierte un juguete en producción
services:
  api:
    image: ghcr.io/auroralibros/aurora-api@sha256:9f2c...   # digest, no etiqueta
    restart: unless-stopped                                  # sobrevive al reinicio
    logging:
      driver: json-file
      options: { max-size: "10m", max-file: "3" }            # el disco no se llena
    deploy:
      resources:
        limits: { cpus: "1.0", memory: 512M }                # nadie se come el host
    healthcheck: { test: ["CMD", "node", "healthcheck.js"], interval: 10s }
Requisito de producción Cómo se cumple con Compose
Arrancar tras reiniciar el host restart: unless-stopped + Docker con systemd
No perder datos Volumen aurora-datos + copia externa probada
No llenar el disco Rotación de logs + docker system prune programado
Imagen reproducible Referencia por digest, nunca :latest
Actualizar sin corte largo docker compose up -d --pull always (segundos)
Vuelta atrás El digest anterior en Git; up -d de nuevo
Vigilancia cAdvisor + Prometheus + alertas (05-06)
Copia de seguridad pg_dump programado a almacenamiento remoto
Certificado TLS Caddy o Traefik delante, con renovación automática

Los dos huecos que no puede tapar: el host es un punto único de fallo (si muere la máquina, muere el servicio hasta que arranques otra) y no hay autoescalado. Si el negocio tolera esos dos huecos —y muchísimos negocios los toleran—, has resuelto la plataforma con un fichero, sin plano de control y sin guardias.

Una regla honesta de dimensionamiento: una VM de 4 vCPU y 8 GB con la pila de Aurora Libros bien afinada atiende con holgura varios cientos de peticiones por segundo. Antes de dar por hecho que necesitas un clúster, mide con la prueba de carga de 06-06.

  1. Swarm como escalón, y los servicios que ejecutan Compose

Si el problema es solo el punto único de fallo, hay un escalón intermedio antes del salto grande.

Opción Qué resuelve Qué cuesta Estado en 2026
Docker Swarm Varios nodos, reprogramación, rolling updates Aprender poco: deploy: en el mismo fichero Mantenido, sin apenas evolución
Compose + host de reserva Reponer rápido tras una caída Un procedimiento manual bien ensayado Trivial
ECS con Compose Ejecuta un compose.yaml en AWS Atarse al proveedor En uso
Cloud Run / Container Apps Autoescalado sin clúster, incluso a cero Menos control de red y de estado Muy maduros
Kubernetes gestionado Todo lo del módulo 6 Coste y conocimiento El estándar

Swarm (06-03) merece un párrafo de honestidad: es sencillo, funciona, y llevar el compose.yaml a tres nodos cuesta añadir deploy: y docker stack deploy. Pero su ecosistema está congelado, contratar a alguien que lo conozca es más difícil cada año y la documentación de terceros escasea. Como escalón temporal es razonable; como apuesta a cinco años, piénsalo dos veces.

Los servicios tipo Cloud Run son la novedad que cambia el árbol de decisión: autoescalan de cero a cientos de instancias sin que exista ningún clúster que mantener. Para ghcr.io/auroralibros/aurora-api:2.0.0 —sin estado, con sondas, doce factores y apagado ordenado— encajan sin tocar nada. El estado se va a una base de datos gestionada, y el problema de la disponibilidad deja de ser tuyo.

  1. Kompose y los límites reales de la conversión

kompose traduce un compose.yaml a manifiestos de Kubernetes. Es útil como punto de partida y peligroso como resultado final.

kompose convert -f compose.yaml -o k8s/
# INFO Kubernetes file "aurora-api-service.yaml" created
# INFO Kubernetes file "aurora-api-deployment.yaml" created
# WARN Volume mount on the host "./datos" isn't supported - ignoring
# WARN Service "aurora-db" won't be created because 'ports' is not specified
Qué convierte bien Qué convierte mal o ignora
image, command, ports depends_on (lo pierde: no hay orden)
environment → variables sueltas healthcheck → no genera las tres sondas
deploy.replicas Bind mounts → advertencia y a tu cuenta
restart → restartPolicy secrets → los deja como ficheros o los pierde
networks (parcialmente) profiles, extends, develop.watch
Ningún Ingress, HPA, PDB ni NetworkPolicy

La conclusión práctica: kompose te ahorra el primer 60 % del tecleo y no te ahorra nada del pensamiento. Todo lo que hace que un despliegue sea apto para producción —sondas separadas, requests y limits medidos, PDB, política de red, Ingress con TLS— sigue siendo trabajo tuyo. Trátalo como un borrador, revísalo línea a línea y no lo apliques nunca directamente contra un clúster real.

  1. El coste oculto de Kubernetes

Lo que no sale en las presentaciones:

Coste Qué significa en la práctica
El clúster Plano de control (gestionado o no) + nodos + margen para reprogramar
Recursos ociosos Necesitas capacidad libre para que quepan los Pods de un nodo caído
Actualizaciones Versiones nuevas cada pocos meses, con soporte limitado y APIs que se retiran
La deuda de YAML Decenas de ficheros por entorno; los overlays tapan pero no eliminan
Modos de fallo nuevos CrashLoopBackOff, ImagePullBackOff, Pending por falta de recursos, Evicted por presión de memoria, PVC que no se enlaza, DNS del clúster que falla
Depuración más difícil El problema puede estar en la app, el Pod, el nodo, el CNI, el CSI o el Ingress
Herramientas alrededor Helm o Kustomize, un GitOps, un gestor de secretos, un sistema de métricas
Formación continua El ecosistema cambia más rápido de lo que se consolida el conocimiento
Las guardias Alguien tiene que responder a las tres de la mañana

La última fila es la que decide. Formúlala así en la reunión: «si el clúster se rompe un sábado a las tres de la mañana, ¿quién lo arregla, en cuánto tiempo y cobrando qué?». Si no hay una respuesta con un nombre propio, Kubernetes no es una opción todavía, por muy bien que quede en la arquitectura.

Y conviene decirlo también al revés: cuando la respuesta existe, Kubernetes devuelve con creces lo que cuesta. Autoescalado real, despliegues sin corte, reprogramación automática, cuotas por equipo y un ecosistema que resuelve problemas que ni sabías que tenías. El error no es elegir Kubernetes; es elegirlo antes de tiempo.

  1. Aurora Libros a tres tamaños

Tienda pequeña En crecimiento Con picos de campaña
Tráfico 5-20 req/s 100-300 req/s 40 req/s con picos ×8-15
Equipo 2 desarrolladores 6 personas, 1 con sistemas 4 desarrolladores
Guardias No Horario laboral No
Corte tolerable 30 min 5 min Cero en campaña
Recomendación Compose en un host Kubernetes gestionado Cloud Run o similar
Por qué Un fichero, cero clúster, coste mínimo El tráfico y el equipo ya lo justifican Autoescala sin clúster que mantener
Base de datos En el mismo host, con copias probadas Gestionada, con réplica Gestionada, obligatorio
Riesgo asumido El host es punto único de fallo Coste operativo permanente Dependencia del proveedor
Siguiente paso Monitorización y copias probadas PDB, HPA y presupuesto de errores Medir el coste por petición en pico

Fíjate en la tercera columna: mucho tráfico en pico no implica Kubernetes. Implica autoescalado, y hay más de una forma de conseguirlo. Y en la segunda, lo que inclina la balanza no es solo el tráfico: es que existe una persona con perfil de sistemas. Sin esa persona, la recomendación sería otra aunque el tráfico fuese el mismo.

Errores Comunes y Consejos

  • Elegir Kubernetes por currículum. Es una razón real y humana, y es la peor de todas para el proyecto. Si quieres aprenderlo, monta un clúster de laboratorio con kind, no la plataforma de producción de la empresa.
  • Creer que «Compose no es para producción». Con digests, límites, rotación de logs, copias probadas y vigilancia, Compose sostiene negocios reales. Lo que no da es tolerancia a la caída del host ni autoescalado.
  • Aplicar la salida de kompose sin revisarla. Pierde depends_on, no genera las tres sondas ni Ingress, HPA o PDB, e ignora los bind mounts.
  • Migrar a Kubernetes sin haber medido. Antes de justificar un clúster por rendimiento, ejecuta la prueba de carga de 06-06 sobre el host actual. El límite real suele estar en la base de datos, y un clúster no lo mueve.
  • Traducir depends_on esperando arranque ordenado. No existe. La aplicación tiene que reintentar; si no lo hace, arréglala antes de migrar.
  • Tratar los Secret como cifrados. Son base64. Sin cifrado en reposo de etcd y RBAC estricto, no protegen nada.
  • Consejo: escribe la decisión y sus motivos en un documento breve dentro del repositorio, con fecha. Dentro de un año querrás saber por qué se eligió, y si las premisas siguen siendo ciertas.
  • Consejo: mantén el compose.yaml vivo aunque despliegues en Kubernetes. Es la mejor forma de levantar la pila entera en local para desarrollar y depurar.
  • Consejo: si dudas, empieza por Compose. Migrar de Compose a Kubernetes con una imagen bien hecha es un trabajo de días; desmontar un clúster que nadie sabe operar cuesta meses.

Ejercicios

Ejercicio 1 — Traduce un servicio completo. Toma el servicio aurora-cache (redis:7-alpine, sin exponer al exterior, con volumen para la persistencia opcional, límite de 256 MB de memoria y un healthcheck con redis-cli ping) tal como está en el compose.yaml. Escríbelo en Kubernetes: Deployment, Service interno y PersistentVolumeClaim. Indica qué elemento del original no tiene equivalente directo y cómo lo resuelves.

Ejercicio 2 — Aplica el árbol de decisión. Para cada escenario, recorre el árbol de §6, indica el nodo final y justifica la decisión en tres frases. (a) Una API interna de facturación que usan 30 empleados en horario de oficina, con un desarrollador que también hace de administrador. (b) Una plataforma de venta de entradas que agota un concierto en 90 segundos, con equipo de plataforma de cinco personas y guardias. (c) Un blog corporativo con 400 visitas al día que un proveedor externo quiere desplegar en un clúster gestionado «porque es lo estándar».

Ejercicio 3 — El coste real de la decisión. Aurora Libros crece y la dirección pregunta si «hay que pasar a Kubernetes». Prepara una comparativa de una página con: coste mensual estimado de infraestructura en ambos escenarios (usa cifras aproximadas de proveedor y explícita tus supuestos), tiempo de aprendizaje y de puesta en marcha, qué mejora medible aporta el clúster, qué riesgo nuevo introduce, y tu recomendación con la condición que tendría que cumplirse para cambiarla.

Soluciones

Solución 1.

apiVersion: v1
kind: PersistentVolumeClaim
metadata: { name: aurora-cache-datos, namespace: aurora }
spec:
  accessModes: [ReadWriteOnce]
  resources: { requests: { storage: 1Gi } }
---
apiVersion: apps/v1
kind: Deployment
metadata: { name: aurora-cache, namespace: aurora }
spec:
  replicas: 1
  strategy: { type: Recreate }        # un solo PVC ReadWriteOnce
  selector: { matchLabels: { app: aurora-cache } }
  template:
    metadata: { labels: { app: aurora-cache } }
    spec:
      containers:
        - name: redis
          image: redis:7-alpine
          args: ["--maxmemory", "200mb", "--maxmemory-policy", "allkeys-lru"]
          ports: [{ containerPort: 6379, name: redis }]
          resources:
            requests: { cpu: "50m", memory: "128Mi" }
            limits:   { cpu: "500m", memory: "256Mi" }
          livenessProbe:
            exec: { command: ["redis-cli", "ping"] }
            periodSeconds: 10
          readinessProbe:
            exec: { command: ["redis-cli", "ping"] }
            periodSeconds: 5
          volumeMounts: [{ name: datos, mountPath: /data }]
      volumes:
        - name: datos
          persistentVolumeClaim: { claimName: aurora-cache-datos }
---
apiVersion: v1
kind: Service
metadata: { name: aurora-cache, namespace: aurora }
spec:
  selector: { app: aurora-cache }
  ports: [{ port: 6379, targetPort: redis }]

Lo que no tiene equivalente directo es el healthcheck único de Compose: aquí se ha desdoblado en liveness y readiness, ambas con redis-cli ping pero con periodos distintos, porque en Kubernetes «está vivo» y «puede recibir tráfico» son preguntas separadas. Tres decisiones más que el enunciado no daba y hay que razonar: strategy: Recreate en lugar de rolling, porque un PVC ReadWriteOnce no puede montarse en dos Pods a la vez y el rolling se quedaría bloqueado; el --maxmemory de Redis puesto por debajo del limits.memory del contenedor, para que Redis expulse claves antes de que el kernel mate el proceso por OOM; y la ausencia de ports expuestos hacia fuera, ya que el Service sin tipo es ClusterIP y solo se alcanza desde dentro del clúster, que es exactamente lo que se pedía.

Solución 2.

(a) API interna de facturación. Corte tolerable: horas, porque fuera del horario de oficina no hay nadie usándola. Un solo nodo basta y no hay picos. Nodo final: Compose en un host bien montado. El único administrador no puede sostener un clúster, y un despliegue con restart: unless-stopped, copias probadas y monitorización básica cubre el requisito con creces. Añadir Kubernetes aquí multiplicaría el coste operativo sin mejorar nada perceptible.

(b) Venta de entradas. El corte tolerable es cero durante los 90 segundos que importan, hay equipo de plataforma y hay guardias. El pico es brutal pero previsible en el instante, lo que permite preescalar antes del evento en lugar de esperar a que reaccione el HPA. Nodo final: Kubernetes gestionado. Es el caso de libro: alta disponibilidad real, autoescalado, despliegues sin corte y un equipo que puede operarlo. Aquí el clúster devuelve lo que cuesta.

(c) Blog corporativo. 400 visitas al día son unas 0,005 peticiones por segundo de media. El corte tolerable es de horas. Nodo final: Compose en un host, o directamente alojamiento estático si el contenido lo permite. Un clúster gestionado para esto cuesta más al mes que el propio blog genera de valor y añade una dependencia que nadie del equipo sabrá reparar. Que sea «lo estándar» no es un requisito: pide al proveedor que justifique la decisión con los mismos criterios del árbol y la conversación se acaba sola.

Solución 3. Estructura de la comparativa (los importes son ilustrativos y hay que sustituirlos por los del proveedor y la región reales):

Concepto Compose en un host Kubernetes gestionado
Cómputo 1 VM de 4 vCPU / 8 GB 3 nodos de 2 vCPU / 4 GB + margen
Plano de control 0 Coste fijo mensual del proveedor
Base de datos En el host (o gestionada) Gestionada, obligatoria en la práctica
Balanceador y TLS Traefik en el mismo host Balanceador del proveedor, facturado aparte
Infraestructura Base Entre 2,5 y 4 veces la base
Puesta en marcha Ya está hecho 2-4 semanas de trabajo real
Formación 0 1-3 meses hasta operar con soltura
Mantenimiento Parches del host Parches + actualizaciones de clúster

Mejoras medibles del clúster: recuperación automática ante caída de nodo (de «minutos con intervención humana» a «segundos sin ella»), autoescalado de 3 a 12 réplicas ante picos, y despliegues sin corte con rollback en un comando. Riesgos nuevos: dependencia de un conocimiento que hoy no existe en el equipo, más superficie de configuración que puede fallar, y coste fijo que no baja cuando baja el tráfico.

Recomendación: seguir en Compose y revisar la decisión con un disparador concreto, no con una fecha. Por ejemplo: «pasamos a Kubernetes gestionado cuando se cumplan dos de estas tres condiciones: superar de forma sostenida el 60 % de CPU del host, que el negocio fije un objetivo de disponibilidad por encima del 99,9 %, o que se incorpore al equipo una persona con experiencia real operando clústeres». Escribir el disparador convierte una discusión de opiniones en una condición verificable, y ese es el verdadero entregable del ejercicio.

Conclusión

Ya puedes defender la decisión con criterios en lugar de con modas. Tienes claro lo primero y lo más olvidado: no son la misma categoría de herramienta. Compose es un descriptor que ejecutas y termina; Kubernetes guarda tu intención en etcd y no deja de reconciliarla con la realidad. De esa diferencia salen todas las demás, incluidas las buenas —reprogramación automática, autoescalado, despliegues sin corte— y las caras.

Tienes la comparativa por dimensiones, con las tres filas que deciden más proyectos que ninguna otra: coste de infraestructura, coste operativo y madurez del equipo. Tienes la tabla de equivalencias completa, con sus dos avisos importantes: depends_on no tiene traducción porque en Kubernetes no existe el arranque ordenado, y un Secret es base64, no cifrado. Y has visto el mismo aurora-api en los dos formatos: 28 líneas frente a 63, un objeto frente a tres, con la pregunta correcta encima —¿necesitas lo que compran esas líneas de más?—.

El árbol de decisión te da cinco criterios concretos y un detalle que conviene recordar: llegar a Kubernetes exige dos síes seguidos, conocimiento y presupuesto, y la peor opción de todas es un clúster autogestionado que nadie sabe reparar. Sabes que Compose en producción es una decisión perfectamente profesional cuando el host está bien montado —digests, límites, rotación de logs, copias probadas, TLS automático—, con sus dos huecos declarados: punto único de fallo y ausencia de autoescalado. Conoces los escalones intermedios: Swarm, honesto pero congelado; los servicios que ejecutan tu imagen sin que exista clúster alguno; y kompose, que te ahorra el 60 % del tecleo y nada del pensamiento.

Y te llevas la pregunta que ordena todo lo demás: si el clúster se rompe un sábado a las tres de la mañana, ¿quién lo arregla, en cuánto tiempo y cobrando qué?. Con las tres versiones de Aurora Libros —la tienda pequeña con Compose, la que crece con Kubernetes gestionado, la de campaña con autoescalado sin clúster— tienes tres respuestas de referencia y el hábito de escribir la decisión, con su fecha y su disparador de revisión, dentro del repositorio.

En la lección siguiente bajamos de la infraestructura al escritorio: Docker Desktop. Qué es exactamente esa VM Linux que llevas usando sin verla, por qué explica el rendimiento de los bind mounts, qué funciones aporta, qué condiciones de licencia tiene —y por qué conviene consultarlas antes de instalarlo en la empresa— y qué alternativas existen en cada sistema operativo.

Docker: De Principiante a Avanzado

Módulo 1: Introducción a Docker

Módulo 2: Trabajando con Imágenes Docker

Módulo 3: Contenedores Docker

Módulo 4: Docker Compose

Módulo 5: Conceptos Avanzados de Docker

Módulo 6: Docker en Producción

Módulo 7: Ecosistema y Herramientas de Docker

© Copyright 2026. Todos los derechos reservados