Docker y Kubernetes no son competidores: Docker crea y ejecuta contenedores, Kubernetes los orquesta en producción. Entender cuándo necesitas uno, otro, o ambos, es fundamental para cualquier ingeniero DevOps.
Docker y Kubernetes: Conceptos Diferentes
La pregunta "Docker vs Kubernetes" parte de una confusión habitual. No es una elección entre uno u otro en la mayoría de los casos. Son herramientas que operan en capas distintas:
- Docker es una plataforma para crear, empaquetar y ejecutar contenedores
- Kubernetes es un sistema para orquestar, escalar y gestionar contenedores en producción
Es como comparar un motor con un sistema de gestión de flotas. El motor (Docker) hace que el vehículo funcione. El sistema de flotas (Kubernetes) coordina cientos de vehículos.
Qué es Docker
Docker es la plataforma que popularizó los contenedores. Permite empaquetar una aplicación junto con todas sus dependencias en una imagen portable que se ejecuta de forma idéntica en cualquier máquina.
Componentes de Docker
| Componente | Función |
|---|---|
| Docker Engine | Runtime que ejecuta contenedores |
| Dockerfile | Receta para construir imágenes |
| Docker Image | Paquete inmutable con app + dependencias |
| Docker Container | Instancia en ejecución de una imagen |
| Docker Hub | Registro público de imágenes |
| Docker Compose | Orquestación local multi-contenedor |
Ejemplo: Dockerizar una Aplicación
# Dockerfile multi-stage para app Node.js
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
FROM node:20-alpine
WORKDIR /app
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./
USER appuser
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=3s \
CMD wget -qO- http://localhost:3000/health || exit 1
CMD ["node", "dist/server.js"]
# Construir y ejecutar
docker build -t mi-app:1.0 .
docker run -d -p 3000:3000 --name mi-app mi-app:1.0
Docker Compose para Desarrollo Local
Docker Compose permite definir aplicaciones multi-contenedor en un solo archivo:
# docker-compose.yml
services:
api:
build: ./api
ports:
- "3000:3000"
environment:
- DATABASE_URL=postgres://user:pass@db:5432/mydb
- REDIS_URL=redis://cache:6379
depends_on:
db:
condition: service_healthy
cache:
condition: service_started
db:
image: postgres:16-alpine
environment:
POSTGRES_DB: mydb
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U user -d mydb"]
interval: 5s
timeout: 3s
retries: 5
cache:
image: redis:7-alpine
volumes:
- redisdata:/data
volumes:
pgdata:
redisdata:
Qué es Kubernetes
Kubernetes (K8s) es un sistema de orquestación de contenedores creado por Google y donado a la CNCF. Gestiona el despliegue, escalado, networking y disponibilidad de aplicaciones containerizadas en clusters de servidores.
Problemas que Kubernetes Resuelve
Cuando tienes una aplicación en producción con múltiples contenedores, necesitas responder preguntas como:
- Si un contenedor muere, ¿quién lo reinicia?
- Si hay más tráfico, ¿quién crea más instancias?
- ¿Cómo distribuyo tráfico entre múltiples instancias?
- ¿Cómo actualizo sin downtime?
- ¿Cómo gestiono secretos y configuración?
- ¿Cómo manejo el almacenamiento persistente?
Kubernetes responde todas estas preguntas.
Componentes de Kubernetes
| Componente | Función |
|---|---|
| Pod | Unidad mínima de despliegue (1+ contenedores) |
| Deployment | Gestiona réplicas de pods y rolling updates |
| Service | Expone pods con IP estable y load balancing |
| Ingress | Enrutamiento HTTP/HTTPS externo |
| ConfigMap/Secret | Configuración y secretos externalizados |
| Namespace | Aislamiento lógico de recursos |
| HPA | Autoescalado basado en métricas |
| PersistentVolume | Almacenamiento persistente |
Ejemplo: Desplegar en Kubernetes
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: mi-app
labels:
app: mi-app
spec:
replicas: 3
selector:
matchLabels:
app: mi-app
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
labels:
app: mi-app
spec:
containers:
- name: mi-app
image: mi-registro/mi-app:1.0
ports:
- containerPort: 3000
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "512Mi"
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 10
periodSeconds: 15
readinessProbe:
httpGet:
path: /ready
port: 3000
initialDelaySeconds: 5
periodSeconds: 5
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: db-credentials
key: url
---
apiVersion: v1
kind: Service
metadata:
name: mi-app-service
spec:
selector:
app: mi-app
ports:
- port: 80
targetPort: 3000
type: ClusterIP
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: mi-app-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: mi-app
minReplicas: 3
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
Comparación Directa
Funcionalidad
| Capacidad | Docker (solo) | Docker + Compose | Kubernetes |
|---|---|---|---|
| Crear contenedores | Si | Si | No (usa containerd/CRI-O) |
| Multi-contenedor local | Manual | Si | Overkill |
| Auto-healing | No | No | Si |
| Autoescalado | No | No | Si (HPA/VPA/KEDA) |
| Load balancing | No | Limitado | Si (Services + Ingress) |
| Rolling updates | No | Limitado | Si (nativo) |
| Service discovery | No | DNS interno | DNS + Services |
| Gestión de secretos | No | Variables de entorno | Si (Secrets + external-secrets) |
| Multi-nodo | No | No | Si (diseñado para clusters) |
| Storage persistente | Volumes locales | Volumes locales | PV/PVC + CSI drivers |
Complejidad
| Aspecto | Docker | Kubernetes |
|---|---|---|
| Curva de aprendizaje | Baja (horas/días) | Alta (semanas/meses) |
| Configuración inicial | Un comando | Cluster completo |
| Operación día a día | Simple | Requiere expertise |
| Debugging | Directo | Múltiples capas |
| Costo operativo | Bajo | Alto (cluster + operadores) |
Costos en Cloud
| Escenario | Docker (ECS Fargate) | Kubernetes (EKS) |
|---|---|---|
| Costo base del servicio | $0 | ~$73/mes por cluster |
| 3 servicios pequeños | ~$30-50/mes | ~$150-200/mes |
| 10 servicios medianos | ~$200-400/mes | ~$400-600/mes |
| 50+ microservicios | ~$1,000-3,000/mes | ~$1,500-4,000/mes |
| Equipo necesario | 1-2 DevOps | 2-4 DevOps/SRE |
Cuándo Usar Solo Docker
Docker sin Kubernetes es suficiente cuando:
- Aplicaciones simples: Un monolito o pocos servicios
- Desarrollo local: Docker Compose para simular el entorno de producción
- Equipos pequeños: 1-5 desarrolladores sin equipo de infraestructura dedicado
- Tráfico predecible: Sin necesidad de autoescalado agresivo
- Presupuesto limitado: No puedes justificar el costo de operar un cluster K8s
- Servicios managed: Usar ECS Fargate, Cloud Run o Azure Container Instances
Ejemplo Real: Startup con 3 Microservicios
Arquitectura:
API Gateway → Auth Service → Product Service → PostgreSQL
Infraestructura:
AWS ECS Fargate (sin servidores que gestionar)
ALB para load balancing
RDS PostgreSQL managed
Costo mensual: ~$150-200
Equipo necesario: 1 DevOps part-time
Cuándo Necesitas Kubernetes
Kubernetes se justifica cuando:
- Muchos microservicios: 10+ servicios que necesitan coordinación
- Autoescalado avanzado: Picos de tráfico impredecibles, escalado basado en métricas custom
- Multi-cloud o híbrido: Portabilidad entre proveedores cloud
- Equipo dedicado: Tienes DevOps/SRE que pueden operar el cluster
- Requisitos de disponibilidad: SLAs exigentes (99.9%+)
- Ecosistema rico: Necesitas service mesh, GitOps, operators personalizados
Ejemplo Real: Fintech con 40 Microservicios
Arquitectura:
Ingress Controller (NGINX)
→ API Gateway (Kong)
→ 40 microservicios en pods
→ 3 bases de datos (RDS)
→ Redis cluster
→ Kafka (event streaming)
Infraestructura:
AWS EKS (3 nodos m5.xlarge + autoscaling)
ArgoCD para GitOps
Prometheus + Grafana para observabilidad
cert-manager para TLS automático
Costo mensual: ~$2,000-3,500
Equipo necesario: 2-3 DevOps/SRE dedicados
Docker + Kubernetes: Cómo Trabajan Juntos
En la práctica, Docker y Kubernetes se complementan en un flujo continuo:
Developer escribe código
↓
Docker build (crear imagen)
↓
Docker push (subir a registro)
↓
Kubernetes pull (descargar imagen)
↓
Kubernetes deploy (crear pods)
↓
Kubernetes manage (escalar, heal, update)
Pipeline CI/CD Completo
# GitHub Actions: build con Docker, deploy con Kubernetes
name: Build and Deploy
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Login to ECR
uses: aws-actions/amazon-ecr-login@v2
- name: Build and push Docker image
run: |
docker build -t $ECR_REGISTRY/mi-app:$GITHUB_SHA .
docker push $ECR_REGISTRY/mi-app:$GITHUB_SHA
deploy:
needs: build
runs-on: ubuntu-latest
steps:
- name: Update Kubernetes deployment
run: |
aws eks update-kubeconfig --name my-cluster
kubectl set image deployment/mi-app \
mi-app=$ECR_REGISTRY/mi-app:$GITHUB_SHA
kubectl rollout status deployment/mi-app
Alternativas a Kubernetes
Si Kubernetes es demasiado complejo para tu caso, considera estas alternativas:
| Alternativa | Descripción | Cuándo Usarla |
|---|---|---|
| AWS ECS Fargate | Contenedores managed sin servidores | AWS-only, equipos pequeños |
| Google Cloud Run | Contenedores serverless | Tráfico variable, pay-per-request |
| Azure Container Apps | Contenedores managed con Dapr | Ecosistema Microsoft |
| Docker Swarm | Orquestación simple integrada en Docker | Clusters pequeños, baja complejidad |
| Nomad | Orquestador de HashiCorp | Multi-workload (containers + VMs) |
| Railway/Render | PaaS con contenedores | Startups, prototipos rápidos |
Errores Comunes
Error 1: Usar Kubernetes para Todo
Kubernetes tiene un costo operativo significativo. Si tienes un monolito o 2-3 servicios, Docker Compose o ECS Fargate son más eficientes. No uses un cañón para matar una mosca.
Error 2: Ignorar Docker Compose
Docker Compose es la mejor herramienta para desarrollo local. Incluso si tu producción usa Kubernetes, tu entorno local debería usar Compose para que los desarrolladores sean productivos sin necesitar un cluster.
Error 3: No Definir Resource Limits
En Kubernetes, siempre define requests y limits de CPU y memoria. Sin ellos, un pod puede consumir todos los recursos del nodo y afectar otros servicios.
Error 4: Imágenes Docker Enormes
Usa multi-stage builds y base images Alpine o distroless. Una imagen de 1.5GB es un problema de seguridad y de performance. Consulta nuestra guía de optimización de imágenes Docker para más detalles.
Decisión Final: Diagrama de Flujo
¿Cuántos servicios tienes?
├─ 1-3 servicios → Docker + Compose + ECS/Cloud Run
├─ 4-10 servicios → Evalúa: ¿necesitas autoescalado avanzado?
│ ├─ No → ECS Fargate o Cloud Run
│ └─ Si → Kubernetes (managed: EKS/GKE/AKS)
└─ 10+ servicios → Kubernetes (managed)
└─ ¿Tienes equipo SRE/DevOps dedicado?
├─ Si → EKS/GKE/AKS
└─ No → Considera contratar o usar PaaS
Conclusión
Docker y Kubernetes no compiten: se complementan. Docker es la base para empaquetar aplicaciones en contenedores. Kubernetes agrega la capa de orquestación que necesitas cuando operas múltiples servicios en producción a escala.
La clave es elegir la complejidad adecuada para tu contexto. No adoptes Kubernetes porque es popular; adoptalo porque resuelve problemas reales que tienes hoy. Y si Docker solo o Docker con ECS/Cloud Run resuelven tu problema, esa es la mejor solución.
Para profundizar, consulta nuestra guía de virtualización y Linux para DevOps y cómo crear infraestructura inmutable con contenedores.
Top comments (0)