Gyors, izolált Docker-környezetek AI-ügynökök RL-kigördítéseihez a gvx segítségével

A biztonsági és infrastruktúrás szűk keresztmetszet a Coding Agent RL rollout-okban
A szoftvermérnöki ágensek képzése és értékelése olyan benchmarkokon és megerősítéses tanulási (RL) keretrendszereken, mint a SWE-bench, SWE-ReX, R2E-Gym, SWE-Gym, Verl és OpenHands, több ezer párhuzamos rollout végrehajtását igényli nem megbízható, LLM által generált kódon. Valós szoftverfejlesztési feladatokban az ágens nem csupán szövegfájlokat szerkeszt: lefuttatja a docker build parancsot, adatbázisokat és mikroszolgáltatásokat indít docker compose segítségével, integrációs tesztcsomagokat hajt végre, és konténernaplókat vizsgál.
Rollout-méretben (klaszterenként 128 és 1024 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:
- Gazdagépi
dockerdzárolási versengés és állapotszivárgás: Több száz egyidejű RL-munkafolyamat egyetlen gazdagépi/var/run/docker.socksocketre irányítása minden ágensnek root-egyenértékű hozzáférést ad a gazdagépi daemonhoz, sorosítja az image-rétegek kicsomagolását, kimeríti adocker0alhálózati címkiosztásait, aAnother app is currently holding the xtables lockhibákat idézi elő a gazdagépiiptables-ben, és epizódok között árva konténereket szivárogtat. - Privilegizált DinD konténer-kiszökési kockázat: A szabványos
docker:dindkonténerek futtatásához--privileged(CAP_SYS_ADMIN) szükséges, ami nyers gazdagépi blokkeszközöket, cgroup release agenteket és kernelmodulokat tesz elérhetővé a megbízhatatlan RL-rollout-ok számára. - Hardveres virtualizáció (
/dev/kvm) korlátai: A MicroVM futtatókörnyezetek (Firecracker,QEMU,Kata Containers) tisztán izolálják a belső kerneleket, de/dev/kvmeszkö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. - Lassú epizódvisszaállítás (
env.reset()) késleltetése: Egy frissdocker:dindkörnyezet indítása, valamint az alapimage-ek letöltése vagy kicsomagolása minden RL-epizódhoz 10 és 60 másodperc közötti időt vesz igénybe, miközben a GPU-s tanítómunkafolyamatok tétlenül várnak a környezetek visszaállítására.
gvx (rungv CLI és z.gfire.dev/gvx/pkg/gvx Go SDK) egy biztonságos, védelmi rétegzésen alapuló rollout futtatókörnyezetet biztosít, amely mind a négy szűk keresztmetszetet megoldja. Minden rollout-környezet egyetlen privilégiumok nélküli, önzárolt felhasználói térbeli folyamatként fut (amelyet Google gVisor Sentry kernele, a pkg/hostlockdown csomag és egy memóriabeli netstack izolál), és teljes /var/lib/docker állapota egyetlen másolás-íráskor (copy-on-write) .ext4 fájlban (ext4lite) található.
Nulla-privilégiumú biztonság megbízhatatlan ágensrolloutokhoz: Minden
gvxepizód zárolja saját gazdagépi OS-folyamatát, mielőtt vendégkódot futtatna (PR_SET_NO_NEW_PRIVS+ BPFseccompLinuxon, Seatbeltdeny defaultmacOS-en, Restricted Token + Job Object Windows-on). Még ha egy RL-ágens az epizódján belüldocker run --privilegedvagy destruktívrm -rf /parancsot hajt végre, ezek a műveletek szigorúan az adott epizód felhasználói térbeliSentrykernelén és átmeneti.ext4kötetfájlján belül maradnak.
Rollout-architektúra: párhuzamos nem privilegizált példányok + reflink pillanatképek
A gvx-ben minden rollout-munkafolyamat egy közönséges, nem privilegizált OS-folyamat, saját gVisor Sentry kernellel, saját felhasználói térbeli netstack hálózattal (10.0.2.15/24) és saját ext4lite blokkkötettel (ep-N.ext4), miközben egy csak olvasható, tartalomcímzett erofs OCI image-gyorsítótárat oszt meg a gazdagép lapgyorsítótárán keresztül.

Miért változtatja meg az egyfájlos ext4lite kötet az RL-visszaállítás gazdaságtanát
Mivel a gvx ext4lite fájlrendszere egy teljes, JBD2 naplózott ext4 kötetet (beleértve a Docker overlayfs rétegkönyvtárait, a trusted.overlay.* kiterjesztett attribútumokat, whiteout eszközcsomópontokat és image-metaadatokat) egyetlen szabályos gazdagépi fájlban tárol, az epizódok életciklus-kezelése egyetlen fájlművelet lesz:
- 100 milliszekundumos
env.reset()acp --reflink=alwayssegítségével:- Egyszer kell létrehozni egy
golden-docker.ext4fájlt, amely előre letöltött alapimage-eket tartalmaz (python:3.11,postgres:16,redis:7, vagy feladatspecifikusSWE-benchalapimage-eket). - Minden RL-epizód elején a
cp --reflink=always golden-docker.ext4 ep-0042.ext4parancs (XFS, Btrfs, ZFS vagy macOS APFS esetén) 100 milliszekundumon belül, a mögöttes lemezblokkok másolása nélkül hoz létre egy önálló másolás-íráskor klónt. - Csak az adott epizód során módosított 4 KiB-os blokkok foglalnak további lemezterületet.
- Egyszer kell létrehozni egy
- Nulla maradékot hagyó lebontás (
env.close()):- A
rungvfolyamat leállítása (Go-bansb.Close()) és azep-0042.ext4fájl leválasztása (unlink) körülbelül 10 milliszekundum alatt felszabadítja az epizód összes folyamatát, hálózati socketjét, csatolását és tárhelyét. Semmi sem kerül a gazdagépi kernel csatolástáblázatába, és nem regisztrálódik gazdagépi cgroupokban.
- A
Lépésről lépésre munkafolyamat: Golden .ext4 pillanatkép és gyors epizódvisszaállítás
A rungv eszközt bármely rollout-munkacsomóponton telepítheti: vagy a SLSA-attesztált, előre fordított binárist töltheti le 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 a golden-docker.ext4 fájlra támaszkodva
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. Alapimage-ek és build-gyorsítótár előzetes feltöltése a golden-docker.ext4 fájlba
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írja és tömörítse az image-eket
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-munkafolyamat reflinken keresztül klónozza a golden-docker.ext4 fájlt, majd 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 másolás-íráskor (CoW) 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, nem privilegizált 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-munkafolyamaton belül az image-ek azonnal elérhetők hálózati letöltés nélkül
22docker images
23
24# Epizód lebontá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örnyezetkiszolgáló vagy az értékelő keretrendszer Go nyelven íródott (vagy gRPC-n keresztül egy Go rollout-démont hív), a párhuzamos konténerepizó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 reflinkes 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 falióra-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 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}
Az Agent RL homokozó futtatókörnyezetek összehasonlítása
| Metrika / követelmény | Megosztott gazdagépi dockerd | Privilegizált docker:dind | Firecracker / KVM MicroVM | Szabványos gVisor (runsc) | gvx (rungv / pkg/gvx) |
|---|---|---|---|---|---|
Gazdagépi root vagy --privileged szükséges | Igen (docker.sock = root) | Igen (--privileged) | Szükséges /dev/kvm + TAP beállítás | Igen (vagy userns + mount) | Nem (egyetlen nem privilegizált folyamat) |
Működik nem privilegizált K8s podokban / meglévő runsc-ben | Nem | Nem | Nem (nincs /dev/kvm) | Sikertelen (EBUSY beágyazott seccomp esetén) | Igen (automatikus futex loop tartalék) |
Támogatja a belső docker build és docker compose műveleteket | Megosztott gazdagépi daemont használ | Igen | Igen | Sikertelen host DinD hangolás nélkül | Igen (overlayfs ext4lite-on) |
| Epizódonkénti hálózati izoláció | Megosztott docker0 / xtables zárolás | Külön névtér | Külön VM NIC | Külön netns | Izolált folyamaton belüli netstack |
Meleg epizódvisszaállítási 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) |
| Többplatformos helyi fejlesztés (Windows / macOS / Linux) | Linux VM szükséges | Linux VM szükséges | Csak Linux KVM | Csak Linux | Natív Windows, macOS és Linux alatt |
További olvasnivaló ebben a sorozatban
- gvx: Linux-konténerek, Docker és Kubernetes futtatása egyetlen nem privilegizált folyamatban: Teljes architekturális áttekintés a
gvx-ről Windows (whpx,systrap), macOS (hvf) és Linux (systrap) rendszereken. - Docker, docker build és docker compose futtatása root nélkül vagy felhasználói névterek nélkül: Gyors, 3 lépéses indítási és hibaelhárítási útmutató arra az esetre, ha a
sudo,unshare(CLONE_NEWUSER),mount(2)és/dev/fusele van tiltva. - Docker-in-Docker futtatása gVisorban és beágyazott homokozókban a gvx segítségével: Részletes elemzés arról, miért hibásodik meg a
docker.ioés a beágyazottrunscagVisor(runsc) belsejében, és hogyan vált agvxa_RUN_SYSCALL_LOOPfutex loopraEBUSYhiba esetén. - Gyors, izolált Docker-környezetek AI-ágens RL-rolloutokhoz a gvx segítségével (ez a bejegyzés)