Como desenhar sandboxes efêmeros para agentes de IA no Kubernetes
Um agente que analisa repositórios, executa testes ou transforma arquivos precisa de ferramentas com efeitos reais: shell, interpretadores, acesso a dados e chamadas de rede. Neste projeto, a premissa de segurança é que o código executado pode ser hostil, mesmo quando a solicitação original é legítima.
A proposta é separar planejamento e execução. O agente solicita uma tarefa; uma plataforma confiável decide onde executá-la, quais capacidades conceder e quando encerrar o ambiente. O agente não recebe liberdade para criar infraestrutura arbitrária.
O objetivo não é apenas criar Pods rapidamente. É construir um serviço de execução com limites verificáveis de identidade, rede, recursos, duração e persistência.
1. O que caracteriza um sandbox efêmero
Neste artigo, um sandbox é um ambiente descartável criado para uma execução ou sessão, com política explícita e armazenamento de trabalho temporário. O modelo de linguagem pode permanecer fora dele: quem precisa do isolamento é, principalmente, o executor de ferramentas.
Um namespace organiza recursos e políticas, mas não constitui sozinho uma fronteira forte contra código hostil. Contêineres convencionais compartilham o kernel do nó; por isso, a escolha do runtime precisa acompanhar o modelo de ameaça. (kubernetes.io)
Adote três propriedades como requisitos de projeto:
- Efemeridade: toda execução tem prazo, responsável e procedimento de destruição.
- Autoridade limitada: o executor só acessa dados e operações necessários à tarefa.
- Estado externo verificável: resultados aceitos, auditoria e estado operacional sobrevivem à exclusão do ambiente.
2. Comece pelo modelo de ameaça
Antes do YAML, documente ativos, adversários e consequências. Para uma plataforma que executa código de repositórios externos, proponho o seguinte contrato:
| Ameaça considerada | Controle proposto |
|---|---|
| Leitura de credenciais | Nenhuma credencial administrativa no executor |
| Exfiltração | Saída bloqueada por padrão e gateway com autorização |
| Consumo ilimitado | Quotas, limites, prazo e concorrência por tenant |
| Contaminação entre tarefas | Workspace novo e nenhum volume gravável compartilhado |
| Ações externas indevidas | Ferramentas mediadas, idempotência e aprovação para operações sensíveis |
Trate arquivos, dependências, respostas de ferramentas e saída do agente como conteúdo não confiável. A política de infraestrutura deve permanecer fora do alcance desses dados.
Para clientes mutuamente não confiáveis ou exigências regulatórias elevadas, considere clusters e contas de cloud separados, além do isolamento de runtime. A documentação de multitenancy distingue isolamento por namespace de modelos com separação mais forte. (kubernetes.io)
3. Arquitetura de referência
Fluxo proposto:
Cliente → API de execução → autorização e orçamento → fila → controller → namespace + políticas → Job → runtime isolado → resultado validado → limpeza
Separe dois planos:
- Plano de controle confiável: autenticação, autorização, fila, reconciliação, auditoria e emissão de capacidades temporárias.
- Plano de execução não confiável: processo do agente ou ferramenta, arquivos de entrada e workspace.
O controller é um componente da plataforma, não uma API fictícia do Kubernetes. Ele pode usar recursos nativos; uma CustomResourceDefinition, ou CRD, é opcional quando houver necessidade de uma API própria de sessões.
Objetos e responsabilidades
- Job: acompanha uma tarefa finita. Não equivale a uma garantia de execução exatamente uma vez; o código precisa tolerar execuções duplicadas. (kubernetes.io)
- RuntimeClass: seleciona uma configuração de runtime previamente instalada nos nós. (kubernetes.io)
- ServiceAccount: representa a identidade Kubernetes do workload. Desabilitar a montagem automática do token evita disponibilizá-lo quando não é necessário. (kubernetes.io)
- ResourceQuota: limita consumo agregado e quantidade de objetos no namespace; não reserva capacidade física. (kubernetes.io)
- NetworkPolicy: controla tráfego selecionado quando a implementação de rede oferece suporte. (kubernetes.io)
Para sessões interativas, proponho um Pod com protocolo de sessão e prazo externo. Não trate um Deployment que recria processos indefinidamente como substituto automático de uma sessão descartável.
4. Escolha o isolamento antes de otimizar o startup
O gVisor implementa uma interface semelhante à do Linux em um kernel de aplicação em espaço de usuário. O Kata Containers utiliza máquinas virtuais leves e virtualização de hardware. São alternativas diferentes, com requisitos de compatibilidade que devem ser avaliados para o workload. (gvisor.dev)
| Alternativa | Critério de decisão proposto |
|---|---|
| Contêiner convencional | Código confiável, risco aceito e controles complementares |
| gVisor | Avaliar para execução não confiável sem exigir uma VM tradicional por tarefa |
| Kata Containers | Avaliar quando a fronteira baseada em virtualização for um requisito |
| VM ou cluster dedicado | Priorizar quando a separação justificar operação e capacidade adicionais |
Não escolha por um benchmark genérico. Teste chamadas de sistema, bibliotecas nativas, filesystem, rede, inicialização e necessidades de GPU com suas próprias tarefas.
RuntimeClass de exemplo
O manifesto pressupõe que o handler runsc já foi instalado e configurado no runtime dos nós. O label sandbox-runtime: gvisor é uma convenção local administrada pela plataforma.
apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
name: agent-gvisor
handler: runsc
scheduling:
nodeSelector:
sandbox-runtime: gvisorEsse objeto seleciona o handler; não instala o gVisor. Adapte nome, handler e seleção de nós. Em produção, configure também overhead com valores medidos quando aplicável, para que o custo adicional do runtime participe do dimensionamento. (kubernetes.io)
Recomendo um pool dedicado, políticas de agendamento administradas pela plataforma e bloqueio de alterações desses controles pelo executor. Separação de agendamento complementa, mas não substitui, isolamento de segurança.
5. Exemplo completo: execução offline e descartável
O exemplo abaixo é didático: executa um comando inofensivo, sem rede, para validar o envelope operacional. Ele não implementa download de entradas, inferência nem upload de artefatos.
Pré-requisitos: runtime anterior funcional, nós compatíveis e plugin de rede com enforcement de NetworkPolicy. A plataforma deve criar e verificar o baseline antes de admitir código não confiável; a aceitação de um objeto pela API não demonstra, sozinha, que o dataplane já aplica a política.
apiVersion: v1
kind: Namespace
metadata:
name: agent-run-7f3a
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/enforce-version: latest
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: sandbox
namespace: agent-run-7f3a
automountServiceAccountToken: false
---
apiVersion: v1
kind: ResourceQuota
metadata:
name: sandbox-budget
namespace: agent-run-7f3a
spec:
hard:
pods: '2'
count/jobs.batch: '2'
requests.cpu: '1'
limits.cpu: '2'
requests.memory: 512Mi
limits.memory: 1Gi
requests.ephemeral-storage: 512Mi
limits.ephemeral-storage: 1Gi
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny
namespace: agent-run-7f3a
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
---
apiVersion: batch/v1
kind: Job
metadata:
name: agent-task
namespace: agent-run-7f3a
spec:
backoffLimit: 0
activeDeadlineSeconds: 300
ttlSecondsAfterFinished: 600
template:
metadata:
labels:
app: agent-sandbox
spec:
runtimeClassName: agent-gvisor
serviceAccountName: sandbox
automountServiceAccountToken: false
restartPolicy: Never
terminationGracePeriodSeconds: 10
securityContext:
runAsNonRoot: true
runAsUser: 10000
runAsGroup: 10000
fsGroup: 10000
seccompProfile:
type: RuntimeDefault
containers:
- name: executor
image: busybox:1.37.0
command:
- sh
- -c
args:
- |
set -eu
echo sandbox-ready > /workspace/result.txt
cat /workspace/result.txt
workingDir: /workspace
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
resources:
requests:
cpu: 250m
memory: 128Mi
ephemeral-storage: 128Mi
limits:
cpu: '1'
memory: 512Mi
ephemeral-storage: 512Mi
volumeMounts:
- name: workspace
mountPath: /workspace
- name: tmp
mountPath: /tmp
volumes:
- name: workspace
emptyDir:
sizeLimit: 256Mi
- name: tmp
emptyDir:
sizeLimit: 64MiO que adaptar e por quê
O perfil restricted exige controles de segurança de Pod; ele não impõe toda a arquitetura proposta. readOnlyRootFilesystem é uma escolha adicional. Em produção, fixe a versão do perfil na versão Kubernetes validada, em vez de deixar latest acompanhar mudanças automaticamente. (kubernetes.io)
backoffLimit: 0 evita retries de falhas pelo Job. activeDeadlineSeconds limita a duração ativa do Job, mas não representa o prazo completo desde a entrada na fila nem uma garantia física instantânea de desligamento. (kubernetes.io)
O Time to Live, ou TTL, de 600 segundos começa após a conclusão ou falha. Ele permite a limpeza do Job e seus dependentes, mas não remove automaticamente o namespace e todos os recursos independentes. (kubernetes.io)
O emptyDir acompanha a vida do Pod e não é armazenamento durável. Seu sizeLimit não deve ser interpretado como reserva de disco; monitore pressão de armazenamento no nó. (kubernetes.io)
Requests orientam o agendamento; limites de CPU podem causar throttling, e limites de memória podem resultar em encerramento por falta de memória. Ajuste também armazenamento efêmero para workspace e logs. (kubernetes.io)
A quota é por namespace: o orçamento global e por tenant continua sendo responsabilidade da plataforma. Substitua o namespace fixo por identificadores únicos. Para produção, use imagem aprovada por digest, dependências pré-instaladas e validação de assinatura conforme sua política.
6. Rede, identidade e ferramentas mediadas
O manifesto não autoriza DNS nem acesso externo. NetworkPolicies são aditivas: outra política permissiva pode ampliar o tráfego permitido. A API padrão não oferece autorização por domínio HTTP, e existem particularidades de tráfego com nós e implementação de rede. Valide explicitamente metadata de cloud e destinos internos; não trate o YAML como prova suficiente de bloqueio. (kubernetes.io) Para dimensionar IPs, DNS, conexões e egress desse tráfego mediado, veja Como dimensionar a rede do Kubernetes para sandboxes efêmeros de agentes de IA.
Para tarefas conectadas, recomendo liberar somente um gateway privado autenticado. Ele deve autorizar destinos e métodos, limitar bytes e tempo, validar redirecionamentos e rejeitar destinos internos indevidos. Se DNS for necessário, use resolução controlada e considere o risco de exfiltração pelo próprio DNS.
Não coloque uma chave ampla de modelo, GitHub ou cloud no processo não confiável. Prefira capacidades curtas, restritas a uma execução, ou chamadas mediadas por serviços confiáveis.
Role-Based Access Control, ou RBAC, deve separar controller e executor. Permissões para criar workloads podem permitir acesso indireto a Secrets e identidades; não aceite manifests arbitrários enviados pelo agente. (kubernetes.io)
No Amazon Elastic Kubernetes Service, ou Amazon EKS, EKS Pod Identity associa uma função de Identity and Access Management, ou IAM, a uma ServiceAccount. Neste desenho, prefira conceder essa identidade ao serviço confiável de artefatos, não ao sandbox. (docs.aws.amazon.com)
Use Transport Layer Security, ou TLS, entre serviços; mantenha rotação e expiração de credenciais. Secrets Kubernetes precisam de proteção de acesso e criptografia em repouso: codificação base64 não equivale a criptografia. (kubernetes.io)
7. Ciclo de vida e recuperação
Proponho registrar os estados queued → provisioning → running → collecting → terminating → cleaned, com caminhos de falha e cancelamento. São estados da aplicação, não fases nativas de Pod.
O controller deve reconciliar estado desejado e observado, usando chave de idempotência por execução. Um reconciliador independente procura ambientes vencidos e recursos órfãos.
Antes de marcar sucesso, exija confirmação durável do resultado. O serviço de artefatos deve impor tamanho máximo, validar caminhos, rejeitar extrações inseguras e tratar arquivos produzidos como não confiáveis. Não dependa de copiar arquivos depois que o Pod já foi removido.
A coleta de lixo Kubernetes utiliza relações de propriedade, com restrições de escopo. Um Job namespaced não deve ser usado como proprietário de um namespace, que é cluster-scoped. Planeje a remoção explícita do namespace e de recursos externos. (kubernetes.io)
Em partições de rede, apagar um objeto na API não prova que o processo deixou de existir no nó. Para operações sensíveis, combine prazo com credenciais expirantes, revogação no gateway e fencing do nó antes de considerar a execução fisicamente encerrada. (kubernetes.io)
8. Cenário corporativo: validar uma alteração de código
Problema: uma equipe quer que agentes analisem pull requests sem executar scripts externos dentro dos runners corporativos privilegiados.
Solução proposta: manter o planejamento fora do sandbox e criar uma execução isolada por tentativa de validação.
Implementação: um serviço confiável prepara um snapshot do repositório, sem credenciais; o executor recebe somente os arquivos necessários e dependências aprovadas. Testes produzem um relatório. Outro serviço valida o artefato e publica o comentário na plataforma de código.
Resultado esperado: reduzir o alcance de uma execução comprometida e facilitar auditoria e reprodução. Isso é uma expectativa arquitetural, não um benchmark ou relato de resultado medido.
Use fixtures sintéticas para PostgreSQL ou Redis quando os testes precisarem desses serviços. Evite reutilizar bancos de produção ou caches graváveis entre clientes. Uma aprovação humana deve continuar separada da execução quando a ação envolver deploy, exclusão ou alteração sensível.
9. Produção: disponibilidade, capacidade e custos
Minha recomendação é manter múltiplas réplicas da API e dos gateways, distribuição entre zonas e reconciliação idempotente. Para controllers com escrita concorrente, adote eleição de líder ou coordenação equivalente. Proteja o banco de estado com backup e testes de restauração.
Aplique readiness, startup e liveness probes aos serviços persistentes conforme sua função. O Job curto do exemplo não precisa de endpoint HTTP artificial: conclusão, deadline e progresso externo são os sinais relevantes. Probes possuem semânticas diferentes e não devem ser adicionadas mecanicamente. (kubernetes.io)
Use PodDisruptionBudget nos serviços adequados, lembrando que ele limita determinadas interrupções voluntárias, não elimina falhas involuntárias. Não o trate como garantia de conclusão de um Job. (kubernetes.io)
Capacity planning
Como exemplo hipotético, 60 tarefas por minuto com duração média de dois minutos implicam aproximadamente 120 execuções simultâneas em regime estável. Com requests de 250m e 128Mi, isso representa 30 CPUs e 15Gi de memória solicitadas, antes de overhead e componentes da plataforma.
Dimensione também rajadas, percentis de duração, imagens, disco, endereços IP e objetos na API. Imponha limites de concorrência antes de criar milhares de Pods pendentes.
Autoscaling de nós considera necessidades de agendamento e restrições dos Pods. Ele complementa, mas não substitui, controle de admissão e capacidade mínima para absorver a inicialização. (kubernetes.io)
Para FinOps, acompanhe custo por tarefa aceita e por tarefa concluída. Inclua capacidade ociosa, egress, armazenamento, logs e retries. Minha política sugerida é reservar capacidade estável para serviços críticos e avaliar Spot apenas para tarefas recuperáveis, com prazo que comporte interrupções.
Prefira gateways compartilhados a um load balancer por sandbox. Use caches imutáveis e identificados por conteúdo; não reaproveite ambientes usados entre tenants apenas para reduzir latência.
10. Observabilidade e troubleshooting
OpenTelemetry oferece sinais como métricas, logs e traces. Instrumente a plataforma para correlacionar admissão, provisionamento, execução e limpeza. (opentelemetry.io)
Proponho acompanhar:
- latência de fila e tempo até o primeiro comando;
- throughput, taxa de sucesso e falhas por categoria;
- CPU, memória, disco, throttling e reinicializações;
- duração da coleta e idade dos ambientes expirados;
- recusas de política, tráfego negado e saturação do gateway.
Defina objetivos de nível de serviço, ou SLOs, separados para disponibilidade da API, início da execução e limpeza. Use identificadores de execução em logs e traces, evitando uma série temporal por execução. Não registre prompts completos ou credenciais por padrão.
Para investigação, os comandos abaixo consultam estado, eventos e logs. Execute-os com uma identidade operacional autorizada, não dentro do sandbox. (kubernetes.io)
kubectl get runtimeclass agent-gvisor
kubectl get nodes -l sandbox-runtime=gvisor
kubectl get jobs,pods -n agent-run-7f3a
kubectl describe job agent-task -n agent-run-7f3a
kubectl describe resourcequota sandbox-budget -n agent-run-7f3a
kubectl get networkpolicy -n agent-run-7f3a
kubectl logs job/agent-task -n agent-run-7f3a
kubectl get events -n agent-run-7f3a --sort-by=.metadata.creationTimestampUse este roteiro operacional, adaptando a correção à evidência coletada:
| Sintoma | Possível causa | Investigação | Correção proposta |
|---|---|---|---|
| Pod em Pending | Capacidade ou seleção de nós incompatível | Eventos e labels dos nós | Ajustar pool, requests ou seleção |
| FailedCreatePodSandBox | Handler ausente ou falha no runtime | Eventos e logs do runtime no nó | Corrigir instalação; não remover isolamento como atalho |
| Job sem Pod | Quota ou admissão recusando criação | Descrição do Job e eventos | Corrigir orçamento ou template |
| OOMKilled | Pico acima do orçamento | Estado terminado e métricas | Reduzir concorrência ou revisar limite |
| Downloads falham | Egress bloqueado intencionalmente | Políticas e logs do gateway | Usar dependências pré-instaladas ou rota aprovada |
| Namespace em Terminating | Finalizer ou recurso dependente pendente | Condições do namespace e recursos restantes | Recuperar o controller responsável antes de remover finalizers |
11. Desenvolvimento, manutenção e critérios de aceitação
Em desenvolvimento, uma réplica e observabilidade simplificada podem bastar para arquivos sintéticos. Isso não justifica executar conteúdo hostil sem isolamento.
Em produção, exija redundância do plano de controle, limites, alertas, trilha de auditoria, backup do estado durável e testes de recuperação. Não faça backup indiscriminado dos workspaces descartáveis: retenha somente o necessário conforme a política de dados.
Gerencie templates e políticas por GitOps e integração e entrega contínuas, ou CI/CD. Deixe o controller gerenciar execuções efêmeras, sem um reconciliador GitOps recriando Jobs já limpos.
Faça upgrades em um pool canário com testes de compatibilidade, isolamento, latência e recuperação. Para rollback, direcione novas tarefas à combinação anterior de imagem, runtime e política validada; não tente transformar um Job concluído em uma execução anterior.
Antes da liberação, recomendo testes que comprovem:
- Acesso negado ao cluster, metadata e destinos não autorizados.
- Impossibilidade de escolher ServiceAccounts, volumes ou privilégios arbitrários.
- Encerramento e revogação após timeout ou cancelamento.
- Recuperação após morte do controller e perda de nó.
- Ausência de arquivos entre execuções e limpeza de recursos órfãos.
- Idempotência de publicações e outras ações externas.
Evite credenciais hardcoded, Docker socket, hostPath, privilégios amplos, saída irrestrita e ausência de orçamento. Não confunda ttlSecondsAfterFinished com limpeza completa nem retries com processamento exatamente uma vez.
12. Quando utilizar — e quando simplificar
Esta arquitetura faz sentido quando há execução recorrente de código não confiável, múltiplos usuários, necessidade de auditoria e equipe capaz de operar a plataforma.
Para automações pequenas e confiáveis, um worker restrito pode ser suficiente. Para fronteiras regulatórias rígidas, uma VM dedicada ou ambiente separado pode ser mais apropriado. Um serviço gerenciado de execução também merece avaliação, considerando isolamento documentado, localização dos dados, limites, custos e dependência do fornecedor.
A decisão deve equilibrar risco e capacidade operacional, não apenas aproveitar um cluster existente.
Resumo prático
- Separe decisão confiável de execução não confiável.
- Escolha isolamento pelo modelo de ameaça.
- Limite identidade, rede, recursos, duração e concorrência.
- Persista resultados aceitos antes da limpeza.
- Trate falhas, duplicação e expiração como parte do contrato.
- Teste os controles no dataplane, não apenas a validade do YAML.
Conclusão
Sandboxes efêmeros devem ser tratados como um produto de Platform Engineering: uma interface simples para consumidores, sustentada por políticas e operação rigorosas.
DevOps fornece entrega reproduzível; SRE, ou Site Reliability Engineering, transforma disponibilidade, recuperação e limpeza em objetivos verificáveis; a arquitetura cloud estabelece fronteiras de identidade e capacidade. O Kubernetes coordena esses mecanismos, mas a segurança depende do contrato completo entre autorização, runtime, rede e ciclo de vida.
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.