Voltar para o blog

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íticas

InferencePool 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:80

Em 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: nvidia

Esse 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-lab

Este 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: FailOpen

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

  1. Baseline de distribuição sem afinidade de prefixo.
  2. Baseline llm-d com roteamento sensível a carga e prefixos.
  3. 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:

CamadaSinais a coletar no experimento
ExperiênciaTempo até o primeiro token — TTFT; latência entre tokens; tempo total da tarefa
AdmissãoEspera em fila, rejeições, timeouts e cancelamentos
EngineTokens processados, cache KV, erros e memória do acelerador
KubernetesPods Pending, reinícios, CPU, memória e tempo até readiness
AgenteSucesso 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:

  1. Renderizar e validar manifestos no CI.
  2. Executar testes funcionais, de carga e falha em staging.
  3. Fazer canary com tráfego limitado e sessões identificáveis.
  4. Drenar requisições antes de remover a revisão antiga.
  5. 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ãoLaboratórioCritério proposto para produção
AcessoPort-forward localEntrada autenticada, TLS e auditoria
RedundânciaRouter básicoFailover testado e capacidade remanescente
RecursosOverlay de referênciaRequests e limits revisados por medição
ObservabilidadeSmoke test e métricas básicasSLOs, alertas, traces e responsáveis
MudançasExecução manualGitOps, canary e rollback ensaiado
EstadoDescartávelBackup, 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-lab

A tabela abaixo é um roteiro proposto de investigação, não um diagnóstico automático:

SintomaPossível causaInvestigaçãoCorreção a validar
Pod PendingGPUs, memória ou placement insuficientesEvents, requests e capacidade allocatableAjustar capacidade ou overlay sem violar memória do modelo
Inicialização reiniciaProbe prematura ou falha ao baixar pesosLogs anteriores, Secret, conectividadeCorrigir acesso e orçamento da startupProbe
Erro de memória de GPUContexto ou concorrência acima do dimensionadoLogs do engine e telemetria do dispositivoReduzir carga ou rever paralelismo e memória
HTTP 503Backend indisponível ou seleção incorretaPods Ready, labels, pool e logs do proxyCorrigir readiness, seletor ou conectividade
TTFT altoFila, cache frio ou excesso de prefillSeparar espera, processamento e transferência nos tracesRever admissão, afinidade ou capacidade
Falha após upgrade P/DRevisões ou transporte incompatíveisLogs de ambos os papéis e estado do controllerReverter 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-lab

Nã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.

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 conversa

Receba os novos artigos por e-mail

Um e-mail quando sair artigo novo sobre infraestrutura, backend e IA. Sem spam, cancele quando quiser.