BLURTEK Academy · Guía técnica
Docker desde cero para desarrolladores
Guía práctica de Docker para desarrolladores: diferencia entre imágenes y contenedores, cómo escribir un Dockerfile eficiente, capas, volúmenes, redes, Docker Compose y buenas prácticas con un ejemplo real.
Docker resuelve el clásico "en mi máquina funciona": empaqueta tu aplicación junto con sus dependencias, librerías y configuración en una unidad portable que se ejecuta igual en tu portátil, en CI y en producción. En esta guía repasamos los conceptos que necesitas para usarlo con criterio, no solo para copiar comandos.
Imágenes vs contenedores
Es la distinción fundamental y la fuente de la mitad de las confusiones al empezar:
- Imagen: una plantilla inmutable, de solo lectura. Contiene el sistema de archivos, el runtime y tu código. Es como una clase en programación orientada a objetos.
- Contenedor: una instancia en ejecución de una imagen. Añade una capa de escritura efímera encima. Es como un objeto instanciado a partir de esa clase.
De una misma imagen puedes levantar decenas de contenedores idénticos. Cuando el contenedor se elimina, su capa de escritura desaparece; la imagen permanece intacta.
# Descargar una imagen y ejecutar un contenedor a partir de ella
docker run --rm -it python:3.12-slim python --version
# Ver imágenes y contenedores
docker images
docker ps -a
El Dockerfile y el sistema de capas
Un Dockerfile es la receta para construir una imagen. Cada instruccion (FROM, COPY, RUN...) genera una capa que Docker cachea. Si una capa no cambia, se reutiliza de la caché; si cambia, esa capa y todas las siguientes se reconstruyen.
Esto tiene una consecuencia práctica enorme: ordena las instrucciones de lo que menos cambia a lo que más cambia. Copia primero el archivo de dependencias e instálalas, y solo después copia tu código fuente. Así, cambiar una línea de código no invalida la instalación de dependencias.
# Imagen base
FROM node:20-alpine
WORKDIR /app
# 1. Dependencias primero (cambian poco) -> capa cacheable
COPY package*.json ./
RUN npm ci --omit=dev
# 2. Código después (cambia mucho)
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]
Multi-stage builds
Para imágenes ligeras, usa varias etapas: compila en una y copia solo el resultado a una imagen final mínima. Reduces tamaño y superficie de ataque.
FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM nginx:alpine
COPY --from=build /app/dist /usr/share/nginx/html
Volúmenes: persistir datos
La capa de escritura de un contenedor es efímera. Para conservar datos (una base de datos, archivos subidos) o compartir código en desarrollo, necesitas volúmenes:
- Named volumes: gestionados por Docker, ideales para datos de producción como el directorio de PostgreSQL.
- Bind mounts: montan una carpeta de tu host en el contenedor, perfectos para desarrollo con recarga en caliente.
# Named volume para datos persistentes
docker run -d -v datos_pg:/var/lib/postgresql/data postgres:16
# Bind mount para desarrollo (código del host dentro del contenedor)
docker run -v "$(pwd)":/app node:20-alpine
Redes entre contenedores
Docker crea redes virtuales para que los contenedores se comuniquen. En una red definida por el usuario, cada contenedor es alcanzable por su nombre de servicio como si fuera un hostname, sin necesidad de exponer puertos al host.
docker network create app_net
docker run -d --name db --network app_net postgres:16
# La app se conecta a "db:5432" gracias al DNS interno de la red
Docker Compose: orquestar el conjunto
Levantar cada contenedor a mano no escala. Docker Compose describe toda la aplicación (servicios, redes, volúmenes) en un único fichero compose.yaml y la arranca con un comando.
services:
web:
build: .
ports:
- "3000:3000"
environment:
DATABASE_URL: postgres://app:secret@db:5432/app
depends_on:
- db
db:
image: postgres:16
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: secret
POSTGRES_DB: app
volumes:
- datos_pg:/var/lib/postgresql/data
volumes:
datos_pg:
# Levantar todo en segundo plano
docker compose up -d
# Ver logs y parar
docker compose logs -f
docker compose down
Compose crea automáticamente una red compartida, por eso web puede referirse a la base de datos simplemente como db.
Buenas prácticas
| Práctica | Por qué |
Fija versiones (node:20-alpine, no latest) | Builds reproducibles y sin sorpresas |
Usa un .dockerignore | Evita copiar node_modules, .git y secretos al contexto |
Multi-stage y bases slim/alpine | Imágenes más pequeñas y seguras |
Ejecuta como usuario no root (USER) | Limita el impacto de una vulnerabilidad |
| No incrustes secretos en la imagen | Pásalos por variables de entorno o gestores de secretos |
| Una responsabilidad por contenedor | Más fácil de escalar, actualizar y depurar |
Resumen
Una imagen es la plantilla y el contenedor la instancia en ejecución. El Dockerfile construye imágenes por capas cacheables, así que el orden importa. Los volúmenes persisten datos, las redes conectan servicios por nombre y Docker Compose orquesta el conjunto de forma declarativa. Con estos cimientos y las buenas prácticas de arriba tienes lo esencial para contenerizar cualquier proyecto con criterio.
Checklist para que tu primera imagen sea mantenible
Construye una aplicación mínima y verifica cinco propiedades: una base pequeña y fijada, dependencias reproducibles, proceso sin privilegios de root, secretos fuera de la imagen y un healthcheck que refleje el servicio real. Analiza también qué archivos entran al contexto de build y añade un .dockerignore; copiar repositorios completos puede incluir credenciales, informes o artefactos innecesarios.
Después ejecuta el contenedor con sistema de archivos de solo lectura cuando sea posible, límites de recursos y una red que exponga únicamente los puertos necesarios. Documenta cómo construir, ejecutar, actualizar y volver a la versión anterior. Docker y Contenedores convierte esta checklist en una práctica guiada y una entrega reproducible, en lugar de una captura aislada que nadie pueda verificar.
Entrega sugerida: adjunta el Dockerfile, el comando de ejecuci??n, el resultado del healthcheck y una breve explicaci??n de qu?? dato persistir??a despu??s de recrear el contenedor.
Volver al blog de BLURTEK Academy