gVisor 与 containerd 快速入门:使用 containerd-shim-runsc-v1 运行沙箱容器
【免费下载链接】gvisorApplication Kernel for Containers项目地址: https://gitcode.com/GitHub_Trending/gv/gvisor
本文是 gVisor 在 containerd 上的官方快速入门指南(对应仓库 g3doc/user_guide/containerd/quick_start.md),完整介绍如何通过 containerd 的 runtime handler 机制接入containerd-shim-runsc-v1,让容器运行在 gVisor 用户态内核沙箱中。读完本文,你将掌握:containerd 运行时配置的完整写法、ctr与crictl两种运行沙箱容器的方式、dmesg验证沙箱是否生效的方法,以及为 Kubernetes 配置 gVisor RuntimeClass 的步骤;同时结合仓库源码,理解 shim v2 的底层工作原理与进阶调参手段。
⚠️注意:如果你使用
kubeadm搭建 Kubernetes 集群,可能会遇到运行时处理程序(runtime handler)相关的问题,详见 FAQ 中 "RuntimeHandler not supported" 一节。GKE Sandbox 与本套配置非常相似,唯一的差异在于 platform 配置。
一、前置要求
- runsc 与 containerd-shim-runsc-v1:gVisor 的运行时二进制与 containerd shim,参见 安装指南。
- containerd:官方安装方式可参考 containerd 项目文档,最低支持版本为 1.3.9 或 1.4.3(shim v2 机制自 containerd 1.3 起可用,见 shim/README.md)。
1.1 安装 runsc 与 containerd shim
gVisor 的发布产物同时包含runsc、containerd-shim-runsc-v1以及gvisor-bin/目录(内含 runsc 运行期执行的 sidecar 二进制)。最简单的安装方式是通过官方 apt 仓库:
sudo apt-get update && \ sudo apt-get install -y apt-transport-https ca-certificates curl gnupg curl -fsSL https://gvisor.dev/archive.key | sudo gpg --dearmor -o /usr/share/keyrings/gvisor-archive-keyring.gpg echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/gvisor-archive-keyring.gpg] https://storage.googleapis.com/gvisor/releases release main" | sudo tee /etc/apt/sources.list.d/gvisor.list > /dev/null sudo apt-get update && sudo apt-get install -y runsc也可以下载最新 release 的压缩包手动安装:
( set -e ARCH=$(uname -m) URL=https://storage.googleapis.com/gvisor/releases/release/latest/${ARCH} wget ${URL}/gvisor.tar.zstd ${URL}/gvisor.tar.zstd.sha512 sha512sum -c gvisor.tar.zstd.sha512 sudo tar --zstd -xf gvisor.tar.zstd -C /usr/local/bin rm -f gvisor.tar.zstd gvisor.tar.zstd.sha512 )注意:
runsc会查找位于自身二进制旁边的gvisor-bin/目录,移动runsc时必须把gvisor-bin/一起移动;且runsc可能以非特权用户身份重新执行自身以增强安全性,建议放在所有用户可读可执行的/usr/local/bin下。
二、配置 containerd
编辑/etc/containerd/config.toml,注册 runsc 为 CRI 的运行时处理程序。请确保containerd-shim-runsc-v1位于${PATH}中,或与containerd二进制位于同一目录:
cat <<EOF | sudo tee /etc/containerd/config.toml version = 2 [plugins."io.containerd.runtime.v1.linux"] shim_debug = true [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc] runtime_type = "io.containerd.runc.v2" [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runsc] runtime_type = "io.containerd.runsc.v1" EOF配置要点说明:
version = 2:containerd 配置文件版本头。若使用 containerd 2.x,可考虑改用version = 3(两者差异可参考 containerd 官方 PLUGINS 文档中的 version header 说明)。[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runsc]:向 CRI 插件注册名为runsc的运行时处理程序。runtime_type = "io.containerd.runsc.v1":指定该运行时使用 gVisor 提供的 shim v2。这一名称与 shim 内部的注册标识一致——仓库中 shim/v1/cli/cli.go 通过shim.NewShimManager("io.containerd.runsc.v1")注册管理器,pkg/shim/v1/manager.go 中的NewShimManager完成插件注册。shim_debug = true:让 containerd 将 shim 日志转发到自身日志(见下文"调试")。
2.1 安装 CNI 插件
后续步骤通常需要 CNI 插件支持(例如沙箱的网络命名空间配置)。快速上手时,用默认设置安装即可:
git clone --depth=1 -b {CONTAINERD_VERSION} https://github.com/containerd/containerd.git cd containerd && ./script/setup/install-cni其中{CONTAINERD_VERSION}请替换为你使用的 containerd 版本标签。
2.2 重启 containerd
sudo systemctl restart containerd三、通过 ctr 运行容器
ctr是 containerd 自带的 CLI,随每次 containerd 发布提供,直接与 containerd 守护进程通信。
3.1 拉取并运行容器
sudo ctr image pull docker.io/library/hello-world:latest sudo ctr run --runtime io.containerd.runsc.v1 -t --rm docker.io/library/hello-world:latest hello-world--runtime io.containerd.runsc.v1是关键参数,它告诉 containerd 使用 gVisor 的 shim 创建容器,而非默认的 runc。
3.2 验证容器确实运行在 gVisor 中
gVisor 沙箱会伪造一个"内核启动"过程。在沙箱内执行dmesg,如果看到 gVisor 的启动日志,即说明容器运行在 gVisor 之上:
$ sudo ctr image pull docker.io/library/busybox:latest $ sudo ctr run --runtime io.containerd.runsc.v1 -t --rm docker.io/library/busybox:latest gvisord dmesg [ 0.000000] Starting gVisor... [ 0.445958] Forking spaghetti code... [ 0.794963] Feeding the init monster... [ 0.842573] Synthesizing system calls... [ 0.985066] Generating random numbers by fair dice roll... [ 1.444465] Mounting deweydecimalfs... [ 1.546130] Waiting for children... [ 1.689078] Searching for socket adapter... [ 2.026282] Accelerating teletypewriter to 9600 baud... [ 2.274752] Creating process schedule... [ 2.498083] Reticulating splines... [ 2.675603] Setting up VFS... [ 2.750186] Setting up FUSE... [ 2.789133] Ready!说明:原文档该示例中运行时参数写作
io.containerd.run.runsc.v1,这是笔误,实际应为io.containerd.runsc.v1(与ctr run一节保持一致)。
四、通过 crictl 运行容器
crictl面向 CRI 兼容的容器运行时(如 Kubernetes 节点上的 kubelet),是另一种更贴近生产的使用方式。
4.1 安装 crictl
下载并安装crictl二进制:
{ wget https://github.com/kubernetes-sigs/cri-tools/releases/download/v1.13.0/crictl-v1.13.0-linux-amd64.tar.gz tar xf crictl-v1.13.0-linux-amd64.tar.gz sudo mv crictl /usr/local/bin }编写crictl配置文件,指定 containerd 的 CRI socket:
cat <<EOF | sudo tee /etc/crictl.yaml runtime-endpoint: unix:///run/containerd/containerd.sock EOF4.2 在 gVisor 中创建 nginx 沙箱
先拉取 nginx 镜像:
sudo crictl pull nginx创建沙箱(Pod 级)请求文件:
cat <<EOF | tee sandbox.json { "metadata": { "name": "nginx-sandbox", "namespace": "default", "attempt": 1, "uid": "hdishd83djaidwnduwk28bcsb" }, "linux": { }, "log_directory": "/tmp" } EOF使用runsc运行时创建沙箱:
SANDBOX_ID=$(sudo crictl runp --runtime runsc sandbox.json)注意--runtime runsc必须与 第二节 中注册的运行时处理程序名称一致。
4.3 在沙箱中运行 nginx 容器
创建容器请求文件:
cat <<EOF | tee container.json { "metadata": { "name": "nginx" }, "image":{ "image": "nginx" }, "log_path":"nginx.0.log", "linux": { } } EOF创建并启动容器:
CONTAINER_ID=$(sudo crictl create ${SANDBOX_ID} container.json sandbox.json) sudo crictl start ${CONTAINER_ID}4.4 验证容器
分别检查沙箱与容器:
sudo crictl inspectp ${SANDBOX_ID} sudo crictl inspect ${CONTAINER_ID}在容器内执行dmesg并过滤 gVisor 关键字,确认运行在 gVisor 中:
sudo crictl exec ${CONTAINER_ID} dmesg | grep -i gvisor五、在 Kubernetes 中启用 gVisor RuntimeClass
对于 Kubernetes 集群,可以通过 RuntimeClass 将 Pod 调度到 gVisor 运行时上。
安装 gVisor 的 RuntimeClass(handler 名称runsc对应 containerd 中注册的运行时):
cat <<EOF | kubectl apply -f - apiVersion: node.k8s.io/v1 kind: RuntimeClass metadata: name: gvisor handler: runsc EOF创建使用该 RuntimeClass 的 Pod:
cat <<EOF | kubectl apply -f - apiVersion: v1 kind: Pod metadata: name: nginx-gvisor spec: runtimeClassName: gvisor containers: - name: nginx image: nginx EOF确认 Pod 正常运行:
kubectl get pod nginx-gvisor -o wide5.1 常见问题:kubeadm 集群中的 RuntimeHandler 报错
如果使用 kubeadm 创建集群,可能出现RuntimeHandler "runsc" not supported之类的错误。根因通常是 kubelet 通过 Docker 套接字(--cri-socket=/var/run/dockershim.sock)而非 containerd 套接字与 CRI 通信,导致 containerd 中注册的runsc运行时对 kubelet 不可见,详见 FAQ。解决办法是:
- 重建集群时,为 kubeadm 显式指定
--cri-socket=/var/run/containerd/containerd.sock; - 对已有集群,编辑
/var/lib/kubelet/kubeadm-flags.env修正 cri-socket 后重启 kubelet。
六、深入:containerd-shim-runsc-v1 是如何工作的
从源码结构看,gVisor 与 containerd 的集成完全基于 containerd 的shim v2(Task API)规范:
- 入口二进制 shim/main.go 注释明确说明其身份:
containerd-shim-runsc-v1是 "the v2 containerd shim (implementing the formal v1 API)"; - shim/v1/cli/cli.go 中通过
containerdshim.Run(...)启动 shim 事件循环,并以shim.NewShimManager("io.containerd.runsc.v1")注册管理器——这就是配置文件中runtime_type = "io.containerd.runsc.v1"的来历; - 管理器内部(pkg/shim/v1/manager.go)负责将 containerd 下发的 OCI 任务转化为对
runsc的调用,由runsc拉起真正的 gVisor 沙箱(Sentry 用户态内核)。
工作链路可概括为:containerd(CRI)→containerd-shim-runsc-v1(shim v2)→runsc(创建并管理沙箱)→ gVisor Sentry(在沙箱内执行容器进程)。这也解释了为什么配置时仅需把runtime_type指向io.containerd.runsc.v1,containerd 就会自动调用同目录下的containerd-shim-runsc-v1二进制。
七、进阶:shim 配置与调试
在 containerd 1.3+ 中,可以通过ConfigPath为 runsc 运行时指定一个 shim 配置文件,对 shim 自身与 runsc 进行细粒度调参,完整可配置项可参考 pkg/shim/v1/runsc/options.go。
7.1 编写 shim 配置文件
cat <<EOF | sudo tee /etc/containerd/runsc.toml option = "value" [runsc_config] flag = "value" EOF- 顶层
option = "value"对应 options.go 中Options结构体定义的 shim 选项,例如shim_cgroup(shim 所在 cgroup)、binary_name(runsc 二进制路径)、log_path、log_level等; [runsc_config]下的flag = "value"会被转换为--flag="value"追加到 runsc 命令行,可用runsc flags查看全部可用 flag。
然后让 containerd 把该配置传给 shim:
cat <<EOF | sudo tee /etc/containerd/config.toml version = 2 [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc] runtime_type = "io.containerd.runc.v2" [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runsc] runtime_type = "io.containerd.runsc.v1" [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runsc.options] TypeUrl = "io.containerd.runsc.v1.options" ConfigPath = "/etc/containerd/runsc.toml" EOF修改后重启 containerd 生效。此外,shim 也会按固定候选路径(如/etc/containerd/runsc/config.toml)自动探测全局配置文件,见 options.go 中GetRuntimeOptions的实现。
7.2 启用调试日志
在 containerd 配置中开启shim_debug后,shim 日志会转发到 containerd 日志,可用sudo journalctl -u containerd查看;如需更细粒度,可再加[debug] level = "debug"。但这样难以区分 containerd 与 shim 的消息,更推荐在 shim 配置文件中单独指定日志位置:
cat <<EOF | sudo tee /etc/containerd/runsc.toml log_path = "/var/log/runsc/%ID%/shim.log" log_level = "debug" [runsc_config] debug = "true" debug-log = "/var/log/runsc/%ID%/gvisor.%COMMAND%.log" EOFlog_path:shim 日志目录,其中%ID%会被替换为容器 ID;log_level:日志级别,调试场景通常设为debug;debug-log:gVisor 自身的调试日志路径,shim 会依次把%ID%替换为容器 ID、%COMMAND%替换为 runsc 子命令(如run、boot)。
更多调试手段参见 调试指南。
八、生产环境与下一步
- 本套配置在 GKE Sandbox 上已开箱即用,是快速体验 gVisor 的便捷途径;
- 部署到生产环境前,务必先阅读 生产指南 了解隔离模型、资源配置与运维建议;
- 涉及共享卷、NVIDIA GPU、多容器 Pod 等场景时,可进一步参考 containerd 高级配置(含
nvidia-container-runtime接入与跨容器 inotify 的 mount hints 注解)以及 GPU 使用指南。
【免费下载链接】gvisorApplication Kernel for Containers项目地址: https://gitcode.com/GitHub_Trending/gv/gvisor
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考