Ollama: Implantação em Produção
Implantação em produção é o salto final da IA local do laboratório para o campo de batalha — altamente disponível, escalável e resiliente a desastres.
least_conn (menos conexões) é recomendada porque a duração das requisições de Inferência varia significativamente.
📋 Pré-requisitos: Você deve dominar o seguinte primeiro
- Aula 15: Implantação Containerizada com Docker
- Aula 20: Fortalecimento de Segurança
1. O Que Você Vai Aprender
- Design de arquitetura de produção: Balanceamento de carga + cluster multi-nó
- Alta disponibilidade: Health checks + failover automático
- Estratégias de escalabilidade: Escalonamento horizontal baseado em fila
- Implantação Kubernetes: Helm Charts e agendamento de GPU
- Recuperação de desastres e rollback: Gerenciamento de versões de modelo e configuração-como-código
2. Uma História Real de Uma Empreendedora SaaS
least_conn (menos conexões) em vez do padrão round_robin. As durações das requisições de Inferência do Ollama variam muito (Q&A curto leva 1 segundo, texto longo leva 30 segundos), e least_conn distribui a carga de forma mais uniforme.
(1) O Problema: Ponto Único de Falha Causa Interrupção Total do Serviço
O SupportBot da Alice roda em um único servidor Ollama. Manutenção do servidor ou GPU OOM causa interrupção do serviço, derrubando o sistema de Atendimento ao Cliente por 2 horas e afetando 500+ clientes.
(2) A Solução: Cluster de Alta Disponibilidade Multi-Nó
Implantar um cluster Ollama de 3 nós + balanceador de carga Nginx, com failover automático se qualquer nó cair:
flowchart TD
A[Nginx Load Balancer] --> B[Ollama Node 1<br/>GPU 1]
A --> C[Ollama Node 2<br/>GPU 2]
A --> D[Ollama Node 3<br/>GPU 3]
3. Design de Arquitetura de Produção
(1) Nó Único vs Multi-Nó
| Dimensão | Nó Único | Cluster Multi-Nó |
|---|---|---|
| Disponibilidade | Ponto único de falha | Tolerância a falhas N-1 |
| Throughput | Limitado | Escalonamento linear |
| Custo | Baixo | Alto (3x+) |
| Complexidade de operações | Baixa | Média |
| Caso de Uso | Dev/pequena escala | Produção |
(2) Visão Geral da Arquitetura de Produção
flowchart TD
A[Internet] --> B[WAF / CDN]
B --> C[Nginx<br/>SSL + Auth + LB]
C --> D[Ollama Node 1<br/>GPU + Model]
C --> E[Ollama Node 2<br/>GPU + Model]
C --> F[Ollama Node 3<br/>GPU + Model]
D --> G[NFS / S3<br/>Shared Model Storage]
E --> G
F --> G
D --> H[Chroma Cluster]
E --> H
F --> H
I[Prometheus] --> D
I --> E
I --> F
I --> J[Grafana Dashboard]
(3) Lista de Componentes
| Componente | Quantidade | Especificações | Propósito |
|---|---|---|---|
| Nginx LB | 1 | 2 vCPU, 4GB RAM | Balanceamento de carga + SSL |
| Nó Ollama | 3 | 8 vCPU, 16GB RAM, 1x RTX 4090 | Inferência de modelo |
| Chroma | 1 | 4 vCPU, 8GB RAM, SSD | Armazenamento vetorial |
| NFS/S3 | 1 | 500GB+ | Armazenamento compartilhado de modelos |
| Prometheus | 1 | 2 vCPU, 4GB RAM | Monitoramento |
4. Alta Disponibilidade
(1) Health Checks e Failover
| Mecanismo | Método de Verificação | Intervalo | Timeout |
|---|---|---|---|
| Verificação passiva Nginx | Marcar indisponível em caso de falha | Cada requisição | 5s |
| Health check ativo | GET periódico /api/tags | 10s | 3s |
| Verificação de camada de aplicação | Teste de Inferência personalizado | 30s | 10s |
▶ Exemplo 1: Configuração do Balanceador de Carga Nginx
# /etc/nginx/conf.d/ollama-lb.conf
upstream ollama_cluster {
least_conn; # Route to least busy node
server ollama-node1:11434 max_fails=3 fail_timeout=30s;
server ollama-node2:11434 max_fails=3 fail_timeout=30s;
server ollama-node3:11434 max_fails=3 fail_timeout=30s;
}
server {
listen 443 ssl;
server_name ai.example.com;
ssl_certificate /etc/ssl/certs/ai.example.com.crt;
ssl_certificate_key /etc/ssl/private/ai.example.com.key;
location /v1/ {
# API Key authentication
if ($http_x_api_key != "your-secret-key") {
return 401 '{"error": "Unauthorized"}';
}
proxy_pass http://ollama_cluster;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_connect_timeout 5s;
proxy_read_timeout 120s;
# Rate limiting
limit_req zone=api burst=20 nodelay;
}
# Health check endpoint
location /health {
proxy_pass http://ollama_cluster/api/tags;
}
}
Saída:
// Execution successful
▶ Exemplo 2: Script de Failover Automático
#!/bin/bash
# Ollama cluster health monitor
NODES=("ollama-node1:11434" "ollama-node2:11434" "ollama-node3:11434")
HEALTH_URL="/api/tags"
ALERT_EMAIL="ops@example.com"
check_node() {
local node=$1
if curl -sf --connect-timeout 3 "http://$node$HEALTH_URL" > /dev/null; then
echo "UP"
else
echo "DOWN"
fi
}
while true; do
for node in "${NODES[@]}"; do
status=$(check_node "$node")
if [ "$status" = "DOWN" ]; then
echo "ALERT: $node is DOWN at $(date)" >> /var/log/ollama_health.log
# Send alert
echo "Ollama node $node is DOWN" | mail -s "OLLAMA ALERT" "$ALERT_EMAIL" 2>/dev/null
fi
done
sleep 10
done
Saída:
NAME ID SIZE
llama3.2:latest a80... 2.0 GB
mistral:latest 61... 4.1 GB
5. Estratégias de Escalabilidade
(1) Condições de Gatilho para Escalonamento
| Métrica | Limiar Scale-Up | Limiar Scale-Down | Tempo de Espera |
|---|---|---|---|
| Latência de requisição P95 | > 5s | < 2s | 5 minutos |
| Conexões concorrentes | > 80% da capacidade | < 30% da capacidade | 5 minutos |
| Utilização de GPU | > 85% | < 40% | 10 minutos |
| Comprimento da fila | > 10 | = 0 | 3 minutos |
(2) Comparação de Métodos de Escalonamento
| Método | Velocidade | Custo | Complexidade |
|---|---|---|---|
| Escalonamento manual | Lento (horas) | Controlável | Baixa |
| Scripts automáticos | Médio (minutos) | Controlável | Média |
| K8s HPA | Rápido (segundos) | Automático | Alta |
▶ Exemplo 3: Auto-Scaling Baseado em Latência
import subprocess
import time
from typing import Optional
class AutoScaler:
def __init__(self, scale_up_threshold: float = 5.0,
scale_down_threshold: float = 2.0,
cooldown: int = 300):
self.scale_up_threshold = scale_up_threshold
self.scale_down_threshold = scale_down_threshold
self.cooldown = cooldown
self.last_scale_time = 0
def get_avg_latency(self) -> Optional[float]:
try:
result = subprocess.run(
["curl", "-sf", "http://localhost:11434/api/chat", "-d",
'{"model":"qwen2.5","messages":[{"role":"user","content":"hi"}],"stream":false}'],
capture_output=True, text=True, timeout=10
)
if result.returncode == 0:
import json
data = json.loads(result.stdout)
duration_ns = data.get("total_duration", 0)
return duration_ns / 1e9
except Exception:
pass
return None
def check_and_scale(self):
latency = self.get_avg_latency()
if latency is None:
return
now = time.time()
if now - self.last_scale_time < self.cooldown:
return
if latency > self.scale_up_threshold:
print(f"High latency ({latency:.1f}s), scaling up...")
self._scale_up()
self.last_scale_time = now
elif latency < self.scale_down_threshold:
print(f"Low latency ({latency:.1f}s), scaling down...")
self._scale_down()
self.last_scale_time = now
def _scale_up(self):
# Add new Ollama node (e.g., via Docker or cloud API)
print("Adding Ollama node...")
def _scale_down(self):
# Remove idle Ollama node
print("Removing idle Ollama node...")
# Usage
# scaler = AutoScaler()
# while True:
# scaler.check_and_scale()
# time.sleep(60)
Saída:
Adding Ollama node...
Removing idle Ollama node...
6. Implantação Kubernetes
(1) Arquitetura de Implantação K8s
flowchart TD
A[Ingress<br/>nginx-ingress] --> B[Service<br/>ollama-svc]
B --> C[Deployment<br/>ollama-deploy<br/>3 replicas]
C --> D[Pod 1<br/>GPU + Model]
C --> E[Pod 2<br/>GPU + Model]
C --> F[Pod 3<br/>GPU + Model]
G[PVC<br/>model-storage] --> D
G --> E
G --> F
(2) Configuração-Chave de Agendamento de GPU
| Recurso | Configuração | Descrição |
|---|---|---|
| nvidia.com/gpu (requests) | Solicitação de recurso | 1 GPU por Pod |
| nvidia.com/gpu (limits) | Limite de recurso | Máximo 1 GPU |
| nodeSelector | Seleção de nó GPU | Especificar nós GPU |
| tolerations | Tolerância a taints de GPU | Permitir agendamento em nós GPU |
▶ Exemplo 4: Implantação K8s do Ollama
# ollama-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: ollama
labels:
app: ollama
spec:
replicas: 3
selector:
matchLabels:
app: ollama
template:
metadata:
labels:
app: ollama
spec:
nodeSelector:
gpu: "true"
tolerations:
- key: nvidia.com/gpu
operator: Exists
effect: NoSchedule
containers:
- name: ollama
image: ollama/ollama
ports:
- containerPort: 11434
resources:
requests:
nvidia.com/gpu: 1
memory: "8Gi"
limits:
nvidia.com/gpu: 1
memory: "16Gi"
env:
- name: OLLAMA_HOST
value: "0.0.0.0:11434"
- name: OLLAMA_NUM_PARALLEL
value: "4"
- name: OLLAMA_KEEP_ALIVE
value: "30m"
volumeMounts:
- name: model-storage
mountPath: /root/.ollama
livenessProbe:
httpGet:
path: /api/tags
port: 11434
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /api/tags
port: 11434
initialDelaySeconds: 10
periodSeconds: 5
volumes:
- name: model-storage
persistentVolumeClaim:
claimName: ollama-models-pvc
---
apiVersion: v1
kind: Service
metadata:
name: ollama-svc
spec:
selector:
app: ollama
ports:
- port: 11434
targetPort: 11434
type: ClusterIP
Saída:
K8s Deployment created successfully, Pods running normally
Saída:
HPA auto-scaling configured successfully, cluster resource monitoring enabled
▶ Exemplo 5: Auto-Scaling K8s HPA
# ollama-hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: ollama-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: ollama
minReplicas: 2
maxReplicas: 6
metrics:
- type: Resource
resource:
name: nvidia.com/gpu
target:
type: Utilization
averageUtilization: 80
behavior:
scaleUp:
stabilizationWindowSeconds: 60
policies:
- type: Pods
value: 1
periodSeconds: 60
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Pods
value: 1
periodSeconds: 300
Saída:
# HPA created successfully
kubectl get hpa ollama-hpa
# NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
# ollama-hpa Deployment/ollama 45%/80% 2 6 3 5m
7. Recuperação de Desastres e Rollback
(1) Estratégias de Recuperação de Desastres
| Estratégia | RTO | RPO | Custo |
|---|---|---|---|
| Ativo-passivo | 5-15 minutos | 0 | 2x hardware |
| Ativo-ativo | 0 (failover automático) | 0 | 3x hardware |
| Standby frio | 1-4 horas | Com perdas | 1x + armazenamento |
(2) Configuração-como-Código
| Recurso | Método de Gerenciamento de Versão |
|---|---|
| Modelfile | Repositório Git |
| Docker Compose | Repositório Git |
| K8s Manifests | Git + ArgoCD |
| Configuração Nginx | Repositório Git |
| Versões de modelos | Tags do Ollama + documentação |
8. Exemplo Abrangente: Checklist de Implantação em Produção
# ============================================
# Comprehensive: Production deployment checklist
# Full verification before going live
# ============================================
PRODUCTION_CHECKLIST = {
"infrastructure": [
("Ollama nodes: 3+ replicas running", "CRITICAL"),
("GPU drivers: installed and verified", "CRITICAL"),
("Model storage: shared NFS/S3 mounted", "CRITICAL"),
("Network: internal VLAN for Ollama traffic", "HIGH"),
("DNS: ai.example.com resolves to LB", "HIGH"),
],
"security": [
("Ollama bound to 127.0.0.1 on each node", "CRITICAL"),
("Nginx SSL certificate valid", "CRITICAL"),
("API Key authentication enabled", "CRITICAL"),
("Rate limiting configured (60 req/min)", "HIGH"),
("WAF rules in place", "MEDIUM"),
],
"high_availability": [
("Health checks configured (Nginx + app layer)", "CRITICAL"),
("Failover tested: kill 1 node, service continues", "CRITICAL"),
("Load balancer: least_conn algorithm", "HIGH"),
("Session affinity: disabled (stateless)", "HIGH"),
],
"monitoring": [
("Prometheus scraping all nodes", "HIGH"),
("Grafana dashboards created", "HIGH"),
("Alert rules: GPU OOM, high latency, service down", "CRITICAL"),
("Log aggregation: Loki or ELK", "MEDIUM"),
],
"disaster_recovery": [
("Config in Git (Modelfile, Compose, K8s)", "HIGH"),
("Model backup: GGUF files on S3/NFS", "HIGH"),
("Chroma data backup: daily snapshot", "HIGH"),
("Recovery drill: tested within last 30 days", "MEDIUM"),
],
"performance": [
("Baseline benchmark recorded", "HIGH"),
("OLLAMA_NUM_PARALLEL configured", "HIGH"),
("OLLAMA_KEEP_ALIVE=30m set", "MEDIUM"),
("num_ctx per endpoint optimized", "MEDIUM"),
]
}
def run_checklist() -> str:
report = ["# Production Deployment Checklist\n"]
total = 0
checked = 0
for category, items in PRODUCTION_CHECKLIST.items():
report.append(f"\n## {category.replace('_', ' ').title()}")
for item, priority in items:
total += 1
report.append(f"- [ ] [{priority}] {item}")
report.append(f"\n---\nTotal items: {total}")
return "\n".join(report)
print(run_checklist())
❓ Perguntas Frequentes
P: Qual é o número mínimo de GPUs para um cluster de 3 nós? R: Mínimo 3 (1 por nó). Se o orçamento é limitado, uma configuração de 2 nós (1 primário + 1 backup) funciona, mas o throughput é reduzido pela metade durante failover.
P: Cada máquina precisa baixar os arquivos de modelo? R: Se usar armazenamento compartilhado (NFS), baixe apenas uma vez. Se usar armazenamento local, cada máquina precisa baixar. NFS compartilhado + cache local é recomendado.
P: Devo escolher Kubernetes ou Docker Compose? R: Docker Compose para < 5 nós (mais simples). Kubernetes para > 5 nós ou quando auto-scaling é necessário. Docker Compose é suficiente para a maioria dos cenários.
P: Como testo o failover? R: Pare manualmente um nó Ollama (docker stop ou systemctl stop), verifique que o Nginx automaticamente roteia o tráfego para outros nós sem impacto ao usuário.
P: O Ollama possui modo cluster embutido? R: Não. O Ollama é um serviço de instância única; clusters requerem combinação com balanceador de carga externo. Cada instância Ollama roda independentemente; Nginx lida com a distribuição de requisições.
P: Como gerencio versões de modelos? R: Use Modelfile + Git para gerenciamento de configuração. Arquivos de modelo usam tags (qwen2.5:v1.0) para identificação de versão. Em produção, use tags fixas, não :latest.
📖 Resumo
- Arquitetura de produção: Nginx LB + múltiplos nós Ollama + armazenamento compartilhado + Chroma
- Alta disponibilidade: Nginx least_conn + health checks + failover automático
- Escalabilidade: Acionada por latência/utilização de GPU; Docker Compose manual ou K8s HPA automático
- Kubernetes: Agendamento de GPU + PVC persistência + HPA auto-scaling
- Recuperação de desastres: Configuração-como-código (Git) + backup de modelos (NFS/S3) + exercícios regulares
- Checklist de Produção cobre 6 áreas: infraestrutura, segurança, alta disponibilidade, monitoramento, recuperação de desastres, desempenho
📝 Exercícios
- Básico (⭐): Projete um plano de implantação de produção em máquina única — Nginx + Ollama único + Chroma. Desenhe um diagrama de arquitetura e escreva uma configuração Docker Compose.
- Intermediário (⭐⭐): Configure um cluster Ollama de 2 nós + balanceador de carga Nginx, e teste failover manual.
- Avançado (⭐⭐⭐): Escreva um documento completo de implantação em produção — diagrama de arquitetura, configuração Docker Compose/K8s, fortalecimento de segurança, monitoramento e alertas, plano de recuperação de desastres — e complete um exercício de failover.