---
id: 83fb2c108243a7df80db3a6e78290daf
author: Lemon Mint
title: Docker-in-Docker futtatása gVisor és Nested Sandboxes környezetben a gvx segítségével
description: Miért hibásodik meg a docker.io telepítése vagy a runsc beágyazása egy gVisor-konténerbe a modprobe, iptables-nft, overlay2, cgroups és seccomp EBUSY esetén, valamint hogyan futtatja biztonságosan a gvx a beágyazott docker:dind környezetet egy izolált, user-space kerneleken belül, a --privileged kapcsoló nélkül.
language: hu
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
---

## Gyors megoldás: `docker:dind` biztonságos futtatása nem privilegizált `gVisor` (`runsc`) sandboxban

Cloud AI agent platformok, Kubernetes `agent-sandbox` telepítések (`RuntimeClass: gvisor`) és megerősítéses tanulási (reinforcement learning) kiértékelő keretrendszerek a megbízhatatlan munkaterheléseket **gVisor (`runsc`)** konténerekbe izolálják, kifejezetten a gazdagép kernelének védelme érdekében. Amikor egy munkaterhelés egy alapértelmezett, nem privilegizált `runsc` konténerben megpróbálja telepíteni a `docker.io` vagy `docker-ce` csomagot `apt-get` segítségével, és elindítani a `dockerd` folyamatot, ez meghiúsul, még akkor is, ha a konténeren belül `root` (`UID 0`) jogosultsággal rendelkezik.

Az **[gvx](https://github.com/gfire-sigs/gvx)** (`rungv`) segítségével **biztonságosan** futtathatja a `docker:dind`, a `docker build` és a `docker compose` parancsokat közvetlenül egy nem privilegizált `gVisor` (`runsc`) konténerben (vagy egymásba ágyazhatja a `gvx`-et a `gvx`-en belül), **anélkül, hogy gyengítené a külső `RuntimeClass` beállítást, host socketeket tenne elérhetővé vagy `--privileged` jogosultságot adna**.

> **A külső sandbox izolációjának megőrzése és megerősítése**: A `rungv` `runsc`-en belüli futtatása nem kerül meg egyetlen korlátozást sem, amelyet a külső `gVisor` sandbox érvényesít. Ehelyett a `gvx` egy második, beágyazott, felhasználói térben futó gVisor `Sentry` kernelt hoz létre, saját `hostlockdown` (`PR_SET_NO_NEW_PRIVS` + `seccomp`) védelemmel, saját memórián belüli `netstack` hálózattal és egyfájlos `ext4lite` kötettel. A belső `dockerd` és `runc` konténerek kétszeresen sandboxoltak: rendszerhívásaik, csatolásaik és nyers csomagjaik soha nem hagyják el a belső `gvx` felhasználói térbeli kernelt.

```sh
# Meglévő gVisor (runsc) konténerben vagy Kubernetes Podban:
# A rungv telepítése SLSA-attesztált GitHub kiadási archívumból vagy go install paranccsal:
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 &

# Szokásos Docker buildek és konténerek futtatása a belső gvx socketen keresztül
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}}'
# Kimenet: overlayfs Alpine Linux ... (konténerizált)

docker build -t nested-app:test .
docker run --rm nested-app:test
```

---

## Öt ok, amiért a közvetlen Docker telepítés meghiúsul a `gVisor` (`runsc`) környezetben

Amikor egy felhasználó vagy egy AI kódoló agent egy szabványos `runsc` konténerben futtatja az `apt-get install -y docker.io` (vagy `docker-ce`) parancsot, és megpróbálja elindítani a `dockerd` folyamatot, öt egymást követő technikai akadályba ütközik.

### 1. Kernelmodul-betöltés (`modprobe`) a virtuális `4.4.0` kernel ellen

A csomagtelepítés és a `dockerd` indítása során az inicializáló szkriptek `modprobe` parancsot futtatnak az `overlay`, `br_netfilter`, `ip_tables`, `nf_nat` és `xt_conntrack` modulok betöltéséhez. A külső gVisor Sentry letiltja az `init_module(2)` és `finit_module(2)` rendszerhívásokat, és olyan virtuális kernelverziót (`4.4.0`) jelent, amelyhez nem tartozik `/lib/modules/4.4.0` könyvtár:

```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) és a `docker0` híd hibái

Az Ubuntu 22.04, Ubuntu 24.04 és Debian 11/12 rendszereken az `apt-get install docker.io` az `iptables-nft` csomagot (amelyet az `nf_tables` netlink alrendszer támogat) választja alapértelmezett `/usr/sbin/iptables` alternatívaként. Egy szabványos gVisor konténerben, hálózati adminisztrátori jogosultságok vagy `nf_tables` netlink támogatás nélkül, a `dockerd` összeomlik a `DOCKER` NAT lánc és a `docker0` híd inicializálása közben:

```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. `overlay2` hiba `gofer` / `9p` rootfs és `vfs` lemezkimerülés esetén

Hacsak a külső `runsc` rendszergazda kifejezetten nem konfigurált dedikált `tmpfs` vagy filestore csatolást a `/var/lib/docker` útvonalon, a konténer rootfs a gVisor `gofer` (`9p`/`lisafs`) rendszerére vagy egy beágyazott overlay csatolásra épül. Az `overlay2` csatolása a `/var/lib/docker` fölé meghiúsul:

```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
```

Amikor a felhasználók vagy az agentek a `dockerd --storage-driver=vfs` indításával próbálják megkerülni ezt a hibát, a Docker teljesen letiltja a Copy-on-Write rétegmegosztást. A `Dockerfile` minden `RUN` utasítása a teljes gyökérfájlrendszer rekurzív másolását (`dirCopy`) hajtja végre, így egy 500 MB-os image 10 build lépésben 5 GB írássá sokszorozódik, és a sandbox `no space left on device` hibával vagy I/O időtúllépéssel leáll.

### 4. Beágyazott `runc` cgroup- és `/proc` csatolási elutasítások

Még akkor is, ha a `dockerd` kényszerítve indul a `dockerd --iptables=false --bridge=none --storage-driver=vfs` paranccsal, a `docker run` vagy `docker build` futtatása meghívja a `runc` programot a belső konténer létrehozásához. Egy alapértelmezett, nem privilegizált gVisor konténerben a `runc` meghiúsul a cgroupok csatolásakor, a `/proc` újracsatolásakor vagy a belső seccomp szűrők betöltésekor:

```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. A beágyazott `runsc` meghiúsul a `SECCOMP_FILTER_FLAG_NEW_LISTENER` (`EBUSY`) miatt

Ha a felhasználó egy másik `runsc` példányt próbál futtatni `runsc`-en belül a `systrap` platform használatával, az inicializálás azonnal megszakad:

```text
creating subprocess: sysmsg: failed to initialize thread: failed to install seccomp notify rules: device or resource busy
```

Mind a Linux kernel, mind a gVisor Sentry legfeljebb **egy** `SECCOMP_FILTER_FLAG_NEW_LISTENER` (`SECCOMP_RET_USER_NOTIF`) listenert enged task-hierarchiánként. Mivel a külső sandbox már birtokolja ezt a listener helyet, a belső `seccomp(2)` hívás `EBUSY` (`device or resource busy`) hibával tér vissza.

---

## Hogyan oldja meg a `gvx` a beágyazott végrehajtást a `gVisor`-on belül

A `gvx` mind az öt akadályt a saját felhasználói térbeli kernelében és platformrétegében kezeli, így a `rungv` képes fut egy külső `gVisor` (`runsc`) konténerben, vagy akár rekurzívan egy másik `gvx` konténerben belül (`gvx` a `gvx`-ben).

![Beágyazott gvx végrehajtás külső gVisor-on belül](/assets/images/gvx/gvx-nested-seccomp-futex.png)

### 1. Automatikus `systrap` visszaesés az `EBUSY` hibától a `_RUN_SYSCALL_LOOP` futex ciklusra

A Linux `systrap` rendszerben minden stub alfolyamat kétféle szálat futtat:

- **Vendég végrehajtási szálak (`sysmsg`)**: A vendég rendszerhívásokat `SECCOMP_RET_TRAP` (`SIGSYS`) és bináris módosított `jmp rel32` trambulinok (`syshandler`) fogják el, a regiszterállapotot pedig megosztott memórián és `SYS_FUTEX` segítségével cserélik ki a Sentry-vel.
- **Egy stub `syscallThread` (`syscall_thread.go`)**: A stub alfolyamaton belül a Sentry nevében végrehajtja a címtér-rendszerhívásokat (`mmap`, `munmap`, `clone`).

A upstream gVisorban a `syscallThread` két ébresztési módot támogat a `stub_amd64.s` és `stub_arm64.s` fájlokban:

1. **Seccomp User-Notify ciklus (`_RUN_SECCOMP_LOOP = 6`, ha `t.seccompNotify != nil`)**: A `syscallThread.init()` során a `t.installSeccompNotify()` egy `SECCOMP_RET_USER_NOTIF` listenert telepít (`seccomp(SECCOMP_SET_MODE_FILTER, SECCOMP_FILTER_FLAG_NEW_LISTENER, stubSyscallRules)`), hogy a Sentry az `ioctl(SECCOMP_IOCTL_NOTIF_SEND)` és `ioctl(SECCOMP_IOCTL_NOTIF_RECV)` hívásokon keresztül továbbíthasson kéréseket.
2. **Futex rendszerhívás ciklus (`_RUN_SYSCALL_LOOP = 5`, ha `t.seccompNotify == nil`)**: A `syscallThread` a `syscall_loop` (`stub_amd64.s` / `stub_arm64.s`) függvényt futtatja, és a `SYS_FUTEX` (`FUTEX_WAIT`) hívásnál alszik a megosztott, 2 lapos `syscallSentryMessage` (a stubban `PROT_READ`) és `syscallStubMessage` (a stubban `PROT_READ | PROT_WRITE`) régión.

Az upstream gVisor feltétel nélkül meghívja a `t.installSeccompNotify()` függvényt, amint a `seccompNotifyIsSupported` értéke igaz, és megszakítja a stub létrehozását, ha a `seccomp(SECCOMP_SET_MODE_FILTER, SECCOMP_FILTER_FLAG_NEW_LISTENER, ...)` `EBUSY` hibát ad vissza.

A `gvx` rendszerben (`pkg/sentry/platform/systrap/seccomp_notify_xplat.go` és `syscall_thread.go`) a `syscallThread.init()` a `t.installSeccompNotifyIfFree()` függvényt hívja:

```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
}
```

Amikor a `installSeccompNotify()` `unix.EBUSY` értéket ad vissza, mert egy külső `gVisor` (`runsc`) vagy külső `gvx` sandbox már birtokolja a folyamat szűrőláncának egyetlen listener helyét:

- Az `installSeccompNotifyIfFree()` beállítja a `seccompNotifyBusy` (`atomic.Bool`) értéket, a folyamatra nézve egyetlen figyelmeztetést naplóz, és a `t.seccompNotify == nil` értéket hagyja az adott stubnál és az összes későbbi stubnál.
- A `syscallThread.detach()` függvényben (`syscall_thread_amd64.go` és `syscall_thread_arm64.go`) a `t.seccompNotify == nil` esetén `regs.Rbx = _RUN_SYSCALL_LOOP` (`5`) értéket állít be `amd64` architektúrán (vagy `regs.Regs[9] = _RUN_SYSCALL_LOOP` értéket `arm64` architektúrán) a `_RUN_SECCOMP_LOOP` (`6`) helyett.
- A `syscallThread.syscall()` függvényben (`syscall_thread.go`) a belső Sentry a stub rendszerhívásokat kizárólag megosztott memórián és `SYS_FUTEX` segítségével továbbítja:

```go
// pkg/sentry/platform/systrap/syscall_thread.go
// Notify the syscall thread about a new syscall request.
atomic.AddUint32(&sentryMsg.state, 1)
futexWakeUint32(&sentryMsg.state)

// Wait for reply.
futexWaitWake(&sentryMsg.state, atomic.LoadUint32(&sentryMsg.state))
```

Mivel a `SECCOMP_RET_TRAP` szűrők a vendég szálakon tetszőlegesen mélyen egymásba ágyazódhatnak, a `syscallThread` pedig további felhasználói értesítési listener kérése nélkül visszaesik a `_RUN_SYSCALL_LOOP` módra, a `gvx` tisztán fut a `runsc`-en belül, és támogatja a rekurzív egymásba ágyazást (`gvx` a `gvx`-ben).

### 2. Natív `overlayfs` egyfájlos `ext4lite` köteteken

Annak elkerülésére, hogy az `overlayfs` a külső gVisor konténer `gofer` (`9p`/`lisafs`) gyökérfájlrendszerére kerüljön, a `-v gvx-docker:/var/lib/docker` paraméter a `/var/lib/docker` könyvtárat a `gvx` belső **`ext4lite`** fájlrendszerével támasztja alá, amely egyetlen szabályos fájlban (`gvx-docker.ext4`) tárolódik.

A külső `runsc` számára a `gvx-docker.ext4` csupán egy szabályos fájl, amelyet `pread64(2)` és `pwrite64(2)` rendszerhívásokkal érnek el. A `gvx` belsejében az `ext4lite` JBD2-naplózott `ext4` fájlrendszert biztosít, `trusted.overlay.*` és `security.capability` kiterjesztett attribútumokkal, karakteres eszköz whiteout csomópontokkal (`0:0`), `d_type` könyvtárbejegyzésekkel és `RENAME_EXCHANGE` támogatással. Ez lehetővé teszi, hogy a belső `dockerd` a saját natív `overlayfs` tárolómeghajtóját használja.

### 3. Önálló `netstack` a `docker0` és `iptables-legacy` támogatással

A `gvx` belsejében a `dockerd` nem lép kapcsolatba a külső `runsc` konténer hálózati interfészeivel vagy netfilter táblázataival. Ehelyett:

- A `dockerd` a `docker0` hidat és a `veth` párokat a `gvx` saját felhasználói térbeli `netstack` rendszerében hozza létre.
- Minden `iptables-legacy` szabály (`-t nat -N DOCKER`, `MASQUERADE`, `conntrack`, `addrtype`) a `gvx` belső netfilter implementációján hajtódik végre.
- A beágyazott `runc` konténerek a `/proc`, `/sys` és `/sys/fs/cgroup` hierarchiáikat a `gvx` memórián belüli `VFS2` csatolási táblázatában csatolják, és soha nem hívják meg a külső `runsc` `mount(2)` rendszerhívását.

---

## Beágyazott `docker:dind` ellenőrzése a `gVisor`-on belül (vagy `gvx` a `gvx`-ben)

Mindkét forgatókönyv (`rungv` a `runsc`-en belül, vagy `rungv` a `rungv`-en belül) a következő parancsokkal ellenőrizhető:

```sh
# Egy külső gVisor (runsc) konténerben vagy egy külső rungv konténerben:
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

# Ellenőrzés: a belső Docker daemon natív overlayfs-t használ-e (nem vfs-t)
docker info --format 'Storage Driver: {{.Driver}}'
# Kimenet: Storage Driver: overlayfs

# Többrétegű build futtatása és egy beágyazott konténer végrehajtása
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
# Kimenet: nested-build-ok

kill -INT $DIND_PID
```

---

## További olvasmányok ebben a sorozatban

1. **[gvx: Linux konténerek, Docker és Kubernetes futtatása egyetlen nem privilegizált folyamatban](/blog/posts/gvx-cross-platform-linux-containers-z48916d01)**: Áttekintés a `gvx` működéséről Windows (`whpx`, `systrap`), macOS (`hvf`) és Linux (`systrap`) rendszereken.
2. **[Docker, docker build és docker compose futtatása root vagy felhasználói névterek nélkül](/blog/posts/run-docker-without-root-or-userns-gvx-z48916d02)**: Hogyan futtatja a `gvx` a `docker:dind` parancsot, ha a `root`, `sudo`, `unshare(CLONE_NEWUSER)`, `mount(2)` és `/dev/fuse` le van tiltva.
3. **[Docker-in-Docker futtatása gVisor-on belül és beágyazott sandboxokban a gvx segítségével](/blog/posts/docker-in-docker-inside-gvisor-with-gvx-z48916d03)** (ez a bejegyzés)
4. **[Gyors, izolált Docker környezetek AI agentek RL rollout feladataihoz a gvx segítségével](/blog/posts/fast-docker-environments-for-agent-rl-rollouts-z48916d04)**: Hogyan használható a `gvx` és az egyfájlos `ext4lite` copy-on-write pillanatképek több száz párhuzamos `SWE-bench` és RL rollout környezet futtatásához, 100 milliszekundumos visszaállítással.
