DevOps · 02

Misma tarea, tres herramientas

Cada bloque resuelve lo mismo con Docker, Kubernetes y Terraform. Los ejemplos de Terraform usan el provider de Docker (kreuzwerker/docker) para poder probarlos en tu PC.

Cómo leer esta página

Cada bloque es una tarea, resuelta en tres columnas. No hace falta conocer las tres herramientas: leé la columna de la que estés usando y compará.

Docker Compose
Archivo docker-compose.yml + comando docker compose. Corre contenedores en tu PC.
Kubernetes
Manifiestos YAML + comando kubectl. Corre contenedores en un cluster.
Terraform
Archivos .tf + comando terraform. Acá usa el provider de Docker, así cada ejemplo se puede probar en tu PC.
Bloques oscuros
Código o comandos para copiar. Lo que está entre < > se reemplaza.
Notas grises
Aclaraciones o diferencias importantes de esa herramienta.

1 · Levantar una app y abrir un puerto

Un nginx accesible en localhost:8080, declarado en archivo.

Docker Compose
services:
  web:
    image: nginx:1.27
    ports:
      - "8080:80"
Kubernetes
apiVersion: apps/v1
kind: Deployment
metadata: { name: web }
spec:
  selector: { matchLabels: { app: web } }
  template:
    metadata: { labels: { app: web } }
    spec:
      containers:
        - name: web
          image: nginx:1.27
---
apiVersion: v1
kind: Service
metadata: { name: web }
spec:
  type: NodePort
  selector: { app: web }
  ports: [{ port: 80, nodePort: 30080 }]
Docker Desktop: localhost:30080. O kubectl port-forward svc/web 8080:80.
Terraform
resource "docker_image" "web" {
  name = "nginx:1.27"
}

resource "docker_container" "web" {
  name  = "web"
  image = docker_image.web.image_id
  ports {
    internal = 80
    external = 8080
  }
}

2 · Aplicar cambios (y ver antes qué va a pasar)

Las tres son declarativas: describís el estado final y la herramienta calcula qué hacer.

Docker Compose
docker compose config
docker compose up -d
config muestra el YAML final. No hay “preview” de cambios.
Kubernetes
kubectl diff -f k8s/
kubectl apply -f k8s/
diff compara contra lo que hay en el cluster.
Terraform
terraform init
terraform plan -out=tfplan
terraform apply tfplan
El plan muestra + ~ - por recurso.

3 · Ver qué está corriendo

Listado y detalle de lo que administra cada herramienta.

Docker
docker compose ps
docker ps -a
docker inspect web
Kubernetes
kubectl get pods -o wide
kubectl get deploy,svc
kubectl describe pod <pod>
Terraform
terraform state list
terraform state show docker_container.web
terraform output
Muestra lo que Terraform cree que existe; plan lo contrasta con la realidad.

4 · Ver logs y entrar al contenedor

Depurar la aplicación que está corriendo.

Docker
docker logs -f web
docker exec -it web sh
Kubernetes
kubectl logs -f deploy/web
kubectl logs <pod> --previous
kubectl exec -it deploy/web -- sh
Terraform
Terraform no corre la app: para sus logs usás docker o kubectl. Para depurar a Terraform mismo:
$env:TF_LOG = "DEBUG"
terraform plan

5 · Pasar configuración (variables de entorno)

Valores no secretos que cambian por entorno.

Docker Compose
services:
  api:
    image: chanchito:1.0.0
    environment:
      - NODE_ENV=production
      - LOG_LEVEL=info
Kubernetes
apiVersion: v1
kind: ConfigMap
metadata: { name: api-config }
data:
  NODE_ENV: production
  LOG_LEVEL: info
# en el contenedor:
#   envFrom:
#     - configMapRef: { name: api-config }
Terraform
variable "log_level" {
  type    = string
  default = "info"
}

resource "docker_container" "api" {
  # ...
  env = ["LOG_LEVEL=${var.log_level}"]
}
Valores por entorno en dev.tfvars / prod.tfvars.

6 · Manejar un secreto (contraseña)

Que no quede en el repo ni se imprima en pantalla.

Docker Compose
# .env (en .gitignore)
# MONGO_PASSWORD=<password>
services:
  db:
    image: mongo:7
    environment:
      - MONGO_INITDB_ROOT_PASSWORD=${MONGO_PASSWORD}
Kubernetes
kubectl create secret generic mongo \
  --from-literal=MONGO_PASSWORD=<password>
envFrom:
  - secretRef: { name: mongo }
Los Secrets son base64, no cifrados.
Terraform
variable "mongo_password" {
  type      = string
  sensitive = true
}
$env:TF_VAR_mongo_password = "<password>"
Igual queda en el .tfstate: protegé el estado.

7 · Persistir datos (volumen para Mongo)

Que los datos sobrevivan aunque el contenedor se recree.

Docker Compose
services:
  db:
    image: mongo:7
    volumes:
      - mongo-data:/data/db
volumes:
  mongo-data:
Kubernetes
kind: PersistentVolumeClaim
apiVersion: v1
metadata: { name: mongo-data }
spec:
  accessModes: [ReadWriteOnce]
  resources: { requests: { storage: 1Gi } }
# en el Pod: volumes + volumeMounts
#   mountPath: /data/db
Terraform
resource "docker_volume" "mongo" {
  name = "mongo-data"
}

resource "docker_container" "db" {
  # ...
  volumes {
    volume_name    = docker_volume.mongo.name
    container_path = "/data/db"
  }
}

8 · Conectar un servicio con otro

La app encuentra a la base por nombre, nunca por IP.

Docker Compose
mongodb://user:pass@db:27017/miapp
Todos los servicios del compose comparten una red; el host es el nombre del servicio.
Kubernetes
mongodb://user:pass@db:27017/miapp
El host es el nombre del Service (db.<namespace> desde otro namespace).
Terraform
resource "docker_network" "app" {
  name = "app-net"
}
# en cada contenedor:
networks_advanced {
  name = docker_network.app.name
}
La red es un recurso explícito; el host es el name del contenedor.

9 · Escalar a 3 réplicas

Más copias de la misma app.

Docker Compose
docker compose up -d --scale api=3
Sin puerto fijo en el host (tres contenedores no pueden usar el mismo).
Kubernetes
kubectl scale deploy api --replicas=3
Mejor: replicas: 3 en el YAML y apply. El Service balancea solo.
Terraform
resource "docker_container" "api" {
  count = 3
  name  = "api-${count.index}"
  image = docker_image.api.image_id
}
Terraform crea copias, pero no balancea ni recrea si se caen.

10 · Actualizar a una versión nueva

Pasar de chanchito:1.0.0 a chanchito:1.1.0.

Docker Compose
docker build -t chanchito:1.1.0 .
# cambiar image: en el compose
docker compose up -d
Recrea el contenedor: hay un corte breve.
Kubernetes
kubectl set image deploy/api api=chanchito:1.1.0
kubectl rollout status deploy/api
Rolling update: sin corte si hay 2+ réplicas.
Terraform
resource "docker_image" "api" {
  name = "chanchito:1.1.0"   # antes 1.0.0
}
terraform apply   # -/+ docker_container.api

11 · Volver a la versión anterior

La nueva versión falló.

Docker Compose
# volver a image: chanchito:1.0.0
docker compose up -d
Por eso conviene taggear versiones y no usar latest.
Kubernetes
kubectl rollout undo deploy/api
kubectl rollout history deploy/api
Guarda revisiones anteriores (un ReplicaSet por versión).
Terraform
git revert HEAD
terraform plan
terraform apply
No hay “undo”: el historial es git. Volvés el código y aplicás.

12 · Borrar todo

Bajar lo que levantaste. Ojo con los datos.

Docker Compose
docker compose down      # conserva volúmenes
docker compose down -v   # ⚠ borra los datos
Kubernetes
kubectl delete -f k8s/
kubectl delete ns chanchito   # ⚠ todo el namespace
Terraform
terraform plan -destroy
terraform destroy
Borra todo lo que figura en el estado, volúmenes incluidos.

No encontré esa tarea. Probá con otra palabra.