Saltar al contenido

Clase 22 - Dockerfile y Docker Compose

#docker #docker-compose #dockerfile

1. 🐳 Dockerfile y Docker Compose: Automatizando la Contenerización

Una vez que comprendemos los fundamentos de Docker y cómo ejecutar contenedores, el siguiente paso es automatizar y orquestar nuestros entornos de desarrollo o producción. Para eso, utilizamos dos herramientas fundamentales:

  • Dockerfile: define cómo construir una imagen.
  • Docker Compose: define y ejecuta múltiples contenedores como un solo servicio.

2. 📦 Dockerfile: Definiendo tu imagen

Un Dockerfile es un archivo de texto que contiene instrucciones que Docker utiliza para construir una imagen personalizada.

🧱 Estructura básica de un Dockerfile

Dockerfile
# Imagen base
FROM node:18
# Establece el directorio de trabajo dentro del contenedor
WORKDIR /app
# Copia los archivos del proyecto al contenedor
COPY package*.json ./
# Instala dependencias
RUN npm install
# Copia el resto del código
COPY . .
# Establece variables de entorno
ENV PORT=3000
# Expone el puerto
EXPOSE 3000
# Comando para ejecutar la aplicación
CMD ["node", "index.js"]

📝 Explicación línea por línea

InstrucciónDescripción
FROMDefine la imagen base desde la que se construirá. Aquí, una imagen oficial de Node.js.
WORKDIRDefine el directorio de trabajo dentro del contenedor. Todas las rutas relativas se basarán en este path.
COPY package*.json ./Copia los archivos de dependencias para permitir un cacheo eficiente de la instalación.
RUN npm installEjecuta comandos en el contenedor. Aquí, instala las dependencias.
COPY . .Copia el resto de los archivos del proyecto al contenedor.
ENVEstablece variables de entorno dentro del contenedor.
LABELAgrega metadatos útiles como el mantenedor del proyecto.
USERCambia el usuario de ejecución dentro del contenedor para mejorar la seguridad.
EXPOSEIndica el puerto que la aplicación escucha. (No publica puertos por sí mismo).
CMDEspecifica el comando por defecto al iniciar el contenedor.

✅ Tips y consideraciones

  • Usa .dockerignore para evitar copiar archivos innecesarios.
  • No instales dependencias globales a menos que sea absolutamente necesario.
  • Considera usar multi-stage builds para reducir el tamaño de la imagen final.
  • Usa ENV para definir valores reutilizables.
  • Usa USER node para ejecutar la app con privilegios mínimos.

3. ⚙️ Docker Compose: Orquestación simple

Cuando tu aplicación necesita más de un contenedor (ejemplo: backend + base de datos), Docker Compose permite definirlos todos en un único archivo docker-compose.yml.

📄 Ejemplo de archivo docker-compose.yml

docker-compose.yml
services:
server:
build: .
ports:
- "3000:3000"
volumes:
- .:/app
- /app/node_modules
environment:
- NODE_ENV=development
depends_on:
- mongo
mongo:
image: mongo:8.0.10
restart: always
ports:
- "27017:27017"
volumes:
- mongo-data:/data/db
environment:
MONGO_INITDB_ROOT_USERNAME: root
MONGO_INITDB_ROOT_PASSWORD: example
mongo-express:
image: mongo-express:1.20-alpine3.19
restart: always
ports:
- "8081:8081"
environment:
ME_CONFIG_MONGODB_ADMINUSERNAME: root
ME_CONFIG_MONGODB_ADMINPASSWORD: example
ME_CONFIG_MONGODB_URL: mongodb://root:example@mongo:27017/
ME_CONFIG_BASICAUTH: false
depends_on: - mongo
volumes:
mongo-data:

🔍 ¿Qué hace cada parte?

SecciónDescripción
servicesDefine los contenedores que usará la app.
serverServicio del backend en Node.js.
mongoServicio de base de datos MongoDB.
mongo-expressVisualizador web para explorar y editar la base de datos. Corre en http://localhost:8081.
buildUsa el Dockerfile del proyecto para construir la imagen.
portsMapea puertos del host al contenedor (host:contenedor).
volumesMonta el código fuente y define volúmenes persistentes.
environmentDefine variables de entorno del contenedor.
depends_onControla el orden de inicio de los servicios.

✅ Tips y consideraciones

  • Persistencia de código: volumes: - .:/app asegura que los cambios se reflejen en tiempo real.
  • Evitar sobrescribir node_modules: montá - /app/node_modules como volumen anónimo.
  • Persistencia de la base de datos: se logra con el volumen mongo-data, que guarda los datos fuera del contenedor.
  • Exploración visual de datos: agregá mongo-express para acceder vía navegador.
  • Separá la configuración de entorno por archivo .env si necesitás más flexibilidad.

4. 🧪 Ejemplo integrador: API Node.js + MongoDB + Mongo Express

Supongamos que tienes una API básica en Node.js que se conecta a una base de datos MongoDB y quieres explorar tus datos visualmente con Mongo Express.

🧱 Estructura del proyecto

  • Directoriomy-app/
    • index.js
  • Dockerfile
  • docker-compose.yml
  • package.json
  • .dockerignore

📝 Archivos del proyecto

index.js
import express from 'express';
import mongoose from 'mongoose';
const PORT = process.env.PORT || 3000;
mongoose.connect('mongodb://mongo:27017/mydb')
.then(() => console.log('✅ MongoDB conectado'))
.catch(err => console.error('❌ Error de conexión', err));
const app = express();
app.get('/', (req, res) => res.send('🚀 ¡Hola desde Docker con ES6!'));
app.listen(PORT, () => console.log(`Servidor corriendo en puerto ${PORT}`));
.dockerignore
node_modules
.env

🚀 Pasos para ejecutar el proyecto

  1. Crear la estructura del proyecto

    Asegúrate de tener todos los archivos en su lugar según la estructura mostrada arriba.

  2. Construir y ejecutar los servicios

    Ventana de terminal
    docker-compose up --build
  3. Verificar que todo funciona

    • API disponible en: http://localhost:3000
    • Mongo Express disponible en: http://localhost:8081
    • MongoDB corriendo en el puerto 27017
  4. Detener los servicios

    Ventana de terminal
    docker-compose down

🤔 ¿Qué hace docker-compose up --build?

Este comando:

  • Construirá la imagen usando el Dockerfile
  • Levantará el servicio de Node.js, MongoDB y el visualizador Mongo Express
  • Expondrá el puerto 3000 para la API y el 8081 para Mongo Express
  • Montará tu código fuente y node_modules correctamente con volúmenes
  • Guardará los datos de Mongo de forma persistente con un volumen

5. 🧠 Conclusión

  • Dockerfile define cómo construir una imagen.
  • docker-compose.yml define cómo ejecutar múltiples servicios juntos.
  • Combinados, permiten tener entornos reproducibles, escalables y fáciles de compartir.