gVisor İçinde ve İç İçe Sandbox'larda gvx ile Docker-in-Docker Çalıştırma

Hızlı Çözüm: Kısıtlı Ayrıcalıklara Sahip Bir gVisor (runsc) Sandbox İçinde docker:dind'i Güvenli Şekilde Çalıştırma
Bulut AI agent platformları, Kubernetes agent-sandbox dağıtımları (RuntimeClass: gvisor) ve pekiştirmeli öğrenme değerlendirme altyapıları, güvenilmeyen iş yüklerini özellikle ana makinenin çekirdeğini korumak amacıyla gVisor (runsc) konteynerlerinin içinde izole eder. Varsayılan, ayrıcalıksız bir runsc konteyneri içindeki bir iş yükü apt-get ile docker.io veya docker-ce kurmaya ve dockerd'yi başlatmaya çalıştığında, konteyner içinde root (UID 0) yetkisine sahip olsa bile işlem başarısız olur.
gvx (rungv) ile docker:dind, docker build ve docker compose komutlarını ayrıcalıksız bir gVisor (runsc) konteyneri içinde doğrudan güvenli şekilde çalıştırabilirsiniz (veya gvx'i gvx içinde iç içe kullanabilirsiniz). Bunu yaparken dış RuntimeClass yapılandırmasını zayıflatmanıza, ana makine soketlerini açmanıza veya --privileged yetkisi vermenize gerek yoktur.
Dış Sandbox İzolasyonunun Korunması ve Güçlendirilmesi:
runsciçinderungvçalıştırmak, dışgVisorsandbox'ı tarafından uygulanan hiçbir kısıtlamayı aşmaz. Bunun yerinegvx, kendihostlockdown(PR_SET_NO_NEW_PRIVS+seccomp) yapılandırmasına, kendi bellek içinetstackbileşenine ve tek dosyalık birext4litebirimine sahip ikinci bir iç içe kullanıcı alanıgVisorSentryçekirdeği oluşturur. İçdockerdverunckonteynerleri çift katmanlı olarak sandbox'lanır: sistem çağrıları, bağlamalar (mount) ve ham paketler içgvxkullanıcı alanı çekirdeğinin dışına çıkmaz.
1# Mevcut bir gVisor (runsc) konteyneri veya Kubernetes Pod'u içinde:
2# rungv'yi SLSA ile doğrulanmış bir GitHub sürüm arşivinden veya go install ile kurun:
3CGO_ENABLED=0 go install z.gfire.dev/gvx/cmd/rungv@latest
4
5mkdir -p ~/.gvx
6rungv run \
7 -p unix:~/.gvx/docker.sock:2375 \
8 -v gvx-docker:/var/lib/docker \
9 -e DOCKER_TLS_CERTDIR= \
10 docker:dind --tls=false &
11
12# Standart Docker build ve konteyner işlemlerini iç gvx soketine karşı çalıştırın
13export DOCKER_HOST="unix://$HOME/.gvx/docker.sock"
14until docker info >/dev/null 2>&1; do sleep 0.5; done
15docker info --format '{{.Driver}} {{.OperatingSystem}}'
16# Çıktı: overlayfs Alpine Linux ... (containerized)
17
18docker build -t nested-app:test .
19docker run --rm nested-app:test
gVisor (runsc) İçinde Doğrudan Docker Kurulumunun Başarısız Olmasının Beş Nedeni
Bir kullanıcı veya AI kodlama agent'ı standart bir runsc konteynerinde apt-get install -y docker.io (veya docker-ce) komutunu çalıştırıp dockerd'yi başlatmaya çalıştığında, art arda gelen beş teknik engelle karşılaşır.
1. Sanal Çekirdek 4.4.0 Üzerinde Kernel Modülü Yükleme (modprobe)
Paket kurulumu ve dockerd başlatma sırasında, başlatma betikleri overlay, br_netfilter, ip_tables, nf_nat ve xt_conntrack modüllerini yüklemek için modprobe çalıştırır. Dış gVisor Sentry, init_module(2) ve finit_module(2) çağrılarını engeller ve /lib/modules/4.4.0 dizini bulunmayan sanal bir çekirdek sürümü (4.4.0) bildirir:
1modprobe: FATAL: Module overlay not found in directory /lib/modules/4.4.0
2modprobe: FATAL: Module br_netfilter not found in directory /lib/modules/4.4.0
3modprobe: FATAL: Module ip_tables not found in directory /lib/modules/4.4.0
4modprobe: FATAL: Module nf_nat not found in directory /lib/modules/4.4.0
2. iptables-nft (nf_tables Netlink) ve docker0 Köprü Hataları
Ubuntu 22.04, Ubuntu 24.04 ve Debian 11/12 üzerinde apt-get install docker.io komutu, varsayılan /usr/sbin/iptables alternatifi olarak iptables-nft seçer (bu, nf_tables netlink alt sistemi üzerine kuruludur). Ağ yönetici ayrıcalıkları veya nf_tables netlink desteği olmayan standart bir gVisor konteynerinde, dockerd DOCKER NAT zincirini ve docker0 köprüsünü başlatırken çöker:
1failed to start daemon: Error initializing network controller: error obtaining controller instance: failed to create NAT chain DOCKER: iptables failed: iptables -t nat -N DOCKER
2iptables v1.8.10 (nf_tables): Could not fetch rule set generation id: Invalid argument
3iptables v1.8.10 (legacy): can't initialize iptables table 'nat': Table does not exist (do you need to insmod?)
4iptables: No chain/target/match by that name.
5Unable to enable SKIP DNAT rule: (iptables failed: iptables --wait -t nat -I DOCKER -i docker0 -j RETURN: iptables: No chain/target/match by that name.)
6Error initializing network controller: error obtaining controller instance: failed to create bridge "docker0": operation not permitted
3. gofer / 9p Kök Dosya Sisteminde overlay2 Hatası ve vfs Disk Tükenmesi
Dış runsc yöneticisi /var/lib/docker yolunda özel olarak yapılandırılmış bir tmpfs veya filestore bağlaması önceden ayarlamadığı sürece, konteyner kök dosya sistemi gVisor'ın gofer (9p/lisafs) bileşeni veya iç içe bir overlay bağlaması tarafından desteklenir. /var/lib/docker üzerine overlay2 bağlamak başarısız olur:
1level=error msg="[graphdriver] prior storage driver overlay2 failed: driver not supported"
2failed to mount overlay: invalid argument
3Error starting daemon: error initializing graphdriver: driver not supported: overlay2
Kullanıcılar veya agent'lar dockerd --storage-driver=vfs ile başlatarak bu hatayı aşmaya çalıştığında, Docker Copy-on-Write katman paylaşımını tamamen devre dışı bırakır. Bir Dockerfile içindeki her RUN talimatı, tüm kök dosya sisteminin tam bir özyinelemeli kopyasını (dirCopy) oluşturur. Bu da 10 build adımında 500 MB'lık bir imajı 5 GB yazma işlemine çıkarır ve sandbox'ın no space left on device hatası veya G/Ç zaman aşımıyla sonlanmasına neden olur.
4. İç İçe runc Cgroup ve /proc Bağlama Reddi
dockerd --iptables=false --bridge=none --storage-driver=vfs seçenekleriyle zorla başlatılsa bile, docker run veya docker build komutu iç konteyneri oluşturmak için runc'yi çağırır. Varsayılan ayrıcalıksız bir gVisor konteynerinde runc, cgroup bağlarken, /proc dosya sistemini yeniden bağlarken veya iç seccomp filtrelerini yüklerken başarısız olur:
1docker: Error response from daemon: failed to create task for container: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process:
2error during container init: error mounting "cgroup" to rootfs at "/sys/fs/cgroup": mount cgroup:/sys/fs/cgroup/cpuset (via /proc/self/fd/6), flags: 0xf, data: cpuset: operation not permitted
3cgroups: cgroup mountpoint does not exist: unknown
4runc create failed: systemd not running on this host: cannot use systemd cgroup manager
5error during container init: error mounting "proc" to rootfs at "/proc": mount proc:/proc (via /proc/self/fd/6), flags: 0xe: operation not permitted
6error during container init: error loading seccomp filter into kernel: operation not permitted
5. İç İçe runsc, SECCOMP_FILTER_FLAG_NEW_LISTENER (EBUSY) Hatasıyla Başarısız Olur
Bir kullanıcı systrap platformunu kullanarak runsc içinde başka bir runsc örneği çalıştırmaya çalışırsa, başlatma hemen iptal edilir:
1creating subprocess: sysmsg: failed to initialize thread: failed to install seccomp notify rules: device or resource busy
Hem Linux çekirdeği hem de gVisor Sentry, görev hiyerarşisi başına en fazla bir SECCOMP_FILTER_FLAG_NEW_LISTENER (SECCOMP_RET_USER_NOTIF) dinleyicisine izin verir. Dış sandbox bu dinleyici yuvasını zaten kullandığı için, iç seccomp(2) çağrısı EBUSY (device or resource busy) döndürür.
gvx'in gVisor İçinde İç İçe Çalıştırmayı Nasıl Çözdüğü
gvx, beş engelin tamamını kendi kullanıcı alanı çekirdeği ve platform katmanı içinde ele alır. Bu sayede rungv, dış bir gVisor (runsc) konteyneri içinde veya başka bir gvx konteyneri içinde (yani gvx içinde gvx) özyinelemeli olarak çalışabilir.

1. EBUSY Hatasından _RUN_SYSCALL_LOOP Futex Döngüsüne Otomatik systrap Geri Dönüşü
Linux systrap içinde her stub alt süreci iki tür iş parçacığı çalıştırır:
- Konuk çalıştırma iş parçacıkları (
sysmsg): Konuk sistem çağrılarınıSECCOMP_RET_TRAP(SIGSYS) ve ikili olarak yamalanmışjmp rel32trampolinleri (syshandler) aracılığıyla yakalar. Kayıt durumlarını paylaşımlı bellek veSYS_FUTEXüzerinden Sentry ile değiş tokuş eder. - Bir stub
syscallThread(syscall_thread.go): Sentry adına, stub alt süreci içinde adres alanı sistem çağrılarını (mmap,munmap,clone) çalıştırır.
Upstream gVisor'da syscallThread, stub_amd64.s ve stub_arm64.s dosyalarında iki uyandırma modunu destekler:
- Seccomp User-Notify Döngüsü (
_RUN_SECCOMP_LOOP = 6,t.seccompNotify != nilolduğunda):syscallThread.init()sırasındat.installSeccompNotify(), birSECCOMP_RET_USER_NOTIFdinleyicisi kurar (seccomp(SECCOMP_SET_MODE_FILTER, SECCOMP_FILTER_FLAG_NEW_LISTENER, stubSyscallRules)). Böylece Sentry, istekleriioctl(SECCOMP_IOCTL_NOTIF_SEND)veioctl(SECCOMP_IOCTL_NOTIF_RECV)ile gönderebilir. - Futex Sistem Çağrısı Döngüsü (
_RUN_SYSCALL_LOOP = 5,t.seccompNotify == nilolduğunda):syscallThread,syscall_loop(stub_amd64.s/stub_arm64.s) çalıştırır. Paylaşımlı 2 sayfalıksyscallSentryMessage(stub içindePROT_READ) vesyscallStubMessage(stub içindePROT_READ | PROT_WRITE) bölgesi üzerindeSYS_FUTEX(FUTEX_WAIT) ile uykuya geçer.
Upstream gVisor, seccompNotifyIsSupported değeri doğru olduğunda t.installSeccompNeotify() fonksiyonunu koşulsuz olarak çağırır ve seccomp(SECCOMP_SET_MODE_FILTER, SECCOMP_FILTER_FLAG_NEW_LISTENER, ...) EBUSY döndürürse stub oluşturmayı iptal eder.
gvx içinde (pkg/sentry/platform/systrap/seccomp_notify_xplat.go ve syscall_thread.go), syscallThread.init() şu fonksiyonu çağırır: t.installSeccompNotifyIfFree():
1// pkg/sentry/platform/systrap/seccomp_notify_xplat.go
2var seccompNotifyBusy atomic.Bool
3
4func (t *syscallThread) installSeccompNotifyIfFree() (*os.File, error) {
5 if seccompNotifyBusy.Load() {
6 return nil, nil
7 }
8 f, err := t.installSeccompNotify()
9 if errors.Is(err, unix.EBUSY) {
10 if seccompNotifyBusy.CompareAndSwap(false, true) {
11 log.Warningf("Another seccomp listener exists in the filter chain of the host process. " +
12 "The stub threads use the slower futex loop instead of seccomp-unotify.")
13 }
14 return nil, nil
15 }
16 return f, err
17}
installSeccompNotify() fonksiyonu, dış bir gVisor (runsc) veya dış gvx sandbox'ı süreç filtre zincirinin tek dinleyici yuvasını zaten kullandığı için unix.EBUSY döndürdüğünde:
installSeccompNotifyIfFree()seccompNotifyBusy(atomic.Bool) değerini ayarlar, süreç için bir uyarı kaydeder ve bu stub ile sonraki tüm stub'lar içint.seccompNotify == nildurumunu korur.syscallThread.detach()(syscall_thread_amd64.govesyscall_thread_arm64.go) içindet.seccompNotify == nilise,_RUN_SECCOMP_LOOP(6) yerineamd64üzerinderegs.Rbx = _RUN_SYSCALL_LOOP(5) veyaarm64üzerinderegs.Regs[9] = _RUN_SYSCALL_LOOPayarlanır.syscallThread.syscall()(syscall_thread.go) içinde iç Sentry, stub sistem çağrılarını yalnızca paylaşımlı bellek veSYS_FUTEXüzerinden gönderir:
1// pkg/sentry/platform/systrap/syscall_thread.go
2// Syscall thread'ine yeni bir sistem çağrısı isteği olduğunu bildir.
3atomic.AddUint32(&sentryMsg.state, 1)
4futexWakeUint32(&sentryMsg.state)
5
6// Yanıtı bekle.
7futexWaitWake(&sentryMsg.state, atomic.LoadUint32(&sentryMsg.state))
SECCOMP_RET_TRAP filtreleri konuk iş parçacıklarında keyfi derinlikte yığılabildiği ve syscallThread, ikinci bir kullanıcı bildirim dinleyicisi talep etmeden _RUN_SYSCALL_LOOP moduna geri döndüğü için gvx, runsc içinde sorunsuz çalışır ve özyinelemeli iç içe kullanımı (gvx içinde gvx) destekler.
2. Tek Dosyalık ext4lite Birimlerinde Yerel overlayfs
Dış gVisor konteynerinin gofer (9p/lisafs) kök dosya sistemi üzerine overlayfs bağlamaktan kaçınmak için -v gvx-docker:/var/lib/docker parametresi, /var/lib/docker dizinini gvx'in dahili ext4lite dosya sistemi ile destekler. Bu dosya sistemi tek bir normal dosyada (gvx-docker.ext4) saklanır.
Dış runsc açısından gvx-docker.ext4, pread64(2) ve pwrite64(2) ile erişilen tek bir normal dosyadır. gvx içinde ise ext4lite, trusted.overlay.* ve security.capability genişletilmiş öznitelikleri, karakter aygıtı silinme (whiteout) düğümleri (0:0), d_type dizin girdileri ve RENAME_EXCHANGE desteğiyle JBD2 günlüklemeli bir ext4 dosya sistemi sağlar. Bu sayede iç dockerd, kendi yerel overlayfs depolama sürücüsünü kullanabilir.
3. docker0 ve iptables-legacy ile Kendi İçinde Yeterli netstack
gvx içinde dockerd, dış runsc konteynerinin ağ arayüzleri veya netfilter tabloları ile etkileşime girmez. Bunun yerine:
dockerd,docker0köprüsünü vevethçiftlerinigvx'in kendi kullanıcı alanınetstackbileşeni içinde oluşturur.- Tüm
iptables-legacykuralları (-t nat -N DOCKER,MASQUERADE,conntrack,addrtype),gvx'in dahili netfilter uygulamasına karşı çalışır. - İç içe
runckonteynerleri,/proc,/sysve/sys/fs/cgrouphiyerarşilerinigvx'in bellek içiVFS2bağlama tablosunda bağlar. Dışrunscmount(2)sistem çağrısını hiçbir zaman çağırmazlar.
gVisor (veya gvx İçinde gvx) İçinde İç İçe docker:dind'i Doğrulama
Aşağıdaki komutlarla her iki senaryoyu (runsc içinde rungv veya rungv içinde rungv) doğrulayabilirsiniz:
1# Dış bir gVisor (runsc) konteyneri veya dış bir rungv konteyneri içinde:
2mkdir -p ~/.gvx
3rungv run \
4 -p unix:~/.gvx/docker.sock:2375 \
5 -v gvx-docker:/var/lib/docker \
6 -e DOCKER_TLS_CERTDIR= \
7 docker:dind --tls=false &
8DIND_PID=$!
9
10export DOCKER_HOST="unix://$HOME/.gvx/docker.sock"
11until docker info >/dev/null 2>&1; do sleep 0.5; done
12
13# İç Docker daemon'ının vfs yerine yerel overlayfs kullandığını doğrulayın
14docker info --format 'Storage Driver: {{.Driver}}'
15# Çıktı: Storage Driver: overlayfs
16
17# Çok katmanlı bir build çalıştırın ve iç içe bir konteyner başlatın
18mkdir -p /tmp/nested-test && cd /tmp/nested-test
19cat <<'EOF' > Dockerfile
20FROM alpine:3.20
21RUN adduser -D -u 1001 appuser && mkdir -p /app && chown appuser:appuser /app
22USER appuser
23WORKDIR /app
24RUN echo "nested-build-ok" > status.txt
25CMD ["cat", "/app/status.txt"]
26EOF
27
28docker build -t nested-test:latest .
29docker run --rm nested-test:latest
30# Çıktı: nested-build-ok
31
32kill -INT $DIND_PID
Bu Serideki Diğer Yazılar
- gvx: Tek Bir Ayrıcalıksız Süreçte Linux Konteynerlerini, Docker'ı ve Kubernetes'i Çalıştırma:
gvx'in Windows (whpx,systrap), macOS (hvf) ve Linux (systrap) üzerindeki genel görünümü. - Root veya Kullanıcı Ad Alanları Olmadan Docker, docker build ve docker compose Çalıştırma:
root,sudo,unshare(CLONE_NEWUSER),mount(2)ve/dev/fuseengellendiğindegvx'indocker:dind'i nasıl çalıştırdığı. - gvx ile gVisor ve İç İçe Sandbox'lar İçinde Docker-in-Docker Çalıştırma (bu yazı)
- gvx ile AI Agent RL Rollout'ları için Hızlı ve İzole Docker Ortamları:
gvxve tek dosyalıext4litecopy-on-write anlık görüntülerini kullanarak yüzlerce paralelSWE-benchve RL rollout ortamını 100 milisaniyelik sıfırlamalarla nasıl çalıştıracağınız.