Docker para Desarrolladores: De Cero a Experto
Desde 2013, Docker redefinió el ciclo de vida del software al empaquetar aplicaciones con sus dependencias en contenedores, acelerando la entrega y reduciendo la fricción entre entornos. En esta guía ampliada pasamos de lo esencial a prácticas de nivel experto que verás en equipos de plataforma y DevOps maduros.
1) Fundamentos de Contenedores: qué son (realmente)
Un contenedor no es una VM. Comparte kernel con el host y aísla procesos mediante namespaces (pid, mnt, net, ipc, uts, user) y controla recursos con cgroups. El sistema de ficheros se apoya en OverlayFS (capas de solo lectura + capa superior de escritura por contenedor).
- Arranque: milisegundos/segundos al no bootear un SO completo.
- Portabilidad: la misma imagen corre en local, CI y prod.
- Inmutabilidad: capas reconstruibles, rollbacks predecibles.
2) Imágenes y BuildKit: velocidad, caché y trazabilidad
Activa BuildKit para builds paralelos, mejores diagnósticos, secretos en tiempo de build y cache mounts.
export DOCKER_BUILDKIT=1
docker build -t miapp:dev .
2.1 Multi-stage builds (patrones)
# Node.js (producción mínima + cache de dependencias)
# Etapa de dependencias
FROM node:20-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN --mount=type=cache,target=/root/.npm npm ci
# Etapa de build
FROM node:20-alpine AS build
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build
# Etapa final (runtime mínimo, sin toolchain)
FROM node:20-alpine AS runtime
ENV NODE_ENV=production
WORKDIR /app
COPY --from=build /app/dist ./dist
COPY package*.json ./
RUN --mount=type=cache,target=/root/.npm npm ci --omit=dev
USER node
CMD ["node","dist/main.js"]
# Python (slim + caches + usuario no root)
FROM python:3.12-slim AS base
ENV PYTHONDONTWRITEBYTECODE=1 PYTHONUNBUFFERED=1
WORKDIR /app
RUN apt-get update && apt-get install -y --no-install-recommends build-essential && rm -rf /var/lib/apt/lists/*
COPY pyproject.toml poetry.lock* ./
RUN --mount=type=cache,target=/root/.cache/pip pip install --upgrade pip && pip install poetry && poetry config virtualenvs.create false && poetry install --no-root --no-dev
COPY . .
RUN useradd -m app && chown -R app:app /app
USER app
CMD ["python","-m","app.main"]
# Go (binario estático + distroless)
FROM golang:1.22 as build
WORKDIR /src
COPY go.mod go.sum ./
RUN --mount=type=cache,target=/go/pkg/mod go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o /out/app ./cmd/app
FROM gcr.io/distroless/static-debian12
USER 65532:65532
COPY --from=build /out/app /app
ENTRYPOINT ["/app"]
2.2 Caché avanzada y reproducibilidad
- Ordena COPY: copia primero manifiestos (package.json/go.mod) para maximizar caché.
- --mount=type=cache: acelera
npm ci
,pip
,go build
. - Etiquetas OCI: añade metadatos útiles (VCS, commit, autor, licencias).
LABEL org.opencontainers.image.source="https://github.com/empresa/miapp"
org.opencontainers.image.revision="$COMMIT_SHA"
org.opencontainers.image.licenses="MIT"
3) Seguridad de imágenes y runtime
- Usuario no root: define
USER
y permisos mínimos. - Capacidades: elimina privilegios innecesarios.
- Seccomp/AppArmor: perfiles restrictivos por defecto.
- Secreto en build: nunca
ARG
con credenciales; usa--secret
.
# Ejemplo: drop de capabilities en Compose (runtime)
# docker-compose.yml
services:
api:
image: empresa/api:prod
cap_drop: ["ALL"]
security_opt:
- no-new-privileges:true
read_only: true
tmpfs:
- /tmp:size=64m
4) Docker Compose v2: orquestación local y de equipo
Compose describe entornos multi-servicio (app + DB + cache) con perfiles, healthchecks y dependencias robustas.
version: "3.9"
services:
app:
build:
context: .
dockerfile: Dockerfile
env_file: .env
ports:
- "3000:3000"
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
healthcheck:
test: ["CMD","curl","-f","http://localhost:3000/health"]
interval: 10s
timeout: 3s
retries: 5
deploy:
resources:
limits:
cpus: "1.00"
memory: 512M
reservations:
memory: 256M
volumes:
- type: bind
source: ./src
target: /app/src
consistency: delegated
profiles: ["dev"]
db:
image: postgres:16-alpine
environment:
POSTGRES_PASSWORD: example
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL","pg_isready -U postgres"]
interval: 10s
retries: 10
redis:
image: redis:7-alpine
volumes:
pgdata: {}
4.1 Perfiles y overrides
Activa servicios opcionales con --profile
y emplea docker-compose.override.yml
para dev.
5) Redes, DNS y rendimiento
- Red bridge por defecto: cada proyecto Compose crea una red aislada con DNS interno.
- Exposición mínima: usa
ports
solo cuando sea necesario; para comunicación interna, usa el nombre del servicio. - macOS/Windows: usa volúmenes o soluciones como mutagen para mejorar IO frente a bind mounts.
6) Patrones de Dockerfile por ecosistema
6.1 PHP-FPM + Nginx (Laravel, Symfony)
# PHP-FPM
FROM php:8.3-fpm-alpine AS php
RUN docker-php-ext-install pdo_mysql bcmath opcache
WORKDIR /var/www/html
COPY . .
RUN adduser -D app && chown -R app:app /var/www/html
USER app
# Nginx
FROM nginx:alpine
COPY ./infra/nginx.conf /etc/nginx/conf.d/default.conf
COPY --from=php /var/www/html /var/www/html
# infra/nginx.conf (fragmento)
server {
listen 80;
root /var/www/html/public;
location / {
try_files $uri /index.php$is_args$args;
}
location ~ \.php$ {
fastcgi_pass php:9000;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
}
6.2 Node (SSR/Next/Nest) con imágenes mínimas
- Evita
npm install
en producción; usanpm ci --omit=dev
. - Congela versiones (lockfile), evita
latest
. - Usa
USER node
yread_only
en runtime.
7) Supply Chain: SBOM, firmas y políticas
Genera SBOM (inventario de dependencias), firma imágenes y aplica políticas de admisión:
- SBOM: genera y archiva junto al artefacto.
- Firmas/Attestations: registra quién construyó qué y cómo.
- Escaneo: integra análisis de vulnerabilidades en CI.
8) CI/CD: buildx, multi-arch y caché remota
# Ejemplo GitHub Actions (fragmento)
- name: Set up Buildx
uses: docker/setup-buildx-action@v3
- name: Login registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GH_TOKEN }}
- name: Build & Push multi-arch
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ghcr.io/empresa/miapp:{{github.sha}},ghcr.io/empresa/miapp:latest
platforms: linux/amd64,linux/arm64
cache-from: type=registry,ref=ghcr.io/empresa/miapp:buildcache
cache-to: type=registry,ref=ghcr.io/empresa/miapp:buildcache,mode=max
9) Observabilidad y operación
- Logs: rota
json-file
o usa drivers centralizados (ELK/Datadog/Fluentd). - Healthchecks: esenciales para restart policies y dependencias.
- Métricas y trazas: expón prometheus endpoints o integra OpenTelemetry.
10) Seguridad en runtime
cap_drop: ["ALL"]
y añade sólo lo que requiera tu app (p. ej.,NET_BIND_SERVICE
).read_only: true
+tmpfs
para directorios temporales.no-new-privileges
,userns-remap
yseccomp
por defecto.- Separa redes por dominio (frontend/backoffice/infra) y restringe tráfico este-oeste.
11) Datos y volúmenes
- Bind vs named: en desarrollo usa bind para iteración; en prod, volúmenes nombrados.
- Backups: estrategias simples con contenedores efímeros.
# Backup de volumen (ejemplo Postgres)
docker run --rm --volumes-from contenedor_db -v $(pwd):/backup alpine
sh -c "tar czf /backup/pgdata-$(date +%F).tar.gz /var/lib/postgresql/data"
12) Rendimiento: tips prácticos
- Ordena pasos del Dockerfile para maximizar caché.
- Minimiza capas combinando
RUN
donde tenga sentido. - Alpine vs slim: alpine es ligera pero puede penalizar builds nativos; evalúa slim.
- macOS: prefiere volúmenes a bind para IO intenso.
13) Troubleshooting avanzado
- DNS/resolución: usa el nombre del servicio Compose; evita
localhost
entre contenedores. - Permisos: al usar
USER
, ajusta propiedad de ficheros (chown) y puertos <1024 requierenNET_BIND_SERVICE
. - “Text file busy”: reconstruye capas que mantienen binarios abiertos; evita editar dentro del contenedor en caliente.
- Capas obsoletas: limpia cachés si cambiaste FROM/ARG clave (
docker builder prune
con cuidado). - Inodos OverlayFS: vigila muchas capas/archivos pequeños; simplifica la imagen o usa stages.
14) Checklist de producción (resumen)
- Imagen mínima (slim/distroless), usuario no root,
cap_drop
,seccomp
,read_only
. - Healthcheck, restart policy, límites de CPU/Mem y ulimits.
- SBOM + firma + escaneo en CI; políticas de admisión en el registry.
- Logs con rotación y métricas/trazas expuestas.
- Backups de volúmenes y procedimiento de restore probado.
- Buildx multi-arch con caché remota; etiquetas de versión inmutables.
Conclusión
El salto de “funciona en mi máquina” a “opera confiable en producción” exige disciplina: imágenes optimizadas y seguras, builds reproducibles, Compose bien diseñado, observabilidad y políticas de supply chain. Dominar estos pilares convierte Docker en un acelerador estratégico, no solo en una herramienta de empaquetado.