GKE Agent Sandbox para Reinforcement Learning: gVisor, warm pools e cold-start
Introdução
Workloads de Reinforcement Learning (RL) e agentes de Inteligência Artificial apresentam um problema diferente do encontrado em aplicações tradicionais.
Uma API convencional executa código conhecido, empacotado e validado durante o pipeline de CI/CD. Em uma plataforma de agentes, o workload pode executar scripts produzidos dinamicamente, bibliotecas escolhidas em runtime, ambientes de simulação e até código gerado por um Large Language Model (LLM).
Isso altera o modelo de ameaça.
Executar esse tipo de código diretamente em um Pod comum significa compartilhar o kernel do nó Kubernetes com outros workloads. Mesmo utilizando namespaces Linux, seccomp e capabilities, ainda existe uma superfície de ataque importante entre container e kernel.
O Google Kubernetes Engine (GKE) Agent Sandbox foi criado justamente para ambientes desse tipo. Ele adiciona gerenciamento de ciclo de vida de sandboxes sobre Kubernetes e utiliza isolamento baseado em gVisor, permitindo criar ambientes efêmeros ou persistentes para agentes, notebooks, código não confiável e aplicações de IA.
O Agent Sandbox é diferente do recurso tradicional GKE Sandbox:
GKE Sandbox
↓
gVisor / isolamento de runtime
GKE Agent Sandbox
↓
Controller + CRDs + lifecycle management
↓
GKE Sandbox / gVisorAtualmente, o Agent Sandbox utiliza recursos Kubernetes como Sandbox, SandboxTemplate, SandboxWarmPool e SandboxClaim. O modelo de warm pools permite manter ambientes previamente inicializados para reduzir significativamente o tempo percebido de provisionamento.
Neste laboratório vamos montar uma arquitetura semelhante à seguinte:
┌──────────────────────┐
│ RL Orchestrator │
│ API / Trainer │
└──────────┬───────────┘
│
SandboxClaim
│
▼
┌──────────────────────┐
│ Agent Sandbox │
│ Controller │
└──────────┬───────────┘
│
┌─────────────┴──────────────┐
│ SandboxWarmPool │
│ SandboxTemplate │
└─────────────┬──────────────┘
│
▼
┌─────────────────────────┐
│ Pod / RuntimeClass │
│ gVisor │
└─────────────┬───────────┘
│
GKE sandbox node pool
│
┌───────────┴───────────┐
│ Gymnasium Environment │
│ RL Agent / Python │
└───────────────────────┘A proposta não é apenas executar CartPole. O objetivo é construir um modelo que possa posteriormente receber ambientes de avaliação, código gerado por agentes e workloads de RL distribuído.
O que é o GKE Agent Sandbox
O GKE Agent Sandbox é uma camada de gerenciamento de ambientes isolados executados em GKE.
Um Sandbox representa essencialmente um ambiente Kubernetes isolado e gerenciado por um controller. O recurso permite controlar identidade, armazenamento, lifecycle e exposição de rede sem obrigar a aplicação que solicita o ambiente a manipular Pods diretamente.
O projeto utiliza Custom Resource Definitions (CRDs) Kubernetes. Entre os principais recursos estão:
Sandbox
Representa o ambiente isolado propriamente dito. É responsável pelo Pod que executará o workload.
SandboxTemplate
Funciona como blueprint. Em vez de repetir a definição completa do Pod cada vez que um agente precisa de ambiente, podemos criar python-rl-template e reutilizá-lo para centenas de sandboxes.
SandboxWarmPool
Mantém uma quantidade determinada de ambientes previamente inicializados. Por exemplo, replicas: 5 significa que o controller tenta manter cinco sandboxes disponíveis para uso imediato.
SandboxClaim
Representa a solicitação de um ambiente. A aplicação solicita "preciso de um sandbox" e o controller associa um Sandbox disponível no warm pool.
A documentação atual do Agent Sandbox recomenda esse modelo quando startup rápido é importante.
Por que isso é interessante para Reinforcement Learning
Um pipeline simplificado de RL pode ser representado assim:
Policy
│
▼
Action
│
▼
Environment
│
▼
Observation + Reward
│
▼
Trainer
│
▼
Nova PolicyO problema surge quando queremos executar milhares de ambientes independentes:
environment-0001
environment-0002
environment-0003
...
environment-5000Cada ambiente pode executar:
- Python;
- simuladores;
- ferramentas externas;
- código produzido pelo agente;
- dependências diferentes;
- scripts potencialmente não confiáveis.
O Agent Sandbox transforma esses environments em unidades isoladas de infraestrutura:
Trainer
│
├── SandboxClaim → Gym Env 001
├── SandboxClaim → Gym Env 002
├── SandboxClaim → Gym Env 003
└── SandboxClaim → Gym Env NIsso separa claramente o control plane de treinamento da execução dos ambientes.
gVisor e o runtime sandboxed
Containers tradicionais compartilham o kernel Linux do host:
Container
↓
Linux namespaces
↓
Host KernelCom gVisor:
Container
↓
gVisor user-space kernel
↓
seccomp / namespaces
↓
Host KernelO gVisor intercepta grande parte das chamadas de sistema e reduz a interação direta entre o processo do container e o kernel do host.
No Kubernetes, isso é selecionado por:
runtimeClassName: gvisorEm GKE, nós habilitados para Sandbox recebem automaticamente o label:
sandbox.gke.io/runtime: gvisore o taint:
sandbox.gke.io/runtime=gvisor:NoScheduleEsse mecanismo impede que workloads comuns sejam agendados inadvertidamente nos nodes destinados às sandboxes.
Arquitetura do laboratório
Vamos utilizar GKE Standard para deixar explícita a separação entre os node pools:
GKE Cluster
│
├── system-pool
│ ├── controllers
│ ├── observability
│ └── RL orchestrator
│
└── rl-sandbox-pool
├── gVisor
├── taint sandbox.gke.io/runtime=gvisor
├── taint workload=rl-sandbox
│
├── Sandbox 1
├── Sandbox 2
├── Sandbox 3
└── Sandbox NEssa separação é importante porque código não confiável não deveria disputar o mesmo node pool utilizado por controllers ou aplicações críticas.
A própria documentação do GKE alerta que workloads não confiáveis devem ser considerados na estratégia de isolamento mesmo quando gVisor está sendo utilizado.
Criando o cluster GKE
No momento deste artigo, a documentação do GKE utiliza a API v1beta1 do Agent Sandbox em clusters 1.36.3-gke.1767000 ou superiores. Como versões mínimas podem mudar, verifique a documentação antes de criar ambientes permanentes.
Defina:
export PROJECT_ID="$(gcloud config get-value project)"
export CLUSTER_NAME="rl-agent-sandbox"
export LOCATION="us-central1-a"
export CLUSTER_VERSION="1.36.3-gke.1767000"Crie o cluster:
gcloud beta container clusters create "${CLUSTER_NAME}" \
--location="${LOCATION}" \
--cluster-version="${CLUSTER_VERSION}" \
--num-nodes=1 \
--machine-type=e2-standard-2 \
--workload-pool="${PROJECT_ID}.svc.id.goog"Nesse laboratório mantemos apenas um node inicial para reduzir custos. Em produção, normalmente utilizaríamos cluster regional e múltiplas zonas.
Criando o node pool dedicado às sandboxes
Agora criamos o pool utilizado para RL:
gcloud container node-pools create rl-sandbox-pool \
--cluster="${CLUSTER_NAME}" \
--location="${LOCATION}" \
--machine-type=e2-standard-4 \
--image-type=cos_containerd \
--sandbox=type=gvisor \
--enable-autoscaling \
--min-nodes=0 \
--max-nodes=10 \
--node-labels=workload=rl-sandbox \
--node-taints=workload=rl-sandbox:NoScheduleO ponto mais importante é --sandbox=type=gvisor. Para GKE Sandbox, o node image precisa utilizar cos_containerd.
Também adicionamos nosso próprio taint, workload=rl-sandbox:NoSchedule. Portanto, teremos duas barreiras de scheduling:
sandbox.gke.io/runtime=gvisor:NoSchedule
workload=rl-sandbox:NoScheduleIsso deixa explícita a intenção daquele node pool.
Habilitando o Agent Sandbox
Depois do pool gVisor:
gcloud beta container clusters update "${CLUSTER_NAME}" \
--location="${LOCATION}" \
--enable-agent-sandboxValide:
gcloud beta container clusters describe "${CLUSTER_NAME}" \
--location="${LOCATION}" \
--format="value(addonsConfig.agentSandboxConfig.enabled)"O resultado esperado é True. Esse é o procedimento atual documentado pelo Google para GKE Standard.
Criando o namespace
apiVersion: v1
kind: Namespace
metadata:
name: rl-sandbox
labels:
workload-type: agent-sandboxkubectl apply -f namespace.yamlEm produção, esse namespace também deveria receber políticas específicas de RBAC, NetworkPolicy, ResourceQuota, LimitRange e Pod Security.
SandboxTemplate para RL
Agora criamos o runtime Python:
apiVersion: extensions.agents.x-k8s.io/v1beta1
kind: SandboxTemplate
metadata:
name: rl-python-runtime
namespace: rl-sandbox
spec:
podTemplate:
metadata:
labels:
app: rl-environment
workload: rl-sandbox
spec:
runtimeClassName: gvisor
automountServiceAccountToken: false
securityContext:
runAsNonRoot: true
runAsUser: 1000
nodeSelector:
sandbox.gke.io/runtime: gvisor
workload: rl-sandbox
tolerations:
- key: sandbox.gke.io/runtime
operator: Equal
value: gvisor
effect: NoSchedule
- key: workload
operator: Equal
value: rl-sandbox
effect: NoSchedule
containers:
- name: rl-runtime
image: us-central1-docker.pkg.dev/PROJECT_ID/rl/rl-runtime:1.0.0
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
resources:
requests:
cpu: "500m"
memory: "512Mi"
limits:
cpu: "2"
memory: "2Gi"
volumeMounts:
- name: tmp
mountPath: /tmp
volumes:
- name: tmp
emptyDir:
sizeLimit: 1Gi
restartPolicy: OnFailureEssa configuração faz quatro coisas particularmente importantes:
runtimeClassName: gvisorfaz o workload utilizar o runtime sandboxed;nodeSelectorcomsandbox.gke.io/runtime: gvisorgarante que o Pod seja executado no pool correto;- as
tolerationspermitem que o workload atravesse os taints existentes; - o
securityContextimpede diversas formas de escalada de privilégio.
O Agent Sandbox possui políticas de admissão que exigem configurações como runAsNonRoot, capabilities.drop: ["ALL"], limites de recursos, gVisor, node selector e toleration. Configurações como privileged, hostNetwork, hostPID, hostIPC, hostPath e capabilities adicionais são proibidas.
Criando um Warm Pool
Agora podemos manter environments prontos:
apiVersion: extensions.agents.x-k8s.io/v1beta1
kind: SandboxWarmPool
metadata:
name: rl-python-warmpool
namespace: rl-sandbox
spec:
replicas: 3
sandboxTemplateRef:
name: rl-python-runtimekubectl apply -f rl-warmpool.yaml
kubectl get sandboxwarmpool -n rl-sandboxConceitualmente:
Warm Pool
│
├── ready sandbox
├── ready sandbox
└── ready sandboxQuando uma aplicação solicitar um ambiente, não será necessário passar novamente por:
schedule
→ image pull
→ container creation
→ Python startup
→ dependency initializationÉ exatamente aí que aparece o ganho de cold-start.
Solicitando um sandbox
Uma aplicação pode solicitar um ambiente através de um SandboxClaim:
apiVersion: extensions.agents.x-k8s.io/v1beta1
kind: SandboxClaim
metadata:
name: rl-session-001
namespace: rl-sandbox
spec:
warmPoolRef:
name: rl-python-warmpoolkubectl apply -f claim.yaml
kubectl get \
sandboxwarmpool,sandboxclaim,sandbox,pod \
-n rl-sandboxO controller associa o claim a um ambiente do warm pool e passa a reconstruir a capacidade disponível em segundo plano. Esse modelo é recomendado pelo GKE para workloads sensíveis a startup latency.
Imagem do runtime
Um requirements.txt simples:
gymnasium[classic-control]
numpy
openai
opentelemetry-api
opentelemetry-sdk
opentelemetry-exporter-otlp-proto-grpcDockerfile:
FROM python:3.12-slim
RUN useradd \
--create-home \
--uid 1000 \
sandbox
WORKDIR /app
COPY requirements.txt .
RUN pip install \
--no-cache-dir \
-r requirements.txt
COPY app.py .
RUN chown -R sandbox:sandbox /app
USER sandbox
CMD ["python", "app.py"]Evite rodar pip install ... durante a inicialização do container. Isso piora drasticamente o cold-start, dificulta reprodutibilidade e aumenta dependência de conectividade externa.
Exemplo com Gymnasium
O antigo projeto OpenAI Gym evoluiu e atualmente o ecossistema utiliza principalmente o Gymnasium, mantido pela Farama Foundation.
A interface fundamental continua baseada em:
observation, info = env.reset()
observation, reward, terminated, truncated, info = env.step(action)Desde as versões modernas da API, terminated e truncated substituem o antigo conceito único de done.
Um exemplo simples:
import gymnasium as gym
env = gym.make("CartPole-v1")
observation, info = env.reset(seed=42)
total_reward = 0.0
for step in range(500):
action = env.action_space.sample()
observation, reward, terminated, truncated, info = env.step(action)
total_reward += reward
if terminated or truncated:
break
print({
"steps": step + 1,
"reward": total_reward,
})
env.close()Esse código pode ser executado integralmente dentro do Sandbox.
Exemplo OpenAI + Gymnasium
Também podemos utilizar um modelo como policy experimental.
Esse exemplo é propositalmente didático: não é uma forma econômica de treinar CartPole. Consultar um LLM a cada timestep cria latência e custo consideravelmente maiores que uma policy neural local.
Porém, o padrão é útil quando o environment representa tarefas de software, browsers, shells ou ferramentas complexas.
import os
import gymnasium as gym
from openai import OpenAI
client = OpenAI(
api_key=os.environ["OPENAI_API_KEY"]
)
env = gym.make("CartPole-v1")
observation, info = env.reset(seed=42)
total_reward = 0
for step in range(50):
prompt = f"""
Você controla um ambiente CartPole.
Observação:
{observation.tolist()}
As ações possíveis são:
0 = mover para esquerda
1 = mover para direita
Escolha uma única ação.
Responda somente:
0
ou
1
"""
response = client.responses.create(
model="gpt-5.6-luna",
input=prompt,
)
result = response.output_text.strip()
if result not in {"0", "1"}:
action = env.action_space.sample()
else:
action = int(result)
observation, reward, terminated, truncated, info = env.step(action)
total_reward += reward
print(
{
"step": step,
"action": action,
"reward": reward,
"total_reward": total_reward,
}
)
if terminated or truncated:
break
env.close()A API atual da OpenAI utiliza o padrão client.responses.create(...) através da Responses API. A arquitetura fica:
Sandbox
│
├── Gymnasium environment
│
├── observation
│
▼
OpenAI API
│
├── decision
▼
action
│
▼
Gymnasium
│
└── rewardPara workloads reais de RL, uma arquitetura mais eficiente normalmente utiliza:
Local Policy Model
↓
Environment
↓
Rollout Buffer
↓
Trainere reserva LLMs externos para tarefas como reward evaluation, planning, reasoning, code generation e environment generation.
Um cenário corporativo realista
Imagine uma plataforma interna desenvolvendo agentes capazes de corrigir aplicações automaticamente. Cada treinamento recebe uma aplicação quebrada. O agente pode:
ler código
→ executar testes
→ alterar arquivos
→ executar comandos
→ observar erro
→ tentar nova soluçãoExecutar tudo no mesmo Pod do trainer seria perigoso. Com Agent Sandbox:
RL Trainer
│
├── Claim #001
│ └── repository clone
│ └── generated code
│ └── tests
│
├── Claim #002
│
└── Claim #003Cada episódio ocorre em seu próprio sandbox. Uma função de recompensa poderia utilizar:
testes aprovados +10
lint aprovado +2
build aprovado +5
timeout -3
crash -5
teste de segurança falha -10Ao terminar o episódio:
SandboxClaim deleted
↓
environment discarded
↓
new clean sandboxIsso reduz contaminação entre episódios e aumenta reprodutibilidade.
Ambiente de produção
Um desenho mais próximo de produção seria:
┌─────────────────────┐
│ RL Control Plane │
│ Trainer / API │
└──────────┬──────────┘
│
┌──────────────┴───────────────┐
│ │
▼ ▼
CPU Sandbox Pool GPU Sandbox Pool
e2 / n2 / c3 etc. L4 / A100 / H100
│ │
gVisor Runtime gVisor Runtime
│ │
Env Pods Training Pods
│ │
└──────────────┬───────────────┘
│
Observability
│
┌─────────────────┼─────────────────┐
▼ ▼ ▼
Prometheus Loki OpenTelemetryGKE Sandbox também suporta determinados tipos de GPU e TPU quando utilizados com versões compatíveis de GKE. Portanto, isolamento e aceleração de hardware não são necessariamente mutuamente exclusivos.
Uma arquitetura madura normalmente separaria environment execution, policy inference, training, reward model, orchestrator e observability em workloads independentes.
Observabilidade para RL
CPU e memória não são suficientes para observar RL.
O Google atualmente documenta instrumentação de workloads de RL em GKE utilizando OpenTelemetry, incluindo métricas específicas de sampling, training, reward e sincronização. Algumas métricas importantes:
| Métrica | Objetivo |
|---|---|
rl.loop.duration | duração do loop |
rl.sample.duration | tempo de sampling |
rl.reward.duration | tempo para calcular reward |
rl.train.duration | tempo de treinamento |
rl.step.duration | latência de cada timestep |
rl.sample.samples | quantidade de samples |
rl.sample.episodes | quantidade de episódios |
rl.train.steps | passos de treinamento |
rl.environment.reward.mean | recompensa média |
rl.environment.episode.length.mean | duração média do episódio |
rl.train.loss | loss atual |
rl.train.mfu | Model FLOP Utilization |
Uma instrumentação simples:
import time
from opentelemetry import metrics
meter = metrics.get_meter("rl-sandbox")
step_duration = meter.create_histogram(
"rl.step.duration",
unit="ms",
)
reward_counter = meter.create_counter(
"rl.reward.total",
)
start = time.perf_counter()
observation, reward, terminated, truncated, info = env.step(action)
elapsed_ms = (
time.perf_counter() - start
) * 1000
step_duration.record(
elapsed_ms,
attributes={
"rl.environment.name": "CartPole-v1",
"rl.run.id": "experiment-001",
},
)
reward_counter.add(
reward,
attributes={
"rl.environment.name": "CartPole-v1",
},
)Prometheus, Grafana, Loki e OpenTelemetry
Em uma stack independente do Cloud Monitoring:
Sandbox
│
├── stdout
│ ↓
│ Loki
│
├── /metrics
│ ↓
│ Prometheus
│
└── OTLP
↓
OpenTelemetry Collector
↓
Tempo / Cloud TraceDashboards deveriam correlacionar infraestrutura e RL: reward médio versus CPU usage, step latency, sandbox cold-start e node provisioning latency. Isso ajuda a diferenciar algoritmo ruim de infraestrutura lenta.
Medindo cold-start
Cold-start não deve ser tratado como uma única métrica. Podemos dividir:
T0 Sandbox solicitado
│
├── scheduler latency
├── node provisioning
├── image pull
├── container creation
├── Python startup
├── environment initialization
│
▼
T1 Sandbox prontoO GKE fornece diretamente a métrica kubernetes.io/pod/latencies/pod_first_ready, que mede o período entre criação do Pod e primeiro estado Ready, incluindo image pull.
Também existe kubernetes.io/node/latencies/startup, para medir criação da VM até o node ficar disponível no Kubernetes.
Essas duas métricas são fundamentais porque permitem separar Pod cold-start de Node cold-start.
Comparação de cold-starts
O laboratório deveria comparar pelo menos quatro cenários:
A. Pod normal + containerd
B. Pod gVisor sem warm pool
C. Agent Sandbox sem instância previamente quente
D. Agent Sandbox + WarmPoolÉ importante não publicar números como universais. Cold-start varia com tamanho da imagem, cache do node, tipo de VM, autoscaler, registry, dependências da aplicação, readiness probe, região e pressão do cluster.
Um resultado hipotético de laboratório, apenas para ilustrar o formato da análise, poderia ser:
| Cenário | P50 | P95 |
|---|---|---|
| Pod containerd com node quente | 1,4 s | 2,3 s |
| Pod gVisor com node quente | 1,9 s | 3,1 s |
| Sandbox criado sob demanda | 3,8 s | 6,7 s |
| SandboxClaim em WarmPool | 180 ms | 420 ms |
| Node novo + sandbox | 55 s | 88 s |
Esses números não são benchmarks oficiais do GKE. Eles devem ser substituídos por medições reais do ambiente.
O comportamento esperado, porém, explica bem a arquitetura. O maior custo normalmente não está em containerd versus gVisor, mas em:
node inexistente
↓
autoscaler
↓
Compute Engine VM
↓
node Ready
↓
image pull
↓
sandboxPor isso warm pools e capacidade mínima podem ter um impacto muito maior na experiência do usuário do que pequenos ajustes no runtime.
Benchmark recomendado
Execute pelo menos 30 ou 50 amostras por cenário. Colete:
request_timestamp
sandbox_assigned_timestamp
pod_created_timestamp
pod_ready_timestamp
environment_ready_timestamp
first_action_timestampCalcule P50, P90, P95 e P99. Nunca compare apenas uma execução.
Também execute dois experimentos distintos: com image cached e image not cached. Caso contrário, o resultado pode medir apenas Artifact Registry/image pull em vez do runtime propriamente dito.
Warm Pool x escala para zero
Existe um trade-off direto:
- Warm Pool: menor latência, maior custo ocioso;
- Scale-to-zero: menor custo, maior cold-start.
Uma estratégia comum é:
horário de pico: warm pool = 20
horário normal: warm pool = 5
madrugada: warm pool = 0A capacidade deve ser determinada observando a taxa de criação de sessões. Se a taxa de chegada é de 10 sandboxes/s e a reposição leva 5 s, um warm pool de apenas três ambientes será rapidamente consumido.
Segurança
Agent Sandbox não elimina a necessidade de controles adicionais. A recomendação é combinar:
gVisor
+
NetworkPolicy
+
RBAC
+
resource limits
+
admission policies
+
secret isolationO Agent Sandbox possui modelo de segurança com políticas de admissão que restringem configurações perigosas e estabelece postura de isolamento de rede para sandboxes.
Service Accounts
Um ponto particularmente importante é automountServiceAccountToken: false. Código executado dentro da sandbox não deveria receber automaticamente credenciais Kubernetes.
API keys
Evite colocar uma chave privilegiada de OpenAI diretamente no environment quando o código executado puder ser modificado pelo agente. Arquitetura mais segura:
Sandbox
│
▼
Internal AI Gateway
│
├── authentication
├── quota
├── model allowlist
├── rate limit
└── auditing
│
▼
OpenAI APIAssim, a sandbox nunca recebe a credencial principal.
NetworkPolicy
Uma postura razoável é começar com deny all e liberar apenas destinos necessários. Em workloads que executam código gerado por IA, egress irrestrito representa um risco particularmente importante.
sandbox
├─X→ metadata
├─X→ databases internos
├─X→ Kubernetes API
├─X→ outros namespaces
│
└──→ AI GatewayEsse modelo transforma acesso externo em capability explicitamente concedida.
Requests e Limits
Outro erro comum é criar sandboxes com requests muito abaixo dos limits:
requests:
cpu: 10m
limits:
cpu: 4Isso permite uma densidade teórica muito maior que a capacidade real. Imagine 100 ambientes com request = 100m e limit = 2 CPU:
Scheduler: 100 × 100m = 10 CPU
Potencial: 100 × 2 CPU = 200 CPUSe todos iniciarem processamento simultaneamente, ocorrerá contenção severa. Para RL, monitore requested CPU, actual CPU, CPU throttling, working set memory, OOMKills, episodes/s e steps/s, e faça sizing baseado em execução real.
FinOps
Os maiores vetores de custo são normalmente:
número de sandboxes
tempo de vida
warm pool
GPU/TPU
node idle
requests superdimensionados
telemetria excessiva
chamadas externas de modelosPara environments tolerantes à interrupção, Spot VMs podem ser bastante interessantes. Um desenho possível:
Trainer crítico → On-Demand
RL evaluation environments → Spot
Warm pool de baixa latência → On-Demand mínimo
Burst capacity → SpotEssa separação evita utilizar capacidade premium para workloads facilmente reconstruíveis.
Desenvolvimento x produção
| Desenvolvimento | Produção |
|---|---|
| Cluster zonal | Preferência por regional quando HA é requisito |
| 1 node | múltiplas zonas |
| warm pool 1-2 | dimensionado por demanda |
| Kubernetes Secret | gateway/secret management controlado |
| logs detalhados | sampling e retenção definida |
| permissões amplas | least privilege |
| egress mais aberto | default deny |
| imagens experimentais | versões imutáveis |
latest | digest ou versão fixa |
| métricas básicas | SLOs e alertas |
| scale-to-zero agressivo | equilíbrio cold-start/custo |
Troubleshooting
Sandbox não é agendado
kubectl get pods -n rl-sandbox -o wide
kubectl describe pod <pod> -n rl-sandboxProcure mensagens como untolerated taint ou node affinity conflict. Valide os nodes e os taints:
kubectl get nodes --show-labels
kubectl describe node <node> | grep -A5 TaintsRuntime não está usando gVisor
Verifique externamente:
kubectl get pod <pod> \
-n rl-sandbox \
-o jsonpath='{.spec.runtimeClassName}'Esperado: gvisor. A documentação do GKE recomenda verificar o RuntimeClass a partir do Kubernetes, e não confiar em informações emitidas pelo workload potencialmente não confiável.
SandboxTemplate rejeitado
kubectl get events \
-n rl-sandbox \
--sort-by=.metadata.creationTimestampPossíveis causas:
automountServiceAccountToken ausente
runAsNonRoot ausente
limits ausentes
capabilities não removidas
hostPath configurado
privileged=true
runtimeClass diferente de gvisorWarm pool não está disponível
kubectl get sandboxwarmpool -n rl-sandbox -o yaml
kubectl get sandbox -n rl-sandbox
kubectl get pods -n rl-sandboxSe os Pods estiverem em Pending, investigue capacidade e autoscaling.
Cold-start alto
Verifique primeiro se é Node cold-start ou Pod cold-start, analisando kubernetes.io/node/latencies/startup e kubernetes.io/pod/latencies/pod_first_ready. Depois investigue image size, image pull, init containers, application initialization, autoscaler, resource pressure e readiness probe.
O que evitar
Não execute código produzido por agentes diretamente em Pods privilegiados. Evite privileged: true. Não utilize hostNetwork: true, hostPID: true ou hostIPC: true.
Não monte via hostPath:
/var/run/docker.sock
/
/proc
/sysTambém evite utilizar o mesmo node pool para agentes não confiáveis, bancos, controllers e observabilidade crítica. Mesmo com gVisor, isolamento de blast radius continua sendo parte fundamental da arquitetura.
Boas práticas
Para workloads desse tipo, uma baseline razoável seria:
- node pool exclusivo para sandboxes;
runtimeClassName: gvisor;- taints e node selectors;
runAsNonRoot;- capabilities removidas;
- filesystem read-only quando possível;
automountServiceAccountToken: false;- limites explícitos de CPU e memória;
- NetworkPolicy default-deny;
- warm pool para workloads interativos;
- imagens pequenas e previamente construídas;
- observabilidade de RL separada da observabilidade de infraestrutura;
- métricas P50/P95/P99 de startup;
- Spot apenas para workloads tolerantes a interrupção;
- expiração automática de sandboxes abandonados.
Quando utilizar (e quando não)
Agent Sandbox é especialmente interessante para:
AI coding agents
RL environments
browser agents
terminal agents
avaliação automática de código
notebooks isolados
CI executando código não confiável
multi-tenant developer environments
agent evaluation farmsO modelo também faz sentido quando ambientes precisam ser criados e destruídos programaticamente em grande escala.
Por outro lado, ele adiciona complexidade operacional. Se sua aplicação executa apenas uma API Java, um worker Go ou um frontend Node.js com código conhecido e controlado, um Deployment Kubernetes comum provavelmente é suficiente. Também não faz sentido transformar cada chamada HTTP em um Sandbox. Para workloads confiáveis, Deployment, Job, CronJob e StatefulSet continuam sendo abstrações mais simples.
Resumo prático
RL Orchestrator
│
▼
SandboxClaim
│
▼
SandboxWarmPool
│
▼
Sandbox
│
▼
Pod
│
runtimeClassName: gvisor
│
▼
Dedicated GKE Node Pool
│
▼
Gymnasium / Agent CodeO principal valor do Agent Sandbox não é simplesmente executar um container com Python. É transformar environments potencialmente não confiáveis em um recurso gerenciado da plataforma.
E o principal aprendizado de performance é igualmente importante: cold-start não é apenas container startup. Ele é resultado de capacity, scheduler, node provisioning, image pull, runtime e application initialization. Warm pools atacam justamente o caminho crítico, removendo várias dessas etapas da requisição do usuário.
Conclusão
Reinforcement Learning e agentes autônomos introduzem um modelo operacional diferente das aplicações tradicionais. O Kubernetes continua sendo um excelente scheduler, mas Pods isolados apenas por containers não são necessariamente a fronteira de segurança adequada quando workloads podem executar código gerado dinamicamente.
A combinação de Agent Sandbox, gVisor, dedicated node pools, taints/tolerations, NetworkPolicy, resource isolation, Warm Pools e OpenTelemetry cria uma base muito mais adequada para plataformas modernas de execução de agentes.
Do ponto de vista de Platform Engineering, o resultado é especialmente interessante porque o time de IA não precisa conhecer detalhes sobre nodes, RuntimeClasses ou políticas de segurança. A aplicação simplesmente solicita um SandboxClaim, e a plataforma resolve scheduling, isolamento, capacidade, runtime, segurança e observabilidade.
Para workloads de RL em escala, a evolução natural é separar environments CPU de trainers GPU/TPU, introduzir filas e quotas, utilizar environments vetorizados quando isolamento por episódio não for necessário e construir autoscaling baseado na taxa real de SandboxClaims.
Nesse ponto, o Kubernetes deixa de ser apenas o lugar onde o modelo roda e passa a funcionar como control plane de uma plataforma de agentes e Reinforcement Learning.
Referências técnicas
- Google Kubernetes Engine — GKE Agent Sandbox: arquitetura e conceitos.
- Google Kubernetes Engine — habilitação e configuração do Agent Sandbox.
- Google Kubernetes Engine — CRDs
Sandbox,SandboxTemplate,SandboxClaimeSandboxWarmPool. - Google Kubernetes Engine — GKE Sandbox e gVisor.
- Google Kubernetes Engine — observabilidade de Reinforcement Learning com OpenTelemetry.
- Google Kubernetes Engine — métricas de startup de Pods e nodes.
- Gymnasium — API de environments e environments vetorizados.
- OpenAI — Responses API para integrações atuais.
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.