---
id: e62d78ef5850c982dbddc9d3ff9134ae
author: Lemon Mint
title: gvx：在单个 Unprivileged Process 中运行 Linux Containers、Docker 和 Kubernetes
description: 对 gvx 的技术概述：gvx 是一款基于 gVisor 用户空间内核构建的、经过安全加固的跨平台容器运行时及 Go SDK，能够在单一无特权进程中安全地运行 Linux 容器、Docker-in-Docker 以及多节点高可用 Kubernetes，支持 Windows、macOS 和 Linux 平台。
language: zh
date: 2026-11-06T12:02:56.008516536Z
path: /blog/posts/gvx-cross-platform-linux-containers-z48916d01
go_package: z.gfire.dev/gvx
go_repourl: https://github.com/gfire-sigs/gvx.git
category: lowlevel
---

## 引言:无需 root 或宿主守护进程的安全加固容器

在开发机、CI 运行器或多租户云节点内运行 Linux 容器、`docker build` 或 Kubernetes,通常迫使用户在安全性上做出令人不快的权衡:

- 在 **Linux** 上,标准 OCI 运行时(`runc`、`crun`、`runsc`)需要 `root` 权限、暴露宿主机的 `/var/run/docker.sock`、使用 `--privileged`(`CAP_SYS_ADMIN`),或使用会扩大宿主内核攻击面的用户命名空间(`unshare(CLONE_NEWUSER)`)。
- 在 **Windows** 和 **macOS** 上,容器引擎(`Docker Desktop`、`Podman Machine`、`Lima`、`OrbStack`)会在 WSL2、Hyper-V 或 `Virtualization.framework` 中启动一个完整的 Linux 虚拟机内核,并在该虚拟机内运行持久的后台守护进程。

**[gvx](https://github.com/gfire-sigs/gvx)**(`rungv` CLI 与 `z.gfire.dev/gvx/pkg/gvx` Go SDK)提供了一种**安全、零特权的替代方案**。`gvx` 是 Google **gVisor** 用户态 Linux 内核(`Sentry`)与用户态网络协议栈(`netstack`)的可跟随上游更新的分支,可在 Windows、macOS 和 Linux 上,直接在**单个无特权、自我锁定的宿主进程**中执行未经修改的 `linux/amd64` 与 `linux/arm64` ELF64 二进制文件及 OCI 容器。

> **安全保证(三环纵深防御)**:`gvx` 从不削弱宿主操作系统的安全边界,从不安装内核驱动程序,也从不在宿主上要求 `root`、`sudo`、`--privileged` 或 `CLONE_NEWUSER`。在执行任何客户机指令之前,`gvx` 会使用 Windows 受限令牌(Restricted Tokens)与作业对象(Job Objects)、macOS Seatbelt(`deny default`),或 Linux 的 `PR_SET_NO_NEW_PRIVS` 加 BPF `seccomp` 锁定其自身的宿主进程。所有客户机系统调用、虚拟 `UID 0` 权限、挂载(`VFS2`)以及网络表(`netstack`)均被严格限制在内存安全的 Go `Sentry` 内核之内。

| 运行时                     | 宿主操作系统                                          |          冷启动时间 |         内存占用 | 是否需要管理员或 `sudo` | 后台虚拟机守护进程 | 直接运行 OCI 镜像 |
| :------------------------- | :---------------------------------------------------- | ------------------: | ---------------: | :---------------------- | :----------------- | :---------------- |
| **`gvx` (`whpx` / `hvf`)** | Windows (`amd64`, `arm64`) 与 macOS (`arm64`)         |          `~20 ms`   |        `~30 MiB` | 否                      | 否                 | 是                |
| **`gvx` (`systrap`)**      | Windows (`amd64`, `arm64`) 与 Linux (`amd64`, `arm64`) |    `~25 至 65 ms`   |        `~31 MiB` | 否                      | 否                 | 是                |
| **WSL2 / Docker Desktop**  | Windows 与 macOS                                      |  `800 至 2500 ms`   |      `> 512 MiB` | 是(初始设置)           | 是                 | 需要守护进程      |
| **QEMU(全系统)**          | Windows 与 macOS                                      | `1500 至 4000 ms`   |      `> 256 MiB` | 否                      | 否                 | 需要磁盘镜像      |

在 Windows 或 macOS 上不会启动任何 Linux 虚拟机内核。宿主上也不运行任何后台守护进程。宿主操作系统上不需要 `root`、`sudo`、用户命名空间(`CLONE_NEWUSER`)、`mount(2)`、`TUN`/`TAP` 驱动程序或 `CAP_NET_ADMIN` 权限。在该无特权进程内部,`gvx` 可运行 **Docker-in-Docker(`docker:dind`)**、`docker build`、`docker compose`,以及**多节点高可用 Kubernetes(带嵌入式 `etcd` 与 `Calico` CNI 的 `k3s`)**。

---

## 容器生命周期与三环宿主锁定

每个 `gvx` 沙箱都运行在一个无特权的宿主进程之内,并强制执行三阶段生命周期:预先打开所有所需的宿主文件描述符与套接字,通过 `pkg/hostlockdown` 锁定宿主操作系统进程,然后进入进程内的 `Sentry` 执行循环。

![gvx 容器生命周期与宿主自锁定](/assets/images/gvx/gvx-lifecycle-lockdown.png)

在将控制权交给第一条客户机指令之前,`gvx` 会预先打开其缓存的 `.erofs` 镜像层描述符、`.ext4` 卷文件描述符、`/workspace` 的 `*os.Root` 句柄以及宿主网络套接字,然后调用 `hostlockdown.Apply()`:

1. **Windows NT(`lockdown_windows.go`)**:调用带有 `SE_PRIVILEGE_REMOVED` 的 `AdjustTokenPrivileges`,就地剥离权限;绑定一个以 Low 完整性级别(`S-1-16-4096`)运行、带 `DISABLE_MAX_PRIVILEGE` 的受限模拟令牌;将进程分配到一个匿名作业对象,其 `JOB_OBJECT_LIMIT_ACTIVE_PROCESS = 1` 会阻止创建任何子进程;并通过 `SetProcessMitigationPolicy` 启用 `DisallowWin32kSystemCalls = 1`。
2. **macOS XNU(`lockdown_darwin.go`)**:设置 `RLIMIT_NPROC = 0`,并应用一个 Seatbelt 配置文件(`sandbox_init_with_parameters`),该配置文件从 `(version 1)(deny default)` 开始,阻止 `process-fork`、`process-exec*` 以及所有新的宿主文件打开操作。
3. **Linux(`lockdown_linux.go` + `pkg/gvx/syscallfilter_linux.go`)**:设置 `PR_SET_NO_NEW_PRIVS`,并通过 `SECCOMP_FILTER_FLAG_TSYNC` 在所有线程上安装 Sentry 的 BPF `seccomp` 允许列表,同时对 `execve`、`execveat`、`clone`、`openat` 和 `socket` 显式设置 `EPERM` 规则。

---

## 跨平台系统调用拦截与 4 KB 分页(`pkg/sentry/platform`)

Linux ELF64 二进制文件假定 `mmap(MAP_FIXED)` 段映射使用 4 KB(`0x1000`)的内存页粒度。然而,Windows 的 `VirtualAlloc` 与 `MapViewOfFile3` 强制 64 KB(`0x10000`)对齐,而 Apple Silicon macOS(`darwin/arm64`)使用 16 KB(`0x4000`)的宿主页。此外,每个操作系统都需要不同的机制来拦截 Linux 的 `SYSCALL`(`amd64`)与 `SVC #0`(`arm64`)指令,且不能启动 Linux 虚拟机。

`gvx` 通过四个实现 `pkg/sentry/platform.Context` 的执行后端解决了这两个问题:

![gvx 平台系统调用拦截与 4KB 分页机制](/assets/images/gvx/gvx-platform-syscalls.png)

### 1. Windows 硬件引擎:`platform/whpx`(`WinHvPlatform.dll`)

在 `windows/amd64` 与 `windows/arm64` 上,`platform/whpx` 通过 `WHvCreatePartition` 创建一个轻量级超级管理程序分区,且不启动客户机操作系统内核:

- **64 KB 到 4 KB 分页解耦器**:`whpx` 通过 `WHvMapGpaRange` 一次性地将 `pgalloc.MemoryFile` 的每个 64 MiB 区域注册到分区中。随后的 `MapFile` 与 `Unmap` 调用仅修改分区内的客户机页表,从而在 Windows 64 KB 分配粒度的前提下,强制执行精确的 4 KB `R/W/X` 权限。
- **通过 `CommPage` 进行零 API 寄存器交换**:
  - 在 `amd64` 上,客户机 Ring-3 的 `SYSCALL` 进入分区内的 Ring-0 `MSR_LSTAR` 桩(`stubgen_x86.go`),该桩将快速路径系统调用所用的通用寄存器(`RAX, RDI, RSI, RDX, R10, R8, R9, RIP, RSP`)写入共享的 4 KB `CommPage`,并执行 `HLT` 以退出 `WHvRunVirtualProcessor`。
  - 在 `arm64` 上,客户机 EL0 的 `SVC #0` 进入 EL1 向量(`stubgen_a64.go`),将 `X0..X8, SP_EL0, ELR_EL1, PSTATE` 写入 `CommPage`,并从未映射的 GPA 零地址加载数据,从而触发一次内存退出,返回 Sentry。

### 2. macOS 硬件引擎:`platform/hvf`(`Hypervisor.framework`)

在 Apple Silicon(`darwin/arm64`)上,`platform/hvf` 在 `Hypervisor.framework` 的 vCPU(`hv_vm_create`、`hv_vcpu_run`)中运行容器线程:

- **基于 16 KB 宿主页的两阶段 4 KB MMU 转换**:第二阶段(`hv_vm_map`)以 16 KB 宿主页粒度,将 1 GiB 的内存文件块一次性映射到中间物理地址(IPA)空间中,权限为 `HV_MEMORY_READ | HV_MEMORY_WRITE | HV_MEMORY_EXEC`。在 vCPU 内部,EL1 的 `bluepill` 桩(`bluepill_arm64.go`)在 `TTBR0_EL1` 中配置 `TCR_EL1.TG0 = 0b00`(4 KB 粒度),并填充 4 KB 的第一阶段页表项,其权限精确为 `R-X` 或 `RW-`(`UXN=1`)。由于 ARM64 MMU 强制执行第一阶段与第二阶段权限的交集,硬件会在精确的 4 KB 边界上捕获权限错误。
- **系统调用陷阱**:客户机 EL0 的 `SVC #0` 陷入 EL1 的 `VBAR_EL1`,将寄存器写入共享的 4 KB `CommPage`,并执行 `HVC #0` 以从 `hv_vcpu_run` 返回 Go Sentry 线程。

### 3. Windows 用户态回退:`platform/systrap`(`winsystrap`)

当硬件虚拟化不可用时(例如在嵌套的云虚拟机内部),`winsystrap` 会在 `windows/amd64` 与 `windows/arm64` 上以用户态运行客户机代码:

- **硬件 `CR3` / `TTBR0_EL1` 隔离**:客户机线程运行在隔离的桩子进程中,这些进程由一个生成的 1 KB 可执行映像(`stubimage_xplat.go`)启动,并受 Low 完整性令牌与作业对象的限制。
- **原地二进制修补与 `winSysmsg` IPC**:在 `amd64` 上,修补器(`patcher_amd64_xplat.go`)将 2 字节的 `syscall`(`0f 05`)指令替换为 5 字节的 `jmp rel32` 跳转(当 5 字节窗口不可用时则使用 `CC 90` 断点陷阱),跳入一个快速路径岛。在 `arm64` 上,`svc #0` 被修补为指向岛槽位的 `B` 分支。该岛将寄存器写入共享的 `winSysmsg` 页面,并通过 `WaitOnAddress` 与 `WakeByAddressSingle` 与 Sentry 同步;而未修补的陷阱与页错误则会停在 Windows 调试对象上(`WaitForDebugEventEx`)。

### 4. Linux 用户态引擎:`platform/systrap`

在 `linux/amd64` 与 `linux/arm64` 上,`systrap` 运行客户机线程(`sysmsg`),它通过 `SECCOMP_RET_TRAP`(`SIGSYS`)捕获客户机系统调用,并将 `syscall` 指令位置二进制修补为共享内存蹦床(`sysmsg`),该蹦床通过 `SYS_FUTEX` 同步;同时,一个桩 `syscallThread`(`syscall_thread.go`)通过 `SECCOMP_RET_USER_NOTIF`(`_RUN_SECCOMP_LOOP`)执行地址空间操作(`mmap`、`munmap`、`clone`)。当 `gvx` 运行在外层 `gVisor`(`runsc`)沙箱中,且唯一的 `SECCOMP_FILTER_FLAG_NEW_LISTENER` 槽位返回 `EBUSY` 时,`gvx` 会自动将 `syscallThread` 切换为共享内存 futex 循环(`_RUN_SYSCALL_LOOP`),详见[本系列第三部分](/blog/posts/docker-in-docker-inside-gvisor-with-gvx-z48916d03)。

---

## `gvx` 如何将客户机 Linux 系统调用映射到虚拟子系统与宿主句柄

一旦平台引擎捕获客户机的 `SYSCALL`(`amd64`)或 `SVC #0`(`arm64`)指令,并将客户机寄存器写入 4 KB 共享页面(`CommPage`、`sysmsg` 或 `winSysmsg`),控制权便返回到 Go `Sentry` 任务协程(`pkg/sentry/kernel/task_syscall.go`)。`Task.doSyscall()` 并不将 Linux 系统调用转发给底层宿主操作系统内核,而是保存原始寄存器状态(`SyscallSaveOrig()`),评估客户机内的 BPF `seccomp` 过滤器与 `ptrace` 停止点,然后通过 `linux.AMD64` 或 `linux.ARM64`(`pkg/sentry/syscalls/linux/linux64.go`,覆盖约 370 个 Linux 系统调用)分派系统调用号,将其送入四个内部领域:

![gvx 如何将客户机 Linux 系统调用映射到子系统与宿主操作系统](/assets/images/gvx/gvx-syscall-mapping.png)

| 系统调用领域 | 代表性客户机 Linux 系统调用 | 内部 `gvx` 子系统(`pkg/sentry`) | 宿主操作系统边界交互(`hostlockdown` 之后) |
| :--- | :--- | :--- | :--- |
| **A. 进程、命名空间、挂载与同步** | `clone`、`execve`、`unshare`、`setns`、`mount`、`umount2`、`pivot_root`、`setuid`、`capset`、`futex`、`epoll_wait`、`pipe2` | `kernel.Task`、虚拟凭据、futex 表,以及内存中的 `VFS2`(`procfs`、`sysfs`、`cgroupfs` v2、`tmpfs`、`overlayfs`) | **零宿主系统调用**(100% 为内存中的 Go 状态;对宿主操作系统完全不可见) |
| **B. 虚拟内存(`MM`)** | `mmap`、`munmap`、`mremap`、`mprotect`、`brk`、`madvise`、`memfd_create` | `mm.MemoryManager`(4 KB VMA 树)与 `pgalloc.MemoryFile` | 通过 `WHvMapGpaRange`、`hv_vm_map` + `TTBR0_EL1` 或桩 `mmap`/`mprotect` 进行双向 4 KB 页表更新 |
| **C. 文件与存储(`VFS2`)** | `openat`、`pread64`、`pwrite64`、`getdents64`、`statx`、`renameat2`、`setxattr`、`mknod`、`inotify_add_watch` | `vfs.VirtualFilesystem` 挂载表路由器(`fsimpl/erofs`、`fsimpl/ext4lite`、`fsimpl/hostfs` v2) | 对 1 个固定的 `.erofs` FD 或 1 个 `.ext4` FD 进行双向 `pread`/`pwrite`,或在 `*os.Root` 上进行受限的相对路径遍历 |
| **D. 网络与 Netfilter** | `socket`、`bind`、`listen`、`connect`、`sendmsg`、`recvmsg`、`netlink`、`setsockopt(IPT_SO_SET_REPLACE)` | 用户态 `pkg/tcpip`(`lo`、`eth0`、`docker0`、`veth`、`vxlan`、`wireguard`、`ipset`、`iptables-legacy`) | 沙箱内部流量不使用**任何宿主系统调用**;外部流量映射到 `net.Dial`/`net.Listen`(`-net=nat`)或 1 个 UDP 套接字(`-net=wg`) |

当处理函数返回 `(rval, ctrl, err)` 时,`Task.doSyscallInvoke()` 会将任何错误转换为负的 Linux `errno`(`-ExtractErrno(err, sysno)`),将返回值写回共享寄存器页面(`amd64` 上为 `RAX`,`arm64` 上为 `X0`),然后恢复客户机执行。

---

## 存储(`VFS2`)与双轨用户态网络(`netstack`)

所有客户机文件与网络操作都在 Go `Sentry` 内部通过三个 `VFS2` 文件系统以及两条 `pkg/netbackend` 轨道来处理:

![gvx VFS2 存储引擎与双轨 Netstack](/assets/images/gvx/gvx-storage-netstack.png)

| 存储引擎               | 在 `gvx` 中的挂载角色                             | 后端宿主资源                             | 运行时宿主 `openat` / `NtCreateFile` 调用次数 | Windows Defender(`WdFilter.sys`)每文件扫描次数 |
| :--------------------- | :----------------------------------------------- | :------------------------------------- | -----------------------------------------------: | -----------------------------------------------: |
| **`fsimpl/erofs`**     | 只读 OCI 镜像下层                                | 每层单个 `<sha256>.erofs` 文件          |                                              `0` |                                              `0` |
| **`fsimpl/ext4lite`**  | 命名卷(`-v name:/path`)                         | 每个卷单个 `<name>.ext4` 文件           |                                              `0` |                                              `0` |
| **`fsimpl/hostfs` v2** | 宿主工作区(`-workspace`、`-v /host:/guest`)     | 固定的 `*os.Root` 宿主目录              |                     每次未缓存打开 `O(depth)` 次 |                          每次关闭宿主文件 `1` 次 |

### 为什么 `ext4lite` 对 Docker、数据库和包管理器很重要

将数以万计的容器文件直接解压或写入 Windows NTFS 或 macOS APFS,会触发杀毒小过滤器驱动的扫描(`WdFilter.sys`)、大小写敏感性冲突以及缺失的 POSIX 扩展属性。

`pkg/sentry/fsimpl/ext4lite` 在一个预先打开的宿主文件中实现了纯 Go 的 `ext4` 文件系统(Windows 上为 `%LOCALAPPDATA%\gvx\cache\volumes\<name>.ext4`,macOS 上为 `~/Library/Caches/gvx/volumes/<name>.ext4`,Linux 上为 `~/.cache/gvx/volumes/<name>.ext4`),并具备以下特性:

- **4 KiB 块与 16 MiB 动态块组(`meta_bg`)**:无需稀疏文件分配停顿即可动态增长(Linux 上使用 `fallocate`,macOS 上使用 `fcntl(F_PREALLOCATE)`,Windows 上使用 `FileAllocationInfo`),并在干净卸载时压缩未使用的尾部块组。
- **JBD2 有序模式日志(inode 8)**:通过 CRC-32 校验和与撤销块实现原子事务提交,并提供 `fsckLite` 恢复机制。
- **`overlayfs` 上层支持**:实现 `trusted.overlay.*` 与 `security.capability` 扩展属性、字符设备白出(`0:0`)、`d_type` 目录项报告以及 `RENAME_EXCHANGE`。

---

## 实践工作负载:Docker 引擎、Kubernetes(`k3s` + `Calico`)与 Go SDK

### 安装 `rungv`:经 SLSA 证明的预构建发布版或 `go install`

你可以从 [GitHub Releases](https://github.com/gfire-sigs/gvx/releases) 下载适用于 `linux/amd64`、`linux/arm64`、`darwin/arm64`、`windows/amd64` 或 `windows/arm64` 的预构建 `rungv` 压缩包(每个压缩包均由 SLSA 发布流水线构建,并附带 Sigstore 溯源包与 `SHA256SUMS`),也可以使用 `go install` 直接从源码安装:

```sh
# 方案 A:下载预构建发布版并验证 SLSA 构建溯源
gh attestation verify rungv-v0.2.0-linux-amd64.tar.gz --repo gfire-sigs/gvx \
  --signer-workflow gfire-sigs/gvx/.github/workflows/release.yml \
  --source-ref refs/tags/v0.2.0 --deny-self-hosted-runners
shasum -a 256 -c SHA256SUMS --ignore-missing
tar -xzf rungv-v0.2.0-linux-amd64.tar.gz

# 方案 B:通过 Go 直接安装(Linux / Windows)
CGO_ENABLED=0 go install z.gfire.dev/gvx/cmd/rungv@latest
```

> 在 macOS(`darwin/arm64`)上,预构建发布压缩包已使用 `com.apple.security.hypervisor` 权限进行签名(`xattr -d com.apple.quarantine rungv` 可清除浏览器隔离属性)。如果你在 macOS 上通过 `go install` 或 `go build` 在本地构建,请使用 `codesign --sign - --entitlements entitlements.plist --force $(which rungv)` 对二进制文件进行签名。

### 1. 运行无特权 Docker 引擎(`docker:dind`)

按照 [`docs/docker-engine.md`](https://github.com/gfire-sigs/gvx/blob/main/docs/docker-engine.md) 的说明,你可以在 Windows、macOS 或 Linux 上的 `rungv` 中运行未经修改的 `docker:dind` 容器,并使用宿主机的 `docker` CLI 对其进行控制:

```sh
# 1. 为转发的 Unix 套接字创建宿主目录
mkdir -p ~/.gvx

# 2. 在带有 ext4lite 卷的 gvx 沙箱中启动 docker:dind
rungv run \
  -p unix:~/.gvx/docker.sock:2375 \
  -p 127.0.0.1:9000-9019:9000-9019 \
  -v gvx-docker:/var/lib/docker \
  -e DOCKER_TLS_CERTDIR= \
  docker:dind --tls=false
```

在第二个终端中,将宿主机的 `docker` CLI 指向沙箱套接字:

```sh
docker context create gvx --docker host=unix://$HOME/.gvx/docker.sock
docker context use gvx

docker info --format '{{.Driver}} {{.OperatingSystem}}'
# 输出:overlayfs Alpine Linux ... (containerized)

docker build -t myapp ./app
docker compose up -d
```

### 2. 在无特权沙箱中运行 Kubernetes(`k3s` + `Calico`)

由于 `gvx` 实现了 `/dev/kmsg`、cgroup v2(`memory.max` 的 OOM 终止器与 `cpu.max` 带宽限流)、`/proc/diskstats`、`wireguard` 与 `vxlan` netlink 设备、`ipset`,以及 `iptables-legacy`(`addrtype`、`conntrack`、`statistic`、`recent`),一个未经修改的 `rancher/k3s` 镜像可以在单个 `rungv` 进程内达到 `Ready` 状态:

```sh
mkdir -p ~/.gvx/k3s

rungv run \
  -e IPTABLES_MODE=legacy \
  -p 127.0.0.1:6443:6443 \
  -p 127.0.0.1:30000-30019:30000-30019 \
  -v k3s:/var/lib/rancher \
  --mount type=bind,src=$HOME/.gvx/k3s,dst=/output \
  rancher/k3s:v1.31.4-k3s1 server \
  --disable=traefik,servicelb \
  --write-kubeconfig=/output/kubeconfig.yaml \
  --write-kubeconfig-mode=644
```

你还可以通过 WireGuard 将多个 `gvx` 节点加入集群(`flannel-backend=wireguard-native` 或 `wg0` 网格),并配合嵌入式 `etcd` 实现高可用;或者运行 **Calico**(使用 `FELIX_IPTABLESBACKEND=Legacy` 的 `calico-vxlan.yaml`),从而完全在用户态中执行 Kubernetes `NetworkPolicy`。

### 3. 通过 `z.gfire.dev/gvx/pkg/gvx` 在 Go 中嵌入 Linux 容器

Go 应用程序可以直接嵌入 Linux 容器,无需外部容器守护进程:

```go
package main

import (
	"context"
	"fmt"
	"os"

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

func main() {
	ctx := context.Background()
	sb, err := gvx.New(ctx, gvx.Config{
		Platform:      gvx.PlatformAuto,
		NetworkMode:   gvx.NetModeNAT,
		OCIImage:      "alpine:3.20",
		WorkspacePath: "./repo",
		Argv:          []string{"/usr/bin/git", "status"},
		Stdout:        os.Stdout,
		Stderr:        os.Stderr,
	})
	if err != nil {
		fmt.Fprintf(os.Stderr, "init sandbox: %v\n", err)
		os.Exit(1)
	}
	defer sb.Close()

	exitCode, err := sb.Run(ctx)
	if err != nil {
		fmt.Fprintf(os.Stderr, "run sandbox: %v\n", err)
		os.Exit(1)
	}
	os.Exit(exitCode)
}
```

---

## 本系列的延伸阅读

1. **[gvx:在一个无特权进程中运行 Linux 容器、Docker 与 Kubernetes](/blog/posts/gvx-cross-platform-linux-containers-z48916d01)**(本文)
2. **[无需 root 或用户命名空间运行 Docker、docker build 与 docker compose](/blog/posts/run-docker-without-root-or-userns-gvx-z48916d02)**:如何在 `sudo`、`unshare(CLONE_NEWUSER)`、`mount(2)` 与 `/dev/fuse` 均被阻止的受限沙箱中引导 `docker:dind`。
3. **[在 gVisor 与嵌套沙箱中借助 gvx 运行 Docker-in-Docker](/blog/posts/docker-in-docker-inside-gvisor-with-gvx-z48916d03)**:为什么在 `gVisor` 内执行 `apt-get install docker.io` 与嵌套 `runsc` 会因五道内核障碍而失败,以及 `gvx` 如何借助共享内存 futex 循环运行嵌套的 `docker:dind`。
4. **[借助 gvx 为 AI 智能体强化学习推演提供快速隔离的 Docker 环境](/blog/posts/fast-docker-environments-for-agent-rl-rollouts-z48916d04)**:利用数百个并行的无特权 `docker:dind` 实例以及 100 毫秒级写时复制 `.ext4` 卷重置,扩展 `SWE-bench` 与智能体强化学习推演。
