AI Ajanı RL Rollout'ları için gvx ile Hızlı ve İzole Docker Ortamları

Kodlama Ajanı RL Rollout'larında Güvenlik ve Altyapı Darboğazı
SWE-bench, SWE-ReX, R2E-Gym, SWE-Gym, Verl ve OpenHands gibi kıyaslamalar ve pekiştirmeli öğrenme (RL) çerçeveleri üzerinde yazılım mühendisliği ajanlarını eğitmek ve değerlendirmek, güvenilmeyen, LLM tarafından üretilen kodun binlerce eşzamanlı rollout'unun yürütülmesini gerektirir. Gerçek dünya yazılım mühendisliği görevlerinde bir ajan yalnızca metin dosyalarını düzenlemez: docker build komutunu çalıştırır, docker compose aracılığıyla veritabanları ve mikro hizmetleri ayağa kaldırır, entegrasyon test paketlerini yürütür ve konteyner günlüklerini inceler.
Rollout ölçeğinde (küme başına 128 ila 1.024 eşzamanlı episode), mevcut konteyner altyapısı dört sistemik güvenlik ve performans darboğazıyla karşılaşır:
- Host
dockerdKilit Çekişmesi ve Durum Sızıntısı: Yüzlerce eşzamanlı RL işçisini tek bir host/var/run/docker.sockdosyasına yönlendirmek, her ajana host daemon'ına kök eşdeğeri erişim verir, image katmanı çıkarmayı seri hale getirir,docker0alt ağ tahsislerini tüketir, hostiptablesüzerindeAnother app is currently holding the xtables lockhatalarını tetikler ve episode'lar arasında yetim konteynerleri sızdırır. - Ayrıcalıklı DinD Konteyner Kaçış Riski: Standart
docker:dindkonteynerlerini çalıştırmak--privileged(CAP_SYS_ADMIN) gerektirir; bu da güvenilmeyen RL rollout'larına ham host blok aygıtlarını, cgroup release agent'larını ve kernel modüllerini açığa çıkarır. - Donanım Sanallaştırma (
/dev/kvm) Kısıtlamaları: MicroVM çalışma zamanları (Firecracker,QEMU,Kata Containers) iç çekirdekleri temiz biçimde izole eder, ancak/dev/kvmgerektirirler. İç içe sanallaştırması olmayan standart Kubernetes pod'ları, KVM'nin devre dışı bırakıldığı bare-metal bulut örnekleri ve mevcutgVisor(runsc) sandbox'ları KVM microVM'lerini başlatamaz. - Yavaş Episode Sıfırlama (
env.reset()) Gecikmesi: Her RL episode'u için yeni birdocker:dindortamı başlatmak ve temel image'ları çekmek veya açmak 10 ila 60 saniye sürer; bu da ortam sıfırlamalarını beklerken GPU eğitim işçilerini boşta bırakır.
gvx (rungv CLI ve z.gfire.dev/gvx/pkg/gvx Go SDK), dört darboğazın tamamını çözen güvenli, düşük çabalı, token açısından verimli bir rollout çalışma zamanı sunar. Her rollout ortamı, tek bir ayrıcalıksız, kendi kendini kilitleyen kullanıcı alanı süreci olarak çalışır (Google'ın gVisor Sentry çekirdeği, pkg/hostlockdown ve bellek içi bir netstack ile izole edilmiş) ve tüm /var/lib/docker durumu tek bir copy-on-write .ext4 dosyasının (ext4lite) içinde bulunur.
Ajanlar için Düşük Çabalı, Token Açısından Verimli Yürütme:
docker buildveyadocker composeher bölümde kutudan çıktığı gibi çalıştığında (ya da ajan tarafındanrungvaracılığıyla tek bir 3 komutluk turda başlatılabildiğinde), RL rollout'larıservice docker startulimithatalarını,iptables-nfthatalarını veyaruncpivot_rootreddetmelerini gidermek için bağlam penceresi token'larını veya adım bütçelerini harcamaz. Trajektorideki her token gerçek yazılım mühendisliği görevini çözmeye yöneltilir.
Güvenilmeyen Ajan Rollout'ları İçin Sıfır Ayrıcalık Güvenliği: Her
gvxepisode'u, konuk kodu çalıştırmadan önce kendi host işletim sistemi sürecini kilitler (Linux'taPR_SET_NO_NEW_PRIVS+ BPFseccomp, macOS'ta Seatbeltdeny default, Windows'ta Restricted Token + Job Object). Bir RL ajanı episode'u içindedocker run --privilegedveya yıkıcırm -rf /komutlarını çalıştırsa bile, bu işlemler kesin olarak o episode'un kullanıcı alanıSentryçekirdeği ve geçici.ext4birim dosyası ile sınırlı kalır.
Rollout Mimarisi: Paralel Ayrıcalıksız Örnekler + Reflink Anlık Görüntüleri
gvx içinde her rollout işçisi, kendi gVisor Sentry çekirdeğine, kendi kullanıcı alanı netstack (10.0.2.15/24) yapılandırmasına ve kendi ext4lite blok birimine (ep-N.ext4) sahip, sıradan bir ayrıcalıksız işletim sistemi sürecidir; ayrıca host sayfa önbelleğini salt okunur, içerik adresli bir erofs OCI image önbelleğiyle paylaşır.

Tek Dosyalı ext4lite Birimleri RL Sıfırlama Ekonomisini Neden Değiştirir
gvx'in ext4lite dosya sistemi, tüm JBD2 günlüklü bir ext4 birimini (Docker'ın overlayfs katman dizinleri, trusted.overlay.* genişletilmiş öznitelikleri, whiteout aygıt düğümleri ve image metadata dahil) tek bir normal host dosyasının içinde sakladığından, episode yaşam döngüsü yönetimi tek bir dosya işlemine dönüşür:
cp --reflink=alwaysile 100 Milisaniyelikenv.reset():- Ön çekilmiş temel image'lar (
python:3.11,postgres:16,redis:7veya göreve özgüSWE-benchtemel image'ları) içerengolden-docker.ext4dosyasını bir kez oluşturursunuz. - Her RL episode'unun başında,
cp --reflink=always golden-docker.ext4 ep-0042.ext4komutu (XFS, Btrfs, ZFS veya macOS APFS üzerinde) altlarındaki disk bloklarını kopyalamadan 100 milisaniyenin altında bağımsız bir Copy-on-Write klonu oluşturur. - Yalnızca o belirli episode sırasında kirletilen 4 KiB bloklar ek disk alanı tüketir.
- Ön çekilmiş temel image'lar (
- Sıfır Artık Kapatma (
env.close()):rungvsürecini sonlandırmak (Go'dasb.Close()) veep-0042.ext4dosyasının bağlantısını kaldırmak, episode'un tüm süreçlerini, ağ soketlerini, bağlama noktalarını ve depolamasını yaklaşık 10 milisaniyede geri kazanır. Host çekirdeğinin bağlama tablosunda hiçbir şey bağlanmaz ve host cgroup'larına kaydedilmez.
Adım Adım İş Akışı: Altın .ext4 Anlık Görüntüsü ve Hızlı Episode Sıfırlama
rungv'yi her rollout işçi düğümüne, GitHub Releases üzerinden SLSA ile doğrulanmış önceden derlenmiş ikiliyi indirerek (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) veya CGO_ENABLED=0 go install z.gfire.dev/gvx/cmd/rungv@latest komutunu çalıştırarak kurabilirsiniz.
Adım 1: Altın docker:dind Birimini Bir Kez Oluşturun
1mkdir -p /srv/rl-snapshots /srv/rl-episodes
2
3# 1. golden-docker.ext4 ile desteklenen bir seed docker:dind örneği başlatın
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. golden-docker.ext4 içinde temel image'ları ve build önbelleğini ön doldurun
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. ext4lite'ın image'ı boşaltıp sıkıştırabilmesi için seed örneğini zarifçe durdurun
20kill -INT $SEED_PID
21wait $SEED_PID
Adım 2: 100 Milisaniyede Paralel RL Episode'ları Başlatın
Her paralel rollout işçisi golden-docker.ext4 dosyasını reflink ile klonlar ve tüm image'ları zaten /var/lib/docker içinde bulunan yalıtılmış bir docker:dind örneği başlatır:
1EPISODE_ID="ep-0042"
2SOCK="/srv/rl-episodes/${EPISODE_ID}.sock"
3
4# Anında Copy-on-Write klonu (XFS/Btrfs/APFS üzerinde ~100 ms)
5cp --reflink=always \
6 /srv/rl-snapshots/golden-docker.ext4 \
7 "/srv/rl-episodes/${EPISODE_ID}.ext4"
8
9# 4 GiB RAM ve 2 CPU ile izole, ayrıcalıksız episode başlatın
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# Rollout işçisinin içinde image'lar ağ çekmesi olmadan hemen kullanılabilir
22docker images
23
24# Ödül hesaplamasından sonra episode'un sonlandırılması (~10 ms)
25kill -KILL $EP_PID
26rm -f "/srv/rl-episodes/${EPISODE_ID}.ext4" "${SOCK}"
z.gfire.dev/gvx/pkg/gvx Go SDK'sını Kullanan Programlı Rollout Havuzu
RL ortam sunucunuz veya değerlendirme harness'iniz Go ile yazılmışsa (ya da gRPC üzerinden bir Go rollout daemon'ını çağırıyorsa), paralel konteyner episode'larını doğrudan z.gfire.dev/gvx/pkg/gvx üzerinden yönetebilirsiniz:
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. Altın ext4lite birimini reflink ile klonlayın
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. Bölüm başına duvar saati zaman aşımını uygulayın
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 üst sınır
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 Sandbox Çalışma Zamanlarının Karşılaştırması
| Metrik / Gereksinim | Paylaşımlı Host dockerd | Ayrıcalıklı docker:dind | Firecracker / KVM MicroVM | Standart gVisor (runsc) | gvx (rungv / pkg/gvx) |
|---|---|---|---|---|---|
Host root veya --privileged gerekli | Evet (docker.sock = root) | Evet (--privileged) | /dev/kvm + TAP kurulumu gerekir | Evet (veya userns + mount) | Hayır (tek ayrıcalıksız süreç) |
Ayrıcalıksız K8s Pod'ları / mevcut runsc içinde çalışır | Hayır | Hayır | Hayır (/dev/kvm yok) | Başarısız (iç içe seccomp'ta EBUSY) | Evet (otomatik futex döngüsü yedeği) |
İç docker build ve docker compose desteği | Host daemon'ı paylaşır | Evet | Evet | Host DinD ayarı olmadan başarısız | Evet (ext4lite üzerinde overlayfs) |
| Episode başına ağ izolasyonu | Paylaşımlı docker0 / xtables kilidi | Ayrı ad alanı | Ayrı VM NIC'i | Ayrı netns | Süreç içi izole netstack |
Sıcak episode sıfırlama gecikmesi (env.reset()) | 5 sn ila 30 sn (konteyner/birim temizleme) | 10 sn ila 60 sn (soğuk daemon) | ~150 ms (VM anlık görüntüsü) | 5 sn ila 20 sn | ~100 ms (cp --reflink=always .ext4) |
| Platformlar arası yerel geliştirme (Windows / macOS / Linux) | Linux VM gerekir | Linux VM gerekir | Yalnızca Linux KVM | Yalnızca Linux | Windows, macOS ve Linux'ta yerel |
Bu Serideki Diğer Okumalar
- gvx: Tek Bir Ayrıcalıksız Süreçte Linux Konteynerleri, Docker ve Kubernetes Çalıştırma: Windows (
whpx,systrap), macOS (hvf) ve Linux (systrap) genelindegvxiçin eksiksiz mimari anlatım. - Root veya Kullanıcı Ad Alanı Olmadan Docker, docker build ve docker compose Çalıştırma:
sudo,unshare(CLONE_NEWUSER),mount(2)ve/dev/fuseengellendiğinde hızlı 3 adımlı önyükleme ve sorun giderme kılavuzu. - gvx ile gVisor ve İç İçe Sandbox'lar İçinde Docker-in-Docker Çalıştırma:
docker.iove iç içerunsc'ingVisor(runsc) içinde neden başarısız olduğuna vegvx'inEBUSYdurumunda_RUN_SYSCALL_LOOPfutex döngüsüne nasıl geri döndüğüne dair derinlemesine inceleme. - AI Ajanı RL Rollout'ları için gvx ile Hızlı İzole Docker Ortamları (bu yazı)