Introducció

Executar càrregues de treball IA a Kubernetes introdueix reptes únics que difereixen significativament del desplegament d’aplicacions tradicional. La planificació de GPU, els patrons de càrrega de models i l’observabilitat requereixen enfocaments especialitzats. Aquest document recull les nostres decisions d’arquitectura i el raonament darrere d’elles.

Arquitectura Nucle

El nostre cluster K3s per a càrregues de treball IA segueix una arquitectura per capes:

                    ┌──────────────────────┐
                    │     Traefik L7       │
                    │   Ingress / TLS      │
                    └──────────┬───────────┘

              ┌────────────────┼────────────────┐
              ▼                ▼                ▼
       ┌────────────┐  ┌────────────┐  ┌────────────┐
       │  Hermes    │  │  LiteLLM   │  │  Cron Jobs │
       │  Agents    │  │  Gateway   │  │  (scheduled│
       │  (workloads│  │            │  │   tasks)   │
       └────────────┘  └────────────┘  └────────────┘
              │                │                │
              ▼                ▼                ▼
       ┌────────────────────────────────────────────┐
       │              K3s Cluster                   │
       │  ┌──────────┐  ┌──────────┐               │
       │  │ Control  │  │  Worker  │  (gamorcloud01)│
       │  │  Plane   │  │  Node    │               │
       │  └──────────┘  └──────────┘               │
       │  ┌──────────┐  ┌──────────┐               │
       │  │  GPU     │  │  GPU     │  (gx10-db4d)  │
       │  │  Worker  │  │  Worker  │               │
       │  └──────────┘  └──────────┘               │
       └────────────────────────────────────────────┘

Estratègia de Separació de Nodes

Dividem el cluster en dos nodes especialitzats:

Node del Plan de Control (gamorcloud01)

  • Rol: servidor d’API, gestor de controladors, planificador
  • CPU: ús general (AMD EPYC / Intel Xeon)
  • RAM: 32 GB
  • Emmagatzematge: 200 GB NVMe
  • Càrregues de treball: components del pla de control, agents lleus, monitoratge

Node de GPU (gx10-db4d)

  • Rol: càrregues de treball IA, servidors de models, inferència
  • GPU: NVIDIA L4 (24 GB VRAM) o A100 (80 GB VRAM)
  • RAM: 64 GB mínim
  • Emmagatzematge: 500 GB NVMe per a pesos de models
  • Càrregues de treball: LiteLLM, servidors de models, tasques intensives en GPU

Per Què Separar?

  1. Planificació de GPU: el NVIDIA GPU Operator utilitzadors selectors de node i tolerances
  2. Contenció de recursos: les càrregues de treball IA fan picos de memòria de GPU — no les compartiu amb el pla de control
  3. Eficiència de cost: els nodes de GPU són caros; mantingueu el pla de control en maquinari més econòmic
  4. Aïllament d’errors: la fallada d’un node de GPU no hauria de caure el pla de control

Planificació de GPU

NVIDIA GPU Operator

El NVIDIA GPU Operator automatitza la instal·lació del controlador de GPU, el plugin de dispositius i el DCGM:

# Install GPU Operator
helm repo add nvidia https://helm.ngc.nvidia.com/nvidia
helm repo update
helm install gpu-operator nvidia/gpu-operator \
  --namespace gpu-operator \
  --create-namespace \
  --set toolkit.enabled=true \
  --set driver.enabled=false \
  --set devicePlugin.enabled=true \
  --set dcgm.enabled=true

Etiquetes i Tolerances de Node

Els nodes de GPU reben etiquetes especials:

apiVersion: v1
kind: Node
metadata:
  name: gx10-db4d
  labels:
    nvidia.com/gpu.deploy-gpu-feature: "true"
    nvidia.com/gpu.deploy-container-engine-launcher: "true"
    nvidia.com/gpu.deploy-dcgm: "true"
    ai-workload: "true"
    kubernetes.io/os: linux
  taints:
  - key: nvidia.com/gpu
    effect: NoSchedule
    value: present

Els pod que necessiten GPU afegeixen la tolerança corresponent:

tolerations:
- key: "nvidia.com/gpu"
  operator: "Exists"
  effect: "NoSchedule"

Sol·licituds de Recursos

Cada pod d’IA ha de sol·licitar recursos de GPU:

resources:
  requests:
    nvidia.com/gpu: "1"
    memory: "16Gi"
    cpu: "4"
  limits:
    nvidia.com/gpu: "1"
    memory: "24Gi"
    cpu: "8"

Sense la sol·licitud nvidia.com/gpu, el pod no es planificarà en un node de GPU.

Arquitectura de Servidor de Models

Patró de Registre de Models

Emmagatzemeu els pesos de models en un directori compartit amb l’afinitat de node adequada:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: model-registry
spec:
  capacity:
    storage: 500Gi
  accessModes:
  - ReadWriteOnce
  storageClassName: nvidia-ai-store
  local:
    path: /data/model-registry
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
          - gx10-db4d
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: model-registry-pvc
  namespace: ia-services-payed
spec:
  accessModes:
  - ReadWriteOnce
  storageClassName: nvidia-ai-store
  resources:
    requests:
      storage: 500Gi
  volumeName: model-registry

Comparació de Servidors de Models

Servidor Memòria de GPU Sol·licituds Concurrents Temps d’Arrancada Ideal per
vLLM Alta 30+ Ràpida Servidat d’alt rendiment
TGI (Text Generation) Mitjana 10-15 Mitjà Nadiu de codi obert
LiteLLM N/A N/A Instantani Gateway d’API, no inferència

La Nosta Opció: vLLM + LiteLLM

Utilitzem vLLM per a la inferència real i LiteLLM com a gateway d’API:

# vLLM deployment (runs on GPU node)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: vllm-serving
  namespace: ia-services-payed
spec:
  replicas: 1
  selector:
    matchLabels:
      app: vllm
  template:
    spec:
      containers:
      - name: vllm
        image: vllm/vllm-openai:v0.6.3
        args:
        - --model
        - "nvidia/Nemotron-4-35B"
        - --tensor-parallel-size
        - "1"
        - --max-model-len
        - "8192"
        resources:
          limits:
            nvidia.com/gpu: "1"
        volumeMounts:
        - name: model-cache
          mountPath: /data/models
      volumes:
      - name: model-cache
        persistentVolumeClaim:
          claimName: model-registry-pvc

Pila d’Observabilitat

Monitoratge de GPU amb DCGM

El DCGM exportava mètriques de GPU a Prometheus:

# Prometheus scrape config (added to prometheus.yaml)
- job_name: 'dcgm-exporter'
  static_configs:
  - targets: ['gpu-operator-dcgm-exporter.gpu-operator:9400']

Mètriques clau de DCGM:

Mètrica Descripció Llindal d’Alerta
DCGM_FI_DEV_POWER_USAGE Consum de potència de GPU (W) > 350W
DCGM_FI_DEV_TEMP_GPU Temperatura de GPU (°C) > 85°C
DCGM_FI_DEV_MEM_USED Memòria de GPU utilitzada (MiB) > 90%
DCGM_FI_DEV_XID_ERRORS Errors XID de GPU > 0

Observabilitat de Nivell de Sol·licitud

Cada sol·licitud d’IA ha de ser rastrejada:

# Add tracing headers to requests
import requests
import uuid

request_id = str(uuid.uuid4())
headers = {
    "X-Request-ID": request_id,
    "X-Model": "nemotron-35b",
    "X-Consumer": "hermes-agent",
}

response = requests.post(
    "http://litellm.ia-services-payed.svc.cluster.local:4000/v1/completions",
    headers=headers,
    json=payload,
)

Registre Centralitzat

Tots els components fan registre en format JSON per a una anàlisi fàcil:

# Container log config
env:
- name: LOG_LEVEL
  value: "info"
- name: LOG_FORMAT
  value: "json"
- name: LOG_OUTPUT
  value: "stdout"

Arquitectura de Xarxa

Configuració d’Ingress

Traefik gestiona la ruta externa amb TLS automàtic:

# TraefikMiddleware for rate limiting
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: rate-limit
  namespace: ia-services-payed
spec:
  rateLimit:
    average: 100
    burst: 200
    period: 1s
---
# TraefikIngressRoute
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
  name: litellm-ingress
  namespace: ia-services-payed
spec:
  entryPoints:
  - websecure
  routes:
  - match: Host(`intranet.cris-mora-cv.es`) && PathPrefix(`/api/litellm`)
    kind: Rule
    services:
    - name: litellm
      port: 4000
    middlewares:
    - name: rate-limit
    - name: strip-prefix
  tls:
    secretName: intranet-tls

Consideracions de Malla de Serveis

Per a configuracions de múltiples clusters, considereu:

  • Serveis integrats de K3s: suficients per a un sol cluster
  • Istio/Linkerd: afegeix complexitat, no val la pena per la majoria de configuracions
  • Traefik + cert-manager: la nostra opció — senzill, efectiu, nadiu de Kubernetes

Alta Disponibilitat

HA de Servidor de Models

Per a producció, executeu múltiples rèpliques darrere d’un servei:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: litellm-ha
  namespace: ia-services-payed
spec:
  replicas: 3
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0

Escalat Automàtic amb KEDA

Escaleu els servidors de models segons la profunditat de la cua de sol·licituds:

apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: litellm-autoscaler
  namespace: ia-services-payed
spec:
  scaleTargetRef:
    name: litellm-ha
  triggers:
  - type: prometheus
    metadata:
      serverAddress: http://prometheus-monitoring.svc.cluster.local:9090
      query: |
        sum(rate(litellm_proxy_request_count[2m]))
      threshold: "50"

Consideracions de Cost

Optimització del Cost de GPU

Estratègia Estalvi Esforç d’Implementació
Quantització 2-3x reducció de VRAM Baix
Batching de models 3-5x augment de rendiment Mitjà
Preció mixta (FP16) 2x reducció de VRAM Baix
Escalat programat 40-60% reducció del cost de GPU Alt

Monitoratge de Cost

Segeu les hores de GPU i l’ús de tokens:

# GPU hours per day
sum(increase(nvidia_gpu_utilization[1d])) / 60 / 60

# Cost per model
sum(rate(litellm_proxy_token_usage[1h])) * model_price_per_token

Conclusió

Els principis d’arquitectura clau per a càrregues de treball IA a Kubernetes:

  1. Separeu la GPU del pla de control — eviteu la contenció de recursos
  2. Utilitzeu el GPU Operator — automatitza el controlador, el plugin de dispositius i el DCGM
  3. Sol·licitueu recursos de GPU explícitament — els pod sense sol·licituds de GPU no es planificaran
  4. Monitoratgeu-ho tot — mètriques de GPU, latència de sol·licitud, ús de tokens
  5. Comenceu petit, escaleu quan calgui — K3s + un node de GPU és suficient per començar

L’arquitectura escala des d’un sol Raspberry Pi fins a un cluster de múltiples nodes amb treballadors de GPU. Els primers de Kubernetes (Deployments, Services, PVs) es mantenen iguals independentment de l’escala.