Docker y JavaScript: contenerización de aplicaciones en Node.js — guía completa
En el desarrollo web moderno, la contenerización se ha convertido no solo en una tendencia, sino en una necesidad. Docker permite empaquetar una aplicación JavaScript junto con todas sus dependencias, configuraciones y entorno de ejecución en un contenedor aislado que funcionará de manera idéntica en cualquier sistema, desde el portátil del desarrollador hasta el servidor de producción. En este artículo, analizaremos el proceso de contenerización de aplicaciones Node.js desde cero, revisaremos las mejores prácticas y los escenarios de uso típicos.
Fundamentos de Docker para el desarrollador JavaScript
Antes de profundizar en los detalles, comprendamos los conceptos clave de Docker aplicados al ecosistema JavaScript.
- Imagen (Image) — una plantilla inmutable que contiene el código, el entorno de ejecución de Node.js, las dependencias (node_modules) y las configuraciones. Las imágenes se almacenan en un registro (por ejemplo, Docker Hub).
- Contenedor (Container) — una instancia en ejecución de una imagen. Cada contenedor está aislado de los demás y del sistema anfitrión.
- Dockerfile — las instrucciones para construir la imagen. Para aplicaciones Node.js, este es el archivo principal que crearemos.
- docker-compose — una herramienta para definir y ejecutar aplicaciones multicontenedor (por ejemplo, Node.js + MongoDB + Redis).
Creación de un Dockerfile para una aplicación Node.js
Comencemos con una aplicación Express simple. Crearemos un archivo Dockerfile en la raíz del proyecto:
# Imagen base de Node.js 18 (Alpine — versión ligera)FROM node:18-alpine
# Creamos el directorio de trabajo dentro del contenedorWORKDIR /usr/src/app
# Copiamos package.json y package-lock.json (si existe)COPY package*.json ./
# Instalamos las dependencias de producciónRUN npm ci --only=production
# Copiamos el código fuente de la aplicaciónCOPY . .
# Abrimos el puerto que la aplicación va a escucharEXPOSE 3000
# Comando para ejecutar la aplicaciónCMD ["node", "src/index.js"]Matices importantes para proyectos JavaScript:
- El orden de las instrucciones importa. Copiar package.json antes del código permite a Docker almacenar en caché la capa de instalación de dependencias. Si solo cambias el código, las dependencias no se reinstalan, lo que acelera la compilación.
- Usa
npm cien lugar denpm install— es más rápido y garantiza que las versiones de las dependencias coincidan con el archivo de bloqueo. - La versión Alpine de la imagen de Node.js (~120 MB en lugar de ~900 MB) reduce significativamente el tamaño de la imagen final.
Trabajo con docker-compose: vinculando Node.js con la base de datos
Las aplicaciones reales rara vez se limitan a un solo contenedor. docker-compose.yml permite ejecutar Node.js, MongoDB y, por ejemplo, Redis con un solo comando:
version: '3.8'services: app: build: . ports: - "3000:3000" environment: - NODE_ENV=production - MONGODB_URI=mongodb://mongo:27017/myapp - REDIS_URL=redis://redis:6379 depends_on: - mongo - redis volumes: - ./logs:/usr/src/app/logs
mongo: image: mongo:6 volumes: - mongo_data:/data/db ports: - "27017:27017"
redis: image: redis:7-alpine ports: - "6379:6379"
volumes: mongo_data:Cómo funciona:
- El servicio
appse construye a partir de nuestro Dockerfile. - Los servicios
mongoyredisutilizan imágenes predefinidas de Docker Hub. - Las variables de entorno
MONGODB_URIyREDIS_URLse pasan a la aplicación; observa que usamos los nombres de los servicios como nombre de host (el DNS de Docker los resuelve automáticamente en las direcciones IP de los contenedores). - El volumen
mongo_d