---
id: 517bf76b20da9b1bac643f4bac1bfd6b
author: Lemon Mint
title: Rýchle izolované prostredia Docker pre RL rollouts AI agentov s gvx
description: Ako bezpečne spúšťať stovky paralelných, silne izolovaných neprivilegovaných prostredí Docker a Kubernetes pre SWE-bench a rollouty posilňovaného učenia kódovacích agentov so 100-milisekundovými obnoveniami zväzkov ext4lite typu copy-on-write.
language: sk
date: 2026-11-06T12:02:55.95126412Z
path: /blog/posts/fast-docker-environments-for-agent-rl-rollouts-z48916d04
go_package: z.gfire.dev/gvx
go_repourl: https://github.com/gfire-sigs/gvx.git
category: ai
---

## Bezpečnostné a infraštruktúrne úzke hrdlo v RL rolloutoch kódovacích agentov

Trénovanie a vyhodnocovanie softvérových inžinierskych agentov naprieč benchmarkami a frameworkmi pre reinforcement learning (RL), ako sú **SWE-bench**, **SWE-ReX**, **R2E-Gym**, **SWE-Gym**, **Verl** a **OpenHands**, vyžaduje spúšťanie tisícov súbežných rolloutov **nedôveryhodného kódu generovaného LLM**. Pri reálnych úlohách softvérového inžinierstva agent nielen upravuje textové súbory: spúšťa `docker build`, zapína databázy a mikroslužby pomocou `docker compose`, vykonáva integračné testovacie sady a kontroluje logy kontajnerov.

Pri rozsahu rolloutov (128 až 1 024 súbežných epizód na klaster) existujúca infraštruktúra kontajnerov naráža na štyri systémové bezpečnostné a výkonnostné úzke hrdlá:

1. **Zámková kolízia a únik stavu hostiteľského `dockerd`**: Nasmerovanie stoviek súbežných RL workerov na jediný `/var/run/docker.sock` hostiteľa dáva každému agentovi prístup ekvivalentný rootovi k hostiteľskému démonovi, serializuje extrakciu vrstiev obrazov, vyčerpáva alokácie podsietí `docker0`, spúšťa chyby `Another app is currently holding the xtables lock` v hostiteľskom `iptables` a necháva osirelé kontajnery naprieč epizódami.
2. **Únik z kontajnera pri privilegovanom DinD**: Štandardné kontajnery `docker:dind` vyžadujú `--privileged` (`CAP_SYS_ADMIN`), ktorý vystavuje nedôveryhodným RL rolloutom surové blokové zariadenia hostiteľa, agenty uvoľnenia cgroup a kernelové moduly.
3. **Obmedzenia hardvérovej virtualizácie (`/dev/kvm`)**: MicroVM runtime (`Firecracker`, `QEMU`, `Kata Containers`) čisto izolujú vnútorné jadrá, no vyžadujú `/dev/kvm`. Štandardné Kubernetes pody bez vnorenej virtualizácie, bare-metal cloudové inštancie, kde je KVM vypnuté, a existujúce sandboxy `gVisor` (`runsc`) nemôžu spúšťať microVM KVM.
4. **Vysoká latencia resetu epizódy (`env.reset()`)**: Spustenie nového prostredia `docker:dind` a sťahovanie alebo rozbaľovanie základných obrazov pre každú RL epizódu trvá 10 až 60 sekúnd, čo necháva GPU trénovacie workery nečinné počas čakania na reset prostredia.

**[gvx](https://github.com/gfire-sigs/gvx)** (CLI `rungv` a Go SDK `z.gfire.dev/gvx/pkg/gvx`) poskytuje **bezpečný rollout runtime s obranou do hĺbky**, ktorý rieši všetky štyri úzke hrdlá. Každé prostredie rolloutu beží ako **jediný proces v používateľskom priestore bez privilégií a s vlastným uzamknutím** (izolovaný jadrom Google gVisor `Sentry`, balíkom `pkg/hostlockdown` a pamäťovým `netstack`), pričom celý stav `/var/lib/docker` sa nachádza vnútri **jediného súboru `.ext4` s copy-on-write (`ext4lite`)**.

> **Bezpečnosť bez privilégií pre rollouty nedôveryhodných agentov**: Každá epizóda `gvx` uzamkne vlastný hostiteľský OS proces pred spustením hosťovského kódu (`PR_SET_NO_NEW_PRIVS` + BPF `seccomp` na Linuxe, Seatbelt `deny default` na macOS, Restricted Token + Job Object na Windows). Aj keď RL agent spustí `docker run --privileged` alebo deštruktívne príkazy `rm -rf /` vo svojej epizóde, tieto operácie zostávajú prísne obmedzené na jadro `Sentry` v používateľskom priestore danej epizódy a na jej dočasný súbor zväzku `.ext4`.

---

## Architektúra rolloutov: Paralelné inštancie bez privilégií + Reflink snímky

V `gvx` je každý rollout worker bežný OS proces bez privilégií s vlastným jadrom gVisor `Sentry`, vlastným `netstack` v používateľskom priestore (`10.0.2.15/24`) a vlastným blokovým zväzkom `ext4lite` (`ep-N.ext4`), pričom zdieľa len na čítanie určenú content-addressable cache obrazov OCI `erofs` cez page cache hostiteľa.

![Architektúra paralelných RL rolloutov agentov s gvx](/assets/images/gvx/gvx-rl-rollout-snapshot.png)

### Prečo jednosúborové zväzky `ext4lite` menia ekonomiku resetov RL

Keďže súborový systém `ext4lite` v `gvx` ukladá celý zväzok `ext4` s žurnálom JBD2 (vrátane adresárov vrstiev `overlayfs` Dockeru, rozšírených atribútov `trusted.overlay.*`, whiteout device uzlov a metadát obrazov) do jedného bežného súboru na hostiteľovi, správa životného cyklu epizódy sa stáva jedinou operáciou so súborom:

1. **100-milisekundový `env.reset()` pomocou `cp --reflink=always`**:
   - Súbor `golden-docker.ext4` vytvoríte raz; obsahuje vopred stiahnuté základné obrazy (`python:3.11`, `postgres:16`, `redis:7` alebo základné obrazy `SWE-bench` špecifické pre úlohu).
   - Na začiatku každej RL epizódy `cp --reflink=always golden-docker.ext4 ep-0042.ext4` (na XFS, Btrfs, ZFS alebo macOS APFS) vytvorí nezávislý copy-on-write klon za menej ako 100 milisekúnd bez kopírovania podkladových diskových blokov.
   - Dodatočné miesto na disku zaberajú iba 4 KiB bloky, ktoré sa počas danej epizódy zmenili.
2. **Ukončenie bez zvyškov (`env.close()`)**:
   - Ukončenie procesu `rungv` (`sb.Close()` v Go) a odstránenie súboru `ep-0042.ext4` uvoľní 100 % procesov, sieťových socketov, prípojok a úložiska epizódy za ~10 milisekúnd. Nič sa nikdy nepripojí do tabuľky prípojok hostiteľského jadra ani sa nezaregistruje v cgroups hostiteľa.

---

## Postup krok za krokom: Zlatá `.ext4` snímka a rýchly reset epizódy

Nástroj `rungv` môžete nainštalovať na každý uzol rollout workera buď stiahnutím prekompilovanej binárky s attestáciou SLSA z [vydaní na GitHube](https://github.com/gfire-sigs/gvx/releases) (`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`), alebo spustením `CGO_ENABLED=0 go install z.gfire.dev/gvx/cmd/rungv@latest`.

### Krok 1: Vytvorenie zlatého zväzku `docker:dind` len raz

```sh
mkdir -p /srv/rl-snapshots /srv/rl-episodes

# 1. Spustenie seed inštancie docker:dind podloženej súborom golden-docker.ext4
rungv run \
  -p unix:/srv/rl-snapshots/seed.sock:2375 \
  --mount type=volume,src=/srv/rl-snapshots/golden-docker.ext4,dst=/var/lib/docker \
  -e DOCKER_TLS_CERTDIR= \
  docker:dind --tls=false &
SEED_PID=$!

# 2. Predbežné naplnenie základných obrazov a vyrovnávacej pamäte zostavení vnútri golden-docker.ext4
export DOCKER_HOST="unix:///srv/rl-snapshots/seed.sock"
until docker info >/dev/null 2>&1; do sleep 0.5; done

docker pull python:3.11-slim
docker pull redis:7-alpine
docker pull postgres:16-alpine

# 3. Elegantné zastavenie seed inštancie, aby ext4lite zapísal a zhutnil obraz
kill -INT $SEED_PID
wait $SEED_PID
```

### Krok 2: Spustenie paralelných RL epizód za 100 milisekúnd

Každý paralelný rollout worker klonuje `golden-docker.ext4` cez reflink a spustí izolovanú inštanciu `docker:dind`, v ktorej sú všetky obrazy už prítomné v `/var/lib/docker`:

```sh
EPISODE_ID="ep-0042"
SOCK="/srv/rl-episodes/${EPISODE_ID}.sock"

# Okamžitý Copy-on-Write klon (~100 ms na XFS/Btrfs/APFS)
cp --reflink=always \
  /srv/rl-snapshots/golden-docker.ext4 \
  "/srv/rl-episodes/${EPISODE_ID}.ext4"

# Spustenie izolovanej epizódy bez privilégií so 4 GiB RAM a 2 CPU
rungv run \
  -m 4g --cpus 2 \
  -p "unix:${SOCK}:2375" \
  --mount "type=volume,src=/srv/rl-episodes/${EPISODE_ID}.ext4,dst=/var/lib/docker" \
  -e DOCKER_TLS_CERTDIR= \
  docker:dind --tls=false &
EP_PID=$!

export DOCKER_HOST="unix://${SOCK}"
until docker info >/dev/null 2>&1; do sleep 0.2; done

# Vnútri rollout workera sú obrazy okamžite dostupné bez sťahovania zo siete
docker images

# Ukončenie epizódy po výpočte odmeny (~10 ms)
kill -KILL $EP_PID
rm -f "/srv/rl-episodes/${EPISODE_ID}.ext4" "${SOCK}"
```

---

## Programový pool rolloutov pomocou Go SDK `z.gfire.dev/gvx/pkg/gvx`

Ak je váš server RL prostredia alebo evaluačný harness napísaný v Go (alebo volá rollout démon v Go cez gRPC), môžete paralelné epizódy kontajnerov spravovať priamo cez `z.gfire.dev/gvx/pkg/gvx`:

```go
package main

import (
	"bytes"
	"context"
	"fmt"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"time"

	"z.gfire.dev/gvx/pkg/gvx"
)

func runEpisode(ctx context.Context, goldenExt4, scratchDir string, epID int) error {
	epExt4 := filepath.Join(scratchDir, fmt.Sprintf("ep-%04d.ext4", epID))
	defer os.Remove(epExt4)

	// 1. Reflink klonovanie zlatého zväzku ext4lite
	cpCmd := exec.CommandContext(ctx, "cp", "--reflink=always", goldenExt4, epExt4)
	if err := cpCmd.Run(); err != nil {
		return fmt.Errorf("clone golden volume: %w", err)
	}

	// 2. Uplatnenie časového limitu epizódy podľa reálneho času
	epCtx, cancel := context.WithTimeout(ctx, 2*time.Minute)
	defer cancel()

	var stdout, stderr bytes.Buffer
	sb, err := gvx.New(epCtx, gvx.Config{
		Platform:               gvx.PlatformAuto,
		NetworkMode:            gvx.NetModeNAT,
		NetRaw:                 true,
		AllowPacketSocketWrite: true,
		AllowSUID:              true,
		OCIImage:               "docker:dind",
		Entrypoint:             []string{"sh", "-c"},
		EntrypointSet:          true,
		Cmd: []string{
			"dockerd-entrypoint.sh --tls=false >/var/log/dockerd.log 2>&1 & " +
				"until docker info >/dev/null 2>&1; do sleep 0.2; done && " +
				"docker run --rm python:3.11-slim python -c 'print(6 * 7)'",
		},
		Envv: []string{"DOCKER_TLS_CERTDIR="},
		Mounts: []gvx.Mount{
			{
				Kind:      gvx.MountVolume,
				ImagePath: epExt4,
				GuestPath: "/var/lib/docker",
			},
		},
		Resources: gvx.Resources{
			MemoryBytes: 4 << 30, // limit 4 GiB
			CPUMillis:   2000,    // 2,0 CPU
		},
		Stdout: &stdout,
		Stderr: &stderr,
	})
	if err != nil {
		return fmt.Errorf("init episode %d: %w", epID, err)
	}
	defer sb.Close()

	exitCode, err := sb.Run(epCtx)
	if err != nil {
		return fmt.Errorf("run episode %d: %w", epID, err)
	}

	log.Printf("episode %d finished: exit=%d stdout=%q", epID, exitCode, stdout.String())
	return nil
}

func main() {
	ctx := context.Background()
	if err := runEpisode(ctx, "/srv/rl-snapshots/golden-docker.ext4", "/srv/rl-episodes", 1); err != nil {
		log.Fatal(err)
	}
}
```

---

## Porovnanie behových prostredí sandboxu pre RL agentov

| Metrika / požiadavka                                      | Zdieľaný hostiteľský `dockerd`    | Privilegovaný `docker:dind` | Firecracker / KVM MicroVM            | Štandardný `gVisor` (`runsc`)       | `gvx` (`rungv` / `pkg/gvx`)                 |
| :-------------------------------------------------------- | :-------------------------------- | :-------------------------- | :----------------------------------- | :---------------------------------- | :------------------------------------------ |
| **Vyžaduje root na hostiteľovi alebo `--privileged`**     | Áno (`docker.sock` = root)        | Áno (`--privileged`)        | Vyžaduje `/dev/kvm` + nastavenie `TAP` | Áno (alebo `userns` + `mount`)    | **Nie (jediný proces bez privilégií)**      |
| **Funguje vnútri K8s Podov bez privilégií / existujúceho `runsc`** | Nie                       | Nie                         | Nie (bez `/dev/kvm`)                 | Zlyháva (`EBUSY` pri vnorenom seccomp) | **Áno (automatický záložný futex loop)** |
| **Podporuje vnútorné `docker build` a `docker compose`**  | Zdieľa hostiteľský démon          | Áno                         | Áno                                  | Zlyháva bez úpravy hostiteľského DinD | **Áno (`overlayfs` na `ext4lite`)**       |
| **Sieťová izolácia na úrovni epizódy**                    | Zdieľaný `docker0` / zámok `xtables` | Oddelený namespace       | Oddelené sieťové rozhranie VM        | Oddelený netns                      | **Izolovaný `netstack` vnútri procesu**     |
| **Latencia resetu epizódy za behu (`env.reset()`)**       | 5 s až 30 s (čistenie kontajnerov/zväzkov) | 10 s až 60 s (studený démon) | ~150 ms (snímka VM)            | 5 s až 20 s                         | **~100 ms (`cp --reflink=always` `.ext4`)** |
| **Multiplatformový lokálny vývoj (Windows / macOS / Linux)** | Vyžaduje VM s Linuxom          | Vyžaduje VM s Linuxom       | Iba Linux KVM                        | Iba Linux                           | **Natívne na Windows, macOS a Linuxe**      |

---

## Ďalšie čítanie v tejto sérii

1. **[gvx: Spúšťanie Linux kontajnerov, Dockeru a Kubernetes v jedinom procese bez privilégií](/blog/posts/gvx-cross-platform-linux-containers-z48916d01)**: Kompletný architektonický prehľad `gvx` naprieč Windows (`whpx`, `systrap`), macOS (`hvf`) a Linuxom (`systrap`).
2. **[Spúšťanie Dockeru, docker build a docker compose bez roota alebo používateľských namespace-ov](/blog/posts/run-docker-without-root-or-userns-gvx-z48916d02)**: Rýchly trojkrokový návod na spustenie a riešenie problémov, keď sú blokované `sudo`, `unshare(CLONE_NEWUSER)`, `mount(2)` a `/dev/fuse`.
3. **[Spúšťanie Docker-in-Docker vnútri gVisor a vnorených sandboxov pomocou gvx](/blog/posts/docker-in-docker-inside-gvisor-with-gvx-z48916d03)**: Podrobný pohľad na to, prečo `docker.io` a vnorený `runsc` zlyhávajú vnútri `gVisor` (`runsc`) a ako `gvx` prechádza na futex slučku `_RUN_SYSCALL_LOOP` pri `EBUSY`.
4. **[Rýchle izolované Docker prostredia pre RL rollouty AI agentov pomocou gvx](/blog/posts/fast-docker-environments-for-agent-rl-rollouts-z48916d04)** (tento príspevok)
