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=postgresNuevo (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 erreichbarDespliegue:
kubectl apply -f ocr-deployment.yaml
kubectl get pods # 3 OCR-Pods sollten laufen
kubectl get service ocr-service # Externe IP holenAutoescalado 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: 300Funcionalidad:
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 anfordernUso 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-storageVentajas:
- 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 ClusterVentajas:
- 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 undoControl 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: 80002. 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 droppen3. 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: passwordMejor 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: spotToleraciones 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 startenSolució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 fehltPod 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