🔎 Buscar

🐳 Docker de verdad

Imágenes vs contenedores, Dockerfile completo con multi-stage builds, docker-compose real con app, postgres y redis, buenas prácticas y comandos esenciales.

Wiki / Apuntes📖 Contenido

Docker de verdad

Docker empaqueta tu aplicación y todo su entorno en una unidad portable: la misma imagen funciona en tu laptop, en CI y en producción. Esta guía va del concepto de imagen/contenedor hasta compose reales multi-servicio y buenas prácticas que evitan imágenes de 2 GB llenas de basura.

Imágenes vs contenedores

Imagen Contenedor
Qué es Plantilla inmutable (capas de archivos + config) Una instancia en ejecución de una imagen
Analogía Un programa / un ISO / una clase Un proceso / una VM / una instancia
Se crea docker build docker run
Se ve docker images docker ps
Ciclo de vida persistente efímero (puede nacer y morir)

De una imagen salen tantos contenedores como quieras, cada uno con su propio filesystem escribible, PID, red y espacio de nombres aislados.

Anatomía de un Dockerfile

# Base del sistema (elige bases pequeñas y con soporte)
FROM node:22-alpine

# Metadatos
LABEL maintainer="dev@midominio.com"

# Directorio de trabajo (se usa para los comandos siguientes)
WORKDIR /app

# Copiar primero solo package.json para cachear capas
COPY package.json package-lock.json ./

# Instalar dependencias
RUN npm ci --production

# Copiar el resto del código
COPY . .

# Variables de entorno por defecto (sobrescribibles en runtime)
ENV NODE_ENV=production \
    PORT=3000

# Puerto que expone el contenedor (documental)
EXPOSE 3000

# Usuario no-root (¡seguridad!)
USER node

# Comando al arrancar
CMD ["node", "src/server.js"]

Directivas clave:

Directiva Qué hace
FROM Base de la imagen (obligatoria, primera)
RUN Ejecuta un comando en build (crea una capa)
COPY Copia archivos del host al contenedor
WORKDIR Directorio de trabajo de los siguientes comandos
ENV Variables de entorno
EXPOSE Puerto expuesto (documental)
CMD Comando por defecto (se puede sobrescribir)
ENTRYPOINT Comando fijo (difícil de sobrescribir)
USER Usuario con el que corre el proceso

💡 CMD vs ENTRYPOINT: ENTRYPOINT define el proceso principal que no debe cambiar; CMD son los argumentos/el comando por defecto. Juntos: ENTRYPOINT ["node"] CMD ["server.js"] — puedes pasar docker run img otros.js y sustituye solo el CMD.

Multi-stage build: compilar Go en un stage, copiar el binario

El patrón clave para imágenes pequeñas: compilas con todo el toolchain en un stage y solo copias el resultado a una imagen final mínima.

# ---- Stage 1: compilar ----
FROM golang:1.22-alpine AS builder

WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download

COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /app/server .

# ---- Stage 2: imagen final ----
FROM alpine:3.20

RUN apk add --no-cache ca-certificates && \
    adduser -D -H appuser

WORKDIR /app
COPY --from=builder /app/server .

USER appuser
EXPOSE 8080
ENTRYPOINT ["/app/server"]

Resultado: una imagen de unos pocos MB en vez de gigas. No llevas el compilador ni el código fuente a producción.

⚠️ CGO_ENABLED=0 produce un binario Go estático (sin librerías C), que corre en alpine sin problemas. Si tu app usa CGO (por ejemplo net con resolvers nativos), necesitas glibc, no musl.

docker-compose: app + postgres + redis

docker-compose.yml (o compose.yaml) define y arranca varios servicios juntos, con redes y volúmenes:

version: "3.9"

services:
  app:
    build: .
    restart: unless-stopped
    environment:
      DB_HOST: postgres
      REDIS_HOST: redis
      PORT: "8080"
    ports:
      - "8080:8080"        # host:contenedor
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy
    networks:
      - backend

  postgres:
    image: postgres:16-alpine
    restart: unless-stopped
    environment:
      POSTGRES_USER: app
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}   # desde .env
      POSTGRES_DB: miapp
    volumes:
      - pgdata:/var/lib/postgresql/data          # volumen nombrado (persistente)
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U app"]
      interval: 5s
      timeout: 3s
      retries: 5
    networks:
      - backend

  redis:
    image: redis:7-alpine
    restart: unless-stopped
    command: redis-server --appendonly yes
    volumes:
      - redisdata:/data
    networks:
      - backend

networks:
  backend:

volumes:
  pgdata:
  redisdata:

💡 Volúmenes guardan datos fuera del contenedor, que es efímero. Sin volumen, docker compose down && up borraría tu base de datos. Los volúmenes nombrados sobreviven al contenedor y a compose down.

docker compose up -d          # construye y arranca en segundo plano
docker compose down           # detiene y elimina contenedores (no volúmenes)
docker compose down -v        # ...y también los volúmenes (¡borra datos!)
docker compose logs -f app    # logs de un servicio
docker compose exec app sh    # shell dentro del contenedor

Buenas prácticas

Imágenes pequeñas y capas

# 1. Usa bases minimalistas (alpine, distroless)
FROM node:22-alpine

# 2. Ordena COPY: lo que cambia menos, primero (mejor cache)
COPY package.json ./
RUN npm ci
COPY . .

# 3. Ejecuta múltiples comandos en un solo RUN (una capa)
RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates \
    && rm -rf /var/lib/apt/lists/*

💡 Cada RUN/COPY crea una capa; la caché de capas acelera builds, pero también engorda la imagen. Combina lo que va junto y borra archivos temporales dentro del mismo RUN.

.dockerignore

Como .gitignore, excluye lo que no debe ir a la imagen (y a la caché de build):

node_modules
.git
.env
dist
*.log
Dockerfile*
.dockerignore

⚠️ Nunca copies .env a una imagen: quedaría dentro de las capas y cualquiera con acceso a la imagen podría extraerlo. Pasa secretos en runtime (-e, archivos secrets, o un secret manager).

Comandos esenciales

Necesito… Comando
Construir imagen docker build -t miapp .
Ejecutar contenedor docker run -d -p 8080:8080 --name app miapp
Listar contenedores docker ps / docker ps -a
Entrar a un contenedor docker exec -it app sh
Ver logs docker logs -f app
Parar/borrar docker stop app / docker rm app
Ver imágenes docker images
Limpiar cosas sin usar docker system prune -a
Redes docker network ls
Volúmenes docker volume ls

Cheatsheet Dockerfile

Directiva Uso típico
FROM base AS stage Base + nombre de stage
COPY --from=builder /ruta /dest Copia entre stages (multi-stage)
RUN Instalar, compilar, limpiar
CMD / ENTRYPOINT Comando de arranque
USER no-root Seguridad

Para profundizar

Estudio · Recursos de todo el mundo (inglés, chino, japonés, español, francés, ruso…) curados y traducidos al español.