← Volver al índice | Arquitectura del Sistema | ENS y Soberanía
Configuración Docker Compose — Entorno de Desarrollo¶
Tipo: Documentación de Infraestructura
Audiencia: Equipo de desarrollo, DevOps, administradores de sistemas
Fecha: 20 de marzo de 2026
Relacionado con: Arquitectura del Sistema
1. Visión General del Entorno¶
El MVP se despliega como un conjunto de 7 servicios Docker orquestados mediante Docker Compose. Todos los servicios corren en una única máquina, comunicándose a través de una red interna Docker.
flowchart TB
subgraph NET ["Red interna: ieo-network"]
subgraph FE ["Frontend"]
WEB["react-spa :5173"]
end
subgraph BE ["Backend"]
API["quarkus-api :8080"]
DAPR_SC["dapr-sidecar :3500"]
end
subgraph AI ["Motor IA"]
OLL["ollama :11434"]
end
subgraph DATA ["Datos"]
PG["postgresql :5432"]
CHR["chromadb :8000"]
RDS["redis :6379"]
end
subgraph STORE ["Almacenamiento"]
MIN["minio :9000 / :9001"]
end
end
WEB --> API
API --> OLL
API --> PG
API --> CHR
API --> RDS
API --> MIN
OLL --> CHR
style API fill:#e74c3c,color:#fff
style OLL fill:#9b59b6,color:#fff
style PG fill:#3498db,color:#fff
2. Servicios¶
2.1 Backend Principal — api¶
| Parámetro | Valor |
|---|---|
| Imagen | Build desde Dockerfile (Java 25 + Quarkus 3.20 LTS) |
| Puerto API | 8080:8080 |
| Puerto Management | 8081:8081 (health, métricas, readiness) |
| Dependencias | postgres, chromadb, redis, minio, ollama |
| Health check | GET :8081/health/live cada 30s |
api:
build:
context: ./backend
dockerfile: Dockerfile
ports:
- "8080:8080"
- "8081:8081"
environment:
QUARKUS_PROFILE: dev
# Puerto de management separado (convención DevOps: 8080=API, 8081=management)
QUARKUS_MANAGEMENT_ENABLED: "true"
QUARKUS_MANAGEMENT_PORT: 8081
QUARKUS_MANAGEMENT_ROOT_PATH: /
# Datos
QUARKUS_DATASOURCE_JDBC_URL: jdbc:postgresql://postgres:5432/ieo_db
QUARKUS_DATASOURCE_USERNAME: ${DB_USER:-ieo}
QUARKUS_DATASOURCE_PASSWORD: ${DB_PASSWORD:-ieo_secret}
QUARKUS_REDIS_HOSTS: redis://redis:6379
QUARKUS_LANGCHAIN4J_OLLAMA_BASE_URL: http://ollama:11434
QUARKUS_CHROMADB_URL: http://chromadb:8000
QUARKUS_S3_ENDPOINT_OVERRIDE: http://minio:9000
QUARKUS_S3_AWS_CREDENTIALS_STATIC_PROVIDER_ACCESS_KEY_ID: ${MINIO_ACCESS_KEY:-minioadmin}
QUARKUS_S3_AWS_CREDENTIALS_STATIC_PROVIDER_SECRET_ACCESS_KEY: ${MINIO_SECRET_KEY:-minioadmin}
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
ollama:
condition: service_started
chromadb:
condition: service_started
minio:
condition: service_started
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8081/health/live"]
interval: 30s
timeout: 10s
retries: 5
networks:
- ieo-network
2.1b Dapr Sidecar¶
| Parámetro | Valor |
|---|---|
| Imagen | daprio/daprd:latest |
| Puerto | 3500 (HTTP API), 50001 (gRPC) |
| Componentes | Directorio ./dapr/components/ |
dapr-sidecar:
image: daprio/daprd:latest
command: [
"./daprd",
"--app-id", "ieo-backend",
"--app-port", "8080",
"--dapr-http-port", "3500",
"--dapr-grpc-port", "50001",
"--resources-path", "/components"
]
volumes:
- ./dapr/components:/components
network_mode: "service:api"
depends_on:
- api
2.2 Frontend Web — web¶
| Parámetro | Valor |
|---|---|
| Imagen | node:20-alpine con Vite |
| Puerto | 5173:5173 |
| Dependencias | api |
web:
build:
context: ./frontend
dockerfile: Dockerfile
ports:
- "5173:5173"
environment:
VITE_API_URL: http://localhost:8080
depends_on:
- api
networks:
- ieo-network
2.3 Motor IA — ollama¶
| Parámetro | Valor |
|---|---|
| Imagen | ollama/ollama:latest |
| Puerto | 11434:11434 |
| GPU | Reserva de GPU NVIDIA si disponible |
| Volumen | Persistencia de modelos descargados |
ollama:
image: ollama/ollama:latest
ports:
- "11434:11434"
volumes:
- ollama_data:/root/.ollama
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: [gpu]
networks:
- ieo-network
[!NOTE] Tras el primer arranque, se deben descargar los modelos requeridos:
docker exec -it ollama ollama pull qwen2.5-vl:7b docker exec -it ollama ollama pull llama3.1:8b
2.4 Base de Datos — postgres¶
| Parámetro | Valor |
|---|---|
| Imagen | pgvector/pgvector:pg16 |
| Puerto | 5432:5432 |
| Extensión | pgvector habilitada automáticamente |
| Volumen | Persistencia de datos |
postgres:
image: pgvector/pgvector:pg16
ports:
- "5432:5432"
environment:
POSTGRES_DB: ieo_db
POSTGRES_USER: ${DB_USER:-ieo}
POSTGRES_PASSWORD: ${DB_PASSWORD:-ieo_secret}
volumes:
- postgres_data:/var/lib/postgresql/data
- ./backend/src/main/resources/db/init:/docker-entrypoint-initdb.d
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${DB_USER:-ieo}"]
interval: 10s
timeout: 5s
retries: 5
networks:
- ieo-network
2.5 Vector DB — chromadb¶
| Parámetro | Valor |
|---|---|
| Imagen | chromadb/chroma:latest |
| Puerto | 8000:8000 |
| Persistencia | Directorio de datos montado |
chromadb:
image: chromadb/chroma:latest
ports:
- "8000:8000"
environment:
ANONYMIZED_TELEMETRY: "false"
PERSIST_DIRECTORY: /chroma/chroma
volumes:
- chromadb_data:/chroma/chroma
networks:
- ieo-network
2.6 Caché — redis¶
| Parámetro | Valor |
|---|---|
| Imagen | redis:7-alpine |
| Puerto | 6379:6379 |
| Uso | Caché KV para CAG, sesiones, rate limiting |
redis:
image: redis:7-alpine
ports:
- "6379:6379"
command: redis-server --appendonly yes --maxmemory 256mb --maxmemory-policy allkeys-lru
volumes:
- redis_data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
networks:
- ieo-network
2.7 Almacenamiento de Objetos — minio¶
| Parámetro | Valor |
|---|---|
| Imagen | minio/minio:latest |
| Puertos | 9000 (API) / 9001 (consola web) |
| Uso | Almacenamiento S3-compatible para imágenes |
minio:
image: minio/minio:latest
ports:
- "9000:9000"
- "9001:9001"
command: server /data --console-address ":9001"
environment:
MINIO_ROOT_USER: ${MINIO_ACCESS_KEY:-minioadmin}
MINIO_ROOT_PASSWORD: ${MINIO_SECRET_KEY:-minioadmin}
volumes:
- minio_data:/data
networks:
- ieo-network
3. Redes y Volúmenes¶
Red¶
networks:
ieo-network:
driver: bridge
name: ieo-network
Todos los servicios comparten la red ieo-network, permitiendo resolución DNS por nombre de servicio (e.g., postgres, ollama).
Volúmenes Persistentes¶
volumes:
postgres_data:
name: ieo-postgres-data
chromadb_data:
name: ieo-chromadb-data
redis_data:
name: ieo-redis-data
ollama_data:
name: ieo-ollama-data
minio_data:
name: ieo-minio-data
4. Variables de Entorno¶
Todas las credenciales se gestionan mediante un fichero .env en la raíz del proyecto:
# Base de datos
DB_USER=ieo
DB_PASSWORD=ieo_secret
# MinIO
MINIO_ACCESS_KEY=minioadmin
MINIO_SECRET_KEY=minioadmin
# Quarkus
QUARKUS_PROFILE=dev
[!WARNING] El fichero
.envnunca debe subirse al repositorio. Debe estar incluido en.gitignore. Para producción, usar un gestor de secretos (HashiCorp Vault, Azure Key Vault o equivalente certificado ENS).
5. Comandos de Operación¶
Arranque completo¶
docker compose up -d
Verificación de estado¶
docker compose ps
docker compose logs -f api
Parada y limpieza¶
# Parar servicios (mantener datos)
docker compose down
# Parar y eliminar volúmenes (datos incluidos)
docker compose down -v
Reconstruir un servicio¶
docker compose build api --no-cache
docker compose up -d api
6. Orden de Arranque y Dependencias¶
flowchart LR
PG["postgres"] --> API["api"]
RDS["redis"] --> API
OLL["ollama"] --> API
CHR["chromadb"] --> API
MIN["minio"] --> API
API --> WEB["web"]
style API fill:#e74c3c,color:#fff
El servicio api espera a que postgres y redis pasen sus health checks antes de arrancar. Los servicios ollama, chromadb y minio solo requieren service_started.
7. Preparación para Producción¶
| Aspecto | Desarrollo | Producción |
|---|---|---|
| Orquestación | Docker Compose | Kubernetes / Docker Swarm |
| Secretos | .env local |
Gestor de secretos (Vault) |
| SSL/TLS | No | Nginx reverse proxy + Let's Encrypt |
| Backups | Manual | CronJob automático (pg_dump + rclone) |
| Monitorización | docker compose logs |
Prometheus + Grafana |
| GPU | Reserva directa | NVIDIA Device Plugin (K8s) |
[!TIP] El diseño con Docker Compose facilita la transición a Kubernetes: cada servicio ya tiene su propia imagen, red y configuración externalizada. La migración consiste en traducir el
docker-compose.ymla manifiestos K8s (Deployments, Services, ConfigMaps, Secrets). Dapr se inyecta automáticamente como sidecar en K8s — no requiere el servicio explícito de Docker Compose.
8. Configuración Multi-Entorno: Docker ↔ Kubernetes¶
8.1 Principio: Mismas Properties, Diferente Fuente¶
Quarkus consume propiedades de configuración de forma agnóstica al entorno. El mismo código funciona sin cambios:
| Entorno | Fuente de Configuración | Fuente de Secretos |
|---|---|---|
| Desarrollo (Docker Compose) | Variables de entorno en docker-compose.yml + .env |
.env local (gitignored) |
| Staging (K8s) | ConfigMap montado como propiedades | Secret de K8s |
| Producción (K8s DragonCloud) | ConfigMap + Dapr Configuration API | Azure Key Vault via Dapr Secrets |
8.2 Docker Compose → Kubernetes: Mapeo Directo¶
# Docker Compose (.env + environment:)
QUARKUS_DATASOURCE_JDBC_URL: jdbc:postgresql://postgres:5432/ieo_db
QUARKUS_LANGCHAIN4J_OLLAMA_BASE_URL: http://ollama:11434
IEO_DEPARTAMENTO_PILOTO: Pesquerías
# Kubernetes ConfigMap — mismo contenido, diferente formato
apiVersion: v1
kind: ConfigMap
metadata:
name: ieo-backend-config
namespace: ieo
data:
application.properties: |
quarkus.datasource.jdbc.url=jdbc:postgresql://postgres-svc:5432/ieo_db
quarkus.langchain4j.ollama.base-url=http://ollama-svc:11434
ieo.departamento-piloto=Pesquerías
# Kubernetes Secret — credenciales separadas
apiVersion: v1
kind: Secret
metadata:
name: ieo-backend-secrets
namespace: ieo
type: Opaque
stringData:
quarkus.datasource.username: ieo
quarkus.datasource.password: ieo_prod_secret
quarkus.s3.aws.credentials.static-provider.access-key-id: prod_minio_key
8.3 Deployment con Dapr + ConfigMap¶
# k8s/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: ieo-backend
namespace: ieo
spec:
replicas: 2
template:
metadata:
annotations:
# Dapr se inyecta automáticamente como sidecar
dapr.io/enabled: "true"
dapr.io/app-id: "ieo-backend"
dapr.io/app-port: "8080"
spec:
containers:
- name: backend
image: registry.csic.es/ieo/backend:1.0.0
ports:
- containerPort: 8080
volumeMounts:
- name: config
mountPath: /deployments/config
envFrom:
- secretRef:
name: ieo-backend-secrets
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "1000m"
livenessProbe:
httpGet:
path: /health/live
port: 8081
initialDelaySeconds: 5
readinessProbe:
httpGet:
path: /health/ready
port: 8081
initialDelaySeconds: 10
volumes:
- name: config
configMap:
name: ieo-backend-config
[!NOTE] En K8s no hace falta definir el sidecar Dapr manualmente — la anotación
dapr.io/enabled: "true"hace que el operador Dapr lo inyecte automáticamente. Esto es una gran ventaja sobre el Docker Compose donde el sidecar es explícito.
8.4 Actualización Dinámica de ConfigMaps¶
¿Qué pasa cuando se actualiza un ConfigMap en producción?
| Estrategia | Cómo | Downtime | Cuándo Usar |
|---|---|---|---|
| Rolling restart | kubectl rollout restart deployment/ieo-backend |
~2s con Quarkus nativo | Cambios infrecuentes |
| Dapr Configuration API | Dapr subscribe a cambios y notifica via gRPC stream | 0 (push) | Configuración que cambia a menudo |
Estrategia recomendada para IEO: Usar Dapr Configuration API para configuración operativa (umbrales IA, departamentos activos) y rolling restart para cambios de infraestructura (URLs de base de datos).
// Quarkus — lectura de configuración dinámica via Dapr
@ApplicationScoped
public class ConfigDinamicaService {
@Inject DaprClient dapr;
// Suscripción a cambios en el ConfigMap via Dapr
@PostConstruct
void subscribirACambios() {
dapr.subscribeConfiguration("ieo-config-store",
List.of("ieo.umbral-confianza", "ieo.departamentos-activos"))
.subscribe(config -> {
log.info("Config actualizada: {}", config);
// Actualizar valores en memoria sin restart
});
}
}
# dapr/components/config-store.yaml
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: ieo-config-store
spec:
type: configuration.kubernetes # Lee de ConfigMaps de K8s
metadata:
- name: namespace
value: ieo
8.5 Resumen: Qué Cambia y Qué No¶
| Componente | Docker Compose | Kubernetes | ¿Cambia el código? |
|---|---|---|---|
| Configuración | .env + environment: |
ConfigMap | ❌ No |
| Secretos | .env (gitignored) |
K8s Secret / Dapr Secrets | ❌ No |
| Dapr sidecar | Servicio explícito en YAML | Inyección automática (operador) | ❌ No |
| Health checks | curl :8081/health/live |
livenessProbe + readinessProbe (puerto 8081) |
❌ No |
| GPU (Ollama) | deploy.resources.reservations |
NVIDIA Device Plugin (K8s) | ❌ No |
| Escalado | 1 réplica (dev) | N réplicas + HPA | ❌ No |
| Networking | Docker bridge network | K8s Services + Ingress | ❌ No (Dapr abstrae) |
[!IMPORTANT] Cero cambios de código entre Docker y Kubernetes. Quarkus + Dapr abstraen completamente la infraestructura. El mismo JAR/imagen Docker funciona en ambos entornos — solo cambia la fuente de configuración.
Documentos Relacionados¶
| Nivel | Documento | Descripción |
|---|---|---|
| Arquitectura | Arquitectura del Sistema | Stack completo, flujos de uso, componentes |
| Arquitectura | Arquitectura IA | Pipeline IA: CAG+RAG, embeddings, modelos |
| Arquitectura | MLOps y Workflows Agénticos | Dapr Workflow, LangChain4j, sistema agéntico |
| Infraestructura | ENS y Soberanía | Seguridad, cifrado, residencia de datos |
| Proyecto | Roadmap | Hitos y criterios de aceptación |