Docker para Desarrolladores: Imágenes, Contenedores y Compose
Has oído hablar de Docker y los contenedores, pero cuando intentas usarlo, te abruman términos como imágenes, contenedores, volúmenes y Compose. O quizás ya usas Docker pero tus imágenes son enormes, las compilaciones son lentas y gestionar múltiples servicios se siente como una tarea pesada. Esta guía elimina el ruido. Cubriremos lo esencial de Docker para desarrolladores, centrándonos en lo que realmente necesitas saber para construir, ejecutar y gestionar aplicaciones en contenedores de forma eficiente.
¿Por qué Docker? El problema que resuelve
Antes de Docker, desplegar una aplicación significaba igualar el entorno exacto: versiones específicas del SO, bibliotecas y configuraciones. "Funciona en mi máquina" se convirtió en un cliché porque era cierto. Docker lo resuelve empaquetando tu aplicación con sus dependencias en una sola unidad portátil llamada contenedor. Los contenedores son ligeros, aislados y se ejecutan de forma consistente dondequiera que Docker esté instalado.
Imágenes vs. Contenedores: La distinción clave
Una imagen es una plantilla de solo lectura que contiene el código de tu aplicación, el entorno de ejecución, las bibliotecas y la configuración. Un contenedor es una instancia en ejecución de una imagen. Piensa en una imagen como una clase y en un contenedor como un objeto. Puedes ejecutar múltiples contenedores desde la misma imagen, cada uno aislado de los demás.
Cómo se construyen las imágenes
Las imágenes se construyen a partir de un Dockerfile, un archivo de texto con instrucciones. Aquí tienes un ejemplo mínimo para una app Node.js:
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]
Cada instrucción crea una capa. Las capas se almacenan en caché, así que si solo cambias tu código fuente, Docker reutiliza las capas en caché para las dependencias, haciendo que las reconstrucciones sean rápidas.
Construir y ejecutar
Construye la imagen y ejecuta un contenedor:
docker build -t my-app .
docker run -p 3000:3000 my-app
La bandera -p mapea el puerto 3000 de tu host al puerto 3000 del contenedor.
Buenas prácticas para imágenes Docker eficientes
Las imágenes grandes ralentizan las compilaciones, los pushes y los despliegues. Sigue estas prácticas para mantener las imágenes ligeras:
- Usa imágenes base oficiales slim o alpine cuando sea posible. Por ejemplo,
node:18-alpinees mucho más pequeña quenode:18. - Aprovecha las compilaciones multi-etapa para separar las dependencias de tiempo de compilación del tiempo de ejecución. Esto mantiene la imagen final mínima.
- Combina comandos RUN para reducir capas y limpiar las cachés del gestor de paquetes en la misma capa.
- Usa un archivo .dockerignore para excluir archivos innecesarios (por ejemplo, node_modules, .git) del contexto de compilación.
Aquí tienes un Dockerfile multi-etapa para una aplicación Go:
# Build stage
FROM golang:1.21-alpine AS builder
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o /app/main .
# Final stage
FROM alpine:latest
COPY --from=builder /app/main /main
CMD ["/main"]
La imagen final contiene solo el binario compilado y Alpine, no el toolchain de Go.
Gestionar datos con volúmenes
Los contenedores son efímeros; cuando se eliminan, sus datos se pierden. Los volúmenes persisten los datos fuera del ciclo de vida del contenedor. Úsalos para bases de datos, subidas o cualquier estado que deba sobrevivir a los reinicios.
docker run -v my-data:/var/lib/postgresql/data postgres
Esto crea un volumen con nombre my-data que Docker gestiona. Para desarrollo, también puedes montar tu código fuente en el contenedor para recarga en vivo:
docker run -v $(pwd):/app -p 3000:3000 my-app
Docker Compose: Orquestar múltiples contenedores
Las aplicaciones reales a menudo necesitan múltiples servicios: un servidor web, una base de datos, una caché. Iniciar cada uno con docker run es tedioso y propenso a errores. Docker Compose te permite definir y ejecutar aplicaciones multi-contenedor con un solo archivo YAML.
Un docker-compose.yml práctico
Aquí tienes un archivo Compose para una app Node.js con PostgreSQL y Redis:
version: '3.8'
services:
web:
build: .
ports:
- "3000:3000"
environment:
- DATABASE_URL=postgres://user:pass@db:5432/mydb
- REDIS_URL=redis://cache:6379
depends_on:
- db
- cache
db:
image: postgres:15-alpine
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
POSTGRES_DB: mydb
volumes:
- postgres_data:/var/lib/postgresql/data
cache:
image: redis:7-alpine
volumes:
postgres_data:
Ejecuta docker compose up para iniciar todos los servicios. Compose crea una red predeterminada para que los contenedores puedan comunicarse entre sí por nombre de servicio (por ejemplo, db, cache).
Comandos comunes de Compose
docker compose up -d– iniciar en modo detached (segundo plano).docker compose logs -f web– seguir los logs de un servicio.docker compose exec web sh– abrir una shell en un contenedor en ejecución.docker compose down– detener y eliminar contenedores, redes y volúmenes (a menos que los volúmenes sean externos).
Cuándo usar Docker Compose vs. Kubernetes
Docker Compose es ideal para desarrollo local, pruebas y despliegues pequeños. Kubernetes destaca en la orquestación de contenedores a través de clústeres para producción a escala. Para muchos desarrolladores, Compose es suficiente para el trabajo diario, y puedes pasar a Kubernetes cuando necesites auto-escalado, auto-reparación y redes avanzadas.
| Característica | Docker Compose | Kubernetes |
|---|---|---|
| Caso de uso | Desarrollo local, despliegues pequeños | Clústeres de producción |
| Complejidad | Baja | Alta |
| Escalado | Manual | Automático |
| Curva de aprendizaje | Suave | Pronunciada |
Preguntas frecuentes
¿Cuál es la diferencia entre una imagen y un contenedor?
Una imagen es una plantilla de solo lectura con tu app y dependencias. Un contenedor es una instancia en ejecución de una imagen. Puedes ejecutar muchos contenedores desde una imagen.
¿Cómo reduzco el tamaño de una imagen Docker?
Usa imágenes base más pequeñas (como Alpine), compilaciones multi-etapa, combina comandos RUN y usa un archivo .dockerignore para excluir archivos innecesarios.
¿Puedo usar Docker Compose en producción?
Sí, para despliegues a pequeña escala. Para entornos más grandes y dinámicos, Kubernetes o Docker Swarm son más adecuados.
¿Listo para optimizar tu flujo de trabajo con Docker? Echa un vistazo a nuestro Analizador de Logs de Nginx para depurar y monitorizar tus servidores web en contenedores.