Inicio / Blog / Gestión del tiempo de ejecución / Profundización en la orquestación de contenedores

Profundización en la orquestación de contenedores

Resumir con ChatGPT

La orquestación de contenedores es la automatización del despliegue, el escalado y la gestión. aplicaciones en contenedores. Para cargas de trabajo intensivas en IA, como el procesamiento de documentos de Konfuzio, Kubernetes (K8s) permite el escalado elástico, la gestión de recursos de GPU y la portabilidad entre nubes. La moderna infraestructura de Alojamiento en nube y Implantaciones in situ confía cada vez más en la tecnología de contenedores para maximizar la flexibilidad y la escalabilidad.

¿Qué es la orquestación de contenedores?

Contenedores 101

La tecnología de contenedores ha revolucionado la forma de desarrollar y utilizar aplicaciones. A diferencia de las máquinas virtuales, que virtualizan un sistema operativo completo, los contenedores comparten el núcleo del sistema anfitrión y encapsulan únicamente la propia aplicación, incluidas sus dependencias específicas. Esta arquitectura reduce significativamente el consumo de recursos y permite ejecutar docenas de contenedores en un hardware que sólo sería capaz de gestionar unas pocas máquinas virtuales.

Contenedor empaquetar aplicaciones con todas sus dependencias en unidades aisladas y portátiles.

Ventajas:

  • PortabilidadFunciona en todas partes (portátil, local, en la nube)
  • Aislamiento: No hay conflictos entre aplicaciones
  • EficaciaCompartir el núcleo del sistema operativo (más ligero que las máquinas virtuales)
  • VelocidadInicio en segundos (máquinas virtuales: minutos)

Ejemplo de Docker:

# Konfuzio OCR-Service
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
CMD ["python", "ocr_service.py"]
# Build & Run
docker build -t konfuzio-ocr:v1 .
docker run -d -p 8000:8000 konfuzio-ocr:v1

¿Por qué la orquestación?

Problema sin orquestación:

  • Despliegue manual en cada servidor
  • No hay reinicio automático en caso de fallo
  • Sin escalado automático
  • Configurar manualmente el equilibrio de carga

orquestación:

  • ✅ Despliegue automático mediante clúster
  • Autorreparación (reinicio en caso de error)
  • ✅ Autoescalador horizontal de vainas (HPA)
  • ✅ Descubrimiento de servicios y equilibrio de carga
  • Actualizaciones continuas sin tiempo de inactividad

Kubernetes: estándar de facto para la orquestación

Arquitectura Kubernetes

Kubernetes Cluster
├── Control Plane (Management)
│   ├── API-Server (Zentrale Schnittstelle)
│   ├── etcd (Key-Value-Store für Cluster-State)
│   ├── Scheduler (Platziert Pods auf Nodes)
│   └── Controller Manager (Steuert Desired State)
└── Worker Nodes (Ausführung)
    ├── kubelet (Agent auf jedem Node)
    ├── kube-proxy (Netzwerk-Routing)
    └── Container Runtime (Docker/containerd)

Conceptos básicos

Pod:

  • Unidad desplegable más pequeña
  • Uno o varios contenedores
  • Red y almacenamiento compartidos

Despliegue:

  • Describe el estado deseado
  • Gestión automática de ReplicaSet
  • Actualizaciones

Servicio:

  • Punto final de red estable para pods
  • Equilibrio de carga mediante réplicas
  • Nombre DNS (por ejemplo ocr-service.default.svc.cluster.local)

ConfigMap & Secret:

  • Configuración separada del código
  • Secretos encriptados

PersistentVolume:

  • Almacenamiento persistente para pods
  • Supera los reinicios de vainas

Konfuzio en Kubernetes: Ejemplo de despliegue

Configuración mínima (Docker Compose → Kubernetes)

Alt (Docker Compose):

version: '3'
services:
  ocr:
    image: konfuzio/ocr:latest
    ports:
      - "8000:8000"
    environment:
      - DB_HOST=postgres

Nuevo (Kubernetes):

# ocr-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: ocr-service
spec:
  replicas: 3  # 3 Instanzen für Load Balancing
  selector:
    matchLabels:
      app: ocr
  template:
    metadata:
      labels:
        app: ocr
    spec:
      containers:
      - name: ocr
        image: konfuzio/ocr:latest
        ports:
        - containerPort: 8000
        env:
        - name: DB_HOST
          value: postgres-service
        resources:
          requests:
            memory: "2Gi"
            cpu: "1000m"
          limits:
            memory: "4Gi"
            cpu: "2000m"
---
# ocr-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: ocr-service
spec:
  selector:
    app: ocr
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8000
  type: LoadBalancer  # Extern erreichbar

Despliegue:

kubectl apply -f ocr-deployment.yaml
kubectl get pods  # 3 OCR-Pods sollten laufen
kubectl get service ocr-service  # Externe IP holen

Autoescalado con HPA

Autoescalador horizontal de vainas:

# hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: ocr-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: ocr-service
  minReplicas: 2
  maxReplicas: 20
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 80
  behavior:
    scaleUp:
      stabilizationWindowSeconds: 30
      policies:
      - type: Percent
        value: 100  # Verdopplung möglich
        periodSeconds: 30
    scaleDown:
      stabilizationWindowSeconds: 300  # 5 Min Cooldown
      policies:
      - type: Pods
        value: 1  # Max 1 Pod/5min runterskalieren
        periodSeconds: 300

Funcionalidad:

Normal-Betrieb (1.000 Docs/Tag):
- CPU: 30% durchschnittlich
- HPA: 2 Pods (Minimum)
Lastspitze (10.000 Docs/Tag):
- CPU: 85% (über Threshold 70%)
- HPA skaliert hoch: 2 → 4 → 8 → 12 Pods
- CPU sinkt auf 60%
Nach Lastspitze:
- CPU: 20% (unter Threshold)
- HPA skaliert runter: 12 → 11 → 10 → ... → 2 Pods
  (langsam, 1 Pod alle 5 Minuten)

Gestión de GPU en Kubernetes

Plugin de dispositivos NVIDIA:

# Installation
kubectl create -f https://raw.githubusercontent.com/NVIDIA/k8s-device-plugin/main/nvidia-device-plugin.yml
# Verifizierung
kubectl get nodes -o json | jq '.items[].status.allocatable'
# Sollte "nvidia.com/gpu: 2" zeigen (wenn Node 2 GPUs hat)

Vaina de GPU:

apiVersion: v1
kind: Pod
metadata:
  name: ocr-gpu
spec:
  containers:
  - name: ocr
    image: konfuzio/ocr-gpu:latest
    resources:
      limits:
        nvidia.com/gpu: 1  # 1 GPU anfordern

Uso compartido de la GPU (MIG - Multi-Instance GPU):

# A100 GPU in 7 Instanzen aufteilen
apiVersion: v1
kind: Pod
metadata:
  name: ocr-mig
spec:
  containers:
  - name: ocr
    image: konfuzio/ocr:latest
    resources:
      limits:
        nvidia.com/mig-1g.5gb: 1  # 1/7 einer A100 (5GB VRAM)

Ventajas:

  • ✅ Varias cargas de trabajo comparten una GPU cara
  • ✅ Mejor utilización de la GPU (sin pérdida de tiempo de inactividad).
  • ✅ Aislamiento (una carga de trabajo no bloquea la GPU para otras).

Almacenamiento: volúmenes persistentes para modelos ML

Problema: Los pods son efímeros (pueden reiniciarse en cualquier momento, los datos desaparecen)
Solución: PersistentVolumes para modelos, documentos

PVC (reclamación de volumen persistente):

# models-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: models-storage
spec:
  accessModes:
    - ReadWriteMany  # Mehrere Pods gleichzeitig
  storageClassName: nfs  # NFS für Shared-Access
  resources:
    requests:
      storage: 100Gi
---
# ocr-deployment mit PVC
apiVersion: apps/v1
kind: Deployment
metadata:
  name: ocr-service
spec:
  template:
    spec:
      containers:
      - name: ocr
        image: konfuzio/ocr:latest
        volumeMounts:
        - name: models
          mountPath: /app/models
      volumes:
      - name: models
        persistentVolumeClaim:
          claimName: models-storage

Ventajas:

  • No es necesario descargar los modelos cada vez que se inicia un pod.
  • Acceso compartido: todos los módulos OCR utilizan los mismos modelos
  • Supera los reinicios de vainas

Distribuciones de Kubernetes: ¿Cuál elegir?

Kubernetes gestionados (nube)

AWS EKS (Elastic Kubernetes Service):

  • Ventajas: Profundamente integrado con los servicios de AWS (IAM, VPC, EBS)
  • Desventajas: Configuración específica de AWS (menos portátil)
  • Costes: 0,10€/h para costes de Plano de Control + Nodo Trabajador
  • Ver: Alojamiento en nube

Google GKE (motor Google Kubernetes):

  • Ventajas: Mejor integración de K8s (K8s se desarrolló en Google)
  • Desventajas: Ecosistema más pequeño que AWS
  • Costes: Costes gratuitos de plano de control + nodo trabajador

Azure AKS (Azure Kubernetes Service):

  • Ventajas: Mejor integración de Microsoft (AD, DevOps)
  • Desventajas: Configuración de red más compleja
  • Costes: Costes gratuitos de plano de control + nodo trabajador

Kubernetes autogestionado (local)

Ranchero (RKE/RKE2):

  • Ventajas: Sencilla configuración local, interfaz web
  • Desventajas: Menos soporte empresarial que Red Hat
  • Costes: Código abierto (gratuito)

Red Hat OpenShift:

  • Ventajas: Compatibilidad con empresas, funciones de seguridad, centro de operaciones
  • Desventajas: Costes de licencia elevados (aprox. 50 euros/núcleo/año)
  • Costes: a partir de 15.000 euros/año para las agrupaciones pequeñas

Kubernetes vainilla (kubeadm):

  • Ventajas: Máximo control, sin dependencia del proveedor
  • Desventajas: Configuración compleja, sin asistencia
  • Costes: Código abierto (gratuito)

K3s (K8s ligeros):

  • Ventajas: Ocupa poco espacio (40 MB binarios), ideal para Edge/IoT
  • Desventajas: Se han eliminado algunas funciones (integraciones con proveedores en la nube)
  • Costes: Código abierto (gratuito)

Recomendación Konfuzio:

  • Nube: GKE (mejor experiencia K8s) o EKS (mayor ecosistema)
  • En las instalaciones: Rancher (simple) u OpenShift (Enterprise con soporte)
  • Borde: K3s

Arquitectura de microservicios con Kubernetes

Monolito (antiguo):

┌───────────────────────┐
│   Konfuzio Monolith   │
│  (API + OCR + DB)     │
└───────────────────────┘
  • Un contenedor grande
  • Difícil de escalar (todo o nada)
  • Un error lo echa todo abajo

Microservicios (nuevo):

┌─────────┐   ┌─────────┐   ┌──────────┐
│   API   │ → │   OCR   │ → │ Extract  │
│ Gateway │   │ Service │   │ Service  │
└─────────┘   └─────────┘   └──────────┘
     ↓              ↓               ↓
┌───────────────────────────────────────┐
│         PostgreSQL (Database)         │
└───────────────────────────────────────┘
  • Cada servicio escala de forma independiente
  • Pasarela API: 2 réplicas (carga baja)
  • Servicio OCR: 10 réplicas (cálculo intensivo)
  • Servicio de extracción: 5 réplicas

Despliegue de Kubernetes:

# api-gateway (wenig Last)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-gateway
spec:
  replicas: 2
  template:
    spec:
      containers:
      - name: api
        resources:
          requests:
            cpu: "500m"
            memory: "1Gi"
---
# ocr-service (rechenintensiv, GPU)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: ocr-service
spec:
  replicas: 10
  template:
    spec:
      containers:
      - name: ocr
        resources:
          requests:
            cpu: "2000m"
            memory: "4Gi"
            nvidia.com/gpu: "1"
---
# extraction-service (mittel)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: extraction-service
spec:
  replicas: 5
  template:
    spec:
      containers:
      - name: extraction
        resources:
          requests:
            cpu: "1000m"
            memory: "2Gi"

Ventajas:

  • Utilización óptima de los recursos (cada servicio según sus necesidades)
  • Errores aislados (el fallo del OCR no afecta a la API)
  • Despliegues independientes (actualización de la extracción sin tiempo de inactividad del OCR)

CI/CD con Kubernetes

GitOps: Infraestructura como código

Concepto:

  • Repositorio Git como única fuente de verdad
  • Cambios mediante commit de Git (sin cambios manuales) kubectl apply)
  • Despliegue automático mediante canalización CI/CD

Flux/ArgoCD (operadores de GitOps):

# FluxCD installieren
flux bootstrap github \
  --owner=konfuzio \
  --repository=k8s-manifests \
  --path=clusters/production
# Flux überwacht Git-Repo
# Bei neuem Commit:
git commit -m "Update OCR to v2.3"
git push
# → Flux erkennt Änderung automatisch
# → Deployed neue Version auf Cluster

Ventajas:

  • Registro de auditoría (todos los cambios en el historial de Git)
  • ✅ Rollback simple (git revert)
  • Recuperación en caso de catástrofe (recreación del clúster a partir de Git)

Ejemplo de canalización CI/CD (GitHub Actions)

# .github/workflows/deploy.yml
name: Build and Deploy
on:
  push:
    branches: [main]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    - name: Build Docker Image
      run: |
        docker build -t konfuzio/ocr:${{ github.sha }} .
    - name: Push to Registry
      run: |
        docker push konfuzio/ocr:${{ github.sha }}
  deploy:
    needs: build
    runs-on: ubuntu-latest
    steps:
    - name: Update Kubernetes Manifest
      run: |
        kubectl set image deployment/ocr-service \
          ocr=konfuzio/ocr:${{ github.sha }} \
          --record
    - name: Wait for Rollout
      run: kubectl rollout status deployment/ocr-service
    - name: Run Smoke Tests
      run: |
        curl https://api.konfuzio.com/health
        # Wenn Fehler: kubectl rollout undo

Control y observabilidad

Pila Prometheus + Grafana

Instalación (casco):

# Prometheus Operator
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm install prometheus prometheus-community/kube-prometheus-stack
# Zugriff
kubectl port-forward svc/prometheus-grafana 3000:80
# → http://localhost:3000 (User: admin, Password: prom-operator)

Métricas clave:

  • Nivel de nodo: CPU, RAM, Disco, Red
  • Nivel de la vaina: Contenedor CPU/RAM, recuento de reinicios
  • Aplicación: Tasa de solicitudes, latencia, tasa de errores
  • GPU: Utilización de la GPU, uso de la VRAM, temperatura

Grafana dashboard (ejemplo):

┌─────────────────────────────────────────┐
│  Konfuzio OCR Service Dashboard         │
├─────────────────────────────────────────┤
│  Pods Running: 12 / 12 (100%)           │
│  Request Rate: 150 req/s                │
│  P95 Latency: 1.8s                      │
│  Error Rate: 0.05%                      │
├─────────────────────────────────────────┤
│  [Graph: CPU Usage over Time]           │
│  [Graph: Memory Usage]                  │
│  [Graph: GPU Utilization]               │
└─────────────────────────────────────────┘

Registro: pila ELK o Loki

Loki (alternativa ligera a Elasticsearch):

# Loki + Promtail (Log-Collector)
helm install loki grafana/loki-stack
# Logs in Grafana
# → Explore → Data Source: Loki
# → Query: {app="ocr-service"} |= "error"

Ventajas:

  • Agregación centralizada de registros en todos los pods
  • Correlación con métricas (Prometheus + Loki en un solo cuadro de mandos)
  • Consultas basadas en etiquetas (similares a Prometheus)

Seguridad: buenas prácticas

1. políticas de red (microsegmentación)

# Nur API-Gateway darf auf OCR-Service zugreifen
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: ocr-service-policy
spec:
  podSelector:
    matchLabels:
      app: ocr
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: api-gateway
    ports:
    - protocol: TCP
      port: 8000

2. normas de seguridad de la vaina

# Restrictive Pod Security
apiVersion: v1
kind: Pod
metadata:
  name: ocr-secure
spec:
  securityContext:
    runAsNonRoot: true  # Nicht als Root laufen
    runAsUser: 1000
    fsGroup: 1000
  containers:
  - name: ocr
    securityContext:
      allowPrivilegeEscalation: false
      readOnlyRootFilesystem: true  # Read-Only-FS
      capabilities:
        drop:
        - ALL  # Alle Linux-Capabilities droppen

3. gestión del secreto

# Secrets verschlüsselt in etcd
kubectl create secret generic db-password \
  --from-literal=password=SuperSecure123
# In Pod nutzen
apiVersion: v1
kind: Pod
spec:
  containers:
  - name: app
    env:
    - name: DB_PASSWORD
      valueFrom:
        secretKeyRef:
          name: db-password
          key: password

Mejor alternativa: Secretos sellados:

# Sealed Secrets: Verschlüsselte Secrets in Git
kubeseal < secret.yaml > sealed-secret.yaml
git add sealed-secret.yaml
# Sicher für Git (nur Cluster kann entschlüsseln)

Mejor alternativa: Integración de bóvedas:

Optimización de costes

1. instancias puntuales / máquinas virtuales preferentes

AWS:

# Node Group mit Spot Instances (70% günstiger)
apiVersion: v1
kind: Node
metadata:
  labels:
    node.kubernetes.io/lifecycle: spot

Toleraciones de Kubernetes:

# Pods tolerieren Spot-Instance-Evictions
apiVersion: v1
kind: Pod
spec:
  tolerations:
  - key: "node.kubernetes.io/lifecycle"
    operator: "Equal"
    value: "spot"
    effect: "NoSchedule"

Importante: Sólo para cargas de trabajo sin estado (stateless)

2. configurar correctamente las solicitudes de recursos

❌ Error: Sobreaprovisionamiento

resources:
  requests:
    cpu: "4000m"
    memory: "8Gi"
# Tatsächliche Nutzung: 500m CPU, 2Gi RAM
# → 75% Ressourcen verschwendet

✅ Correcto: Optimizar tras la supervisión

# Prometheus-Query: Tatsächliche Nutzung
avg(rate(container_cpu_usage_seconds_total[1d]))
# Ergebnis: 550m
# Requests entsprechend setzen (+ 20% Buffer)
resources:
  requests:
    cpu: "700m"
    memory: "2.5Gi"

3. autoescalador vertical de vainas (VPA)

# VPA empfiehlt/setzt automatisch Resource Requests
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
  name: ocr-vpa
spec:
  targetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: ocr-service
  updatePolicy:
    updateMode: "Auto"  # Automatisch Pods mit neuen Requests neu starten

Solución de problemas

Pod no arranca

# Status prüfen
kubectl get pods
# NAME                   READY   STATUS              RESTARTS
# ocr-6d5b8f9-xxx        0/1     ImagePullBackOff    0
# Logs
kubectl describe pod ocr-6d5b8f9-xxx
# Events: Failed to pull image "konfuzio/ocr:v99": not found
# → Image-Tag falsch oder Registry-Auth fehlt

Pod se bloquea repetidamente

# Logs des abstürzenden Pods
kubectl logs ocr-6d5b8f9-xxx --previous
# OOMKilled → Speicher-Limit zu niedrig
# Memory-Limit erhöhen
resources:
  limits:
    memory: "8Gi"  # vorher: 2Gi

¿Le ha resultado útil esta página?

Gracias por sus comentarios.

¿Podría darme su opinión? (anónimo)

Desarrollamos software de inteligencia artificial para empresas y evitamos deliberadamente los molestos banners publicitarios. A través de nuestros artículos, documentamos temas que nos ocupan e interesan y también financian nuestro pan de cada día.

Como nuestro contenido es gratuito, sus comentarios son nuestro elogio.

Cada autor lee personalmente tus comentarios anónimos, aunque la IA podría automatizarlos, e integra las sugerencias constructivas directamente en la siguiente revisión o las utiliza como inspiración para el siguiente artículo.



    </artículo
    • Florian Zyprian
      (Autor)

      Como Director Técnico de Helm & Nagel GmbH, la empresa que está detrás del Konfuzio.

    es_ESES