---
id: 83fb2c108243a7df80db3a6e78290daf
author: Lemon Mint
title: gVisor İçinde ve İç İçe Sandbox'larda gvx ile Docker-in-Docker Çalıştırma
description: docker.io kurulumunun veya runsc'nin bir gVisor konteyneri içinde iç içe çalıştırılmasının modprobe, iptables-nft, overlay2, cgroups ve seccomp EBUSY üzerinde neden başarısız olduğunu ve gvx'in nested docker:dind'i --privileged kullanmadan, yalıtılmış bir kullanıcı alanı çekirdeği içinde nasıl güvenli biçimde çalıştırdığını açıklayın.
language: tr
date: 2026-11-06T12:02:55.931762327Z
path: /blog/posts/docker-in-docker-inside-gvisor-with-gvx-z48916d03
go_package: z.gfire.dev/gvx
go_repourl: https://github.com/gfire-sigs/gvx.git
category: lowlevel
---

## 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](https://github.com/gfire-sigs/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**: `runsc` içinde `rungv` çalıştırmak, dış `gVisor` sandbox'ı tarafından uygulanan hiçbir kısıtlamayı aşmaz. Bunun yerine `gvx`, kendi `hostlockdown` (`PR_SET_NO_NEW_PRIVS` + `seccomp`) yapılandırmasına, kendi bellek içi `netstack` bileşenine ve tek dosyalık bir `ext4lite` birimine sahip ikinci bir iç içe kullanıcı alanı `gVisor` `Sentry` çekirdeği oluşturur. İç `dockerd` ve `runc` konteynerleri çift katmanlı olarak sandbox'lanır: sistem çağrıları, bağlamalar (mount) ve ham paketler iç `gvx` kullanıcı alanı çekirdeğinin dışına çıkmaz.

```sh
# Mevcut bir gVisor (runsc) konteyneri veya Kubernetes Pod'u içinde:
# rungv'yi SLSA ile doğrulanmış bir GitHub sürüm arşivinden veya go install ile kurun:
CGO_ENABLED=0 go install z.gfire.dev/gvx/cmd/rungv@latest

mkdir -p ~/.gvx
rungv run \
  -p unix:~/.gvx/docker.sock:2375 \
  -v gvx-docker:/var/lib/docker \
  -e DOCKER_TLS_CERTDIR= \
  docker:dind --tls=false &

# Standart Docker build ve konteyner işlemlerini iç gvx soketine karşı çalıştırın
export DOCKER_HOST="unix://$HOME/.gvx/docker.sock"
until docker info >/dev/null 2>&1; do sleep 0.5; done
docker info --format '{{.Driver}} {{.OperatingSystem}}'
# Çıktı: overlayfs Alpine Linux ... (containerized)

docker build -t nested-app:test .
docker 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:

```text
modprobe: FATAL: Module overlay not found in directory /lib/modules/4.4.0
modprobe: FATAL: Module br_netfilter not found in directory /lib/modules/4.4.0
modprobe: FATAL: Module ip_tables not found in directory /lib/modules/4.4.0
modprobe: 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:

```text
failed to start daemon: Error initializing network controller: error obtaining controller instance: failed to create NAT chain DOCKER: iptables failed: iptables -t nat -N DOCKER
iptables v1.8.10 (nf_tables): Could not fetch rule set generation id: Invalid argument
iptables v1.8.10 (legacy): can't initialize iptables table 'nat': Table does not exist (do you need to insmod?)
iptables: No chain/target/match by that name.
Unable 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.)
Error 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:

```text
level=error msg="[graphdriver] prior storage driver overlay2 failed: driver not supported"
failed to mount overlay: invalid argument
Error 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:

```text
docker: 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:
error 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
cgroups: cgroup mountpoint does not exist: unknown
runc create failed: systemd not running on this host: cannot use systemd cgroup manager
error during container init: error mounting "proc" to rootfs at "/proc": mount proc:/proc (via /proc/self/fd/6), flags: 0xe: operation not permitted
error 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:

```text
creating 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.

![gVisor İçinde İç İçe gvx Çalıştırma](/assets/images/gvx/gvx-nested-seccomp-futex.png)

### 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 rel32` trampolinleri (`syshandler`) aracılığıyla yakalar. Kayıt durumlarını paylaşımlı bellek ve `SYS_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:

1. **Seccomp User-Notify Döngüsü (`_RUN_SECCOMP_LOOP = 6`, `t.seccompNotify != nil` olduğunda)**: `syscallThread.init()` sırasında `t.installSeccompNotify()`, bir `SECCOMP_RET_USER_NOTIF` dinleyicisi kurar (`seccomp(SECCOMP_SET_MODE_FILTER, SECCOMP_FILTER_FLAG_NEW_LISTENER, stubSyscallRules)`). Böylece Sentry, istekleri `ioctl(SECCOMP_IOCTL_NOTIF_SEND)` ve `ioctl(SECCOMP_IOCTL_NOTIF_RECV)` ile gönderebilir.
2. **Futex Sistem Çağrısı Döngüsü (`_RUN_SYSCALL_LOOP = 5`, `t.seccompNotify == nil` olduğunda)**: `syscallThread`, `syscall_loop` (`stub_amd64.s` / `stub_arm64.s`) çalıştırır. Paylaşımlı 2 sayfalık `syscallSentryMessage` (stub içinde `PROT_READ`) ve `syscallStubMessage` (stub içinde `PROT_READ | PROT_WRITE`) bölgesi üzerinde `SYS_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()`:

```go
// pkg/sentry/platform/systrap/seccomp_notify_xplat.go
var seccompNotifyBusy atomic.Bool

func (t *syscallThread) installSeccompNotifyIfFree() (*os.File, error) {
	if seccompNotifyBusy.Load() {
		return nil, nil
	}
	f, err := t.installSeccompNotify()
	if errors.Is(err, unix.EBUSY) {
		if seccompNotifyBusy.CompareAndSwap(false, true) {
			log.Warningf("Another seccomp listener exists in the filter chain of the host process. " +
				"The stub threads use the slower futex loop instead of seccomp-unotify.")
		}
		return nil, nil
	}
	return f, err
}
```

`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çin `t.seccompNotify == nil` durumunu korur.
- `syscallThread.detach()` (`syscall_thread_amd64.go` ve `syscall_thread_arm64.go`) içinde `t.seccompNotify == nil` ise, `_RUN_SECCOMP_LOOP` (`6`) yerine `amd64` üzerinde `regs.Rbx = _RUN_SYSCALL_LOOP` (`5`) veya `arm64` üzerinde `regs.Regs[9] = _RUN_SYSCALL_LOOP` ayarlanır.
- `syscallThread.syscall()` (`syscall_thread.go`) içinde iç Sentry, stub sistem çağrılarını yalnızca paylaşımlı bellek ve `SYS_FUTEX` üzerinden gönderir:

```go
// pkg/sentry/platform/systrap/syscall_thread.go
// Syscall thread'ine yeni bir sistem çağrısı isteği olduğunu bildir.
atomic.AddUint32(&sentryMsg.state, 1)
futexWakeUint32(&sentryMsg.state)

// Yanıtı bekle.
futexWaitWake(&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`, `docker0` köprüsünü ve `veth` çiftlerini `gvx`'in kendi kullanıcı alanı `netstack` bileşeni içinde oluşturur.
- Tüm `iptables-legacy` kuralları (`-t nat -N DOCKER`, `MASQUERADE`, `conntrack`, `addrtype`), `gvx`'in dahili netfilter uygulamasına karşı çalışır.
- İç içe `runc` konteynerleri, `/proc`, `/sys` ve `/sys/fs/cgroup` hiyerarşilerini `gvx`'in bellek içi `VFS2` bağlama tablosunda bağlar. Dış `runsc` `mount(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:

```sh
# Dış bir gVisor (runsc) konteyneri veya dış bir rungv konteyneri içinde:
mkdir -p ~/.gvx
rungv run \
  -p unix:~/.gvx/docker.sock:2375 \
  -v gvx-docker:/var/lib/docker \
  -e DOCKER_TLS_CERTDIR= \
  docker:dind --tls=false &
DIND_PID=$!

export DOCKER_HOST="unix://$HOME/.gvx/docker.sock"
until docker info >/dev/null 2>&1; do sleep 0.5; done

# İç Docker daemon'ının vfs yerine yerel overlayfs kullandığını doğrulayın
docker info --format 'Storage Driver: {{.Driver}}'
# Çıktı: Storage Driver: overlayfs

# Çok katmanlı bir build çalıştırın ve iç içe bir konteyner başlatın
mkdir -p /tmp/nested-test && cd /tmp/nested-test
cat <<'EOF' > Dockerfile
FROM alpine:3.20
RUN adduser -D -u 1001 appuser && mkdir -p /app && chown appuser:appuser /app
USER appuser
WORKDIR /app
RUN echo "nested-build-ok" > status.txt
CMD ["cat", "/app/status.txt"]
EOF

docker build -t nested-test:latest .
docker run --rm nested-test:latest
# Çıktı: nested-build-ok

kill -INT $DIND_PID
```

---

## Bu Serideki Diğer Yazılar

1. **[gvx: Tek Bir Ayrıcalıksız Süreçte Linux Konteynerlerini, Docker'ı ve Kubernetes'i Çalıştırma](/blog/posts/gvx-cross-platform-linux-containers-z48916d01)**: `gvx`'in Windows (`whpx`, `systrap`), macOS (`hvf`) ve Linux (`systrap`) üzerindeki genel görünümü.
2. **[Root veya Kullanıcı Ad Alanları Olmadan Docker, docker build ve docker compose Çalıştırma](/blog/posts/run-docker-without-root-or-userns-gvx-z48916d02)**: `root`, `sudo`, `unshare(CLONE_NEWUSER)`, `mount(2)` ve `/dev/fuse` engellendiğinde `gvx`'in `docker:dind`'i nasıl çalıştırdığı.
3. **[gvx ile gVisor ve İç İçe Sandbox'lar İçinde Docker-in-Docker Çalıştırma](/blog/posts/docker-in-docker-inside-gvisor-with-gvx-z48916d03)** (bu yazı)
4. **[gvx ile AI Agent RL Rollout'ları için Hızlı ve İzole Docker Ortamları](/blog/posts/fast-docker-environments-for-agent-rl-rollouts-z48916d04)**: `gvx` ve tek dosyalı `ext4lite` copy-on-write anlık görüntülerini kullanarak yüzlerce paralel `SWE-bench` ve RL rollout ortamını 100 milisaniyelik sıfırlamalarla nasıl çalıştıracağınız.
