GoSuda

Gyors, izolált Docker-környezetek AI-ügynökök RL-kimenetelének (rollout) generálásához gvx segítségével

By Lemon Mint views ...

A biztonsági és infrastrukturális szűk keresztmetszet a kódoló ágensek RL rollout-jaiban

Szoftverfejlesztési ágensek betanítása és értékelése olyan benchmarkokon és megerősítéses tanulási (reinforcement learning, RL) keretrendszerekben, mint a SWE-bench, SWE-ReX, R2E-Gym, SWE-Gym, Verl és OpenHands, ezer egyidejű, megbízhatatlan, LLM által generált kód futtatását igényli. Valós szoftverfejlesztési feladatokban egy ágens nem csupán szövegfájlokat szerkeszt: lefuttatja a docker build parancsot, adatbázisokat és mikroszolgáltatásokat indít a docker compose segítségével, integrációs teszteket futtat, és konténernaplókat vizsgál.

Rollout-méretekben (klaszterenként 128 és 1,024 egyidejű epizód között) a meglévő konténerinfrastruktúra négy rendszerszintű biztonsági és teljesítménybeli szűk keresztmetszetbe ütközik:

  1. Host dockerd zárversengés és állapotszivárgás: Ha több száz egyidejű RL worker mutat ugyanarra a host /var/run/docker.sock fájlra, minden ágens root-egyenértékű hozzáférést kap a host daemonhoz, sorosítja az image rétegek kicsomagolását, kimeríti a docker0 alhálózati címkészletét, a host iptables rendszerben a Another app is currently holding the xtables lock hibákat okoz, és árva konténereket hagy vissza az epizódok között.
  2. Privilegizált DinD konténer-kiszökési kockázat: A szabványos docker:dind konténerek futtatásához --privileged (CAP_SYS_ADMIN) szükséges, amely nyers host blokkeszközöket, cgroup release agenteket és kernelmodulokat tesz elérhetővé a megbízhatatlan RL rollout-ok számára.
  3. Hardveres virtualizáció (/dev/kvm) korlátai: A MicroVM futtatókörnyezetek (Firecracker, QEMU, Kata Containers) tisztán izolálják a belső kernelt, de /dev/kvm eszközt igényelnek. A beágyazott virtualizáció nélküli szabványos Kubernetes podok, a KVM-et letiltott bare-metal felhőpéldányok és a meglévő gVisor (runsc) sandboxok nem tudnak KVM microVM-eket indítani.
  4. Lassú epizódreset (env.reset()) késleltetés: Egy friss docker:dind környezet indítása, valamint az alap image-ek letöltése és kicsomagolása minden RL epizódhoz 10 és 60 másodperc közötti időt vesz igénybe, így a GPU-s tanító workerek tétlenül várnak a környezetek újraindítására.

A gvx (rungv CLI és z.gfire.dev/gvx/pkg/gvx Go SDK) egy biztonságos, kis erőfeszítésű, tokenhatékony rollout futtatókörnyezetet biztosít, amely mind a négy szűk keresztmetszetet megoldja. Minden rollout környezet egyetlen, unprivileged, önzárolt user-space folyamatként fut (amelyet a Google gVisor Sentry kerneléje, a pkg/hostlockdown és egy memóriabeli netstack izolál), és teljes /var/lib/docker állapota egyetlen copy-on-write .ext4 fájlban (ext4lite) található.

Kis erőfeszítésű, tokenhatékony végrehajtás ügynökök számára: Ha a docker build vagy a docker compose minden epizódban azonnal működik (vagy az ügynök egyetlen, 3 parancsból álló körben, a rungv segítségével bootstrapelheti), az RL rollout-ok nem pazarolnak kontextusablak-tokeneket vagy lépésbüdzsét a service docker start ulimit hibák, az iptables-nft hibák vagy a runc pivot_root elutasítások hibaelhárítására. A trajektória minden tokene a tényleges szoftverfejlesztési feladat megoldására fordítódik.

Nulla jogosultságú biztonság megbízhatatlan ügynök-rollout-okhoz: Minden gvx epizód lezárja saját host OS folyamatát, mielőtt a vendégkódot futtatná (PR_SET_NO_NEW_PRIVS + BPF seccomp Linuxon, Seatbelt deny default macOS-en, Restricted Token + Job Object Windowson). Még akkor is, ha egy RL ágens az epizódján belül docker run --privileged vagy romboló rm -rf / parancsokat futtat, ezek a műveletek szigorúan az adott epizód user-space Sentry kernelébe és átmeneti .ext4 kötetfájljába vannak korlátozva.


Rollout architektúra: párhuzamos unprivileged példányok + reflink pillanatképek

A gvx-ben minden rollout worker egy közönséges unprivileged OS folyamat, saját gVisor Sentry kernellel, saját user-space netstack (10.0.2.15/24) hálózattal és saját ext4lite blokkkötettel (ep-N.ext4). Mindezek mellett egy csak olvasható, tartalomcímzett erofs OCI image-gyorsítótárat osztanak meg a host oldali lapgyorsítótáron keresztül.

Párhuzamos ágens RL rollout architektúra a gvx-szel

Miért változtatják meg az egyfájlos ext4lite kötetek az RL reset gazdaságtanát

Mivel a gvx ext4lite fájlrendszere egy teljes JBD2-naplózott ext4 kötetet tárol (beleértve a Docker overlayfs rétegkönyvtárait, a trusted.overlay.* bővített attribútumokat, a whiteout eszköz-csomópontokat és az image metaadatokat) egyetlen szabályos host fájlban, az epizód életciklus-kezelése egyetlen fájlműveletté válik:

  1. 100 milliszekundumos env.reset() a cp --reflink=always segítségével:
    • Egyszer kell létrehozni egy golden-docker.ext4 fájlt, amely előre letöltött alap image-eket tartalmaz (python:3.11, postgres:16, redis:7, vagy feladatspecifikus SWE-bench alap image-eket).
    • Minden RL epizód elején a cp --reflink=always golden-docker.ext4 ep-0042.ext4 parancs (XFS, Btrfs, ZFS vagy macOS APFS esetén) 100 milliszekundumon belül létrehoz egy független Copy-on-Write klónt, anélkül hogy a mögöttes lemezblokkokat másolná.
    • Csak az adott epizódban módosított 4 KiB-os blokkok foglalnak el további lemezterületet.
  2. Nulla maradványt hagyó leállítás (env.close()):
    • A rungv folyamat leállítása (Go-ban sb.Close()) és az ep-0042.ext4 fájl törlése az epizód összes folyamatát, hálózati socketjét, csatolását és tárolóját kb. 10 milliszekundum alatt felszabadítja. Semmi nincs csatolva a host kernel mount-táblájában, és semmi nincs regisztrálva a host cgroupjaiban.

Lépésenkénti munkafolyamat: golden .ext4 pillanatkép és gyors epizódreset

A rungv telepíthető minden rollout worker csomópontra, vagy a SLSA-val igazolt, előre lefordított bináris letöltésével a GitHub Releases oldalról (gh attestation verify rungv-v0.2.0-linux-amd64.tar.gz --repo gfire-sigs/gvx --signer-workflow gfire-sigs/gvx/.github/workflows/release.yml --deny-self-hosted-runners), vagy a CGO_ENABLED=0 go install z.gfire.dev/gvx/cmd/rungv@latest parancs futtatásával.

1. lépés: A golden docker:dind kötet egyszeri létrehozása

 1mkdir -p /srv/rl-snapshots /srv/rl-episodes
 2
 3# 1. Seed docker:dind példány indítása golden-docker.ext4 háttértárral
 4rungv run \
 5  -p unix:/srv/rl-snapshots/seed.sock:2375 \
 6  --mount type=volume,src=/srv/rl-snapshots/golden-docker.ext4,dst=/var/lib/docker \
 7  -e DOCKER_TLS_CERTDIR= \
 8  docker:dind --tls=false &
 9SEED_PID=$!
10
11# 2. Alap image-ek és build-gyorsítótár előtöltése a golden-docker.ext4 fájlon belül
12export DOCKER_HOST="unix:///srv/rl-snapshots/seed.sock"
13until docker info >/dev/null 2>&1; do sleep 0.5; done
14
15docker pull python:3.11-slim
16docker pull redis:7-alpine
17docker pull postgres:16-alpine
18
19# 3. A seed példány szabályos leállítása, hogy az ext4lite kiürítse és tömörítse az image-et
20kill -INT $SEED_PID
21wait $SEED_PID

2. lépés: Párhuzamos RL epizódok indítása 100 milliszekundum alatt

Minden párhuzamos rollout worker reflinkkel klónozza a golden-docker.ext4 fájlt, és elindít egy izolált docker:dind példányt, amelyben az összes image már jelen van a /var/lib/docker könyvtárban:

 1EPISODE_ID="ep-0042"
 2SOCK="/srv/rl-episodes/${EPISODE_ID}.sock"
 3
 4# Azonnali Copy-on-Write klón (~100 ms XFS/Btrfs/APFS esetén)
 5cp --reflink=always \
 6  /srv/rl-snapshots/golden-docker.ext4 \
 7  "/srv/rl-episodes/${EPISODE_ID}.ext4"
 8
 9# Izolált, unprivileged epizód indítása 4 GiB RAM-mal és 2 CPU-val
10rungv run \
11  -m 4g --cpus 2 \
12  -p "unix:${SOCK}:2375" \
13  --mount "type=volume,src=/srv/rl-episodes/${EPISODE_ID}.ext4,dst=/var/lib/docker" \
14  -e DOCKER_TLS_CERTDIR= \
15  docker:dind --tls=false &
16EP_PID=$!
17
18export DOCKER_HOST="unix://${SOCK}"
19until docker info >/dev/null 2>&1; do sleep 0.2; done
20
21# A rollout workeren belül az image-ek azonnal elérhetők hálózati letöltés nélkül
22docker images
23
24# Epizód leállítása a jutalom kiszámítása után (~10 ms)
25kill -KILL $EP_PID
26rm -f "/srv/rl-episodes/${EPISODE_ID}.ext4" "${SOCK}"

Programozott rollout-készlet a z.gfire.dev/gvx/pkg/gvx Go SDK használatával

Ha az RL környezetszerver vagy az értékelő keretrendszer Go-ban íródott (vagy gRPC-n keresztül egy Go rollout daemont hív), a párhuzamos konténer-epizódokat közvetlenül a z.gfire.dev/gvx/pkg/gvx csomagon keresztül kezelheti:

 1package main
 2
 3import (
 4	"bytes"
 5	"context"
 6	"fmt"
 7	"log"
 8	"os"
 9	"os/exec"
10	"path/filepath"
11	"time"
12
13	"z.gfire.dev/gvx/pkg/gvx"
14)
15
16func runEpisode(ctx context.Context, goldenExt4, scratchDir string, epID int) error {
17	epExt4 := filepath.Join(scratchDir, fmt.Sprintf("ep-%04d.ext4", epID))
18	defer os.Remove(epExt4)
19
20	// 1. A golden ext4lite kötet reflink-klónozása
21	cpCmd := exec.CommandContext(ctx, "cp", "--reflink=always", goldenExt4, epExt4)
22	if err := cpCmd.Run(); err != nil {
23		return fmt.Errorf("clone golden volume: %w", err)
24	}
25
26	// 2. Epizódonkénti valós idejű (wall-clock) időkorlát alkalmazása
27	epCtx, cancel := context.WithTimeout(ctx, 2*time.Minute)
28	defer cancel()
29
30	var stdout, stderr bytes.Buffer
31	sb, err := gvx.New(epCtx, gvx.Config{
32		Platform:               gvx.PlatformAuto,
33		NetworkMode:            gvx.NetModeNAT,
34		NetRaw:                 true,
35		AllowPacketSocketWrite: true,
36		AllowSUID:              true,
37		OCIImage:               "docker:dind",
38		Entrypoint:             []string{"sh", "-c"},
39		EntrypointSet:          true,
40		Cmd: []string{
41			"dockerd-entrypoint.sh --tls=false >/var/log/dockerd.log 2>&1 & " +
42				"until docker info >/dev/null 2>&1; do sleep 0.2; done && " +
43				"docker run --rm python:3.11-slim python -c 'print(6 * 7)'",
44		},
45		Envv: []string{"DOCKER_TLS_CERTDIR="},
46		Mounts: []gvx.Mount{
47			{
48				Kind:      gvx.MountVolume,
49				ImagePath: epExt4,
50				GuestPath: "/var/lib/docker",
51			},
52		},
53		Resources: gvx.Resources{
54			MemoryBytes: 4 << 30, // 4 GiB felső korlát
55			CPUMillis:   2000,    // 2.0 CPU
56		},
57		Stdout: &stdout,
58		Stderr: &stderr,
59	})
60	if err != nil {
61		return fmt.Errorf("init episode %d: %w", epID, err)
62	}
63	defer sb.Close()
64
65	exitCode, err := sb.Run(epCtx)
66	if err != nil {
67		return fmt.Errorf("run episode %d: %w", epID, err)
68	}
69
70	log.Printf("episode %d finished: exit=%d stdout=%q", epID, exitCode, stdout.String())
71	return nil
72}
73
74func main() {
75	ctx := context.Background()
76	if err := runEpisode(ctx, "/srv/rl-snapshots/golden-docker.ext4", "/srv/rl-episodes", 1); err != nil {
77		log.Fatal(err)
78	}
79}

Agent RL homokozó futtatókörnyezetek összehasonlítása

Metrika / KövetelményMegosztott host dockerdPrivilegizált docker:dindFirecracker / KVM MicroVMStandard gVisor (runsc)gvx (rungv / pkg/gvx)
Host root vagy --privileged szükségesIgen (docker.sock = root)Igen (--privileged)/dev/kvm és TAP beállítás szükségesIgen (vagy userns + mount)Nem (egyetlen unprivileged folyamat)
Unprivileged K8s Podokban / meglévő runsc alatt működikNemNemNem (nincs /dev/kvm)Hibázik (EBUSY beágyazott seccomp esetén)Igen (automatikus futex loop visszalépés)
Támogatja a belső docker build és docker compose parancsokatMegosztja a host daemontIgenIgenHibázik host DinD hangolás nélkülIgen (overlayfs az ext4lite felett)
Epizódonkénti hálózati izolációMegosztott docker0 / xtables zárKülön namespaceKülön VM NICKülön netnsFolyamaton belüli izolált netstack
Meleg epizódreset késleltetés (env.reset())5 s és 30 s között (konténer/kötet törlés)10 s és 60 s között (hideg daemon)~150 ms (VM pillanatkép)5 s és 20 s között~100 ms (cp --reflink=always .ext4)
Platformfüggetlen helyi fejlesztés (Windows / macOS / Linux)Linux VM szükségesLinux VM szükségesCsak Linux KVMCsak LinuxNatív módon Windows, macOS és Linux alatt

További olvasnivaló ebben a sorozatban

  1. gvx: Linux konténerek, Docker és Kubernetes futtatása egyetlen unprivileged folyamatban: A gvx teljes architekturális bemutatója Windowson (whpx, systrap), macOS-en (hvf) és Linuxon (systrap).
  2. Docker, docker build és docker compose futtatása root vagy user namespace nélkül: Gyors, 3 lépéses bootstrap és hibaelhárítási útmutató arra az esetre, ha a sudo, az unshare(CLONE_NEWUSER), a mount(2) és a /dev/fuse le van tiltva.
  3. Docker-in-Docker futtatása gVisorban és beágyazott homokozókban a gvx segítségével: Részletes elemzés arra, miért hibázik a docker.io és a beágyazott runsc a gVisor (runsc) környezetben, és hogyan lép vissza a gvx az _RUN_SYSCALL_LOOP futex ciklusra EBUSY hiba esetén.
  4. Gyors, izolált Docker környezetek AI ágens RL rollout-okhoz a gvx-szel (ez a bejegyzés)