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.

💡 Dica: Proxy reverso Nginx + balanceamento de carga é a arquitetura padrão para implantação em produção — Nginx lida com terminação SSL, autenticação por API Key, rate limiting e distribuição de tráfego, enquanto os nós Ollama só precisam se vincular a 127.0.0.1. Para clusters multi-nó, a estratégia 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

1. O Que Você Vai Aprender


2. Uma História Real de Uma Empreendedora SaaS

💡 Dica: Em ambientes de produção, o balanceamento de carga Nginx deve usar a estratégia 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.

ℹ️ Info: O Ollama não suporta nativamente o modo cluster (sem sincronização master-slave, sem Inferência distribuída). A consistência de modelos e a afinidade de sessão para clusters multi-nó precisam ser implementadas na camada superior (Nginx/FastAPI). Isso também se aplica a implantações Kubernetes.

(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:

100%
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

⚠️ Aviso: Em um cluster multi-nó, cada nó Ollama gerencia independentemente seus modelos e estado — não há replicação master-slave como em bancos de dados. Um modelo baixado no Nó A não sincronizará automaticamente para o Nó B. Você deve garantir a consistência de modelos em todos os nós através de um script de inicialização unificado ou armazenamento compartilhado.

(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

100%
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

⚠️ Nota: A configuração de certificado SSL é obrigatória para produção — HTTPS não apenas criptografa a transmissão mas também é pré-requisito para autenticação por API Key (enviar API Keys em texto plano via HTTP significa zero segurança). Use certbot para certificados Let's Encrypt gratuitos, ou Caddy para HTTPS automático.

(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

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:

TEXT
// Execution successful

▶ Exemplo 2: Script de Failover Automático

BASH
#!/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:

TEXT
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

PYTHON
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:

TEXT
Adding Ollama node...
Removing idle Ollama node...

6. Implantação Kubernetes

(1) Arquitetura de Implantação K8s

100%
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

YAML
# 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:

TEXT
K8s Deployment created successfully, Pods running normally

Saída:

TEXT
HPA auto-scaling configured successfully, cluster resource monitoring enabled

▶ Exemplo 5: Auto-Scaling K8s HPA

YAML
# 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:

TEXT
# 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

PYTHON
# ============================================
# 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


📝 Exercícios

  1. 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.
  2. Intermediário (⭐⭐): Configure um cluster Ollama de 2 nós + balanceador de carga Nginx, e teste failover manual.
  3. 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.
Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%