README · by ansango
← Volver al libro

Path to production

El camino a producción con Docker: workflow de build/test/deploy, concerns de producción (job control, resource limits, networking, configuration, logging, monitoring, scheduling, service discovery)

~5 min de lectura
Resumen

Esta nota cubre el camino de una imagen Docker local a producción: el workflow de build/test/push/deploy, los concerns de producción que Docker y la plataforma deben cubrir (job control, resource limits, networking, configuration, logging, monitoring, scheduling, service discovery), y dónde Docker se queda corto (típicamente scheduling y service discovery, que se delegan a la plataforma: Kubernetes, Swarm, ECS). Es la nota que conecta todo lo anterior con la realidad operacional.

El workflow básico

El camino a producción pasa por estos pasos:

  1. Build local de la imagen en tu máquina de desarrollo.
  2. Build oficial desde CI o build system.
  3. Push de la imagen al registry.
  4. Deploy al server, configurar, arrancar el container.

A medida que el workflow madura, todos estos pasos colapsan en un pipeline automatizado que orquesta build, test, storage y deploy.

Una historia de producción debe cubrir tres cosas:

Docker’s role en producción

Docker provee la base de la aplicación containerized, pero la plataforma (Kubernetes, Swarm, ECS, etc.) maneja la mayoría de las decisiones de producción. La plataforma suele ser un sistema que envuelve un cluster de servers con una interfaz común para gestión de containers.

ConcernLo proveeLo reemplaza
Job control (start/stop/restart)Docker engine (start/stop/run/kill)Init systems (systemd, runit)
Resource limitsDocker (cgroups)ulimit, runtime-level controls
NetworkingDocker networksNetwork config manual
ConfigurationEnvironment variablesChef, Puppet, Ansible (a nivel OS)
Packaging & deliveryImágenes + registryTarballs, paquetes custom
LoggingDocker log drivers (json-file, syslog, fluentd)Syslog local, logfiles
MonitoringHealth checks + tools externos (cAdvisor, Prometheus)Nagios, Zabbix
SchedulingLa plataformaRunbooks manuales, scripts
Service discoveryLa plataforma (DNS, service mesh)Load balancers manuales
Containerize primero, schedule después

No necesitas un scheduler distribuido el día uno. Empieza containerizando tus apps en el mismo server donde corren hoy. Una vez estable, introduce el scheduler gradualmente.

Job control y resource limits

Docker provee primitivas de job control (docker container start, stop, run, kill) que se mapean a las fases del lifecycle de una aplicación. Todas las plataformas (Kubernetes, Swarm, ECS) respetan este lifecycle.

Para resource limits, usa las flags de docker container run (--memory, --cpu-shares, --cpuset-cpus, etc.) o, mejor, configúralas en tu plataforma (en Kubernetes, los Pod specs; en ECS, los task definitions).

Production = no root

Configura tu aplicación para correr como unprivileged user (USER en Dockerfile o --user en runtime). Root en el container es un riesgo de seguridad aunque tenga algo de aislamiento.

Networking

Docker provee muchas opciones de networking, pero en producción, deja que la plataforma decida. Tu app solo debe:

# Ejemplo de service discovery automático
environment:
  DATABASE_URL: "postgresql://postgres-db:5432/myapp"
  # 'postgres-db' es el nombre del service, no un IP
Service discovery en Compose

En docker-compose, otros services se acceden por su nombre de service, no por IP. Compose configura DNS automáticamente. Es la misma idea en Kubernetes (Services) y Swarm.

Configuration

Docker’s native mechanism es environment variables. Esto funciona en todas las plataformas modernas.

docker container run -d \
  -e DATABASE_URL="postgresql://..." \
  -e LOG_LEVEL=info \
  -e FEATURE_FLAGS="new_ui,beta_export" \
  myapp:1.2.3
No archivos de config tradicionales

Algunos sistemas (como Kubernetes) hacen fácil usar archivos de config. Recomendamos evitarlos porque reduce la observabilidad de tu app y te ata a mecanismos específicos de la plataforma. ENV vars funcionan en todos lados.

Para secrets, usa el mecanismo de la plataforma:

Logging

Docker captura todo lo que el container escribe a stdout/stderr y lo streamea a un backend de logging configurable. Tu app solo necesita escribir a stdout/stderr. La plataforma se encarga del resto.

# json-file (default, hasta que configures log rotation)
# syslog (UDP, recomendado para producción)
# fluentd, awslogs, gcplogs, splunk
# journald (Linux con systemd)
Configura log rotation SIEMPRE

Sin esto, el log file crece sin límite y eventualmente llena el disco.

Monitoring

Health checks

Define HEALTHCHECK en tu Dockerfile o en el spec de tu plataforma. Es la forma estándar de preguntar “¿está esta app funcionando?”:

HEALTHCHECK CMD curl -f http://localhost:3000/health || exit 1

El status aparece en docker container ls, en Kubernetes (readiness probes), en ECS, etc. Todos los orquestadores lo respetan.

Métricas y APM

Pager a humanos solo cuando la plataforma no pueda

En sistemas tradicionales, los engineers se pagen y deciden qué hacer. En sistemas dinámicos, el scheduler re-arranca el container, lo mueve a otro nodo, o escala. Los humans solo se pagen cuando la plataforma no puede intervenir.

Scheduling y service discovery

Scheduling es la decisión de qué corre en qué servidor. Service discovery es cómo las apps se encuentran unas a otras en la red. Ambos los maneja la plataforma, no Docker directamente.

PlataformaSchedulingService discovery
Docker ComposeManual (todo en un host)DNS por nombre de service
Docker Swarm modeBuilt-in, simpleBuilt-in
KubernetesMuy sofisticado, dominio del líderServices + DNS, Ingress
AWS ECSTasks en clustersService discovery, ALB
HashiCorp NomadFlexible, multi-runtimeConsul integration
Kelsey Hightower

El scheduler es el sistema que juega Tetris por ti, colocando services en servers para el mejor fit, on the fly.

Service discovery patterns

Cuidado con service discovery bidireccional

En sistemas blended (legacy + containers), entrar al sistema nuevo es más fácil que salir. Load balancers dinámicos que apuntan a tu sistema nuevo son un buen primer paso. Salir del sistema nuevo al legacy suele requerir configuración manual.

Testing en CI con Docker

El workflow típico de testing:

  1. Build trigger (webhook desde Git, manual, schedule).
  2. Build server kickea un container image build.
  3. La imagen se crea en el Docker server.
  4. La imagen se taggea con un build number o commit hash.
  5. Se crea un nuevo container con la imagen, corriendo el test suite.
  6. Los resultados se capturan (exit code es la señal principal).
  7. Build pasa/falla según los resultados.
  8. Builds pasados se pushean al registry.
# Build de la imagen
docker image build -t myapp:commit-abc123 .

# Tag con el commit hash
docker image tag myapp:commit-abc123 myapp:build-42

# Run tests
docker container run --rm -e ENVIRONMENT=testing -e API_KEY=12345 \
  myapp:build-42 /opt/myapp/test.sh

# Si exit code es 0, push
docker image push myapp:build-42

Construye la imagen EXACTA que va a producción

El container que testeas debe ser idéntico al que se deploya. No hagas imágenes separadas para test y producción. Si necesitas diferencias, usa env vars o command-line args al container.

No uses

latest en CI Si el tag latest cambia (porque otro build se pushea), puedes testear la imagen equivocada. Usa siempre tags específicos (commit hash, build number, semantic version).

External dependencies con Docker Compose

Para tests con DBs, cache, etc., Docker Compose es ideal:

# docker-compose.test.yml
services:
  app:
    build: .
    command: /opt/myapp/test.sh
    environment:
      DATABASE_URL: "postgresql://test-db:5432/myapp_test"
    depends_on:
      test-db:
        condition: service_healthy
  test-db:
    image: postgres:14
    environment:
      POSTGRES_DB: myapp_test
    healthcheck:
      test: ["CMD-SHELL", "pg_isready"]
      interval: 2s
Jenkins, CircleCI, GitHub Actions

Todos tienen integraciones con Docker, Mesos, Kubernetes. Muchas plataformas de CI modernas (incluyendo hosted) proveen environments containerizados listos para tus tests.

Próximos pasos