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 genelinde yazılım mühendisliği ajanlarının eğitilmesi ve değerlendirilmesi, güvenilmeyen, LLM tarafından üretilen kodun binlerce eşzamanlı rollout'ta çalıştırılmasını 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 worker'ını tek bir host/var/run/docker.socküzerine yönlendirmek, her ajana host daemon'ına karşı root eşdeğeri erişim verir, imaj katmanı çıkarımını seri hale getirir,docker0alt ağ tahsislerini tüketir, hostiptablesüzerindeAnother app is currently holding the xtables lockhatalarını tetikler ve episode'lar arasında sahipsiz konteynerlerin sızmasına yol açar. - Ayrıcalıklı DinD Konteyner Kaçış Riski: Standart
docker:dindkonteynerlerini çalıştırmak--privileged(CAP_SYS_ADMIN) gerektirir; bu da ham host blok aygıtlarını, cgroup release agent'larını ve çekirdek modüllerini güvenilmeyen RL rollout'larına açar. - Donanım Sanallaştırma (
/dev/kvm) Kısıtları: MicroVM çalışma zamanları (Firecracker,QEMU,Kata Containers) iç çekirdekleri temiz biçimde izole eder, ancak/dev/kvmgerektirirler. İç içe sanallaştırma 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 için yeni birdocker:dindortamı başlatmak ve temel imajları çekmek veya açmak 10 ila 60 saniye sürer; bu süre boyunca GPU eğitim worker'ları ortam sıfırlamalarını beklerken boşta kalır.
gvx (rungv CLI ve z.gfire.dev/gvx/pkg/gvx Go SDK), dört darboğazın tamamını çözen güvenli, katmanlı savunmaya sahip bir rollout çalışma zamanı sunar. Her rollout ortamı, tek başına çalışan, ayrıcalıksız ve kendini kilitleyen bir kullanıcı alanı süreci olarak çalışır (Google'ın gVisor Sentry çekirdeği, pkg/hostlockdown ve bellek içi netstack ile izole edilmiştir); bu sürecin tüm /var/lib/docker durumu tek bir copy-on-write .ext4 dosyasının (ext4lite) içinde bulunur.
Güvenilmeyen Ajan Rollout'ları için Sıfır Ayrıcalık Güvenliği: Her
gvxepisode'u, misafir 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.ext4hacim dosyasıyla 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 worker'ı, kendi gVisor Sentry çekirdeğine, kendi kullanıcı alanı netstack yapılandırmasına (10.0.2.15/24) ve kendi ext4lite blok hacmine (ep-N.ext4) sahip sıradan bir ayrıcalıksız işletim sistemi sürecidir; host sayfa önbelleğini ise salt okunur, içerik adresli bir erofs OCI imaj önbelleğiyle paylaşır.

Tek Dosyalı ext4lite Hacimleri RL Sıfırlama Ekonomisini Neden Değiştirir
gvx'in ext4lite dosya sistemi, tüm JBD2 günlüklemeli ext4 hacmini (Docker'ın overlayfs katman dizinleri, trusted.overlay.* genişletilmiş öznitelikleri, whiteout aygıt düğümleri ve imaj meta verileri dahil) tek bir normal host dosyasında sakladığından, episode yaşam döngüsü yönetimi tek bir dosya işlemine dönüşür:
cp --reflink=alwaysile 100 Milisaniyelikenv.reset():- Önceden çekilmiş temel imajları (
python:3.11,postgres:16,redis:7veya göreve özgüSWE-benchtemel imajları) içeren birgolden-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), altta yatan 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 kirlenen 4 KiB'lik bloklar ek disk alanı tüketir.
- Önceden çekilmiş temel imajları (
- Artık Bırakmayan Kapatma (
env.close()):rungvsürecinin sonlandırılması (Go'dasb.Close()) veep-0042.ext4dosyasının bağlantısının kaldırılması, episode'un tüm süreçlerini, ağ soketlerini, bağlama noktalarını ve depolamasını yaklaşık 10 milisaniyede %100 geri kazanır. Host çekirdeğinin bağlama tablosuna hiçbir şey bağlanmaz ve host cgroup'larına hiçbir şey kaydedilmez.
Adım Adım İş Akışı: Golden .ext4 Anlık Görüntüsü ve Hızlı Episode Sıfırlama
rungv aracını her rollout worker düğümüne, ya GitHub Releases adresinden 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) ya da CGO_ENABLED=0 go install z.gfire.dev/gvx/cmd/rungv@latest komutunu çalıştırarak kurabilirsiniz.
Adım 1: Golden docker:dind Hacmini Bir Kez Oluşturun
1mkdir -p /srv/rl-snapshots /srv/rl-episodes
2
3# 1. golden-docker.ext4 ile desteklenen bir tohum 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 imajları ve derleme önbelleğini önceden 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 imajı boşaltıp sıkıştırması için tohum örneğini zarifçe durdurun
20kill -INT $SEED_PID
21wait $SEED_PID
Adım 2: Paralel RL Episode'larını 100 Milisaniyede Başlatın
Her paralel rollout worker'ı golden-docker.ext4 dosyasını reflink ile klonlar ve tüm imajları zaten /var/lib/docker içinde bulunan yalıtılmış bir docker:dind örneğini başlatır:
1EPISODE_ID="ep-0042"
2SOCK="/srv/rl-episodes/${EPISODE_ID}.sock"
3
4# Anlık 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 yalıtılmış ayrıcalıksız episode'u 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 worker'ının içinde imajlar ağ üzerinden çekilmeden hemen kullanılabilir
22docker images
23
24# Ödül hesaplamasından sonra episode'un kapatı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 Programatik Rollout Havuzu
RL ortam sunucunuz veya değerlendirme altyapınız 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. Golden ext4lite hacminin reflink klonunu oluşturun
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. Episode başına duvar saati zaman aşımı 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 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 mi | 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 mı | Hayır | Hayır | Hayır (/dev/kvm yok) | Başarısız (iç içe seccomp üzerinde EBUSY) | Evet (otomatik futex döngüsü yedeği) |
İç docker build ve docker compose desteği | Host daemon'ını paylaşır | Evet | Evet | Host DinD ayarı olmadan başarısız | Evet (ext4lite üzerinde overlayfs) |
| Episode başına ağ yalıtımı | Paylaşımlı docker0 / xtables kilidi | Ayrı namespace | Ayrı VM NIC | Ayrı netns | Süreç içi yalıtılmış netstack |
Sıcak episode sıfırlama gecikmesi (env.reset()) | 5 sn ila 30 sn (konteyner/hacim temizliği) | 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 üzerinde yerel |
Bu Serideki Diğer Okumalar
- gvx: Linux Konteynerlerini, Docker'ı ve Kubernetes'i Tek Bir Ayrıcalıksız Süreçte Çalıştırmak: Windows (
whpx,systrap), macOS (hvf) ve Linux (systrap) üzerindegvxiçin eksiksiz bir mimari inceleme. - Root veya Kullanıcı Namespace'leri Olmadan Docker, docker build ve docker compose Çalıştırmak:
sudo,unshare(CLONE_NEWUSER),mount(2)ve/dev/fuseengellendiğinde hızlı 3 adımlı başlangıç ve sorun giderme kılavuzu. - gvx ile gVisor ve İç İçe Sandbox'lar İçinde Docker-in-Docker Çalıştırmak:
docker.iove iç içerunsc'ningVisor(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 ayrıntılı inceleme. - gvx ile AI Ajanı RL Rollout'ları için Hızlı Yalıtılmış Docker Ortamları (bu yazı)