Skip to content
Appunti personali

Agent Sandbox OSS

Un runtime Kubernetes per agenti AI di lunga durata e ad alto consumo di dati

Perché l’ho fatto

Portare gli agenti in produzione significa scontrarsi subito con tre muri: un processo condiviso non è né sicuro né in grado di garantire QoS, creare un pod su richiesta costa decine di secondi e il guasto di un nodo si porta via lo stato in corso. Questo progetto affronta i tre problemi uno alla volta, in sette lab dove ogni passo risolve solo ciò che il precedente ha fatto emergere.

Cosa fa

Pool caldo: operativi in millisecondi

Un gruppo di pod già avviati resta in attesa: la richiesta ne occupa uno direttamente, saltando scheduling, download dell’immagine e init del container. Sullo stesso percorso, crearlo su richiesta costa 45–90 secondi. La cifra esatta dipende dalla macchina e dalla dimensione del pool; l’endpoint restituisce il valore appena misurato.

Workspace separati dal calcolo

Lo stato dell’utente vive su PVC o su checkpoint remoto: il pod è solo l’esecutore. Si elimina a mano un pod in esecuzione e un pod nuovo rimonta lo stesso workspace e prosegue.

Scheduling in pull

Non è più un allocatore a spingere il lavoro verso un pod scelto: sono i pod a prendere i task dalla coda. Doppio scheduling e prelazione spariscono con esso — niente Lease di K8s, niente Redlock.

Isolamento a runtime

Una RuntimeClass sposta i carichi non affidabili su gVisor / Kata, comprando un confine che non condivide il kernel e pagandolo in tempo di avvio.

Sette lab progressivi

Si parte dal pod singolo ingenuo e ogni passo prima rompe qualcosa e poi introduce il componente: l’intera evoluzione resta nel repository.

Il punto di partenza

Un agente non è una richiesta web: dura a lungo, consuma memoria, scrive file ed esegue codice che un modello ha scritto pochi secondi prima. Messo in un processo condiviso, il primo giorno insegna due cose — un task va in OOM e si porta via gli altri, e un payload malevolo legge tranquillamente un secret montato nel container. L’isolamento non è un’ottimizzazione, è il biglietto d’ingresso.

Ma l’isolamento si fa pagare subito, e ogni pagamento fa emergere il problema successivo. Questo repository è quella catena, messa per iscritto.

Come procedono i sette passi

Passo Il muro La risposta del passo
1 Pod singolo I task si bloccano a vicenda, un OOM li abbatte tutti, il codice malevolo legge i secret Si fissa la baseline: serve isolamento a livello di container
2 Su richiesta Il TTI passa da 0,5 s a 45–90 s; CPU dell’APIServer alle stelle, latenza di scrittura su etcd L’avvio a freddo è fisica, e KubeAPI non può stare sul percorso sincrono
3 Pool caldo La latenza torna a poche centinaia di ms, al prezzo di pod che girano a vuoto Si scambia ridondanza per latenza e iniziano i conti sulla capacità
4 Workspace Un pod che muore si porta via un’analisi a metà Lo stato passa su PVC / checkpoint; il pod diventa sostituibile
5 Da push a pull L’allocatore deve gestire prelazione, lock e contese I worker prendono dalla coda, e in più si ottengono le priorità
6 Isolamento duro Con kernel condiviso un carico ostile può ancora esaurire il nodo o tentare la fuga Si passa a gVisor / Kata via RuntimeClass, misurando il costo d’avvio
7 Caos Sei passi validi singolarmente non è detto reggano insieme Si uccidono nodi e il primario della coda, si misurano MTTD / MTTR / disconnessioni

Tre compromessi che vale la pena capire

Il pool caldo non è un conto tecnico. Passare da decine di secondi a poche centinaia di millisecondi è centinaia di volte più veloce, pagato con pod che restano accesi a vuoto. Finito quel passo, la domanda non è più «come lo rendiamo veloce» ma «quanti conviene tenere caldi e come si scalano con il carico»: economia, non tuning.

I pod muoiono, il contesto no. Solo dopo aver estratto il workspace dal pod la prova di guasto significa qualcosa: si elimina un pod in esecuzione, un pod nuovo monta lo stesso workspace e prosegue, e si misurano tempo di ripristino e coerenza dei dati invece del semplice «è sopravvissuto?».

Lo scheduling si risolve non facendolo. Spingere il lavoro richiede sapere chi è libero e difendersi dalla prelazione, e quindi introdurre lock. Quando sono i worker a prendersi i task, quei problemi non vengono risolti: smettono di esistere. I sistemi batch finiscono sempre qui.

Come si verifica

L’ultimo passo rimette insieme tutto e lo rompe di proposito: si tengono centinaia di sessioni attive, si uccide il trenta per cento dei nodi e poi il primario della coda, e si osserva quanto ci mette il sistema ad accorgersi del lavoro fermo (MTTD), quanto impiega un workspace a riagganciarsi a un pod nuovo (MTTR) e quanti utenti hanno davvero visto un errore. Senza questo passo, i primi sei sono corretti solo presi singolarmente.

Stato attuale

In corso. Le interfacce cambiano ancora, nessuna release. Tutto è open source: le issue sul design sono benvenute.