Inicio / Inteligencia Artificial / AI Engineering Pro / Docker y Kubernetes para IA

Docker y Kubernetes para IA

Dockerfile multi-stage, docker-compose, K8s deployments, HPA y GPU.

Avanzado
🔒 Solo lectura
📖

Estás en modo lectura

Puedes leer toda la lección, pero para marcar progreso, hacer ejercicios y ganar XP necesitas una cuenta Pro.

Desbloquear por $9/mes

Containerización: Docker y Kubernetes para IA

¿Por Qué Containers para IA?

Las aplicaciones de IA tienen dependencias complejas: versiones específicas de Python, CUDA, bibliotecas de ML y modelos pesados. Docker garantiza que el entorno de desarrollo sea idéntico al de producción.

Dockerfile para Aplicación RAG

# ── Multi-stage build para aplicación RAG ─────────────
# Stage 1: Builder
FROM python:3.12-slim AS builder

WORKDIR /build

# Instalar dependencias del sistema para compilación
RUN apt-get update && apt-get install -y --no-install-recommends \
    gcc g++ && \
    rm -rf /var/lib/apt/lists/*

COPY requirements.txt .
RUN pip install --no-cache-dir --prefix=/install -r requirements.txt

# Stage 2: Runtime
FROM python:3.12-slim AS runtime

WORKDIR /app

# Solo copiar las dependencias instaladas
COPY --from=builder /install /usr/local

# Copiar el código fuente
COPY src/ ./src/
COPY prompts/ ./prompts/
COPY config/ ./config/

# Usuario no-root
RUN useradd --create-home appuser
USER appuser

# Health check
HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
    CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')"

EXPOSE 8000

# Uvicorn con workers configurables
CMD ["uvicorn", "src.main:app", "--host", "0.0.0.0", \
     "--port", "8000", "--workers", "4"]

Docker Compose para Desarrollo Local

# docker-compose.yml
services:
  # ── API RAG ─────────────────────────────────────────
  api:
    build: .
    ports:
      - "8000:8000"
    environment:
      - OPENAI_API_KEY=${OPENAI_API_KEY}
      - QDRANT_HOST=qdrant
      - REDIS_HOST=redis
      - LOG_LEVEL=debug
    volumes:
      - ./src:/app/src  # Hot reload en desarrollo
    depends_on:
      qdrant:
        condition: service_healthy
      redis:
        condition: service_healthy
    deploy:
      resources:
        limits:
          memory: 2G

  # ── Vector Database ─────────────────────────────────
  qdrant:
    image: qdrant/qdrant:v1.9
    ports:
      - "6333:6333"
    volumes:
      - qdrant_data:/qdrant/storage
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:6333/healthz"]
      interval: 10s
      timeout: 5s
      retries: 3

  # ── Cache ───────────────────────────────────────────
  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s

  # ── Observabilidad ──────────────────────────────────
  langfuse:
    image: langfuse/langfuse:2
    ports:
      - "3000:3000"
    environment:
      - DATABASE_URL=postgresql://langfuse:pass@postgres:5432/langfuse
    depends_on:
      - postgres

  postgres:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: langfuse
      POSTGRES_PASSWORD: pass
      POSTGRES_DB: langfuse
    volumes:
      - pg_data:/var/lib/postgresql/data

volumes:
  qdrant_data:
  pg_data:

Kubernetes: Deployment para IA

# k8s/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: rag-api
  labels:
    app: rag-api
spec:
  replicas: 3
  selector:
    matchLabels:
      app: rag-api
  template:
    metadata:
      labels:
        app: rag-api
    spec:
      containers:
        - name: rag-api
          image: 123456789.dkr.ecr.us-east-1.amazonaws.com/rag-api:latest
          ports:
            - containerPort: 8000

          # ── Configuración desde Secrets/ConfigMaps ──
          envFrom:
            - configMapRef:
                name: rag-config
            - secretRef:
                name: rag-secrets

          # ── Recursos: importante para IA ────────────
          resources:
            requests:
              cpu: "500m"
              memory: "1Gi"
            limits:
              cpu: "2"
              memory: "4Gi"

          # ── Health checks ───────────────────────────
          readinessProbe:
            httpGet:
              path: /health
              port: 8000
            initialDelaySeconds: 15
            periodSeconds: 10

          livenessProbe:
            httpGet:
              path: /health
              port: 8000
            initialDelaySeconds: 30
            periodSeconds: 15

---
# HPA para escalar según latencia
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: rag-api-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: rag-api
  minReplicas: 2
  maxReplicas: 20
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 70
    # Escalar basado en request queue depth
    - type: Pods
      pods:
        metric:
          name: http_request_queue_length
        target:
          type: AverageValue
          averageValue: "10"

Service y Ingress

# k8s/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: rag-api
spec:
  selector:
    app: rag-api
  ports:
    - port: 80
      targetPort: 8000
  type: ClusterIP

---
# k8s/ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: rag-api
  annotations:
    kubernetes.io/ingress.class: "alb"
    alb.ingress.kubernetes.io/scheme: "internet-facing"
    alb.ingress.kubernetes.io/target-type: "ip"
    alb.ingress.kubernetes.io/healthcheck-path: "/health"
    # Rate limiting
    nginx.ingress.kubernetes.io/limit-rps: "50"
spec:
  rules:
    - host: api.ai-platform.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: rag-api
                port:
                  number: 80

GPU en Kubernetes

# k8s/gpu-deployment.yaml — Pod con GPU para embedding local
apiVersion: apps/v1
kind: Deployment
metadata:
  name: embedding-service
spec:
  replicas: 2
  template:
    spec:
      containers:
        - name: embedder
          image: embedding-service:latest
          resources:
            limits:
              # Solicita 1 GPU dedicada; el scheduler de K8s
              # coloca el pod únicamente en un nodo con GPU libre
              nvidia.com/gpu: 1
              memory: "16Gi"
          # ── Persistir pesos del modelo ────────────────
          # Monta un volumen persistente para cachear los
          # pesos descargados. Evita re-descargar ~GBs de
          # modelo cada vez que el pod se reinicia.
          volumeMounts:
            - name: model-cache
              mountPath: /models
      volumes:
        - name: model-cache
          # PersistentVolumeClaim: almacenamiento que
          # sobrevive a reinicios y re-schedulings del pod
          persistentVolumeClaim:
            claimName: model-cache-pvc
      # ── Restringir a nodos con GPU específica ───────
      # nodeSelector filtra nodos por etiqueta; aquí solo
      # nodos con Tesla T4 son elegibles para este pod
      nodeSelector:
        accelerator: nvidia-tesla-t4
      # ── Toleraciones para nodos GPU ─────────────────
      # Los nodos con GPU suelen tener un "taint" que
      # impide que workloads no-GPU se programen ahí.
      # Esta toleration permite que ESTE pod sí se
      # programe en esos nodos reservados.
      tolerations:
        - key: nvidia.com/gpu
          operator: Exists
          effect: NoSchedule

Helm Chart Básico

# Crear chart para la aplicación de IA
helm create rag-api

# Estructura generada:
# rag-api/
# ├── Chart.yaml
# ├── values.yaml          ← Configuración
# ├── templates/
# │   ├── deployment.yaml
# │   ├── service.yaml
# │   ├── ingress.yaml
# │   └── hpa.yaml
# └── charts/              ← Dependencias
# values.yaml — Configuración del chart
replicaCount: 3

image:
  repository: 123456789.dkr.ecr.us-east-1.amazonaws.com/rag-api
  tag: "latest"

resources:
  requests:
    cpu: 500m
    memory: 1Gi
  limits:
    cpu: "2"
    memory: 4Gi

autoscaling:
  enabled: true
  minReplicas: 2
  maxReplicas: 20
  targetCPU: 70

# Configuración específica de IA
ai:
  modelProvider: bedrock
  embeddingModel: amazon.titan-embed-text-v2
  vectorDB: qdrant
  cacheEnabled: true
  cacheTTL: 3600

Resumen

Concepto Herramienta Uso en IA
Container Docker Empaquetar app + dependencias ML
Orquestación Kubernetes Escalar API + GPU workloads
GPU NVIDIA plugin Embedding local, inferencia
Autoscaling HPA Escalar según CPU, latencia, queue
Configuration ConfigMap/Secret API keys, model config
Package Manager Helm Despliegues reproducibles

🧠 Preguntas de Repaso

1. ¿Por qué se usa un Dockerfile multi-stage para aplicaciones de IA?

  • A) Para soportar múltiples lenguajes de programación
  • B) Para separar la fase de build (con compiladores gcc/g++) de la fase runtime, resultando en imágenes más pequeñas y seguras sin herramientas de compilación
  • C) Para ejecutar múltiples procesos en un solo container
  • D) Para soportar diferentes arquitecturas de CPU

Respuesta: B) — Multi-stage separa el build (stage 1: instala gcc/g++, compila dependencias) del runtime (stage 2: solo copia las dependencias compiladas y el código). La imagen final es más pequeña (sin compiladores) y más segura (menor superficie de ataque).

2. En Kubernetes, ¿cuál es la diferencia entre readinessProbe y livenessProbe?

  • A) readinessProbe verifica si el pod puede recibir tráfico (15s initial, 10s period); livenessProbe verifica si el proceso está vivo (30s initial, 15s period) y lo reinicia si falla
  • B) Son lo mismo pero con nombres diferentes
  • C) readinessProbe es para pods en desarrollo, livenessProbe para producción
  • D) readinessProbe verifica la CPU, livenessProbe verifica la memoria

Respuesta: A) — readinessProbe determina cuándo un pod está listo para recibir tráfico (si falla, se remueve del Service). livenessProbe verifica que el proceso siga vivo (si falla, Kubernetes reinicia el container).

3. Para solicitar GPUs en Kubernetes, ¿qué configuración es necesaria?

  • A) Solo agregar nodeSelector: gpu=true
  • B) Especificar nvidia.com/gpu: 1 en limits, usar PersistentVolumeClaim para caché de modelos, nodeSelector para nodos con GPU, y tolerations para el taint NoSchedule de nodos GPU
  • C) Instalar CUDA directamente en el contenedor
  • D) Usar un Deployment especial tipo "GPUDeployment"

Respuesta: B) — Se necesita: (1) nvidia.com/gpu: 1 en resources.limits, (2) PVC para caché de modelos que persista entre reinicios, (3) nodeSelector para restricir a nodos con GPU (ej: nvidia-tesla-t4), (4) tolerations para permitir scheduling en nodos con taint NoSchedule.

4. ¿Qué hace el HPA (HorizontalPodAutoscaler) en un deployment de IA?

  • A) Escala verticalmente agregando más CPU y memoria a cada pod
  • B) Escala horizontalmente entre un mínimo y máximo de réplicas basándose en métricas como CPU (70%) y longitud de cola de requests
  • C) Escala el clúster de Kubernetes agregando más nodos
  • D) Balancea el tráfico entre pods existentes

Respuesta: B) — HPA escala horizontalmente (agrega/remueve pods) entre minReplicas y maxReplicas basándose en métricas: CPU utilization target 70% y métricas custom como http_request_queue_length con valor promedio de 10.

¿Te gustó esta lección?

Con Pro puedes marcar progreso, hacer ejercicios, tomar quizzes, ganar XP y obtener tu constancia.

Ver planes desde $9/mes