Ollama Kubernetes: Manifiestos y Despliegue Prod

2026.04.01
Technology
477 Words
Ollama Kubernetes: Manifiestos y Despliegue Prod

Parte 3 de 4. Parte 1 | Parte 2 | Parte 3 | Parte 4

Paso 3: Desplegar Ollama con Manifiestos Listos para Producción

Este manifiesto agrupa Deployment, PVC y ConfigMap en un solo archivo que he usado en seis clusters de producción.

02-ollama-deployment.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: ollama-models
namespace: ollama
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 100Gi
storageClassName: fast-ssd # Reemplaza con tu StorageClass SSD
---
apiVersion: v1
kind: ConfigMap
metadata:
name: ollama-config
namespace: ollama
data:
OLLAMA_HOST: "0.0.0.0"
OLLAMA_PORT: "11434"
OLLAMA_NUM_PARALLEL: "4"
OLLAMA_MAX_LOADED_MODELS: "2"
OLLAMA_KEEP_ALIVE: "30m"
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: ollama
namespace: ollama
labels:
app.kubernetes.io/name: ollama
app.kubernetes.io/component: inference-server
spec:
replicas: 1
strategy:
type: Recreate # Requerido para PVCs RWO
selector:
matchLabels:
app.kubernetes.io/name: ollama
template:
metadata:
labels:
app.kubernetes.io/name: ollama
spec:
nodeSelector:
gpu-type: nvidia
tolerations:
- key: "nvidia.com/gpu"
operator: "Equal"
value: "true"
effect: "NoSchedule"
terminationGracePeriodSeconds: 60
containers:
- name: ollama
image: ollama/ollama:0.5.7
ports:
- containerPort: 11434
name: http
protocol: TCP
envFrom:
- configMapRef:
name: ollama-config
resources:
requests:
cpu: "4"
memory: "16Gi"
nvidia.com/gpu: "1"
limits:
cpu: "8"
memory: "64Gi" # 32Gi para modelos 7B-13B, 64Gi para 70B
nvidia.com/gpu: "1"
volumeMounts:
- name: models
mountPath: /root/.ollama
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "sleep 15"]
livenessProbe:
httpGet:
path: /api/tags
port: 11434
initialDelaySeconds: 60
periodSeconds: 30
timeoutSeconds: 10
failureThreshold: 3
readinessProbe:
httpGet:
path: /api/tags
port: 11434
initialDelaySeconds: 10
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: false
runAsNonRoot: false
capabilities:
drop:
- ALL
volumes:
- name: models
persistentVolumeClaim:
claimName: ollama-models

Decisiones clave validadas en producción:

  • strategy: Recreate: El PVC ReadWriteOnce no puede montarse en dos pods simultáneamente.
  • Límites de recursos: 64Gi soporta modelos 70B con cuantización. 32Gi para modelos 7B-13B.
  • OLLAMA_NUM_PARALLEL: "4": Agrupa solicitudes concurrentes. Incrementa solo si sobra VRAM.
  • lifecycle.preStop + terminationGracePeriodSeconds: 60: Da 15 segundos a inferencias en vuelo antes de SIGKILL.
  • OLLAMA_KEEP_ALIVE: "30m": Mantiene modelos en VRAM 30 minutos tras la última solicitud, reduciendo latencia de arranque en frío.

Aplica el manifiesto:

Terminal window
kubectl apply -f 02-ollama-deployment.yaml

Espera a que el pod esté Running:

Terminal window
kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=ollama -n ollama --timeout=300s

Paso 4: Exponer Ollama con Service e Ingress

Crea un Service ClusterIP para comunicación interna y un Ingress para acceso externo.

03-service-ingress.yaml
apiVersion: v1
kind: Service
metadata:
name: ollama
namespace: ollama
labels:
app.kubernetes.io/name: ollama
spec:
type: ClusterIP
selector:
app.kubernetes.io/name: ollama
ports:
- port: 11434
targetPort: 11434
name: http
protocol: TCP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ollama
namespace: ollama
annotations:
nginx.ingress.kubernetes.io/proxy-body-size: "512m"
nginx.ingress.kubernetes.io/proxy-read-timeout: "300"
nginx.ingress.kubernetes.io/proxy-send-timeout: "300"
spec:
ingressClassName: nginx
rules:
- host: ollama.<your-cluster-domain>.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: ollama
port:
number: 11434

Advertencia: Ollama no tiene autenticación integrada. Expón solo con proxy de auth (oauth2-proxy, Authelia) o VPN interna.

Tip: proxy-body-size: "512m" es crítico si subes modelos personalizados vía API.

Aplica y verifica:

Terminal window
kubectl apply -f 03-service-ingress.yaml
kubectl get ingress -n ollama

Paso 5: Verificación: Descargar Modelo y Ejecutar Inferencia

Con el pod ejecutándose, descarga un modelo y prueba la inferencia:

Terminal window
kubectl exec -it deployment/ollama -n ollama -- ollama pull llama3.2

Salida esperada:

pulling manifest
pulling dde5aa3fc5ff... 100% ▕████████████████▏ 2.0 GB
verifying sha256 digest
writing manifest
success

Prueba inferencia vía port-forward:

Terminal window
kubectl port-forward svc/ollama 11434:11434 -n ollama

En otra terminal:

Terminal window
curl http://localhost:11434/api/generate -d '{
"model": "llama3.2",
"prompt": "Why is Kubernetes the right platform for self-hosted AI?",
"stream": false
}'

Si ves {"done":true,"response":"..."}, tu stack de inferencia local LLM está activo.

Verifica el endpoint compatible con OpenAI:

Terminal window
curl http://localhost:11434/v1/chat/completions -H "Content-Type: application/json" -d '{
"model": "llama3.2",
"messages": [{"role": "user", "content": "Hello!"}]
}'

FAQ

¿Por qué usar strategy: Recreate en lugar de RollingUpdate?

Los PVCs ReadWriteOnce solo se montan en un pod a la vez. Un rolling update crearía un nuevo pod antes de terminar el anterior, y el nuevo fallaría al montar el PVC. Recreate termina el pod anterior primero, luego arranca el nuevo.

¿Qué verifican los liveness y readiness probes?

Ambos golpean /api/tags. Liveness (cada 30s tras 60s de retardo) reinicia el pod si Ollama deja de responder. Readiness (cada 10s tras 10s de retardo) saca el pod del Service hasta que Ollama esté listo. He ajustado estos retardos para considerar el tiempo de carga de modelos.

¿Cómo verifico que la aceleración GPU funciona?

Ejecuta kubectl exec -it deployment/ollama -n ollama -- nvidia-smi dentro del pod. Si ves procesos GPU con el PID de Ollama y la utilización sube a 90-100% durante inferencia, la GPU está activa.

¿Qué hace OLLAMA_KEEP_ALIVE: "30m"?

Mantiene el modelo cargado en VRAM 30 minutos tras la última solicitud. Sin esto, Ollama descarga el modelo inmediatamente después de cada inferencia, añadiendo 5-30 segundos de latencia de arranque en frío. Para chats, recomiendo 30m. Para batch, usa -1 (nunca descargar).

¿Puedo ejecutar múltiples modelos simultáneamente?

Sí, hasta OLLAMA_MAX_LOADED_MODELS: "2". Cada modelo consume VRAM. En un A100 de 80 GB puedes ejecutar dos modelos 7B simultáneamente. Para producción multi-modelo, recomiendo el patrón de un Deployment por modelo en la Parte 4.

Siguientes Pasos

Tu instancia de Ollama funciona en Kubernetes con GPU, almacenamiento persistente e ingress. En la Parte 4 cubro endurecimiento de producción: alternativas a HPA, NetworkPolicies, monitoreo, troubleshooting y cuándo NO usar Kubernetes.

Partes de esta serie: ← Parte 2 | Parte 4 →

# Ollama # Kubernetes # ia-self-hosted # inferencia-gpu # nvidia # DevOps # Llm