INFRA

Docker: De Desarrollo a Contenedores en Producción

Guía enfocada en producción para Docker — desde Dockerfiles optimizados y multi-stage builds hasta workflows con Docker Compose, escaneo de seguridad de imágenes, gestión de registries y Kaniko para builds rootless en CI/CD. Cubre volúmenes, healthchecks, BuildKit, modos de networking y .dockerignore. Basada en containerizar 26 microservicios.

Por Jose Nobile | Actualizado 2026-06-11 | 22 min de lectura

Buenas Prácticas de Dockerfile

Un Dockerfile es una receta para construir una imagen de contenedor. Cada instrucción crea una capa, y las capas se cachean. El orden de las instrucciones impacta directamente la velocidad de build y el tamaño de imagen. Coloca instrucciones que cambian poco (instalar paquetes del sistema) antes de instrucciones que cambian seguido (copiar código de la aplicación). Esto maximiza los cache hits y reduce los rebuilds de minutos a segundos.

Usa tags específicos de imagen base, nunca latest. Fija a un digest o tag de versión como node:20.11-alpine3.19 para builds reproducibles. Combina instrucciones RUN con && para minimizar capas y limpiar archivos temporales en la misma capa. Cada capa persiste en la imagen final, así que descargar, compilar y limpiar en instrucciones RUN separadas infla la imagen aunque los archivos se borren después.

Siempre incluye un archivo .dockerignore para excluir node_modules/, .git/, *.md, archivos de test y otros artefactos que no deberían estar en el contexto de build. Un contexto de build inflado ralentiza docker build porque todo el contexto se envía al daemon antes de que se ejecute cualquier instrucción. En producción, cada microservicio tiene un .dockerignore curado que reduce el contexto de build de ~500MB a ~20MB.

# Bad: Changes to code invalidate npm install cache
COPY . .
RUN npm install

# Good: Only re-install when dependencies change
COPY package.json package-lock.json ./
RUN npm ci --production
COPY . .

.dockerignore

El archivo .dockerignore funciona como .gitignore pero para el contexto de build de Docker. Sin él, docker build envía cada archivo del directorio de build al daemon Docker, incluyendo .git/ (frecuentemente cientos de megabytes), node_modules/, fixtures de test, documentación y configuración de IDE. Esto desperdicia tiempo, ancho de banda y puede filtrar secretos a la imagen.

Coloca.dockerignore en la raíz de tu contexto de build (junto al Dockerfile). Lista directorios y patrones de archivos que no tienen por qué estar dentro del contenedor: historial de control de versiones, dependencias de desarrollo, suites de test, configuración de CI, archivos de entorno local y configuración del editor. Un .dockerignore bien mantenido puede reducir el contexto de build de cientos de megabytes a menos de 20MB.

Sé explícito en vez de permisivo. Algunos equipos usan un enfoque de allowlist: ignoran todo con * y luego selectivamente des-ignoran lo que el build realmente necesita con !. Esto previene la inclusión accidental de archivos nuevos agregados al repositorio. En producción, cada microservicio usa el patrón allowlist, asegurando que solo src/, package.json, package-lock.json y tsconfig.json entren al contexto de build.

# .dockerignore - Standard approach
.git
.gitignore
node_modules
npm-debug.log*
dist
coverage
*.md
.env*
.vscode
.idea
Dockerfile*
docker-compose*
__tests__
*.test.ts
*.spec.ts
.github
.gitlab-ci.yml

# .dockerignore - Allowlist approach (stricter)
*
!src/
!package.json
!package-lock.json
!tsconfig.json

Multi-Stage Builds

Los multi-stage builds son la técnica de mayor impacto para reducir el tamaño de imágenes Docker. Permiten usar una imagen para compilar (con compiladores, dependencias dev, herramientas de build) y una imagen separada y mínima para el runtime final. Solo los artefactos que explícitamente haces COPY --from=build terminan en la imagen de producción. En producción, los multi-stage builds redujeron las imágenes de servicios Node.js de 1.2GB (con dependencias dev) a 180MB (Alpine + deps de producción + código compilado).

Para servicios TypeScript, el patrón es: Etapa 1 compila TypeScript a JavaScript con todas las dependencias dev, Etapa 2 copia solo el JavaScript compilado y los node_modules de producción en una imagen runtime basada en Alpine. Para servicios Go, es aún más dramático: la imagen final puede ser scratch (vacía) ya que Go compila a un binario estático sin dependencias de runtime. Para la menor superficie de ataque, usa imágenes distroless de Google (gcr.io/distroless/nodejs20-debian12) que incluyen el runtime pero sin shell, package manager ni utilidades del SO.

Nombra tus etapas con AS para claridad: FROM node:20-alpine AS build. También puedes apuntar a etapas específicas durante desarrollo con docker build --target=build para obtener una imagen con herramientas dev y test runners. Esto hace que el mismo Dockerfile sirva tanto para desarrollo como para producción.

# Stage 1: Build
FROM node:20-alpine AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY tsconfig.json ./
COPY src/ ./src/
RUN npm run build

# Stage 2: Production (Alpine)
FROM node:20-alpine AS production
WORKDIR /app
RUN addgroup -g 1001 app && adduser -u 1001 -G app -D app
COPY package.json package-lock.json ./
RUN npm ci --production && npm cache clean --force
COPY --from=build /app/dist ./dist
USER app
EXPOSE 3000
CMD ["node", "dist/index.js"]

# Alternative Stage 2: Distroless (no shell, minimal attack surface)
# FROM gcr.io/distroless/nodejs20-debian12
# WORKDIR /app
# COPY --from=build /app/dist ./dist
# COPY --from=build /app/node_modules ./node_modules
# USER 1000
# CMD ["dist/index.js"]

BuildKit

BuildKit es el motor de build de próxima generación de Docker, habilitado por defecto desde Docker 23.0. Reemplaza al builder legacy con ejecución paralela de builds, mejor caching y nuevas features del Dockerfile. Si estás en una versión anterior de Docker, habilítalo con DOCKER_BUILDKIT=1 docker build o configura { "features": { "buildkit": true } } en la configuración del daemon Docker.

La feature más impactante de BuildKit es la ejecución paralela de etapas. En un Dockerfile multi-stage, las etapas independientes se buildean concurrentemente en vez de secuencialmente, reduciendo significativamente el tiempo total de build. También provee soporte de cache mount vía RUN --mount=type=cache,target=/root/.npm npm ci, que persiste cachés del package manager entre builds sin inflar la capa de imagen. Esto elimina la necesidad de npm cache clean --force y acelera la instalación de dependencias.

BuildKit introduce secret mounts (RUN --mount=type=secret,id=npmrc cat /run/secrets/npmrc) que inyectan secretos en tiempo de build sin escribirlos en ninguna capa de imagen. Esto es crítico para registries npm privados o API keys necesarias durante el build. Otras features incluyen --mount=type=ssh para forwarding del agente SSH (repos Git privados), exportación de cache de build inline (--cache-to y --cache-from), y sintaxis heredoc para instrucciones RUN multilínea.

# Enable BuildKit (Docker < 23.0)
export DOCKER_BUILDKIT=1

# Cache mount: persist npm cache across builds
# syntax=docker/dockerfile:1
FROM node:20-alpine AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN --mount=type=cache,target=/root/.npm npm ci
COPY . .
RUN npm run build

# Secret mount: private registry auth without leaking to layers
RUN --mount=type=secret,id=npmrc,target=/root/.npmrc npm ci

# SSH mount: clone private repos at build time
RUN --mount=type=ssh git clone [email protected]:org/private-lib.git

# Inline cache for CI/CD
docker build --cache-from type=registry,ref=registry/app:cache \
  --cache-to type=registry,ref=registry/app:cache \
  -t registry/app:latest .

Docker Compose para Desarrollo Local

Docker Compose define aplicaciones multi-contenedor en un solo archivo YAML. Es la herramienta estándar para ambientes de desarrollo local que replican las dependencias de servicios de producción. Un solo docker compose up levanta tu aplicación junto con MySQL, Redis, Elasticsearch o cualquier otro servicio que tu app necesite, con networking ya configurado entre ellos.

Usa volumes para montar tu código fuente en el contenedor para hot-reloading durante desarrollo. Combina con depends_on y health checks para asegurar que los servicios arranquen en el orden correcto. La feature de profiles te permite definir servicios opcionales (como herramientas de debug o bases de datos de test) que solo corren cuando se activan explícitamente con --profile debug.

Mantén archivos Compose separados para diferentes contextos: compose.yaml para servicios core, compose.override.yaml para overrides de desarrollo local (montajes de volúmenes, puertos de debug), y compose.test.yaml para ambientes de test en CI. Compose automáticamente mergea compose.yaml con compose.override.yaml, así que los developers obtienen overrides locales sin modificar el archivo base. Nota: la clave version está deprecada en Compose V2 y puede omitirse.

# compose.yaml (Compose V2 - no version key needed)
services:
  api:
    build:
      context: .
      target: build
    volumes:
      - ./src:/app/src
    ports:
      - "3000:3000"
    depends_on:
      mysql:
        condition: service_healthy
    environment:
      - DB_HOST=mysql
      - REDIS_HOST=redis
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 10s
      timeout: 5s
      retries: 3
      start_period: 15s
  mysql:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: dev
      MYSQL_DATABASE: myapp
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 5s
      retries: 10
    volumes:
      - mysql_data:/var/lib/mysql
  redis:
    image: redis:7-alpine
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      retries: 5
volumes:
  mysql_data:

Volúmenes y Bind Mounts

Los contenedores Docker son efímeros: cuando se elimina un contenedor, todos los datos dentro de él se pierden. Los volúmenes resuelven esto persistiendo datos fuera de la capa escribible del contenedor. Docker gestiona volúmenes nombrados en /var/lib/docker/volumes/, y sobreviven a la recreación del contenedor, actualizaciones y reinicios. Usa volúmenes nombrados para bases de datos, uploads de archivos y cualquier dato con estado que deba persistir.

Los bind mounts mapean un directorio del host directamente al contenedor. Son esenciales para desarrollo local: monta tu directorio src/ para que los cambios de código aparezcan instantáneamente dentro del contenedor sin reconstruir. Sin embargo, los bind mounts tienen implicaciones de rendimiento en macOS y Windows debido al overhead de traducción del filesystem. Usa los flags de consistencia :cached o :delegated de Docker, o el backend VirtioFS más nuevo, para mejorar el rendimiento en hosts que no sean Linux.

En producción, prefiere volúmenes nombrados sobre bind mounts por portabilidad y gestión. Usa montajes tmpfs para datos sensibles que nunca deberían escribirse en disco (tokens de sesión, secretos temporales). Para bases de datos en contenedores (solo desarrollo), siempre usa un volumen nombrado para evitar perder datos con docker compose down. El comando docker volume prune limpia volúmenes huérfanos, pero úsalo con cuidado para evitar borrar volúmenes de datos.

# Named volume (Docker-managed, persists data)
docker run -v mysql_data:/var/lib/mysql mysql:8.0

# Bind mount (host directory into container)
docker run -v $(pwd)/src:/app/src node:20-alpine

# tmpfs mount (in-memory, never written to disk)
docker run --tmpfs /tmp:rw,noexec,size=100m myapp

# Compose volumes with driver options
volumes:
  mysql_data:
    driver: local
  uploads:
    driver: local
    driver_opts:
      type: none
      o: bind
      device: /data/uploads

Healthchecks

Los healthchecks le dicen a Docker si un contenedor está funcionando correctamente, no solo corriendo. Un contenedor puede tener su proceso vivo pero estar en deadlock, sin memoria o incapaz de servir requests. La instrucción HEALTHCHECK en un Dockerfile o la clave healthcheck en Compose define un comando que Docker ejecuta periódicamente. Si el chequeo falla consecutivamente, Docker marca el contenedor como unhealthy.

Define healthchecks que prueben la funcionalidad real de tu servicio, no solo la existencia del proceso. Para servicios HTTP, golpea un endpoint /health o /readyz. Para bases de datos, usa el comando ping nativo (mysqladmin ping, redis-cli ping, pg_isready). El parámetro start_period le da tiempo al contenedor para inicializarse antes de que los healthchecks empiecen a fallar, lo que previene falsos negativos durante el arranque.

Los healthchecks habilitan el depends_on: condition: service_healthy de Compose, que asegura que los servicios dependientes solo arranquen después de que sus dependencias estén genuinamente listas, no solo corriendo. Sin healthchecks, tu contenedor API podría arrancar antes de que MySQL esté listo para aceptar conexiones, causando crashes al inicio. En producción, cada servicio de Compose tiene un healthcheck, y el servicio API espera a que MySQL, Redis y Elasticsearch reporten healthy antes de arrancar.

# Dockerfile HEALTHCHECK
FROM node:20-alpine
WORKDIR /app
COPY . .
RUN npm ci --production
HEALTHCHECK --interval=15s --timeout=5s --start-period=10s --retries=3 \
  CMD ["node", "-e", "require('http').get('http://localhost:3000/health', (r) => { process.exit(r.statusCode === 200 ? 0 : 1) })"]
CMD ["node", "dist/index.js"]

# Compose healthchecks
services:
  api:
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 10s
      timeout: 5s
      retries: 3
      start_period: 15s
  postgres:
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      retries: 10

Optimización de Imágenes

El tamaño de imagen impacta directamente los tiempos de pull, costos de almacenamiento y superficie de ataque. Cada megabyte cuenta cuando estás bajando imágenes de 20+ servicios en cada deployment. Empieza con imágenes basadas en Alpine (node:20-alpine son 50MB vs. node:20 con 350MB). Si tu aplicación tiene dependencias glibc que rompen en musl de Alpine, usa variantes Debian slim (node:20-slim con 80MB).

El caché de capas es tu mejor aliado. Docker cachea cada capa y la reutiliza si la instrucción y todas las capas padre no cambiaron. Estructura tu Dockerfile para que el contenido que cambia más seguido (código fuente) venga al final. Usa npm ci en vez de npm install para instalaciones determinísticas y cache-friendly. Copia solo package.json y package-lock.json antes de correr npm ci, después copia el resto del código fuente.

Para las imágenes más pequeñas posibles, considera las imágenes base distroless de Google (gcr.io/distroless/nodejs20-debian12, gcr.io/distroless/static-debian12). Las imágenes distroless contienen solo el runtime del lenguaje y tu aplicación — sin shell, sin package manager, sin utilidades del SO. Esto elimina clases enteras de vulnerabilidades y reduce las imágenes a su tamaño mínimo absoluto. Usa docker image history y dive (un TUI para explorar capas de imagen) para identificar bloat. En producción, auditorías regulares de imágenes con dive redujeron la imagen promedio de microservicio de 350MB a 140MB, ahorrando ~4.2GB de almacenamiento en registry por deployment en 20 servicios.

TIP

Imágenes Base Alpine

Las imágenes Alpine Linux son 5-10x más chicas que las alternativas basadas en Debian. Usa node:20-alpine, python:3.12-alpine. Cuidado con problemas de compatibilidad musl con módulos nativos.

TIP

Imágenes Distroless

Las imágenes distroless de Google contienen solo el runtime. Sin shell, sin package manager. Usa gcr.io/distroless/nodejs20-debian12 para Node.js o gcr.io/distroless/static-debian12 para binarios Go. La menor superficie de ataque posible.

TIP

Orden de Cache de Capas

Paquetes del sistema primero, luego archivos de dependencias, luego npm ci, luego código fuente. Cambiar un archivo fuente solo reconstruye desde la instrucción COPY en adelante, manteniendo todas las capas de dependencias cacheadas.

TIP

Dive para Análisis

Ejecuta dive tu-imagen:tag para explorar interactivamente cada capa, ver espacio desperdiciado e identificar archivos que no deberían estar en la imagen. Apunta a un score de eficiencia de imagen superior al 90%.

Escaneo de Seguridad

Las imágenes de contenedor heredan vulnerabilidades de los paquetes OS base, runtime del lenguaje y dependencias de la aplicación. Un solo CVE sin parchear en una imagen base afecta cada servicio construido sobre ella. Escanea imágenes en tiempo de build (en CI/CD), al pushear (del lado del registry), y continuamente (scans programados de imágenes en ejecución). Herramientas como Trivy, Grype y Snyk Container detectan CVEs conocidos en paquetes OS, librerías del lenguaje e incluso misconfiguraciones del Dockerfile.

Integra el escaneo en tu pipeline de CI como una puerta: falla el build si se encuentran vulnerabilidades críticas o de alta severidad. Trivy corre en menos de 30 segundos y genera formato SARIF para dashboards de seguridad de GitLab/GitHub. Snyk provee análisis más profundo del árbol de dependencias y sugerencias de corrección. En producción, cada pipeline de GitLab CI ejecuta trivy image --severity HIGH,CRITICAL --exit-code 1 antes de pushear a GCR. Esto atrapó 3 CVEs críticos en la imagen base de Node.js en el primer mes.

Más allá del escaneo, sigue la higiene de seguridad de contenedores: corre como usuario no-root (USER app), usa filesystems root de solo lectura donde sea posible, descarta todas las capabilities de Linux y agrega solo las necesarias, y nunca embebas secrets en la imagen. Usa secret mounts de BuildKit para credenciales en tiempo de build. Usa imágenes distroless o scratch para la menor superficie de ataque posible — sin shell significa sin exploits basados en shell.

# Trivy: scan image for vulnerabilities
trivy image --severity HIGH,CRITICAL gcr.io/myproject/api:v2.4.1

# Trivy: fail CI on findings
trivy image --severity HIGH,CRITICAL --exit-code 1 $IMAGE

# Trivy: scan Dockerfile for misconfigurations
trivy config --severity HIGH,CRITICAL ./Dockerfile

# Trivy: generate SARIF report for GitLab/GitHub
trivy image --format sarif --output trivy-results.sarif $IMAGE

# Snyk: scan image with fix suggestions
snyk container test $IMAGE --severity-threshold=high

# Snyk: monitor for new vulnerabilities
snyk container monitor $IMAGE

Gestión de Registries

Un registry de contenedores almacena y distribuye tus imágenes Docker. Google Container Registry (GCR) y su sucesor Artifact Registry, Amazon ECR, y GitHub Container Registry (ghcr.io) son las principales opciones cloud. Elige el registry en el mismo proveedor cloud que tu cluster para minimizar latencia de pull y costos de egress. Los nodos GKE bajan de GCR/Artifact Registry a velocidad de red sin cargos de egress.

Etiqueta imágenes con versiones semánticas y SHA de Git para trazabilidad: gcr.io/myproject/api:v2.4.1 para releases y gcr.io/myproject/api:abc1234 para cada build. Nunca uses :latest en manifiestos Kubernetes de producción — anula el propósito de deployments inmutables y hace imposible los rollbacks. Implementa una política de retención para borrar automáticamente imágenes más viejas de N días o mantener solo los últimos N tags para controlar costos de almacenamiento.

Para imágenes multi-arquitectura (amd64 + arm64), usadocker buildx para buildear y pushear listas de manifiestos. Esto es cada vez más importante a medida que los equipos adoptan nodos basados en ARM (AWS Graviton, GKE Arm) para ahorrar costos. En producción, las imágenes se pushean a Google Artifact Registry con tags de versión y SHA de Git, y una política de lifecycle retiene solo las últimas 30 versiones por servicio.

REGISTRY

Google Artifact Registry

Sucesor de GCR. Repositorios regionales con IAM granular, escaneo de vulnerabilidades y políticas de limpieza. Egress gratis a GKE en la misma región. Usado en producción para las 20+ imágenes de servicio.

REGISTRY

Amazon ECR

Integrado con ECS/EKS. Políticas de lifecycle para limpieza automática de imágenes. Replicación cross-region para deployments multi-región. Pull gratis desde la misma región.

REGISTRY

GitHub Container Registry

ghcr.io se integra con GitHub Actions. Imágenes públicas son gratis. Bueno para proyectos open-source y equipos chicos usando GitHub para CI/CD.

Kaniko para Builds en CI/CD

Kaniko construye imágenes Docker dentro de un contenedor sin requerir un daemon Docker o modo privilegiado. Esto resuelve el problema de seguridad "Docker-in-Docker" en ambientes CI/CD como GitLab CI, donde correr un daemon Docker privilegiado dentro de un job CI es un riesgo de seguridad. Kaniko ejecuta instrucciones Dockerfile en userspace, construyendo la imagen y pusheando directamente a un registry.

La configuración es mínima: monta tu contexto de build, pasa la ruta del Dockerfile y especifica el registry destino. Kaniko soporta cache de capas vía un repositorio de cache remoto, lo cual acelera drásticamente los builds reutilizando capas sin cambios de builds anteriores. En producción, Kaniko con cache remoto redujo los tiempos de build promedio de 4 minutos a 90 segundos en todos los microservicios.

Kaniko soporta todas las instrucciones estándar de Dockerfile, multi-stage builds y build arguments. Los flags --cache=true y --cache-repo habilitan el cache remoto. Usa --snapshot-mode=redo para snapshotting más rápido de capas en imágenes grandes. El flag --use-new-run mejora el rendimiento de instrucciones RUN en 20-30%.

En producción, cada pipeline de GitLab CI usa Kaniko para builds de imágenes. Sin contenedores privilegiados, sin daemon Docker, sin Docker-in-Docker. El pipeline se autentica a GCR vía una service account key montada como secret de Kubernetes. Build, push y scan suceden en menos de 2 minutos por servicio.

# GitLab CI job using Kaniko
build:
  stage: build
  image:
    name: gcr.io/kaniko-project/executor:v1.22.0-debug
    entrypoint: [""]
  script:
    - /kaniko/executor
      --context $CI_PROJECT_DIR
      --dockerfile $CI_PROJECT_DIR/Dockerfile
      --destination $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
      --destination $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG
      --cache=true
      --cache-repo=$CI_REGISTRY_IMAGE/cache
      --snapshot-mode=redo

Networking de Contenedores

Docker provee varios drivers de red, cada uno adecuado para distintos casos de uso. La red bridge por defecto aísla contenedores del host y entre sí a menos que los puertos se publiquen explícitamente. Las redes bridge definidas por el usuario (creadas con docker network create) agregan resolución DNS automática entre contenedores por nombre, razón por la cual Docker Compose crea una por proyecto. Todos los servicios de Compose comparten una red bridge custom automáticamente y pueden alcanzarse entre sí por nombre de servicio.

El modo de red host elimina el aislamiento de red por completo: el contenedor comparte el stack de red del host directamente. Esto elimina el overhead de traducción de direcciones de red y es útil para escenarios de alto rendimiento donde el contenedor necesita máximo throughput de red. Usa --network host o network_mode: host en Compose. La contrapartida es que no hay aislamiento de puertos — los puertos del contenedor se bindean directamente al host.

El driver de red overlay habilita networking multi-host para Docker Swarm y es la base para la comunicación de contenedores entre múltiples máquinas. Las redes overlay encriptan el tráfico entre nodos por defecto y soportan service discovery. En ambientes Kubernetes, el networking overlay lo maneja el plugin CNI (Calico, Cilium, o VPC nativa de GKE) en vez del driver overlay de Docker, pero el concepto es el mismo: contenedores en diferentes hosts se comunican sobre una red virtual.

Para seguridad en desarrollo local, publica puertos con 127.0.0.1:3000:3000 para bindear solo a localhost en vez de todas las interfaces. Para debuggear problemas de networking: docker network inspect bridge muestra IPs y conexiones de contenedores, docker exec container curl other-service:3000 testea conectividad entre contenedores, y docker logs revela errores de conexión.

NETWORK

Bridge (Default)

Red aislada con publicación de puertos. Las bridges definidas por el usuario agregan resolución DNS. Default para Compose. Mejor para la mayoría del desarrollo local y producción en un solo host.

NETWORK

Host

El contenedor comparte el stack de red del host. Sin overhead de NAT. Máximo throughput. Sin aislamiento de puertos. Úsalo para servicios críticos en rendimiento o cuando el contenedor necesita acceso a la red del host.

NETWORK

Overlay

Networking multi-host para Docker Swarm. Tráfico inter-nodo encriptado. Service discovery entre hosts. En Kubernetes, los plugins CNI (Calico, Cilium) cumplen el mismo propósito.

Caso Real: Containerización en Producción

Cada microservicio está containerizado con Docker, buildeado con Kaniko en GitLab CI, y desplegado a GKE vía charts de Helm. La estrategia de containerización evolucionó de Dockerfiles single-stage ingenuos a multi-stage builds optimizados, reduciendo el tamaño de imágenes un 85% y los tiempos de build un 60%.

20+ Servicios Containerizados

Microservicios Node.js/TypeScript, cada uno con Dockerfile multi-stage, base Alpine y usuario no-root. Tamaño promedio de imagen: 140MB. Todos buildeados con Kaniko en GitLab CI, pusheados a Google Artifact Registry.

Kaniko + GitLab CI

Cero daemons Docker en CI. Builds Kaniko con cache de capas remoto redujeron tiempos de build de 4 minutos a 90 segundos. Escaneo de seguridad Trivy bloquea cada imagen antes de llegar al registry.

Docker Compose para Dev

El desarrollo local refleja la topología de producción: servicios API, MySQL, Redis y Elasticsearch definidos en Compose con healthchecks. Developers nuevos pasan de clone a ambiente corriendo en menos de 5 minutos.

Últimas Características de Docker (2025-2026)

Docker Scout: Docker Scout es ahora la herramienta principal de escaneo de seguridad en el ecosistema Docker. Provee detección de CVEs en tiempo real durante builds, evaluación de políticas contra estándares de seguridad organizacionales, y soporte de documentos VEX (Vulnerability Exploitability eXchange) para gestionar excepciones. Scout expone un endpoint Prometheus para métricas de vulnerabilidades, habilitando integración con stacks de monitoreo existentes. A diferencia de scanners externos que corren post-build, Scout se integra directamente en el proceso de build, atrapando vulnerabilidades antes de que las imágenes se pusheen a registries.

Docker Compose v5 ("Mont Blanc") (Dic 2025): Compose v5 elimina el builder interno por completo, delegando todos los builds a Docker Bake. Esto produce builds más rápidos con mejor caching, soporte multi-plataforma nativo y comportamiento de build consistente entre docker compose build e invocaciones standalone de Bake. Compose v5 también incluye un nuevo SDK oficial en Go, habilitando control programático de stacks Compose desde tooling custom y sistemas CI/CD.

Docker Build Cloud: Builds basados en la nube con caches de build compartidos entre equipos. En vez de que cada developer y runner de CI mantenga su propio cache local, Build Cloud provee un cache centralizado que todos los miembros del equipo y pipelines comparten. Esto reduce drásticamente los tiempos de build para equipos distribuidos y elimina las penalidades de cache frío cuando los runners de CI son efímeros.

Docker Engine v29 (Ene 2026): El image store de containerd es ahora el default para instalaciones nuevas, reemplazando el storage driver legacy. Esto alinea a Docker con el ecosistema de contenedores más amplio (Kubernetes ya usa containerd). Engine v29 también agrega soporte de backend nftables para networking, modernizando la dependencia de iptables de Docker. La versión mínima de API es ahora 1.44, requiriendo Docker v25 o posterior como cliente -- clientes más viejos necesitarán actualizarse.

Docker Model Runner y MCP (Desktop 4.40+, Abril 2026): Docker Model Runner, basado en llama.cpp, permite ejecutar LLMs localmente vía una API compatible con OpenAI sin configuración adicional. Aceleración GPU soportada en macOS (Metal), Windows (NVIDIA) y Windows (Qualcomm). Model Runner ahora funciona con Docker Compose y Testcontainers (Java y Go), integrando servicios AI en stacks de microservicios. El Docker AI Agent adopta completamente el Model Context Protocol (MCP), exponiendo capacidades como servidores MCP para Claude Desktop, Cursor o cualquier cliente compatible con MCP. El MCP Toolkit integrado (Desktop 4.42+) descubre y gestiona 100+ servidores MCP sin extensiones. A partir de Docker Desktop 4.69.0 (15 de abril de 2026), Docker publica releases semanales con iteración rápida de herramientas AI -- tarjetas de template MCP, soporte de modelos expandido e inspección de requests de inferencia para debugging de workflows AI.

SECURITY

Docker Scout

Detección de CVEs en tiempo real durante builds. Evaluación de políticas, excepciones VEX y endpoint de métricas Prometheus. La herramienta principal de escaneo de seguridad de Docker.

v5

Compose v5 "Mont Blanc"

Eliminó builder interno, delega a Bake. Builds más rápidos, mejor caching, soporte multi-plataforma. Nuevo SDK oficial en Go para control programático.

CLOUD

Docker Build Cloud

Builds basados en la nube con caches compartidos entre equipos. Elimina penalidades de cache frío en runners CI efímeros. Builds más rápidos para equipos distribuidos.

ENGINE

Docker Engine v29

Estable actual (2026). Image store containerd como default. Soporte backend nftables. IPv6 nativo (dual-stack, solo IPv4 o solo IPv6). Versión mínima de API 1.44 (Docker v25+ requerido). Stack de networking modernizado.

v5.1.4

Compose v5.1.4

Última versión de Compose (20 de mayo de 2026). El comando docker-compose (con guión) está completamente deprecado -- usadocker compose (separado con espacio). El campo version en archivos compose ya no es requerido y se ignora.

4.40+

Model Runner y MCP Toolkit

Ejecuta LLMs localmente vía API compatible con OpenAI (llama.cpp). GPU en macOS, Windows NVIDIA y Qualcomm. Integración con Compose y Testcontainers. MCP Toolkit ahora con tarjetas de template de perfil y tour de onboarding. Model Runner agrega soporte Qwen3.5. Desktop 4.74 (19 de mayo de 2026) hace generalmente disponibles la vista Logs y el asistente AI Gordon. Engine 29.5.3 (3 de junio de 2026) corrige las vulnerabilidades de docker cp CVE-2026-41567 y CVE-2026-41568 y agrega el placeholder .HealthStatus a docker ps --format.

Más Guías