Skip to content

Contenedores Docker

Prefacio

"Funciona en mi máquina" es la excusa más clásica de los desarrolladores, y Docker hace que esta excusa desaparezca por completo. La tecnología de contenedores empaqueta una aplicación y todas sus dependencias en una unidad estandarizada, garantizando que se ejecute de manera consistente en cualquier entorno. Es la piedra angular de la entrega de software moderno.

¿Qué aprenderás en este artículo?

Después de completar este capítulo, obtendrás:

  • Conceptos centrales: comprender los tres conceptos fundamentales: imagen, contenedor y registro
  • Comparación de arquitecturas: entender la diferencia esencial entre contenedores y máquinas virtuales
  • Habilidades prácticas: dominar la escritura de Dockerfiles y los comandos más utilizados
  • Fundamentos de orquestación: aprender a gestionar aplicaciones multisericio con Docker Compose
  • Mejores prácticas: conocer optimización de imágenes, endurecimiento de seguridad y otras prácticas de nivel productivo
CapítuloContenidoConcepto clave
Capítulo 1Por qué se necesitan contenedoresConsistencia del entorno, eficiencia de recursos, entrega estandarizada
Capítulo 2Conceptos centralesImagen, contenedor, registro, Dockerfile
Capítulo 3Ciclo de vida de DockerEscribir, construir, pushear, ejecutar, gestionar
Capítulo 4Docker ComposeOrquestación multisericio, redes, volúmenes de datos
Capítulo 5Mejores prácticasOptimización de imágenes, seguridad, construcción en múltiples etapas

1. Por qué se necesitan contenedores

Antes de la aparición de los contenedores, desplegar una aplicación requería instalar manualmente el entorno de ejecución, configurar variables de entorno y resolver conflictos de dependencias en el servidor. Las diferencias entre entornos (desarrollo, pruebas, producción) eran un caldo de cultivo para bugs.

Virtual Machines vs Containers
Switch between both virtualization models to compare their architecture
App A / App B / App C
App A + Bins/Libs
App B + Bins/Libs
App C + Bins/Libs
Docker Engine
Host OS
Physical hardware
Startup speedSeconds
Resource usageShared host OS kernel (MB scale)
IsolationProcess-level isolation
DensityHundreds of containers per host
Image sizeMB scale

¿Qué problemas resuelven los contenedores?

ProblemaEnfoque tradicionalEnfoque con contenedores
Inconsistencia del entorno"Funciona en mi máquina"Empaquetar todas las dependencias, consistente en todas partes
Conflictos de dependenciasApp A necesita Node 14, App B necesita Node 18Cada contenedor tiene su propio entorno aislado
Desperdicio de recursosCada VM necesita un SO completoComparte el kernel, consumo a nivel de MB
Despliegue lentoInstalación y configuración manualUn solo comando docker run
Difícil de escalarCrear nueva VM, instalar entorno, desplegarIniciar nuevos contenedores en segundos

La esencia de los contenedores

Un contenedor no es una máquina virtual ligera. Su esencia es un proceso aislado. El kernel de Linux implementa los contenedores mediante dos mecanismos:

  • Namespace: aísla la visibilidad de los procesos (PID, red, sistema de archivos, etc.)
  • Cgroups: limita el uso de recursos de los procesos (CPU, memoria, IO)

Los procesos dentro de un contenedor no son fundamentalmente diferentes de los procesos ordinarios del host, simplemente están "encerrados en una habitación que no puede ver el exterior".


2. Conceptos centrales

El mundo de Docker gira en torno a tres conceptos fundamentales: Imagen (Image), Contenedor (Container) y Registro (Registry).

ConceptoAnalogíaDescripción
Imagen (Image)Clase / PlantillaPlantilla de solo lectura que contiene código, entorno de ejecución, librerías y configuración
Contenedor (Container)Instancia / ObjetoInstancia en ejecución de una imagen, con lectura/escritura y ciclo de vida independiente
Registro (Registry)Tienda de aplicacionesServicio para almacenar y distribuir imágenes (Docker Hub, ACR, ECR)
DockerfileReceta / PlanoArchivo de texto que define cómo construir una imagen
Volumen (Volume)Disco duro externoPersistencia de datos, los datos no se pierden al eliminar el contenedor

Estructura en capas de las imágenes

Las imágenes de Docker están compuestas por múltiples capas de solo lectura (Layer), cada instrucción del Dockerfile crea una capa:

┌─────────────────────────┐
│  CMD ["node", "app.js"] │  ← Capa de comando de inicio
├─────────────────────────┤
│  COPY . /app            │  ← Capa de código de la aplicación (cambia frecuentemente)
├─────────────────────────┤
│  RUN npm install        │  ← Capa de instalación de dependencias (cambia ocasionalmente)
├─────────────────────────┤
│  FROM node:18-alpine    │  ← Capa de imagen base (cambia raramente)
└─────────────────────────┘

¿Por qué es importante la estructura en capas?

Docker almacena en caché cada capa. Si una capa no ha cambiado, se reutiliza la caché durante la construcción. Por lo tanto, en el Dockerfile debes colocar las instrucciones que cambian con menor frecuencia al principio (como instalar dependencias) y las que cambian con mayor frecuencia al final (como copiar código). De esta manera, la mayoría de las construcciones aprovecharán la caché y serán mucho más rápidas.


3. Ciclo de vida de Docker

Desde la escritura del Dockerfile hasta la ejecución del contenedor, el flujo de trabajo de Docker es una línea de producción clara y definida.

Docker Lifecycle
Click each stage to inspect the details
📝
Write a Dockerfile
🔨
Build the image
☁️
Push to registry
▶️
Run the container
⚙️
Manage containers
Write a Dockerfile
A Dockerfile is the recipe for building an image. It starts from a base image and defines how to assemble the application environment step by step. Each instruction creates an image layer that Docker can cache for later builds.
Common commands
FROM node:18-alpineChoose the base image
WORKDIR /appSet the working directory
COPY package*.json ./Copy dependency files for cache reuse
RUN npm installInstall dependencies
COPY . .Copy application code
EXPOSE 3000Declare the port
CMD ["node", "server.js"]Start command

Referencia rápida de instrucciones del Dockerfile

InstrucciónFunciónEjemplo
FROMEspecifica la imagen baseFROM node:18-alpine
WORKDIREstablece el directorio de trabajoWORKDIR /app
COPYCopia archivos a la imagenCOPY package.json ./
RUNEjecuta comandos durante la construcciónRUN npm install
ENVEstablece variables de entornoENV NODE_ENV=production
EXPOSEDeclara un puerto (solo documentación)EXPOSE 3000
CMDComando de inicio del contenedorCMD ["node", "app.js"]
ENTRYPOINTPunto de entrada del contenedor (difícil de sobrescribir)ENTRYPOINT ["nginx"]

4. Docker Compose: orquestación multisericio

Los proyectos reales generalmente necesitan más de un contenedor. Una aplicación web puede necesitar: servidor de aplicaciones + base de datos + Redis + Nginx. Docker Compose define y gestiona múltiples contenedores con un solo archivo YAML.

Ejemplo de docker-compose.yml

yaml
version: '3.8'
services:
  app:
    build: .
    ports:
      - "3000:3000"
    environment:
      - DB_HOST=db
      - REDIS_HOST=redis
    depends_on:
      - db
      - redis

  db:
    image: postgres:15-alpine
    volumes:
      - db-data:/var/lib/postgresql/data
    environment:
      - POSTGRES_PASSWORD=secret

  redis:
    image: redis:7-alpine

volumes:
  db-data:

Conceptos centrales de Compose

ConceptoDescripciónEjemplo
servicesDefine cada servicio de contenedorapp, db, redis
volumesVolúmenes de datos persistentesdb-data guarda los archivos de la base de datos
networksRed personalizada (se crea automáticamente por defecto)Los servicios se acceden mutuamente por nombre de servicio
depends_onDependencia de orden de inicioapp depende de db y redis
environmentVariables de entornoContraseña de base de datos, dirección de conexión

Descubrimiento de servicios

En Docker Compose, el nombre del servicio es el nombre del host. El contenedor app puede acceder directamente a la base de datos usando db:5432 y a Redis usando redis:6379, sin necesidad de conocer la dirección IP. Esto es gracias al DNS integrado de Docker.


5. Mejores prácticas

5.1 Construcción en múltiples etapas (Multi-stage Build)

La construcción en múltiples etapas es una herramienta poderosa para optimizar el tamaño de las imágenes. La etapa de construcción instala todas las herramientas y dependencias, mientras que la etapa final solo conserva los archivos necesarios en tiempo de ejecución.

dockerfile
# Etapa de construcción
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# Etapa de ejecución
FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
EXPOSE 3000
CMD ["node", "dist/server.js"]

5.2 Lista de optimización de imágenes

Elemento de optimizaciónQué hacerEfecto
Elegir imagen base pequeñaUsar alpine en lugar de ubuntuImagen de ~200MB a ~50MB
Combinar instrucciones RUNConectar múltiples comandos con &&Reducir capas de la imagen
Usar .dockerignoreExcluir node_modules, .git, etc.Acelerar la construcción, reducir contexto
Construcción en múltiples etapasSeparar entornos de construcción y ejecuciónLa imagen final no contiene herramientas de construcción
Fijar número de versiónnode:18.17-alpine en lugar de node:latestConstrucciones reproducibles

5.3 Prácticas de seguridad

PrácticaDescripción
No ejecutar como rootUSER node especifica un usuario no root
Escanear vulnerabilidadesdocker scout o Trivy para escanear imágenes
Privilegios mínimosInstalar solo los paquetes necesarios, sin herramientas de depuración
No codificar secretosUsar variables de entorno o Docker Secrets
Actualizar la imagen base regularmenteCorregir vulnerabilidades de seguridad oportunamente

Resumen

La contenerización con Docker es la infraestructura de entrega de software moderno; comprenderla es crucial para cualquier desarrollador.

Repaso de los puntos clave de este capítulo:

  1. Contenedores vs máquinas virtuales: los contenedores comparten el kernel del host, son más ligeros y rápidos, pero el aislamiento es ligeramente inferior al de las VM
  2. El trío central: imagen (plantilla), contenedor (instancia), registro (distribución)
  3. Dockerfile: construcción en capas, aprovechar la caché, instrucciones que cambian poco al principio
  4. Docker Compose: definir aplicaciones multisericio con YAML, el nombre del servicio es el nombre del host
  5. Prácticas de producción: construcción en múltiples etapas para reducir tamaño, imagen base alpine, ejecutar como no root

Lecturas complementarias