Despliegue de pila completa de IA con Docker Compose: orquestación con un clic desde LLM hasta base de datos vectorial

DevOps

¿Configurar un entorno de desarrollo de IA sigue tardando tres días?

En 2026, la configuración del entorno de desarrollo de IA sigue siendo una pesadilla para los desarrolladores. Necesitas instalar Ollama para el servicio LLM, configurar Qdrant para almacenamiento vectorial, configurar servicios de embedding, construir una puerta de enlace API para autenticación y gestionar controladores GPU, versiones de CUDA y descargas de modelos... Tres días desaparecidos, y no has escrito una sola línea de código.

El despliegue de pila completa de IA con Docker Compose orquesta todo en un solo archivo. docker compose up -d lanza toda la pila de IA en minutos. Este artículo es una guía práctica completa que cubre 7 patrones principales, 5 errores comunes, 10 pasos de solución de problemas y estrategias de endurecimiento para producción.

Puntos clave

  • Despliegue de pila completa de IA con Docker Compose = LLM + Base de datos vectorial + Embedding + Puerta de enlace API + Monitoreo, un archivo para gobernarlos a todos
  • Ollama + OpenWebUI es la solución de servicio LLM local más madura
  • Qdrant/Milvus son las bases de datos vectoriales de referencia con despliegue Docker ultrasencillo
  • El paso de GPU es crítico para el despliegue de IA — configúralo mediante deploy.resources.reservations.devices
  • La producción requiere autenticación, limitación de tasa, monitoreo y endurecimiento de respaldos

Tabla de contenido

  • Visión general de la arquitectura de pila completa de IA
  • Patrón 1: Servicio LLM con Ollama + OpenWebUI
  • Patrón 2: Bases de datos vectoriales Qdrant/Milvus
  • Patrón 3: Servicios de embedding y gestión de modelos
  • Patrón 4: Puerta de enlace API y autenticación
  • Patrón 5: Paso de GPU y límites de recursos
  • Patrón 6: Monitoreo y observabilidad
  • Patrón 7: Endurecimiento para producción y seguridad
  • 5 errores comunes y soluciones
  • 10 soluciones a errores comunes
  • Consejos avanzados de optimización
  • Comparación: Docker Compose vs K8s vs Docker Swarm
  • Herramientas en línea recomendadas
  • Resumen

Visión general de la arquitectura de pila completa de IA

El despliegue de pila completa de IA con Docker Compose se basa en una arquitectura de 7 capas, desde la GPU en la parte inferior hasta la puerta de enlace API en la parte superior:

┌─────────────────────────────────────────────────────┐
│                   API Gateway                        │
│              (Traefik / Nginx)                       │
│         Auth · Rate Limit · Routing · TLS            │
├──────────┬──────────┬──────────┬────────────────────┤
│ OpenWebUI│  RAG App │  Agent   │  Admin Panel       │
│  (Chat)  │ (Search) │  (Proxy) │  (Management)      │
├──────────┴──────────┴──────────┴────────────────────┤
│              Embedding Service                       │
│       (TEI / Infinity / FastEmbed)                  │
├──────────────────┬──────────────────────────────────┤
│   Ollama LLM     │    vLLM / TGI                    │
│  (Model Serving) │  (High-Perf Inference)           │
├──────────────────┴──────────────────────────────────┤
│           Vector Database                            │
│     (Qdrant / Milvus / Weaviate)                    │
├─────────────────────────────────────────────────────┤
│              Infrastructure                          │
│   Redis · PostgreSQL · MinIO · Prometheus            │
├─────────────────────────────────────────────────────┤
│              GPU / CPU Runtime                       │
│     NVIDIA CUDA · ROCm · CPU Fallback               │
└─────────────────────────────────────────────────────┘

Estructura de directorios del proyecto para el despliegue de pila completa de IA con Docker Compose:

ai-stack/
├── docker-compose.yml
├── docker-compose.gpu.yml
├── docker-compose.prod.yml
├── .env
├── ollama/
│   └── Modelfile
├── qdrant/
│   └── config.yaml
├── traefik/
│   ├── traefik.yml
│   └── acme.json
├── monitoring/
│   ├── prometheus.yml
│   └── grafana/
│       └── dashboards/
└── scripts/
    ├── init-models.sh
    └── backup-vectors.sh

Patrón 1: Servicio LLM con Ollama + OpenWebUI

Ollama es la solución de servicio LLM local más madura en 2026, compatible con Llama 4, Qwen 3, DeepSeek V3 y otros modelos principales. OpenWebUI proporciona una interfaz web estilo ChatGPT.

Configuración básica

services:
  ollama:
    image: ollama/ollama:latest
    container_name: ollama
    ports:
      - "11434:11434"
    volumes:
      - ollama_data:/root/.ollama
    environment:
      OLLAMA_KEEP_ALIVE: "24h"
      OLLAMA_NUM_PARALLEL: "4"
      OLLAMA_MAX_LOADED_MODELS: "3"
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:11434/api/tags"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 60s
    restart: unless-stopped

  open-webui:
    image: ghcr.io/open-webui/open-webui:main
    container_name: open-webui
    ports:
      - "3000:8080"
    volumes:
      - open_webui_data:/app/backend/data
    environment:
      OLLAMA_BASE_URL: "http://ollama:11434"
      WEBUI_SECRET_KEY: "${WEBUI_SECRET_KEY}"
      ENABLE_SIGNUP: "false"
      DEFAULT_USER_ROLE: "user"
    depends_on:
      ollama:
        condition: service_healthy
    restart: unless-stopped

volumes:
  ollama_data:
  open_webui_data:

Descarga automática de modelos

Después de iniciar Ollama, necesitas descargar modelos manualmente. Automatiza esto con un script de inicialización:

#!/bin/bash
# scripts/init-models.sh

MODELS=(
  "qwen3:8b"
  "llama4:8b"
  "deepseek-v3:8b"
  "nomic-embed-text"
)

for model in "${MODELS[@]}"; do
  echo "Pulling model: $model"
  until curl -s http://localhost:11434/api/pull -d "{\"name\":\"$model\"}" | grep -q "success"; do
    echo "  Retrying $model..."
    sleep 5
  done
  echo "  ✓ $model ready"
done

echo "All models pulled successfully!"

Agrega el servicio de inicialización a Docker Compose:

  model-init:
    image: curlimages/curl:latest
    container_name: model-init
    depends_on:
      ollama:
        condition: service_healthy
    volumes:
      - ./scripts/init-models.sh:/init-models.sh:ro
    entrypoint: ["/bin/sh", "/init-models.sh"]
    restart: "no"

Modelfile personalizado

# ollama/Modelfile
FROM qwen3:8b

PARAMETER temperature 0.7
PARAMETER top_p 0.9
PARAMETER num_ctx 8192
PARAMETER stop "<|im_end|>"

SYSTEM """
You are a professional AI assistant. When answering questions:
1. Give a concise conclusion first
2. Then provide detailed explanation
3. If uncertain, say so explicitly
"""

Construye un modelo personalizado:

docker exec ollama ollama create my-assistant -f /root/.ollama/Modelfile

Patrón 2: Bases de datos vectoriales Qdrant/Milvus

Las bases de datos vectoriales son el núcleo de la arquitectura RAG. El despliegue de pila completa de IA con Docker Compose generalmente usa Qdrant (ligero) o Milvus (gran escala).

Configuración de Qdrant (Recomendado para proyectos pequeño-medianos)

  qdrant:
    image: qdrant/qdrant:latest
    container_name: qdrant
    ports:
      - "6333:6333"
      - "6334:6334"
    volumes:
      - qdrant_data:/qdrant/storage
      - ./qdrant/config.yaml:/qdrant/config/production.yaml:ro
    environment:
      QDRANT__SERVICE__GRPC_PORT: "6334"
      QDRANT__LOG_LEVEL: "INFO"
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:6333/healthz"]
      interval: 10s
      timeout: 5s
      retries: 3
    restart: unless-stopped

Archivo de configuración de Qdrant:

# qdrant/config.yaml
storage:
  performance:
    max_search_threads: 4
  wal:
    wal_capacity_mb: 32
    wal_segments_ahead: 0
  optimizers:
    indexing_threshold: 20000
    memmap_threshold: 50000
service:
  max_request_size_mb: 64
  enable_cors: true
telemetry_disabled: true

Configuración de Milvus (Recomendado para proyectos a gran escala)

  etcd:
    image: quay.io/coreos/etcd:v3.5.16
    container_name: milvus-etcd
    environment:
      ETCD_AUTO_COMPACTION_MODE: "revision"
      ETCD_AUTO_COMPACTION_RETENTION: "1000"
      ETCD_QUOTA_BACKEND_BYTES: "4294967296"
    volumes:
      - etcd_data:/etcd
    command: etcd -advertise-client-urls=http://127.0.0.1:2379 -listen-client-urls http://0.0.0.0:2379
    restart: unless-stopped

  minio:
    image: minio/minio:latest
    container_name: milvus-minio
    environment:
      MINIO_ACCESS_KEY: "${MINIO_ACCESS_KEY}"
      MINIO_SECRET_KEY: "${MINIO_SECRET_KEY}"
    ports:
      - "9001:9001"
      - "9000:9000"
    volumes:
      - minio_data:/minio_data
    command: minio server /minio_data --console-address ":9001"
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
      interval: 30s
      timeout: 20s
      retries: 3
    restart: unless-stopped

  milvus:
    image: milvusdb/milvus:v2.5-latest
    container_name: milvus
    ports:
      - "19530:19530"
      - "9091:9091"
    volumes:
      - milvus_data:/var/lib/milvus
    environment:
      ETCD_ENDPOINTS: "etcd:2379"
      MINIO_ADDRESS: "minio:9000"
    depends_on:
      - etcd
      - minio
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:9091/healthz"]
      interval: 30s
      timeout: 20s
      retries: 3
      start_period: 90s
    restart: unless-stopped

Comparación de bases de datos vectoriales

Característica Qdrant Milvus Weaviate ChromaDB
Complejidad de despliegue Muy baja (1 contenedor) Alta (3+ contenedores) Baja (1 contenedor) Muy baja (1 contenedor)
Rendimiento (Millones) Excelente Excelente Bueno Regular
Rendimiento (Miles de millones) Bueno Excelente Regular N/A
Búsqueda filtrada ✅ Potente ✅ Potente ✅ Buena ⚠️ Básica
Persistencia ⚠️ En memoria por defecto
Multi-réplica
Soporte gRPC
Compatibilidad con Docker Compose ✅ Mejor ⚠️ Pesado ✅ Bueno ✅ Solo desarrollo
Listo para producción ❌ Solo desarrollo

Recomendación: Para el despliegue de pila completa de IA con Docker Compose, Qdrant es la primera opción — despliegue simple, rendimiento excelente. Considera Milvus cuando el conteo de vectores supere los 100 millones. ChromaDB solo es adecuado para prototipado.


Patrón 3: Servicios de embedding y gestión de modelos

Los servicios de embedding convierten texto en vectores, un paso crítico en el pipeline RAG. La orquestación de contenedores Docker Compose AI ofrece tres soluciones principales.

Hugging Face TEI (Recomendado)

  tei:
    image: ghcr.io/huggingface/text-embeddings-inference:latest
    container_name: tei
    ports:
      - "8080:80"
    volumes:
      - tei_cache:/data
    environment:
      MODEL_ID: "BAAI/bge-m3"
      REVISION: "main"
      MAX_BATCH_TOKENS: "16384"
      MAX_CLIENT_BATCH_SIZE: "32"
      HF_TOKEN: "${HF_TOKEN}"
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:80/health"]
      interval: 10s
      timeout: 5s
      retries: 3
      start_period: 120s
    restart: unless-stopped

Servicio de embedding Infinity

  infinity:
    image: michaelf34/infinity:latest
    container_name: infinity
    ports:
      - "7997:7997"
    volumes:
      - infinity_cache:/app/.cache
    environment:
      MODEL_ID: "BAAI/bge-m3"
      ENGINE: "optimum"
      BATCH_SIZE: "32"
    command: >
      --model-id BAAI/bge-m3
      --engine optimum
      --port 7997
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:7997/health"]
      interval: 10s
      timeout: 5s
      retries: 3
    restart: unless-stopped

Ejemplo de uso del servicio de embedding

import httpx
import numpy as np

async def get_embeddings(texts: list[str]) -> list[list[float]]:
    async with httpx.AsyncClient(timeout=30.0) as client:
        response = await client.post(
            "http://tei:80/embed",
            json={"inputs": texts}
        )
        response.raise_for_status()
        return response.json()

async def search_similar(query: str, top_k: int = 5) -> list[dict]:
    query_embedding = await get_embeddings([query])
    async with httpx.AsyncClient(timeout=30.0) as client:
        response = await client.post(
            "http://qdrant:6333/collections/documents/points/search",
            json={
                "vector": query_embedding[0],
                "limit": top_k,
                "with_payload": True
            }
        )
        response.raise_for_status()
        return response.json()["result"]

Comparación de servicios de embedding

Característica TEI Infinity FastEmbed
Aceleración GPU ✅ Nativa ✅ Nativa ❌ Solo CPU
Inferencia por lotes ✅ Eficiente ✅ Eficiente ⚠️ Regular
Multi-modelo
Tamaño de imagen Docker ~2GB ~4GB ~500MB
Listo para producción ⚠️ Solo desarrollo
API compatible con OpenAI

Patrón 4: Puerta de enlace API y autenticación

El despliegue de pila completa de IA con Docker Compose en producción requiere una puerta de enlace API para autenticación unificada, limitación de tasa y enrutamiento.

Configuración de Traefik

  traefik:
    image: traefik:v3.2
    container_name: traefik
    ports:
      - "80:80"
      - "443:443"
      - "8080:8080"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./traefik/traefik.yml:/etc/traefik/traefik.yml:ro
      - traefik_certs:/etc/traefik/certs
      - ./traefik/dynamic:/etc/traefik/dynamic:ro
    command:
      - "--api.dashboard=true"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--providers.file.directory=/etc/traefik/dynamic"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      - "--entrypoints.web.http.redirections.entrypoint.to=websecure"
    labels:
      traefik.enable: "true"
      traefik.http.routers.traefik.rule: "Host(`traefik.ai-stack.local`)"
      traefik.http.routers.traefik.entrypoints: "websecure"
      traefik.http.routers.traefik.tls: "true"
      traefik.http.services.traefik.loadbalancer.server.port: "8080"
    restart: unless-stopped

OpenWebUI con Traefik

  open-webui:
    image: ghcr.io/open-webui/open-webui:main
    container_name: open-webui
    volumes:
      - open_webui_data:/app/backend/data
    environment:
      OLLAMA_BASE_URL: "http://ollama:11434"
    labels:
      traefik.enable: "true"
      traefik.http.routers.webui.rule: "Host(`chat.ai-stack.local`)"
      traefik.http.routers.webui.entrypoints: "websecure"
      traefik.http.routers.webui.tls: "true"
      traefik.http.services.webui.loadbalancer.server.port: "8080"
    depends_on:
      ollama:
        condition: service_healthy
    restart: unless-stopped

Middleware de autenticación

# traefik/dynamic/auth.yml
http:
  middlewares:
    auth-middleware:
      forwardAuth:
        address: "http://auth-service:8000/verify"
        trustForwardHeader: true
        authResponseHeaders:
          - "X-User-Id"
          - "X-User-Role"

    rate-limit:
      rateLimit:
        average: 30
        burst: 60
        period: 1m

  routers:
    api-router:
      rule: "Host(`api.ai-stack.local`)"
      entrypoints:
        - "websecure"
      tls: true
      middlewares:
        - "auth-middleware"
        - "rate-limit"
      service: "ollama-api"

Patrón 5: Paso de GPU y límites de recursos

La GPU es el núcleo del despliegue de IA. El despliegue de pila completa de IA con Docker Compose habilita el paso de GPU mediante deploy.resources.reservations.devices.

Paso de GPU NVIDIA

  ollama:
    image: ollama/ollama:latest
    container_name: ollama
    volumes:
      - ollama_data:/root/.ollama
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: 1
              capabilities: [gpu]
        limits:
          memory: 16G
          cpus: "8.0"
    environment:
      NVIDIA_VISIBLE_DEVICES: "all"
      NVIDIA_DRIVER_CAPABILITIES: "compute,utility"
      OLLAMA_KEEP_ALIVE: "24h"
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:11434/api/tags"]
      interval: 30s
      timeout: 10s
      retries: 3
    restart: unless-stopped

Asignación multi-GPU

  ollama:
    image: ollama/ollama:latest
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              device_ids: ["0"]
              capabilities: [gpu]
    environment:
      CUDA_VISIBLE_DEVICES: "0"

  tei:
    image: ghcr.io/huggingface/text-embeddings-inference:latest
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              device_ids: ["1"]
              capabilities: [gpu]
    environment:
      CUDA_VISIBLE_DEVICES: "1"

Configuración de respaldo a CPU

  ollama-cpu:
    image: ollama/ollama:latest
    profiles: ["cpu-only"]
    container_name: ollama
    volumes:
      - ollama_data:/root/.ollama
    environment:
      OLLAMA_NUM_PARALLEL: "2"
      OLLAMA_MAX_LOADED_MODELS: "1"
    deploy:
      resources:
        limits:
          memory: 8G
          cpus: "4.0"

  ollama-gpu:
    image: ollama/ollama:latest
    profiles: ["gpu"]
    container_name: ollama
    volumes:
      - ollama_data:/root/.ollama
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: all
              capabilities: [gpu]
        limits:
          memory: 16G

Comandos de inicio:

# Modo GPU
docker compose --profile gpu up -d

# Modo CPU
docker compose --profile cpu-only up -d

Script de monitoreo de recursos GPU

import subprocess
import json
import time

def monitor_gpu_usage(interval: int = 60):
    while True:
        result = subprocess.run(
            ["nvidia-smi", "--query-gpu=index,name,memory.used,memory.total,utilization.gpu",
             "--format=csv,noheader,nounits"],
            capture_output=True, text=True
        )
        for line in result.stdout.strip().split("\n"):
            idx, name, mem_used, mem_total, util = line.split(", ")
            print(f"GPU {idx} ({name}): {mem_used}/{mem_total}MB, Util: {util}%")
        time.sleep(interval)

if __name__ == "__main__":
    monitor_gpu_usage()

Patrón 6: Monitoreo y observabilidad

El monitoreo del despliegue de pila completa de IA con Docker Compose debe cubrir la utilización de GPU, la latencia de inferencia, el rendimiento de la base de datos vectorial y otras métricas específicas de IA.

Prometheus + Grafana

  prometheus:
    image: prom/prometheus:v3.2.0
    container_name: prometheus
    volumes:
      - ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml:ro
      - prometheus_data:/prometheus
    command:
      - "--config.file=/etc/prometheus/prometheus.yml"
      - "--storage.tsdb.retention.time=30d"
      - "--storage.tsdb.retention.size=10GB"
    ports:
      - "9090:9090"
    restart: unless-stopped

  grafana:
    image: grafana/grafana:11.5.0
    container_name: grafana
    volumes:
      - grafana_data:/var/lib/grafana
      - ./monitoring/grafana/dashboards:/etc/grafana/provisioning/dashboards:ro
      - ./monitoring/grafana/datasources:/etc/grafana/provisioning/datasources:ro
    environment:
      GF_SECURITY_ADMIN_USER: "${GRAFANA_USER}"
      GF_SECURITY_ADMIN_PASSWORD: "${GRAFANA_PASSWORD}"
      GF_USERS_ALLOW_SIGN_UP: "false"
    ports:
      - "3001:3000"
    depends_on:
      - prometheus
    restart: unless-stopped

  dcgm-exporter:
    image: nvidia/dcgm-exporter:latest
    container_name: dcgm-exporter
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: all
              capabilities: [gpu]
    ports:
      - "9400:9400"
    restart: unless-stopped

Configuración de Prometheus

# monitoring/prometheus.yml
global:
  scrape_interval: 15s
  evaluation_interval: 15s

scrape_configs:
  - job_name: "ollama"
    static_configs:
      - targets: ["ollama:11434"]
    metrics_path: "/metrics"
    scrape_interval: 30s

  - job_name: "qdrant"
    static_configs:
      - targets: ["qdrant:6333"]
    metrics_path: "/metrics"
    scrape_interval: 30s

  - job_name: "dcgm"
    static_configs:
      - targets: ["dcgm-exporter:9400"]
    scrape_interval: 10s

  - job_name: "node-exporter"
    static_configs:
      - targets: ["node-exporter:9100"]

  - job_name: "traefik"
    static_configs:
      - targets: ["traefik:8080"]

Reglas de alerta clave

# monitoring/alerts.yml
groups:
  - name: ai-stack
    rules:
      - alert: OllamaHighLatency
        expr: histogram_quantile(0.95, rate(ollama_request_duration_seconds_bucket[5m])) > 30
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "Latencia de inferencia de Ollama demasiado alta"

      - alert: GPUMemoryHigh
        expr: DCGM_FI_DEV_FB_USED / DCGM_FI_DEV_FB_TOTAL > 0.9
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: "El uso de memoria GPU supera el 90%"

      - alert: QdrantHighLatency
        expr: histogram_quantile(0.95, rate(qdrant_request_duration_seconds_bucket[5m])) > 5
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "Latencia de consulta de Qdrant demasiado alta"

      - alert: OllamaContainerDown
        expr: up{job="ollama"} == 0
        for: 2m
        labels:
          severity: critical
        annotations:
          summary: "Servicio Ollama no disponible"

Patrón 7: Endurecimiento para producción y seguridad

La seguridad es la línea base para el despliegue de pila completa de IA con Docker Compose en producción.

Gestión de secretos

services:
  ollama:
    image: ollama/ollama:latest
    secrets:
      - hf_token
    environment:
      HF_TOKEN_FILE: /run/secrets/hf_token

  qdrant:
    image: qdrant/qdrant:latest
    secrets:
      - qdrant_api_key
    environment:
      QDRANT__SERVICE__API_KEY_FILE: /run/secrets/qdrant_api_key

secrets:
  hf_token:
    file: ./secrets/hf_token.txt
  qdrant_api_key:
    file: ./secrets/qdrant_api_key.txt
  db_password:
    file: ./secrets/db_password.txt

Aislamiento de red

networks:
  frontend:
    driver: bridge
  backend:
    driver: bridge
    internal: true
  monitoring:
    driver: bridge
    internal: true

services:
  traefik:
    networks:
      - frontend
      - backend

  open-webui:
    networks:
      - frontend
      - backend

  ollama:
    networks:
      - backend

  qdrant:
    networks:
      - backend

  tei:
    networks:
      - backend

  prometheus:
    networks:
      - monitoring
      - backend

  grafana:
    networks:
      - frontend
      - monitoring

Estrategia de respaldo

#!/bin/bash
# scripts/backup-vectors.sh

BACKUP_DIR="/backups/$(date +%Y%m%d_%H%M%S)"
mkdir -p "$BACKUP_DIR"

echo "Backing up Qdrant..."
curl -s -X POST "http://localhost:6333/snapshots" | jq .

echo "Backing up Ollama models list..."
curl -s "http://localhost:11434/api/tags" | jq . > "$BACKUP_DIR/ollama_models.json"

echo "Backing up environment config..."
cp .env "$BACKUP_DIR/.env.backup"
cp docker-compose.yml "$BACKUP_DIR/docker-compose.yml.backup"

echo "Backup completed: $BACKUP_DIR"

Configuración completa de producción

# docker-compose.prod.yml
services:
  ollama:
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: 1
              capabilities: [gpu]
        limits:
          memory: 16G
          cpus: "8.0"
      restart_policy:
        condition: on-failure
        delay: 10s
        max_attempts: 5
        window: 120s
    logging:
      driver: json-file
      options:
        max-size: "100m"
        max-file: "5"
    read_only: true
    tmpfs:
      - /tmp

  qdrant:
    deploy:
      resources:
        limits:
          memory: 4G
          cpus: "2.0"
      restart_policy:
        condition: on-failure
        delay: 5s
        max_attempts: 3
    logging:
      driver: json-file
      options:
        max-size: "50m"
        max-file: "3"

5 errores comunes y soluciones

Error 1: Tiempo de espera agotado al descargar modelos de Ollama

Síntoma: Después de docker compose up, Ollama se queda atascado descargando modelos. Los modelos grandes (ej., Llama 4 70B) pueden tardar más de una hora en descargarse.

Solución: Usa el servicio model-init para descarga asíncrona. El servicio Ollama en sí no necesita esperar por los modelos.

  model-init:
    image: curlimages/curl:latest
    depends_on:
      ollama:
        condition: service_healthy
    volumes:
      - ./scripts/init-models.sh:/init-models.sh:ro
    entrypoint: ["/bin/sh", "/init-models.sh"]
    restart: "no"
    deploy:
      resources:
        limits:
          memory: 256M

Error 2: OOM del contenedor Qdrant

Síntoma: A medida que crecen los datos vectoriales, el contenedor Qdrant sufre OOM Killed.

Solución: Establece límites de memoria y habilita el mapeo de memoria.

  qdrant:
    image: qdrant/qdrant:latest
    deploy:
      resources:
        limits:
          memory: 8G
    environment:
      QDRANT__STORAGE__PERFORMANCE__MAX_SEARCH_THREADS: "4"
      QDRANT__STORAGE__WAL__WAL_CAPACITY_MB: "64"

Error 3: Incompatibilidad de versión del controlador GPU

Síntoma: docker compose up muestra el error CUDA driver version is insufficient.

Solución: Asegúrate de que el controlador NVIDIA del host sea ≥ 535, instala nvidia-container-toolkit.

# Verificar versión del controlador
nvidia-smi | head -3

# Instalar nvidia-container-toolkit (Ubuntu)
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | \
  sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | \
  sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
  sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
sudo apt-get update
sudo apt-get install -y nvidia-container-toolkit
sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker

Error 4: Servicio de embedding y LLM compitiendo por la GPU

Síntoma: Memoria GPU insuficiente cuando TEI y Ollama se ejecutan simultáneamente; la carga del modelo falla.

Solución: Usa device_ids para asignación precisa de GPU, o ejecuta el servicio de embedding en CPU.

  ollama:
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              device_ids: ["0"]
              capabilities: [gpu]

  tei:
    # Ejecutar servicio de embedding en modo CPU
    environment:
      MODEL_ID: "BAAI/bge-m3"
      # Sin asignación de GPU, usa CPU

Error 5: Fallo de resolución DNS entre contenedores

Síntoma: OpenWebUI informa ollama: Name or service not known.

Solución: Asegúrate de que todos los servicios estén en la misma red, usa container_name o el nombre del servicio como nombre de host.

networks:
  ai-network:
    driver: bridge

services:
  ollama:
    container_name: ollama
    networks:
      - ai-network

  open-webui:
    container_name: open-webui
    networks:
      - ai-network
    environment:
      OLLAMA_BASE_URL: "http://ollama:11434"

10 soluciones a errores comunes

1. could not select device driver — Runtime NVIDIA no instalado

# Instalar nvidia-container-toolkit y reiniciar Docker
sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker
docker info | grep -i runtime
# Deberías ver el runtime nvidia

2. OOM Killed — Memoria GPU insuficiente

# Verificar memoria GPU
nvidia-smi
# Usar modelos más pequeños o cuantizados
docker exec ollama ollama run qwen3:4b

3. Connection refused a Ollama — Servicio no listo

# Verificar estado de salud de Ollama
docker compose ps
docker compose logs ollama
# Esperar a que pase el healthcheck antes de conectar

4. permission denied en el socket Docker

sudo usermod -aG docker $USER
newgrp docker

5. Qdrant collection not found — Colección no creada

curl -X PUT "http://localhost:6333/collections/documents" \
  -H "Content-Type: application/json" \
  -d '{"vectors": {"size": 1024, "distance": "Cosine"}}'

6. model not found — Modelo de Ollama no descargado

docker exec ollama ollama pull qwen3:8b

7. CUDA out of memory — Desbordamiento de memoria GPU durante inferencia

# Reducir solicitudes paralelas
# Configurar en docker-compose.yml
environment:
  OLLAMA_NUM_PARALLEL: "1"
  OLLAMA_MAX_LOADED_MODELS: "1"

8. TLS handshake error — Problema de certificado Traefik

# Verificar permisos del archivo de certificado
chmod 600 traefik/acme.json
# Verificar logs de Traefik
docker compose logs traefik

9. too many open files — Límite de descriptores de archivo

# Aumentar límite temporalmente
ulimit -n 65536
# Configuración permanente (/etc/security/limits.conf)
# * soft nofile 65536
# * hard nofile 65536

10. vector dimension mismatch — Inconsistencia de dimensiones de embedding

# Asegurar que la dimensión de la colección Qdrant coincida con la salida del modelo de embedding
# bge-m3: 1024 dimensiones
# nomic-embed-text: 768 dimensiones
curl -X PUT "http://localhost:6333/collections/documents" \
  -d '{"vectors": {"size": 1024, "distance": "Cosine"}}'

Consejos avanzados de optimización

Calentamiento de modelos en múltiples etapas

  model-warmer:
    image: curlimages/curl:latest
    container_name: model-warmer
    depends_on:
      ollama:
        condition: service_healthy
    entrypoint: >
      /bin/sh -c "
        echo 'Warming up models...' &&
        curl -s http://ollama:11434/api/generate -d '{\"model\":\"qwen3:8b\",\"prompt\":\"hi\",\"stream\":false}' > /dev/null &&
        curl -s http://ollama:11434/api/generate -d '{\"model\":\"nomic-embed-text\",\"prompt\":\"test\",\"stream\":false}' > /dev/null &&
        echo 'Models warmed up!'
      "
    restart: "no"

Descarga inteligente de modelos

  ollama:
    environment:
      OLLAMA_KEEP_ALIVE: "5m"
      OLLAMA_NUM_PARALLEL: "4"
      OLLAMA_MAX_LOADED_MODELS: "2"

OLLAMA_KEEP_ALIVE: "5m" descarga automáticamente modelos inactivos durante 5 minutos, liberando memoria GPU.

Dependencias encadenadas de verificaciones de salud

  tei:
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:80/health"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 120s

  qdrant:
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:6333/healthz"]
      interval: 10s
      timeout: 5s
      retries: 3

  rag-app:
    depends_on:
      ollama:
        condition: service_healthy
      tei:
        condition: service_healthy
      qdrant:
        condition: service_healthy

Docker Compose Watch para desarrollo

# docker-compose.yml
services:
  rag-app:
    build: .
    develop:
      watch:
        - action: rebuild
          path: ./app
          target: /app
        - action: sync
          path: ./app/static
          target: /app/static

Comparación: Docker Compose vs K8s vs Docker Swarm

Dimensión Docker Compose Kubernetes Docker Swarm
Complejidad de despliegue de pila IA ⭐ Muy baja ⭐⭐⭐⭐⭐ Muy alta ⭐⭐ Baja
Programación de GPU ✅ Nativa ✅ Device Plugin ⚠️ Necesita configuración
Auto-escalado ✅ HPA ⚠️ Manual
Descubrimiento de servicios ✅ DNS ✅ CoreDNS ✅ DNS
Actualizaciones continuas ⚠️ Necesita scripts ✅ Nativo ✅ Nativo
Gestión de configuración ✅ .env ✅ ConfigMap ⚠️ Config
Gestión de secretos ✅ Docker Secret ✅ K8s Secret ⚠️ Básica
Ecosistema de monitoreo ✅ Prometheus ✅ Completo ⚠️ Limitado
Orquestación multi-nodo ❌ Nodo único ✅ Capacidad central ✅ Nativo
Curva de aprendizaje Baja Alta Baja
Actividad de la comunidad ✅ Activa ✅ Muy activa ❌ En declive
Escala de proyecto IA adecuada 1-5 GPUs 10+ GPUs 2-5 GPUs

Recomendación: El despliegue de pila completa de IA con Docker Compose es ideal para escenarios de máquina única con 1-5 GPUs — la mejor opción para desarrollo de IA y producción a pequeña escala. Para 5+ GPUs o necesidades multi-nodo, considera Kubernetes + KServe/vLLM. Docker Swarm no se recomienda para despliegue de IA.


Herramientas en línea recomendadas


Resumen

El despliegue de pila completa de IA con Docker Compose transforma los entornos de desarrollo de IA de "tres días para configurar" a "un comando para lanzar". Ollama + OpenWebUI gestiona el servicio LLM, Qdrant gestiona el almacenamiento vectorial, TEI gestiona los embeddings, Traefik gestiona la puerta de enlace, Prometheus + Grafana gestiona el monitoreo, y el paso de GPU hace volar la inferencia. 7 patrones cubren toda la cadena desde el desarrollo hasta la producción, 5 errores comunes y 10 pasos de solución de problemas te ayudan a evitar desvíos. Para proyectos de IA con 1-5 GPUs, Docker Compose es la solución de orquestación de contenedores de IA más práctica en 2026.

Artículos relacionados

Referencias externas

Prueba estas herramientas que se ejecutan en tu navegador — no requieren registro →

#Docker#Docker Compose#AI部署#LLM#向量数据库#Ollama#2026#DevOps