Lingkungan Docker Terisolasi yang Cepat untuk AI Agent RL Rollouts dengan gvx

Hambatan Keamanan dan Infrastruktur dalam Rollout RL Coding Agent
Pelatihan dan evaluasi agen rekayasa perangkat lunak lintas benchmark dan kerangka kerja reinforcement learning (RL) seperti SWE-bench, SWE-ReX, R2E-Gym, SWE-Gym, Verl, dan OpenHands memerlukan eksekusi ribuan rollout konkuren terhadap kode yang tidak tepercaya dan dihasilkan oleh LLM. Dalam tugas rekayasa perangkat lunak dunia nyata, agen tidak sekadar menyunting berkas teks: agen menjalankan docker build, menjalankan basis data dan microservice melalui docker compose, mengeksekusi rangkaian pengujian integrasi, dan memeriksa log container.
Pada skala rollout (128 hingga 1.024 episode konkuren per klaster), infrastruktur container yang ada menghadapi empat hambatan keamanan dan kinerja sistemik:
- Perebutan Lock
dockerdHost dan Kebocoran State: Mengarahkan ratusan worker RL konkuren ke satu/var/run/docker.sockhost memberikan setiap agen akses setara root ke daemon host, menyerialkan ekstraksi layer image, menghabiskan alokasi subnetdocker0, memicu galatAnother app is currently holding the xtables lockpadaiptableshost, dan membocorkan container yatim lintas episode. - Risiko Container Escape pada DinD Istimewa: Menjalankan container
docker:dindstandar memerlukan--privileged(CAP_SYS_ADMIN), yang mengekspos block device host mentah, release agent cgroup, dan kernel module kepada rollout RL yang tidak tepercaya. - Batasan Virtualisasi Perangkat Keras (
/dev/kvm): Runtime MicroVM (Firecracker,QEMU,Kata Containers) mengisolasi kernel internal secara bersih, tetapi memerlukan/dev/kvm. Pod Kubernetes standar tanpa nested virtualization, instans cloud bare-metal tempat KVM dinonaktifkan, dan sandboxgVisor(runsc) yang ada tidak dapat menjalankan microVM KVM. - Latensi Reset Episode (
env.reset()) yang Lambat: Memulai lingkungandocker:dindbaru serta menarik atau mengekstrak base image untuk setiap episode RL memerlukan 10 hingga 60 detik, sehingga worker pelatihan GPU menganggur selama menunggu reset lingkungan.
gvx (CLI rungv dan Go SDK z.gfire.dev/gvx/pkg/gvx) menyediakan runtime rollout yang aman, berupaya rendah, dan efisien token yang mengatasi keempat hambatan tersebut. Setiap lingkungan rollout dijalankan sebagai satu proses user-space tanpa privilege dan terkunci sendiri (diisolasi oleh kernel Sentry gVisor milik Google, pkg/hostlockdown, dan netstack dalam memori) yang seluruh state /var/lib/docker-nya berada di dalam satu file .ext4 copy-on-write (ext4lite).
Eksekusi Berupaya Rendah dan Efisien Token untuk Agen: Ketika
docker buildataudocker composeberfungsi langsung di dalam setiap episode (atau dapat di-bootstrap oleh agen dalam satu giliran 3 perintah melaluirungv), rollout RL tidak membuang token context window maupun anggaran langkah untuk memecahkan masalah kegagalanservice docker startulimit, galatiptables-nft, atau penolakanpivot_rootolehrunc. Setiap token dalam trajektori diarahkan untuk menyelesaikan tugas rekayasa perangkat lunak yang sebenarnya.
Keamanan Tanpa Privilege untuk Rollout Agen yang Tidak Tepercaya: Setiap episode
gvxmengunci proses OS host miliknya sendiri sebelum menjalankan kode guest (PR_SET_NO_NEW_PRIVS+seccompBPF pada Linux,deny defaultSeatbelt pada macOS, Restricted Token + Job Object pada Windows). Bahkan jika agen RL menjalankandocker run --privilegedatau perintah destruktifrm -rf /di dalam episodenya, operasi tersebut tetap terbatas secara ketat pada kernelSentryuser-space episode tersebut dan file volume.ext4yang bersifat sementara.
Arsitektur Rollout: Instans Paralel Tanpa Privilege + Snapshot Reflink
Dalam gvx, setiap worker rollout adalah proses OS tanpa privilege biasa dengan kernel gVisor Sentry sendiri, netstack user-space sendiri (10.0.2.15/24), dan volume blok ext4lite sendiri (ep-N.ext4), sementara itu berbagi cache image OCI erofs yang bersifat read-only dan content-addressable melalui page cache host.

Mengapa Volume ext4lite Berkas Tunggal Mengubah Ekonomi Reset RL
Karena filesystem ext4lite milik gvx menyimpan seluruh volume ext4 ber-journal JBD2 (termasuk direktori layer overlayfs Docker, atribut extended trusted.overlay.*, node perangkat whiteout, dan metadata image) di dalam satu file reguler host, manajemen siklus hidup episode menjadi satu operasi file:
env.reset()100 Milidetik melaluicp --reflink=always:- Anda membangun file
golden-docker.ext4sekali yang berisi base image yang sudah ditarik sebelumnya (python:3.11,postgres:16,redis:7, atau base imageSWE-benchspesifik tugas). - Pada awal setiap episode RL,
cp --reflink=always golden-docker.ext4 ep-0042.ext4(pada XFS, Btrfs, ZFS, atau APFS macOS) membuat klon Copy-on-Write yang independen dalam waktu kurang dari 100 milidetik tanpa menyalin blok disk yang mendasarinya. - Hanya blok 4 KiB yang diubah selama episode tertentu yang memakan ruang disk tambahan.
- Anda membangun file
- Teardown Tanpa Sisa (
env.close()):- Menghentikan proses
rungv(sb.Close()pada Go) dan menghapus tautanep-0042.ext4membebaskan 100% proses, soket jaringan, mount, dan penyimpanan episode tersebut dalam ~10 milidetik. Tidak ada yang pernah di-mount dalam tabel mount kernel host atau terdaftar di cgroup host.
- Menghentikan proses
Alur Kerja Langkah demi Langkah: Snapshot .ext4 Emas dan Reset Episode Cepat
Anda dapat memasang rungv pada setiap node worker rollout baik dengan mengunduh binary prebuilt yang diatestasi SLSA dari GitHub Releases (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) atau dengan menjalankan CGO_ENABLED=0 go install z.gfire.dev/gvx/cmd/rungv@latest.
Langkah 1: Buat Volume docker:dind Emas Sekali Saja
1mkdir -p /srv/rl-snapshots /srv/rl-episodes
2
3# 1. Jalankan instans seed docker:dind yang didukung oleh golden-docker.ext4
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. Isi awal base image dan build cache di dalam golden-docker.ext4
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. Hentikan instans seed secara bertahap agar ext4lite melakukan flush dan kompaksi image
20kill -INT $SEED_PID
21wait $SEED_PID
Langkah 2: Jalankan Episode RL Paralel dalam 100 Milidetik
Setiap worker rollout paralel mengklon golden-docker.ext4 melalui reflink dan menjalankan instans docker:dind terisolasi dengan semua image sudah tersedia di /var/lib/docker:
1EPISODE_ID="ep-0042"
2SOCK="/srv/rl-episodes/${EPISODE_ID}.sock"
3
4# Klon Copy-on-Write instan (~100 ms pada XFS/Btrfs/APFS)
5cp --reflink=always \
6 /srv/rl-snapshots/golden-docker.ext4 \
7 "/srv/rl-episodes/${EPISODE_ID}.ext4"
8
9# Jalankan episode tanpa privilege yang terisolasi dengan RAM 4 GiB dan 2 CPU
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# Di dalam worker rollout, image langsung tersedia tanpa pull dari jaringan
22docker images
23
24# Teardown episode setelah perhitungan reward (~10 ms)
25kill -KILL $EP_PID
26rm -f "/srv/rl-episodes/${EPISODE_ID}.ext4" "${SOCK}"
Pool Rollout Terprogram Menggunakan Go SDK z.gfire.dev/gvx/pkg/gvx
Jika server lingkungan RL atau harness evaluasi Anda ditulis dalam Go (atau memanggil daemon rollout Go melalui gRPC), Anda dapat mengelola episode container paralel secara langsung melalui z.gfire.dev/gvx/pkg/gvx:
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. Klon reflink volume ext4lite emas
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. Terapkan batas waktu wall-clock per episode
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, // Batas maksimum 4 GiB
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}
Perbandingan Runtime Sandbox RL Agen
| Metrik / Persyaratan | Shared Host dockerd | Privileged docker:dind | Firecracker / KVM MicroVM | gVisor Standar (runsc) | gvx (rungv / pkg/gvx) |
|---|---|---|---|---|---|
Memerlukan root host atau --privileged | Ya (docker.sock = root) | Ya (--privileged) | Membutuhkan /dev/kvm + pengaturan TAP | Ya (atau userns + mount) | Tidak (satu proses tanpa privilege) |
Berfungsi di dalam Pod K8s tanpa privilege / runsc yang ada | Tidak | Tidak | Tidak (tanpa /dev/kvm) | Gagal (EBUSY pada seccomp bersarang) | Ya (fallback futex loop otomatis) |
Mendukung docker build & docker compose di dalam | Berbagi daemon host | Ya | Ya | Gagal tanpa penyetelan DinD host | Ya (overlayfs pada ext4lite) |
| Isolasi jaringan per episode | docker0 / kunci xtables bersama | Namespace terpisah | NIC VM terpisah | Netns terpisah | Terisolasi dalam netstack di dalam proses |
Latensi reset episode hangat (env.reset()) | 5 dtk hingga 30 dtk (prune container/volume) | 10 dtk hingga 60 dtk (daemon dingin) | ~150 ms (snapshot VM) | 5 dtk hingga 20 dtk | ~100 ms (cp --reflink=always .ext4) |
| Pengembangan lokal lintas platform (Windows / macOS / Linux) | Membutuhkan VM Linux | Membutuhkan VM Linux | Hanya Linux KVM | Hanya Linux | Native di Windows, macOS, dan Linux |
Bacaan Lanjutan dalam Seri Ini
- gvx: Menjalankan Container Linux, Docker, dan Kubernetes dalam Satu Proses Tanpa Privilege: Penjelasan arsitektur lengkap
gvxdi Windows (whpx,systrap), macOS (hvf), dan Linux (systrap). - Menjalankan Docker, docker build, dan docker compose Tanpa Root atau User Namespace: Panduan bootstrap cepat 3 langkah dan pemecahan masalah ketika
sudo,unshare(CLONE_NEWUSER),mount(2), dan/dev/fusediblokir. - Menjalankan Docker-in-Docker di dalam gVisor dan Sandbox Bersarang dengan gvx: Pembahasan mendalam mengapa
docker.iodanrunscbersarang gagal di dalamgVisor(runsc) dan bagaimanagvxberalih ke loop futex_RUN_SYSCALL_LOOPsaatEBUSY. - Lingkungan Docker Terisolasi Cepat untuk Rollout RL Agen AI dengan gvx (artikel ini)