Ir al contenido

Introducción a Docker Compose

Docker Compose es una herramienta que permite definir y ejecutar aplicaciones con múltiples contenedores usando un archivo de texto llamado docker-compose.yml. En lugar de escribir varios comandos docker run con todas sus opciones, describes toda tu infraestructura en un solo archivo y la levantas con un comando.

Sin Compose, para levantar el stack Node-RED + Mosquitto tendrías que ejecutar:

Ventana de terminal
# Sin Compose — tedioso y propenso a errores
docker network create iot-net
docker volume create mosquitto-data
docker volume create nodered-data
docker run -d --name mosquitto --network iot-net -p 1883:1883 \
-v ./mosquitto/config:/mosquitto/config \
-v mosquitto-data:/mosquitto/data eclipse-mosquitto:2.0
docker run -d --name node-red --network iot-net -p 1880:1880 \
--depends-on mosquitto -v nodered-data:/data nodered/node-red:latest

Con Compose, todo eso se describe una vez en un archivo y se ejecuta con:

Ventana de terminal
docker compose up -d

# Versión del formato de Compose (opcional desde Compose v2)
version: '3.8'
# ── Redes compartidas entre servicios ─────────────────────────────────────
networks:
iot-net: # nombre de la red (tú lo defines)
driver: bridge # tipo de red (bridge es el estándar)
# ── Volúmenes persistentes ─────────────────────────────────────────────────
volumes:
mosquitto-data: # volumen para datos de Mosquitto
nodered-data: # volumen para flujos de Node-RED
# ── Servicios (= contenedores) ─────────────────────────────────────────────
services:
mosquitto: # nombre del servicio (también es el hostname en la red)
image: eclipse-mosquitto:2.0 # imagen de Docker Hub
container_name: mosquitto # nombre del contenedor en el sistema
restart: unless-stopped # reiniciar automáticamente si falla
networks:
- iot-net # conectar a la red compartida
ports:
- "1883:1883" # host:contenedor
volumes:
- ./mosquitto/config:/mosquitto/config:ro # carpeta local → contenedor (solo lectura)
- mosquitto-data:/mosquitto/data # volumen Docker → contenedor
healthcheck: # prueba de salud
test: ["CMD", "mosquitto_sub", "-t", "$$SYS/#", "-C", "1", "-W", "3"]
interval: 15s
timeout: 5s
retries: 3
node-red:
image: nodered/node-red:latest
container_name: node-red
restart: unless-stopped
networks:
- iot-net
ports:
- "1880:1880"
depends_on:
mosquitto:
condition: service_healthy # espera que Mosquitto esté sano antes de iniciar
volumes:
- nodered-data:/data

Cada entrada bajo services define un contenedor. El nombre del servicio (ej: mosquitto) se convierte automáticamente en el hostname que otros contenedores en la misma red pueden usar para comunicarse.

services:
mosquitto: ← otros contenedores pueden usar "mosquitto" como hostname
node-red: ← puede conectarse a Mosquitto usando host: "mosquitto"

Especifica la imagen de Docker a usar. Puede incluir una etiqueta de versión:

image: eclipse-mosquitto:2.0 # versión específica (recomendado)
image: nodered/node-red:latest # siempre la última versión
image: influxdb:2.7 # versión exacta

Mapea puertos del host a puertos del contenedor con el formato "host:contenedor":

ports:
- "1883:1883" # el puerto 1883 del host → puerto 1883 del contenedor
- "3000:3000"
- "8086:8086"

Los servicios que no necesitan ser accesibles desde fuera (solo entre contenedores) no necesitan exponer puertos.

Dos tipos de montajes:

volumes:
# Carpeta local → contenedor (bind mount)
- ./mosquitto/config:/mosquitto/config:ro # :ro = solo lectura
# Volumen Docker → contenedor (volumen gestionado)
- mosquitto-data:/mosquitto/data

Los bind mounts (carpeta local) son útiles para archivos de configuración que quieres editar fácilmente. Los volúmenes Docker son mejores para datos que deben persistir (bases de datos, estados de aplicación).

Define variables de entorno dentro del contenedor:

environment:
POSTGRES_DB: flowfuse
POSTGRES_USER: admin
POSTGRES_PASSWORD: secreto
# También puedes usar formato de lista:
environment:
- POSTGRES_DB=flowfuse
- POSTGRES_USER=admin

Controla el orden de inicio de los servicios:

depends_on:
# Forma simple — solo espera que el contenedor esté "running"
- mosquitto
# Forma con condición — espera hasta que el healthcheck pase
mosquitto:
condition: service_healthy

Define una prueba para verificar si el servicio está listo:

healthcheck:
test: ["CMD", "influx", "ping"] # comando a ejecutar dentro del contenedor
interval: 10s # cada cuánto ejecutar la prueba
timeout: 5s # tiempo máximo de espera
retries: 5 # intentos antes de marcar como unhealthy

Política de reinicio automático:

ValorComportamiento
noNunca reinicia (por defecto)
alwaysSiempre reinicia, incluso al reiniciar Docker
unless-stoppedReinicia siempre excepto si lo detuviste manualmente
on-failureSolo reinicia si el proceso salió con error

Para servicios IoT en producción, usa unless-stopped.


Todos los comandos se ejecutan desde la carpeta que contiene el docker-compose.yml:

Ventana de terminal
# Iniciar todos los servicios en segundo plano
docker compose up -d
# Iniciar y ver los logs en tiempo real
docker compose up
# Detener todos los servicios (los datos en volúmenes se conservan)
docker compose down
# Detener y eliminar todos los volúmenes (¡borra todos los datos!)
docker compose down -v
# Ver el estado de los servicios
docker compose ps
# Ver logs de todos los servicios
docker compose logs
# Ver logs de un servicio específico en tiempo real
docker compose logs -f node-red
# Reiniciar un servicio específico
docker compose restart mosquitto
# Ejecutar un comando dentro de un servicio en ejecución
docker compose exec mosquitto sh
# Ver el uso de recursos de los contenedores
docker compose top
# Detener servicios sin eliminar los contenedores
docker compose stop
# Iniciar servicios detenidos (sin recrearlos)
docker compose start
# Recrear contenedores (aplica cambios del docker-compose.yml)
docker compose up -d --force-recreate

Para no exponer contraseñas en el docker-compose.yml, usa un archivo .env en la misma carpeta:

.env
INFLUX_PASSWORD=mi_password_secreto
INFLUX_TOKEN=mi_token_secreto
GRAFANA_PASSWORD=grafana_pass

Y en docker-compose.yml refiérelas con ${VARIABLE}:

environment:
DOCKER_INFLUXDB_INIT_PASSWORD: ${INFLUX_PASSWORD}
DOCKER_INFLUXDB_INIT_ADMIN_TOKEN: ${INFLUX_TOKEN}

Para los stacks de este curso:

mi-proyecto-iot/
├── docker-compose.yml ← definición de servicios
├── .env ← contraseñas y tokens (NO subir a git)
├── .gitignore
├── mosquitto/
│ └── config/
│ └── mosquitto.conf ← configuración de Mosquitto
└── telegraf/
└── telegraf.conf ← configuración de Telegraf (stack TIG)

Con Docker y Docker Compose dominados, estás listo para los tutoriales del curso: