Clase 22 - Dockerfile y Docker Compose
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
# Imagen baseFROM 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ón | Descripción |
|---|---|
FROM | Define la imagen base desde la que se construirá. Aquí, una imagen oficial de Node.js. |
WORKDIR | Define 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 install | Ejecuta comandos en el contenedor. Aquí, instala las dependencias. |
COPY . . | Copia el resto de los archivos del proyecto al contenedor. |
ENV | Establece variables de entorno dentro del contenedor. |
LABEL | Agrega metadatos útiles como el mantenedor del proyecto. |
USER | Cambia el usuario de ejecución dentro del contenedor para mejorar la seguridad. |
EXPOSE | Indica el puerto que la aplicación escucha. (No publica puertos por sí mismo). |
CMD | Especifica el comando por defecto al iniciar el contenedor. |
✅ Tips y consideraciones
- Usa
.dockerignorepara evitar copiar archivos innecesarios. - No instales dependencias globales a menos que sea absolutamente necesario.
- Considera usar
multi-stage buildspara reducir el tamaño de la imagen final. - Usa
ENVpara definir valores reutilizables. - Usa
USER nodepara 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
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ón | Descripción |
|---|---|
services | Define los contenedores que usará la app. |
server | Servicio del backend en Node.js. |
mongo | Servicio de base de datos MongoDB. |
mongo-express | Visualizador web para explorar y editar la base de datos. Corre en http://localhost:8081. |
build | Usa el Dockerfile del proyecto para construir la imagen. |
ports | Mapea puertos del host al contenedor (host:contenedor). |
volumes | Monta el código fuente y define volúmenes persistentes. |
environment | Define variables de entorno del contenedor. |
depends_on | Controla el orden de inicio de los servicios. |
✅ Tips y consideraciones
- Persistencia de código:
volumes: - .:/appasegura que los cambios se reflejen en tiempo real. - Evitar sobrescribir node_modules: montá
- /app/node_modulescomo 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-expresspara acceder vía navegador. - Separá la configuración de entorno por archivo
.envsi 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
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}`));node_modules.env🚀 Pasos para ejecutar el proyecto
-
Crear la estructura del proyecto
Asegúrate de tener todos los archivos en su lugar según la estructura mostrada arriba.
-
Construir y ejecutar los servicios
Ventana de terminal docker-compose up --build -
Verificar que todo funciona
- API disponible en:
http://localhost:3000 - Mongo Express disponible en:
http://localhost:8081 - MongoDB corriendo en el puerto
27017
- API disponible en:
-
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_modulescorrectamente con volúmenes - Guardará los datos de Mongo de forma persistente con un volumen
5. 🧠 Conclusión
Dockerfiledefine cómo construir una imagen.docker-compose.ymldefine cómo ejecutar múltiples servicios juntos.- Combinados, permiten tener entornos reproducibles, escalables y fáciles de compartir.