GoSuda

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

By Lemon Mint views ...

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:

  1. Gazdagépi dockerd zárolási versengés és állapotszivárgás: Több száz egyidejű RL-munkafolyamat egyetlen gazdagépi /var/run/docker.sock socketre 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 a docker0 alhálózati címkiosztásait, a Another app is currently holding the xtables lock hibákat idézi elő a gazdagépi iptables-ben, és epizódok között árva konténereket szivárogtat.
  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, 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.
  3. 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/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ódvisszaállítás (env.reset()) késleltetése: Egy friss docker:dind kö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 gvx epizód zárolja saját gazdagépi OS-folyamatát, mielőtt vendégkódot futtatna (PR_SET_NO_NEW_PRIVS + BPF seccomp Linuxon, Seatbelt deny default macOS-en, Restricted Token + Job Object Windows-on). Még ha egy RL-ágens az epizódján belül docker run --privileged vagy destruktív rm -rf / parancsot hajt végre, ezek a műveletek szigorúan az adott epizód felhasználói térbeli Sentry kernelén és átmeneti .ext4 kö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.

Párhuzamos Agent RL Rollout architektúra a gvx-szel

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:

  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 alapimage-eket tartalmaz (python:3.11, postgres:16, redis:7, vagy feladatspecifikus SWE-bench alapimage-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, 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.
  2. Nulla maradékot hagyó lebontás (env.close()):
    • A rungv folyamat leállítása (Go-ban sb.Close()) és az ep-0042.ext4 fá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.

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ényMegosztott gazdagépi dockerdPrivilegizált docker:dindFirecracker / KVM MicroVMSzabványos gVisor (runsc)gvx (rungv / pkg/gvx)
Gazdagépi root vagy --privileged szükségesIgen (docker.sock = root)Igen (--privileged)Szükséges /dev/kvm + TAP beállításIgen (vagy userns + mount)Nem (egyetlen nem privilegizált folyamat)
Működik nem privilegizált K8s podokban / meglévő runsc-benNemNemNem (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űveleteketMegosztott gazdagépi daemont használIgenIgenSikertelen host DinD hangolás nélkülIgen (overlayfs ext4lite-on)
Epizódonkénti hálózati izolációMegosztott docker0 / xtables zárolásKülön névtérKülön VM NICKülön netnsIzolá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égesLinux VM szükségesCsak Linux KVMCsak LinuxNatív Windows, macOS és Linux alatt

További olvasnivaló ebben a sorozatban

  1. 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.
  2. 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/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 arról, miért hibásodik meg a docker.io és a beágyazott runsc a gVisor (runsc) belsejében, és hogyan vált a gvx a _RUN_SYSCALL_LOOP futex loopra EBUSY hiba esetén.
  4. Gyors, izolált Docker-környezetek AI-ágens RL-rolloutokhoz a gvx segítségével (ez a bejegyzés)