Saltar a contenido

← 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 .env nunca 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.yml a 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