Laboratório com llm-d 1.0: inferência distribuída no Kubernetes e agentes em produção
Um agente corporativo pode alternar consultas a documentos, chamadas de ferramentas e novas solicitações ao modelo. Esse tráfego combina contextos crescentes, reutilização de prefixos e concorrência variável. Distribuir requisições sem considerar esses fatores pode desperdiçar cache e concentrar trabalho em réplicas já saturadas. É justamente esse problema que o baseline otimizado do llm-d procura tratar. (llm-d.ai)
O llm-d 1.0 foi anunciado em 20 de outubro de 2026. Este artigo, publicado em 22 de outubro de 2026, utiliza a release v1.0.0 como referência, não uma versão futura presumida. O laboratório é uma proposta reproduzível baseada nos artefatos oficiais; não representa um benchmark executado nem uma configuração certificada para produção. (llm-d.ai)
O objetivo é responder a uma pergunta operacional: quais evidências precisamos reunir antes de colocar uma carga de agentes nessa infraestrutura?
1. O que é llm-d — e o que ele não substitui
llm-d é uma stack aberta de serving de modelos que integra mecanismos como vLLM e SGLang à infraestrutura de inferência distribuída. No Kubernetes, seus caminhos documentados combinam roteamento, execução dos modelos, gerenciamento de cache e componentes operacionais. Não se trata de um novo modelo de linguagem. (llm-d.ai)
Neste projeto de laboratório, a responsabilidade do llm-d termina na camada de inferência. Orquestração do agente, autorização de ferramentas, persistência de tarefas e aprovação de ações continuam pertencendo à aplicação. Essa separação será um requisito arquitetural, não uma capacidade presumida do servidor de modelos.
Também convém distinguir três formas de distribuir trabalho:
- Réplicas independentes: diferentes servidores atendem requisições do mesmo modelo.
- Paralelismo do modelo: uma instância lógica utiliza vários aceleradores; tensor parallelism divide operações do modelo entre dispositivos.
- Desagregação prefill/decode: processamento inicial do contexto e geração de tokens executam em papéis separados.
São dimensões diferentes, que podem ser combinadas. O baseline deste artigo usa réplicas com paralelismo tensorial; não habilita automaticamente desagregação. (llm-d.ai)
2. Componentes e fluxo de uma requisição
Router, proxy e Endpoint Picker
O llm-d Router reúne o proxy de camada de aplicação e o Endpoint Picker — EPP. O proxy recebe e encaminha tráfego; o EPP seleciona o servidor usando sinais e políticas de inferência. Em integrações compatíveis, a consulta ocorre pelo protocolo de processamento externo ext-proc. Não confunda esse escalonamento de requisições com a escolha do nó de um Pod pelo scheduler do Kubernetes. (llm-d.ai)
Fluxo simplificado:
Agente → Proxy → Servidor selecionado → Tokens de resposta
↕
EPP ← Estado dos servidores e políticasInferencePool e Gateway API
InferencePool é um recurso customizado da Gateway API Inference Extension. Ele identifica Pods por labels, informa portas e pode referenciar o serviço do EPP. A definição do recurso é instalada como uma Custom Resource Definition — CRD. O pool não cria os servidores de modelos. (gateway-api-inference-extension.sigs.k8s.io)
No modo Gateway, um controller compatível conecta recursos como Gateway e HTTPRoute à camada de inferência. O laboratório usa o modo standalone, com proxy integrado, evitando introduzir um gateway externo antes de validar o serving. (llm-d.ai)
Servidor de modelos e cache KV
O engine carrega pesos e executa inferência. O cache key-value — KV preserva estado intermediário de atenção. Roteamento por prefixo tenta aproveitar estado já existente; indexação precisa acompanha eventos do cache; offloading amplia capacidade utilizando outras camadas de memória ou armazenamento. Essas técnicas não são equivalentes e devem ser habilitadas conforme o caminho escolhido. (llm-d.ai)
Controllers para topologias distribuídas
LeaderWorkerSet organiza grupos de Pods de uma réplica distribuída. DisaggregatedSet coordena topologias com múltiplos papéis, como prefill e decode, e suas revisões. Esses controllers tornam-se relevantes quando o experimento deixa de ser apenas um conjunto de réplicas independentes. (llm-d.ai)
O que merece atenção na versão 1.0
A release destaca configuração do router em API v1, controles de qualidade de serviço multi-tenant, operação com alta disponibilidade e recursos orientados a sessões de agentes. Políticas de prioridade e fairness precisam ser configuradas; a instalação básica não deve ser interpretada como isolamento automático entre clientes. (llm-d.ai)
3. Laboratório: começar pelo baseline, não pela desagregação
Pré-requisitos e capacidade
Utilize um cluster dedicado a testes, Bash, Git, kubectl, Helm e acesso aos registros de imagens e pesos. Para o caminho NVIDIA abaixo, confirme drivers e device plugin funcionais, com recursos nvidia.com/gpu disponíveis nos nós. (llm-d.ai)
O overlay oficial selecionado serve Qwen/Qwen3-32B com duas réplicas e tensor parallelism de dois: quatro GPUs no total. A referência publicada utiliza H100 de 80 GB. Cada Pod solicita também 8 CPUs e 96 GiB de memória de host; o router e o cluster precisam de capacidade adicional. Não é um laboratório pequeno para um notebook. (llm-d.ai)
Se o hardware for diferente, escolha outro overlay oficialmente documentado ou produza uma adaptação medida. Reduzir apenas a contagem de GPUs não demonstra que pesos, contexto e cache caberão na memória disponível.
Fixar os artefatos e instalar os pré-requisitos
Os comandos seguintes adaptam o quickstart oficial, fixando o checkout na tag em vez de acompanhar uma branch mutável. O arquivo guides/env.sh fornece as versões e coordenadas dos charts. Instalar CRDs é uma alteração de escopo de cluster: obtenha aprovação antes de executar em infraestrutura compartilhada. (llm-d.ai)
git clone --branch v1.0.0 --depth 1 https://github.com/llm-d/llm-d.git
cd llm-d
export REPO_ROOT="$PWD"
source guides/env.sh
export NAMESPACE=llm-d-lab
export GUIDE_NAME=llm-d-lab
git rev-parse HEAD
kubectl get nodes
kubectl create namespace "$NAMESPACE" --dry-run=client -o yaml | kubectl apply -f -
kubectl apply -f "https://github.com/kubernetes-sigs/gateway-api-inference-extension/${GAIE_URL}/v1-manifests.yaml"Registre o commit, as imagens efetivamente resolvidas e os manifestos renderizados no relatório do experimento. Para produção, proponho promover imagens por digest e manter os artefatos aprovados em GitOps, em vez de depender apenas da existência de uma tag.
Criar o Secret e subir os componentes
O overlay espera o Secret llm-d-hf-token, com a chave HF_TOKEN. A proposta abaixo lê o token sem eco e evita incluí-lo literalmente no histórico do shell. Execute em uma estação confiável, sem set -x; remova o arquivo temporário mesmo se algum comando falhar. Para produção, substitua esse procedimento manual por gestão e rotação de segredos. (raw.githubusercontent.com)
umask 077
read -rsp 'Hugging Face token: ' HF_TOKEN
printf '\n'
TOKEN_FILE=$(mktemp)
printf '%s' "$HF_TOKEN" > "$TOKEN_FILE"
kubectl create secret generic llm-d-hf-token -n "$NAMESPACE" --from-file="HF_TOKEN=$TOKEN_FILE" --dry-run=client -o yaml | kubectl apply -f -
rm -f "$TOKEN_FILE"
unset HF_TOKEN TOKEN_FILE
helm upgrade --install "$GUIDE_NAME" "$ROUTER_STANDALONE_CHART" --version "$ROUTER_CHART_VERSION" -n "$NAMESPACE" -f guides/recipes/router/base.values.yaml -f guides/optimized-baseline/router/optimized-baseline.values.yaml
kubectl kustomize guides/optimized-baseline/modelserver/gpu/vllm/base/ > modelserver.rendered.yaml
kubectl apply --dry-run=server -n "$NAMESPACE" -f modelserver.rendered.yaml
kubectl apply -n "$NAMESPACE" -f modelserver.rendered.yaml
kubectl wait -n "$NAMESPACE" --for=condition=Ready pod -l llm-d.ai/guide=optimized-baseline --timeout=3600s
kubectl get pods,svc -n "$NAMESPACE"As coordenadas Helm e o caminho Kustomize correspondem ao baseline documentado. Inspecione modelserver.rendered.yaml antes do apply: recursos, volumes, imagens, probes e permissões devem ser compatíveis com seu cluster. O dry-run valida a admissão, mas não prova disponibilidade de GPU nem sucesso no carregamento do modelo. (llm-d.ai)
Validar o endpoint
Encaminhe a porta do serviço standalone para sua máquina:
kubectl port-forward -n "$NAMESPACE" svc/llm-d-lab-epp 8000:80Em outro terminal:
curl --fail-with-body -N http://127.0.0.1:8000/v1/completions -H 'Content-Type: application/json' -d '{"model":"Qwen/Qwen3-32B","prompt":"Explain Kubernetes readiness probes briefly.","max_tokens":128,"stream":true}'Esse é um teste funcional do caminho cliente → proxy → modelo, usando a interface de completions do quickstart. Não comprova distribuição equilibrada, ganho de cache ou capacidade. Não exponha o endpoint publicamente apenas para executar esse teste. (llm-d.ai)
4. YAML Kubernetes: proteção contra disrupções voluntárias
Salve o manifesto completo abaixo como modelserver-pdb.yaml:
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: optimized-baseline-modelservers
namespace: llm-d-lab
spec:
minAvailable: 1
selector:
matchLabels:
llm-d.ai/guide: optimized-baseline
llm-d.ai/model: Qwen3-32B
llm-d.ai/accelerator-vendor: nvidiaEsse PodDisruptionBudget — PDB seleciona os servidores pelas labels do overlay e solicita pelo menos um Pod disponível durante evictions voluntárias. Adapte namespace e seletor caso altere o modelo. Com uma única réplica, essa política pode impedir um drain; ela não protege contra falha física nem substitui a estratégia de rollout do controller. (raw.githubusercontent.com)
kubectl apply --dry-run=server -f modelserver-pdb.yaml
kubectl apply -f modelserver-pdb.yaml
kubectl get pdb -n llm-d-labEste PDB é um complemento do laboratório, não uma declaração de alta disponibilidade. O desenho de produção ainda precisa considerar placement, capacidade remanescente e falhas do router.
5. Integração com Gateway API: o contrato do pool
Para visualizar uma integração separada em modo Gateway, considere este recurso completo. Não o aplique sobre o laboratório standalone como se fosse uma etapa obrigatória. Ele pressupõe um EPP configurado para esse pool, exposto pelo Service agents-epp, e um gateway compatível já instalado:
apiVersion: inference.networking.k8s.io/v1
kind: InferencePool
metadata:
name: agents-pool
namespace: llm-d-lab
spec:
selector:
matchLabels:
llm-d.ai/guide: optimized-baseline
llm-d.ai/model: Qwen3-32B
llm-d.ai/accelerator-vendor: nvidia
targetPorts:
- number: 8000
endpointPickerRef:
name: agents-epp
port:
number: 9002
failureMode: FailOpenselector identifica servidores; targetPorts aponta para a porta do modelo; endpointPickerRef conecta o pool ao EPP. Ajuste nomes, portas e labels aos recursos realmente instalados. O recurso, sozinho, não cria Service, EPP, listener TLS ou HTTPRoute. (gateway-api-inference-extension.sigs.k8s.io)
FailOpen permite encaminhamento sem a seleção otimizada quando o EPP falha. Isso exige uma decisão explícita: para este projeto, autenticação e autorização devem permanecer independentes do scheduler, e o teste de falha deve verificar quais políticas deixam de ser aplicadas. Não trate disponibilidade de transporte como preservação de toda a qualidade de serviço. (llm-d.ai)
6. Experimento realista: agentes de suporte interno
Considere o seguinte cenário hipotético, sem resultados medidos:
Problema → agentes consultam documentação e um sistema de chamados. Cada etapa acrescenta contexto; equipes interativas competem com processamento em lote.
Solução proposta → separar o estado durável da aplicação do serving. PostgreSQL guarda tarefas e auditoria; workers executam ferramentas autorizadas; llm-d atende inferência. Nenhum componente recebe credenciais administrativas por conveniência.
Implementação proposta → comparar três configurações, mantendo modelo, hardware e conjunto de requisições equivalentes:
- Baseline de distribuição sem afinidade de prefixo.
- Baseline llm-d com roteamento sensível a carga e prefixos.
- Desagregação prefill/decode, somente após medir a configuração anterior.
Resultado esperado do experimento → produzir evidência, não prometer um percentual de ganho. O relatório deve mostrar latência, erros, consumo e conclusão das tarefas para cada configuração.
A desagregação introduz transferência de KV entre os papéis e dependências de transporte. O caminho oficial documenta combinações específicas de engine, acelerador e rede; não basta criar dois Deployments chamados prefill e decode. (llm-d.ai)
Minha proposta de testes inclui cache frio e quente, prefixos comuns e distintos, contextos longos, cancelamentos e rajadas. Acrescente perda de um servidor, reinício do EPP e rollout durante streaming. Para cada execução, registre a distribuição de tokens de entrada e saída, não apenas requisições por segundo.
7. Observabilidade e critérios de aprovação
A documentação oferece integração com Prometheus, Grafana e OpenTelemetry. Para o laboratório, a proposta é correlacionar métricas e traces desde a aplicação até o engine, sem registrar prompts sensíveis por padrão. (llm-d.ai)
Defina Service Level Objectives — SLOs, ou objetivos de nível de serviço, antes dos testes. Sugiro acompanhar:
| Camada | Sinais a coletar no experimento |
|---|---|
| Experiência | Tempo até o primeiro token — TTFT; latência entre tokens; tempo total da tarefa |
| Admissão | Espera em fila, rejeições, timeouts e cancelamentos |
| Engine | Tokens processados, cache KV, erros e memória do acelerador |
| Kubernetes | Pods Pending, reinícios, CPU, memória e tempo até readiness |
| Agente | Sucesso da tarefa, chamadas de ferramentas e ações repetidas |
Use percentis por classe de tráfego e perfil de contexto. Como critérios propostos, rejeite uma configuração que aumente throughput às custas de starvation de uma equipe, perda de streaming ou duplicação de ações.
Para alertas, proponho priorizar consumo do orçamento de erro, crescimento sustentado de fila e insuficiência de réplicas prontas. Evite usar identificadores de sessão como labels de métricas; reserve correlação individual para traces e logs com retenção controlada.
8. Produção: capacidade, disponibilidade e segurança
Escalar a demanda correta
O caminho documentado com Kubernetes Event-driven Autoscaling — KEDA utiliza sinais do EPP, como fila, backlog de tokens e saturação. Ele é mais alinhado à inferência que uma regra baseada somente em CPU. Ainda assim, escalar réplicas não resolve sozinho a falta de nós com aceleradores. (llm-d.ai)
Para capacity planning, proponho medir separadamente:
- Capacidade de prefill e decode por perfil de requisição.
- Tempo de provisionamento de nó, download, inicialização e aquecimento.
- Memória necessária para pesos, cache e concorrência.
- Capacidade após perder um domínio de falha.
- Folga temporária exigida por upgrades.
A política inicial deve manter capacidade quente para o tráfego crítico e limitar concorrência enquanto novas réplicas não estiverem prontas.
Alta disponibilidade não é apenas replicas: 2
No router, standby e active-active têm comportamentos distintos. No active-active, o estado de flow control é local a cada EPP e existem restrições de compatibilidade dos plugins. Aumentar réplicas sem revisar esse modelo pode alterar fairness e afinidade. (llm-d.ai)
Para a arquitetura proposta, distribua réplicas independentes por nós e, quando viável, zonas. Preserve a localidade de rede dos grupos fortemente acoplados. Não espalhe automaticamente os membros de uma única instância distribuída por zonas só para cumprir uma regra genérica de anti-affinity. Essa decisão deve seguir a topologia da interconexão e os testes de falha. (llm-d.ai)
Use startupProbe para acomodar inicialização, readinessProbe para controlar elegibilidade e livenessProbe para detectar falhas recuperáveis por reinício. Uma liveness agressiva durante carregamento pode provocar ciclos de reinicialização. Dimensione os tempos a partir do comportamento medido. (kubernetes.io)
Segurança e isolamento
A proposta de produção exige Role-Based Access Control — RBAC mínimo, ServiceAccounts separados e nenhuma permissão administrativa nos workers de agentes. Desabilite montagem automática do token da ServiceAccount onde o processo não utiliza a API Kubernetes. (kubernetes.io)
Adote políticas de rede que permitam somente os fluxos necessários entre proxy, EPP, engine, DNS, telemetria e armazenamento. NetworkPolicy depende de implementação compatível da rede do cluster e não substitui autenticação de aplicação. (kubernetes.io)
Como requisitos adicionais deste projeto, estabeleça TLS na entrada, criptografia do estado persistente, rotação de credenciais, identidade confiável de tenant e revisão de imagens. Não aceite uma prioridade enviada livremente pelo cliente como prova de autorização.
Para agentes, exija limites de ferramentas, execução isolada, aprovação humana para ações sensíveis e chaves de idempotência. Teste explicitamente o que ocorre se houver timeout depois de uma ferramenta alterar um sistema externo.
Estado de sessão não é backup
O caminho de Responses API inclui continuidade com previous_response_id. Isso não autoriza presumir durabilidade, recuperação entre réplicas ou retenção compatível com seu negócio; essas propriedades precisam ser verificadas na configuração implantada. (llm-d.ai)
Neste desenho, proponho backup e teste de restauração do estado durável de tarefas e auditoria, além de retenção dos artefatos de modelos e configurações aprovadas. O plano de recuperação deve definir se uma sessão pode ser reconstruída e quando o agente precisa recomeçar.
9. Upgrades, rollback e FinOps
Para upgrades, proponho uma unidade de compatibilidade: chart, CRDs, controller, engine, modelo, tokenizer e configuração de transporte. A release 1.0 documenta requisitos específicos de LeaderWorkerSet e habilitação de DisaggregatedSet para o caminho desagregado; versões antigas de tutoriais não devem ser misturadas indiscriminadamente. (github.com)
O procedimento de promoção deve ser:
- Renderizar e validar manifestos no CI.
- Executar testes funcionais, de carga e falha em staging.
- Fazer canary com tráfego limitado e sessões identificáveis.
- Drenar requisições antes de remover a revisão antiga.
- Reverter o conjunto compatível, não apenas a imagem de um worker.
Defina antecipadamente o destino das sessões abertas durante rollback. Não considere a promessa de operações sem downtime uma garantia para qualquer configuração.
Para FinOps, minha recomendação é comparar custo por tarefa concluída dentro do SLO, além de custo por token. Inclua GPUs ociosas, CPU do router, armazenamento de pesos, rede entre zonas, load balancers e telemetria.
Avalie Spot apenas para parcelas que tolerem interrupção e retomada; proponho manter a capacidade mínima crítica em infraestrutura não interrompível. Registre o custo de reprocessar contexto, de recarregar modelos e de sustentar capacidade duplicada durante canary. Nenhum preço atual ou economia percentual é presumido aqui.
10. Desenvolvimento versus produção: o que evitar
| Dimensão | Laboratório | Critério proposto para produção |
|---|---|---|
| Acesso | Port-forward local | Entrada autenticada, TLS e auditoria |
| Redundância | Router básico | Failover testado e capacidade remanescente |
| Recursos | Overlay de referência | Requests e limits revisados por medição |
| Observabilidade | Smoke test e métricas básicas | SLOs, alertas, traces e responsáveis |
| Mudanças | Execução manual | GitOps, canary e rollback ensaiado |
| Estado | Descartável | Backup, retenção e recuperação validados |
Evite credenciais no Git, imagens mutáveis sem controle, ausência de probes, privilégios excessivos, filas sem limite, retries ilimitados e exposição pública desnecessária. Não promova os defaults do laboratório apenas porque a primeira completion funcionou.
11. Troubleshooting orientado por sintomas
Comece por:
kubectl get nodes
kubectl get pods -n llm-d-lab -o wide
kubectl get deployments -n llm-d-lab
kubectl get events -n llm-d-lab --sort-by=.metadata.creationTimestamp
kubectl describe pod <pod> -n llm-d-lab
kubectl logs <pod> -n llm-d-lab --all-containers=true
kubectl logs <pod> -n llm-d-lab -c <container> --previous
kubectl get inferencepools.inference.networking.k8s.io -n llm-d-labA tabela abaixo é um roteiro proposto de investigação, não um diagnóstico automático:
| Sintoma | Possível causa | Investigação | Correção a validar |
|---|---|---|---|
| Pod Pending | GPUs, memória ou placement insuficientes | Events, requests e capacidade allocatable | Ajustar capacidade ou overlay sem violar memória do modelo |
| Inicialização reinicia | Probe prematura ou falha ao baixar pesos | Logs anteriores, Secret, conectividade | Corrigir acesso e orçamento da startupProbe |
| Erro de memória de GPU | Contexto ou concorrência acima do dimensionado | Logs do engine e telemetria do dispositivo | Reduzir carga ou rever paralelismo e memória |
| HTTP 503 | Backend indisponível ou seleção incorreta | Pods Ready, labels, pool e logs do proxy | Corrigir readiness, seletor ou conectividade |
| TTFT alto | Fila, cache frio ou excesso de prefill | Separar espera, processamento e transferência nos traces | Rever admissão, afinidade ou capacidade |
| Falha após upgrade P/D | Revisões ou transporte incompatíveis | Logs de ambos os papéis e estado do controller | Reverter a unidade compatível e repetir o teste |
Os pontos de investigação seguem os mecanismos documentados de GPUs, probes, pools e desagregação. Não tente corrigir todas as falhas aumentando timeouts. (kubernetes.io)
Ao terminar, remova apenas os recursos do laboratório dedicado:
helm uninstall llm-d-lab -n llm-d-lab
kubectl delete namespace llm-d-labNão remova CRDs compartilhadas como parte de uma limpeza local. Confirme separadamente se infraestrutura externa e armazenamento provisionados para o teste também precisam ser desativados.
12. Quando utilizar e quais alternativas avaliar
Minha recomendação é avaliar llm-d quando há demanda relevante de inferência própria, contexto reutilizável, múltiplos consumidores e uma equipe capaz de operar Kubernetes e aceleradores.
Para baixa escala ou requisitos simples, comece comparando um engine com balanceamento convencional: a menor superfície operacional pode ser mais importante que otimizações distribuídas. APIs gerenciadas também devem entrar na análise, com avaliação explícita de dados, contratos, custos e dependência do fornecedor.
KServe é uma plataforma de serving com separação entre controle e execução; sua documentação também apresenta integração com llm-d. Portanto, não é necessariamente uma escolha excludente: pode fornecer a camada de gestão sobre uma stack de inferência distribuída. (kserve.github.io)
A decisão proposta é incremental: só introduzir desagregação, cache distribuído ou controles adicionais quando um experimento demonstrar benefício suficiente para justificar sua manutenção.
Resumo prático
- Fixe a release e registre a matriz de componentes utilizada.
- Comece pelo baseline e estabeleça uma comparação controlada.
- Trate cache, afinidade e desagregação como escolhas distintas.
- Avalie tarefas de agentes, não apenas tokens por segundo.
- Teste falhas, overload, sessões e rollback antes da promoção.
- Separe estado durável, autorização de ferramentas e serving.
Conclusão
A proposta deste laboratório é transformar inferência distribuída em uma decisão de engenharia verificável. O critério de sucesso não é colocar mais componentes no cluster: é demonstrar que a arquitetura atende objetivos de latência, confiabilidade, segurança e custo sob tráfego representativo.
Em uma estratégia de Platform Engineering, o resultado deve virar um caminho padronizado com artefatos versionados, limites operacionais e responsabilidades claras. DevOps automatiza a entrega; SRE exige evidência de recuperação e comportamento sob falha. Só depois dessa validação a carga de agentes deve deixar de ser experimento e passar a ser serviço de produção.
Artigos relacionados
Quer aplicar isso no seu ambiente?
Agende uma conversa de 30 minutos sem compromisso. Eu olho o seu cenário e digo por onde começar.
Agendar uma conversaReceba os novos artigos por e-mail
Um e-mail quando sair artigo novo sobre infraestrutura, backend e IA. Sem spam, cancele quando quiser.