StackPractices
beginner Por Mathias Paulenko

Fundamentos de Docker

Cómo containerizar una aplicación, escribir un Dockerfile y ejecutar contenedores con Docker Compose.

Temas: devops

Overview

Docker empaqueta tu aplicación y sus dependencias en un contenedor ligero y portable que se ejecuta consistentemente en desarrollo, staging y producción. Un Dockerfile es una receta para construir esa imagen de contenedor, y Docker Compose te permite ejecutar configuraciones multi-contenedor con un solo comando.

Lo siguiente cubre las instrucciones esenciales del Dockerfile, el layering de imágenes y un ejemplo práctico de Docker Compose para una aplicación web con base de datos.

When to Use

Usa esta receta cuando:

  • Quieras eliminar problemas de “funciona en mi máquina”. Consulta Environment Variables para gestionar configuración de contenedores.
  • Configures un entorno de desarrollo local que refleje producción. Consulta Docker Compose Local Dev para entornos multi-servicio.
  • Prepares una aplicación para deployment en Kubernetes, AWS ECS o plataformas similares. Consulta Serverless Functions para despliegues function-as-a-service.
  • Ejecutes pruebas de integración que dependan de bases de datos, caches o brokers de mensajes. Consulta Integration Testing para estrategias de aislamiento de tests.

Solution

Dockerfile para una App Node.js

# syntax=docker/dockerfile:1
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/package*.json ./
RUN npm ci --omit=dev
EXPOSE 3000
USER node
CMD ["node", "dist/main.js"]

Dockerfile para una App Python

# syntax=docker/dockerfile:1
FROM python:3.12-slim
WORKDIR /app
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["python", "-m", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

Docker Compose (Web + Base de Datos)

version: "3.9"
services:
  app:
    build: .
    ports:
      - "3000:3000"
    environment:
      - DATABASE_URL=postgres://user:pass@db:5432/mydb
    depends_on:
      - db

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: user
      POSTGRES_PASSWORD: pass
      POSTGRES_DB: mydb
    volumes:
      - pgdata:/var/lib/postgresql/data
    ports:
      - "5432:5432"

volumes:
  pgdata:

Ejecutar: docker compose up --build

Explanation

  • Builds multi-etapa (AS builder, AS runner) mantienen las imágenes de producción pequeñas al excluir herramientas de build, source maps y dependencias de desarrollo.
  • Caché de capas: Docker cachea cada capa de instrucciones. Coloca COPY package*.json y RUN npm ci antes de COPY . . para que la instalación de dependencias se cachee a menos que los archivos de paquete cambien.
  • Usuario no-root: USER node (o un usuario personalizado) reduce la superficie de ataque si el contenedor es comprometido.
  • .dockerignore: créalo para excluir node_modules, .git y archivos de entorno local — inflan el contexto de build y pueden filtrar secretos.

Variants

ObjetivoEnfoque
Imagen más pequeñaUsar imágenes base alpine o distroless
Build más rápidoOrdenar instrucciones del Dockerfile de menos a más frecuentemente cambiadas
Inyección de secretosUsar secretos de BuildKit (--secret) o variables de entorno en runtime, nunca COPY de secretos
Health checksAgregar instrucción HEALTHCHECK o bloque healthcheck de Docker Compose

Lo que funciona

  • Fija tags de imágenes base: node:20-alpine es mejor que node:latest para evitar cambios breaking inesperados.
  • Un proceso por contenedor: deja que Docker gestione el ciclo de vida del proceso; usa Compose u un orquestador para setups multi-proceso.
  • Usa mounts de volumen para desarrollo: monta el código fuente en el contenedor para hot-reload durante desarrollo.
  • Escanea imágenes: ejecuta docker scan o Trivy para detectar vulnerabilidades del SO y dependencias en tus imágenes.
  • Apagado graceful: maneja SIGTERM en tu aplicación para que Docker pueda detener contenedores limpiamente.

Common Mistakes

  • Imágenes gigantes: copiar archivos innecesarios (logs, datos de prueba, .git) infla el tamaño de imagen y tiempo de build.
  • Ejecutar como root: los usuarios por defecto en imágenes base suelen ser root. Crea y cambia a un usuario no-root.
  • Secretos hardcodeados: incrustar contraseñas de base de datos en la imagen las hace visibles para cualquiera que la descargue.
  • Ignorar .dockerignore: sin él, COPY . . envía todo tu repo — incluyendo archivos sensibles — al daemon de Docker.
  • No manejar señales: aplicaciones que ignoran SIGTERM son matadas con SIGKILL después de un timeout, arriesgando corrupción de datos.

Tips de Rendimiento

  1. Usa BuildKit para builds más rápidos. Habilítalo con DOCKER_BUILDKIT=1 o docker buildx:
$ DOCKER_BUILDKIT=1 docker build -t myapp:latest .
  1. Ordena las capas del Dockerfile de menos a más cambiante. Las dependencias cambian raramente; el código fuente cambia seguido:
# Bien: deps primero, código al final
COPY package*.json ./
RUN npm ci
COPY . .
  1. Usa --target para builds parciales. Construye solo la etapa que necesitas:
$ docker build --target builder -t myapp:builder .
  1. Usa docker compose up --build en dev. Reconstruye solo las capas cambiadas:
$ docker compose up --build --watch  # Rebuild on file changes (Compose v2.22+)
  1. Usa docker save y docker load para transferencia offline. Mueve imágenes entre máquinas sin registry:
$ docker save myapp:latest | gzip > myapp.tar.gz
$ docker load < myapp.tar.gz

Preguntas frecuentes

¿Cuál es la diferencia entre una imagen Docker y un contenedor?

Una imagen es una plantilla de solo lectura con tu código y dependencias. Un contenedor es una instancia en ejecución de esa imagen. Puedes ejecutar muchos contenedores de la misma imagen.

¿Debería usar Docker Compose en producción?

Docker Compose es excelente para deployments de producción de un solo host y desarrollo local. Para cargas de producción multi-host y alta disponibilidad, usa Kubernetes o un servicio de contenedores gestionado.

¿Cómo reduzco el tamaño de mi imagen Docker?

Usa builds multi-etapa, imágenes base alpine o distroless, y asegúrate de que tu .dockerignore excluya artefactos de build y caches de dependencias.