gVisor GPU 支持完全指南:nvproxy 架构、环境配置与安全模型
gVisor GPU 支持完全指南nvproxy 架构、环境配置与安全模型【免费下载链接】gvisorApplication Kernel for Containers项目地址: https://gitcode.com/GitHub_Trending/gv/gvisorgVisor 通过内置的nvproxy代理驱动在沙箱内为 AI/ML 与 GPU 工作负载提供透明、几乎零开销的 NVIDIA GPU 访问能力。本文基于 gVisor 官方用户指南系统讲解 nvproxy 的工作原理、四种主流容器环境NVIDIA Container Runtime、CDI、Docker、Kubernetes 设备插件的接入方式、驱动版本与设备文件的兼容边界以及 GPU 场景下的安全模型与调试手段帮助你安全地在 gVisor 沙箱中运行 CUDA、Vulkan、NVENC/NVDEC 等真实 GPU 应用。nvproxy沙箱内的 NVIDIA 驱动代理gVisor 对 AI/ML 应用或其他 GPU 工作负载增加了一层安全隔离同时带来可忽略不计的额外开销。把这类应用运行在沙箱环境中可以将宿主机与 AI 代码中潜在的安全漏洞隔离开来这对于处理敏感数据或部署不受信任的 AI 工作负载至关重要。gVisor 支持在选定的 NVIDIA 开源驱动版本上运行大多数 CUDA 应用。其核心机制是在沙箱内部实现一个代理驱动即nvproxy它代理应用与宿主机 NVIDIA 驱动之间的交互向沙箱应用暴露 NVIDIA GPU 专属设备。GPU 应用无需修改即可在沙箱内运行并透明地与这些设备交互。从实现上看nvproxy包在 gVisor 沙箱内实现了模拟 NVIDIA 设备文件如/dev/nvidiactl、/dev/nvidia-uvm、/dev/nvidia#的虚拟字符设备见 pkg/sentry/devices/nvproxy/README.md。当沙箱内应用打开并交互这些设备时nvproxy 拦截其ioctl与mmap系统调用在必要转换后转发给宿主机真正的 NVIDIA 驱动应用其余的系统调用仍全部由 gVisor Sentry 处理从而在保持强安全边界的同时提供 GPU 算力。启动开关runsc必须指定--nvproxy标志才能启用 GPU 支持。相关配置定义于 runsc/config/config.go 与 runsc/config/flags.gorunsc 标志默认值说明--nvproxyfalse启用 NVIDIA GPU 支持旧式标志若 OCI spec 中存在 NVIDIA 设备则自动启用--nvproxy-dockerfalse已废弃注入nvidia-container-runtime-hook作为 prestart hook建议改用 nvidia-container-runtime 或docker run --gpus--nvproxy-driver-version空自动探测指定使用的 NVIDIA 驱动 ABI 版本特殊值latest表示使用最新支持的 ABI--nvproxy-allow-unsupported-driverfalse允许以未支持的驱动版本初始化 nvproxy--nvproxy-allowed-driver-capabilitiesutility,compute允许容器请求的驱动能力逗号分隔all表示全部支持的能力支持的运行环境gVisor 支持在以下环境中使用 GPU。NVIDIA Container Runtimenvidia-container-runtime作为 NVIDIA GPU 容器栈的一部分发布。它本身只是一个 shim把所有命令委托给配置的低层运行时默认是runc。要使用 gVisor在/etc/nvidia-container-runtime/config.toml中通过runtimes选项把低层运行时指定为runsc然后使用nvidia-container-runtime运行 GPU 容器。runtimes选项允许指定可执行文件路径或能在$PATH中搜索到的可执行文件名。如需为runsc附带特定标志可使用如下包装脚本# !/bin/bash exec /path/to/runsc --nvproxy other runsc flags $注意两点gVisor 目前只支持 NVIDIA 的legacy 模式其替代方案csv 模式尚未支持。nvidia-container-runtimeshim 是一条遗留的GPU 注入路径。在高层容器运行时已理解 CDI 的环境中如 containerd ≥ 1.7 或 CRI-OGPU 可以通过 CDI spec 声明runsc可被直接调用无需 shim 或其prestarthook。宿主机上仍需安装nvidia-container-toolkit包因为 CDI spec 引用了它的nvidia-ctkhook 二进制但不再需要 runtimeshim。CDI容器设备接口Container Device Interface (CDI) 是一种厂商中立的规范用于描述容器运行时如何向容器注入设备。与依赖 runtime shim 不同宿主机发布 CDI spec 文件通常位于/etc/cdi/或/var/run/cdi/描述暴露设备所需的设备节点、绑定挂载、环境变量与 hooks。支持 CDI 的容器运行时containerd ≥ 1.7、CRI-O、较新版本的 Docker会在调用低层运行时之前把这些 spec 合并进 OCI spec。runsc完全兼容 CDI尤其会执行 NVIDIA CDI spec 发出的createContainerhooks这使得nvidia-ctk hook的调用客户端库的符号链接创建、update-ldcache等能够在沙箱内正确运行。无论是 NVIDIA 的k8s-device-plugin以DEVICE_LIST_STRATEGYcdi-cri模式运行还是通过nvidia-ctk cdi generate静态生成的 CDI spec均受支持。使用 CDI 时不需要nvidia-container-runtimeshimrunsc直接作为低层运行时被调用高层运行时负责应用 CDI spec。Dockernvidia-container-runtime的 legacy 模式与 docker CLI 的--gpus标志直接兼容因此使用 Docker 时runsc可以直接使用无需经由nvidia-container-runtime$ docker run --runtimerunsc --gpusall --rm -it nvcr.io/nvidia/k8s/cuda-sample:vectoradd-cuda11.7.1-ubi8 [Vector addition of 50000 elements] Copy input data from the host memory to the CUDA device CUDA kernel launch with 196 blocks of 256 threads Copy output data from the CUDA device to the host memory Test PASSED DoneNVIDIAk8s-device-pluginnvproxy与 NVIDIA 的k8s-device-plugin完全兼容包括将其配置为通过 CDI 宣告 GPU 的场景。GKE 设备插件GKE 使用了与 NVIDIA 不同的 GPU 容器栈。GKE 拥有自己的设备插件与k8s-device-plugin不同其修改容器 spec 的方式与上述方法不同nvproxy同样支持。兼容性gVisor 支持广泛的 CUDA 工作负载包括 PyTorch 以及 LLM 等各类生成式模型也支持 Vulkan 和 NVENC/NVDEC 工作负载并持续进行测试以保证功能稳健。跨不同 GPU 工作负载的真实使用帮助发现和解决nvproxy中的兼容性或性能问题。工作原理nvproxy是一个直通passthrough驱动将容器内应用发往 NVIDIA 设备的ioctl(2)调用直接转发给宿主机 NVIDIA 驱动。转发过程直截了当ioctl参数从应用地址空间复制到 sentry 地址空间然后发起宿主机ioctl系统调用。ioctl以最小干预直通nvproxy不模拟 NVIDIA 内核态驱动KMD逻辑因此 GPU 操作的开销极低GPU 密集型工作负载的性能影响可忽略不计。复杂性来源部分ioctl结构体中含有指针和文件描述符迫使nvproxy进行相应的翻译这就要求nvproxy了解 KMD 的 ABI即ioctl结构体的布局。而 NVIDIA KMD 不提供 ABI 稳定性保证ioctl定义可能随版本任意变化虽然 NVIDIA 安装器能保证 KMD 与用户态驱动UMD版本匹配但单个 gVisor 版本可能搭配多个 NVIDIA 驱动使用。因此nvproxy必须理解每个受支持驱动版本的 ABI内置了针对ioctl的版本化逻辑。从源码看nvproxy通过一张稀疏的版本树来建模 ABI 演进——见 pkg/sentry/devices/nvproxy/version.go 中的Init()。运行时它读取宿主驱动版本在版本树中找到对应节点再从根节点遍历到该节点沿途应用所有 ABI 变更最终组合出该版本完整的frontendIoctl、uvmIoctl、controlCmd与allocationClass处理器映射。由此nvproxy存在以下限制支持选定的 GPU 型号。支持选定的 NVIDIA 驱动版本。支持选定的 NVIDIA 驱动能力。支持选定的 NVIDIA 设备文件。支持每个设备文件上选定的ioctl。支持选定的平台。支持的 GPUgVisor 当前支持的 NVIDIA GPUT4基于 Turing 微架构A100与A10G基于 Ampere 微架构L4基于 Ada Lovelace 微架构H100基于 Hopper 微架构虽然非官方支持但基于上述相同微架构的其他 NVIDIA GPU 大概率也能工作包括RTX 3090Ampere和RTX 4090Ada Lovelace等消费级 GPU。因此如果你在上述微架构的某个 GPU即使是未支持的型号上遇到不兼容的工作负载该工作负载在官方支持的 GPU 上大概率也会以相同方式不兼容。请附带复现步骤提交 issue以便在官方支持的 GPU 上进行测试。驱动版本滚动支持窗口gVisor 将 NVIDIA 驱动版本分为三类支持的驱动Supported官方支持并通过认证的版本gVisor 对其持续运行测试开箱即用。不支持的驱动Unsupported在 gVisor 源码中定义但未完全测试的版本。默认情况下runsc在这些版本上会启动失败但可通过--nvproxy-allow-unsupported-driver标志放行若放行runsc会记录一条警告并尝试运行但不提供官方支持。未知的驱动UnknowngVisor 完全不知道的驱动版本源码中完全未定义。即使指定--nvproxy-allow-unsupported-driver也总会启动失败。由于测试基础设施可用的 GPU 有限持续测试和认证太多驱动不现实因此策略是尽量控制受支持驱动集合的规模。官方支持的驱动版本范围与 GKE 可用的驱动直接对齐GKE 引入新驱动nvproxy就相应扩展支持反之当驱动从 GKE 移除时nvproxy会收缩窗口以控制版本化复杂度、避免无限增长。查看某个runsc版本官方支持的驱动$ runsc nvproxy list-supported-drivers该子命令实现于 runsc/cmd/nvproxy/list_supported_drivers.go直接读取 nvproxy 的版本注册表。注意runsc的驱动版本是严格匹配的因为runsc无法假设驱动版本之间存在 ABI 兼容性。即使在宿主机驱动版本未知的情况下你也可以通过--nvproxy-driver-version强制runsc使用某个受支持的 ABI 版本但这不受官方支持并且运行旧驱动通常不安全因为许多驱动更新正是修复安全漏洞。支持的驱动能力容器 spec 中的NVIDIA_DRIVER_CAPABILITIES环境变量控制哪些驱动库/二进制文件会被挂载进容器。不同 GPU 工作负载需求各异Vulkan 需要graphics能力CUDA 需要computeNVENC/NVDEC 需要video。nvproxy支持以下驱动能力compute、utility、graphics和video。默认只允许compute和utility如需更多能力请用--nvproxy-allowed-driver-capabilities传入逗号分隔的能力列表。放行更多能力会扩大暴露给沙箱的宿主机驱动面因此应保守配置传入all会放行所有支持的能力。若容器设置NVIDIA_DRIVER_CAPABILITIESall则使用全部已允许的能力。从源码看能力模型定义在 pkg/sentry/devices/nvproxy/nvconf/caps.gocompute、display、graphics、ngx、utility、video、compat32对应 nvidia-container-toolkit 的能力此外还有不随all启用的特权能力fabric-imex-mgmt、profilingGPU 硬件性能计数器访问如 Nsight与rdmaGPUDirect RDMA把 GPU 内存导出为 dma-buf fd需要显式启用。DefaultDriverCapscompute|utility与默认 flag 一致AllContainerDriverCapscompute|utility|graphics|video|ngx则是all展开后的集合。支持的设备文件gVisor 只暴露/dev/nvidiactl、/dev/nvidia-uvm和/dev/nvidia#。以下 NVIDIA 设备文件不受支持/dev/nvidia-caps/*控制nvidia-capabilities主要用于多实例 GPUMIG。/dev/nvidia-drm接入 Linux 的 Direct Rendering ManagerDRM子系统。/dev/nvidia-modeset为nvidia-drm设备启用DRIVER_MODESET能力。支持的ioctl集合为降低多驱动版本间的维护开销受支持的 NVIDIA 设备ioctl集合被有意保持精简。该集合通过在 gVisor 中运行大量 GPU 工作负载生成并会随着nvproxy适配更多用例而持续演进。目前nvproxy聚焦于 compute、graphics 和 video 工作负载如 CUDA、Vulkan、NVENC/NVDEC。如果你的 GPU 计算负载在 gVisor 下失败可能是因为某些ioctl命令尚未实现。请在 issue 中描述你的用例若问题出在缺失的ioctl实现上调试日志 中会包含前缀为nvproxy: handler is undefined *的警告。关于如何运行ioctl_sniffer工具见下文调试章节。支持的平台所有 nvproxy 功能都支持 systrap 和 ptrace 平台。cudaMallocManaged()目前在 KVM 平台上因虚拟内存布局限制而不稳定issue #11436其余 nvproxy 功能在 KVM 平台上均受支持。调试 GPU 工作负载调试 GPU 工作负载时有几种方法第一步应使用 gVisor 的 ioctl_sniffer 工具如果 GPU 工作负载因 gVisor 中未实现的ioctl命令而失败该工具会列出具体是哪些命令。偶尔你可能需要深入 NVIDIA GPU 驱动本身。为此可以安装 OSS 驱动仓库并检出对应驱动版本DRIVER_VERSION550.54.15 git clone https://github.com/NVIDIA/open-gpu-kernel-modules.git cd open-gpu-kernel-modules git checkout tags/$DRIVER_VERSION对于printk()调试建议使用portDbgPrintf()相关讨论见 NVIDIA 仓库的 discussion #347打印可通过dmesg(1)查看。然后卸载现有 NVIDIA 驱动从本地源码构建内核模块并重新安装sudo /usr/bin/nvidia-uninstall make modules -j$(nproc) sudo make modules_install -j$(nproc) sudo insmod kernel-open/nvidia.ko sudo insmod kernel-open/nvidia-uvm.ko sudo insmod kernel-open/nvidia-drm.ko sudo insmod kernel-open/nvidia-modeset.ko # 使用 .run 文件安装用户态 NVIDIA GPU 驱动组件。 sudo sh NVIDIA-Linux-x86_64-$DRIVER_VERSION.run --no-kernel-modules宿主机配置在 gVisor 内下载大型模型时可能会因宿主机 VMA 耗尽而遇到应用段错误segmentation fault。作为规避手段可以把/proc/sys/vm/max_map_count设为一个较大的值echo 1000000 | sudo tee /proc/sys/vm/max_map_count或者直接给 runsc 传--host-settingsenforce标志。安全模型GPU 支持为 gVisor 带来了重要用例但用户需要理解沙箱内使用 GPU 的安全模型。简言之gVisor 会保护宿主机免受沙箱内应用的攻击但无论是否使用 gVisorNVIDIA 驱动更新都必须是任何安全计划的一部分。先简要回顾 gVisor 的安全模型gVisor 通过多层防御保护宿主机免受沙箱应用攻击。与本讨论最相关的两层是将应用系统调用重定向到 gVisor 沙箱以及在 gVisor 沙箱上使用 seccomp-bpf。gVisor 使用平台platform让宿主内核把系统调用重路由到沙箱进程即 sentry。Sentry 实现了一张系统调用表处理所有应用系统调用。Sentry可能在需要时向宿主内核发起系统调用以完成应用请求但它不会简单地把应用系统调用直接传给宿主内核。沙箱启动时seccomp 过滤器被应用到沙箱约束它可以向宿主内核发起的系统调用集合——即使沙箱被攻破也能阻断对大多数宿主内核漏洞的利用。例如 CVE-2022-0185 之所以被缓解是因为 gVisor 自己处理了使用命名空间和 capabilities 所需的系统调用应用使用的是 gVisor 的实现而非宿主内核的而一旦沙箱被攻破利用该漏洞所需的系统调用会被 seccomp 过滤器阻断。此外seccomp-bpf 过滤器可按参数名过滤从而能按ioctl(2)参数做细粒度白名单。ioctl(2)因实现复杂是所有内核中大量 bug 的来源。目前 gVisor 确实按参数对部分ioctl做了白名单如终端支持。例如 CVE-2024-21626 之所以被缓解是因为应用会使用 gVisor 的ioctl(2)实现对于被攻破的 sentry带所需参数的ioctl(2)调用不在 seccomp 过滤器白名单中从而阻断攻击者发起该调用。gVisor 同样缓解了设备驱动带来的类似漏洞如 CVE-2023-33107。nvproxy 的安全设计回顾nvproxy允许应用直接与 NVIDIA 驱动中受支持的 ioctl 交互。gVisor 的 seccomp 过滤器规则被修改为ioctl(2)调用只允许针对受支持的 ioctl见 pkg/sentry/devices/nvproxy/seccomp_filters.go 的frontendIoctlFilters等规则其中每个 ioctl 都通过seccomp.EqualTo/MaskedEqual精确匹配命令号并关联所需能力位。这些白名单规则与每个驱动版本对齐。这种方式与上文所述的终端支持 ioctl 白名单类似让 gVisor 在允许访问 GPU 的同时保留对宿主机的绝大部分防护上述所有 CVE 即使在使用 nvproxy 时仍然得到缓解。然而gVisor 在缓解 NVIDIA GPU 驱动自身漏洞方面的效果要弱得多因为gVisor 会把调用直通给内核模块处理。如果某个被直通的 GPUioctl读功能存在漏洞gVisor 同样会受影响如果漏洞位于未实现的功能中gVisor 会用 seccomp 过滤器阻断所需调用。此外gVisor 不会引入超出 NVIDIA 内核态驱动所配置范围之外的硬件级隔离例如不会校验 DMA 缓冲区。唯一的检查来自 seccomp-bpf 规则确保ioctl(2)调用发生在受支持且已白名单的 ioctl 上。因此无论是否使用 gVisor都务必及时更新 NVIDIA 驱动。查看 gVisor 最新支持的驱动可用你的 runsc 发行版执行$ runsc nvproxy list-supported-drivers也可以直接查看 nvproxy 版本注册源码或下载源码后运行$ make run TARGETSrunsc:runsc ARGSnvproxy list-supported-drivers既然不能防护所有东西为何还要用虽然 gVisor 无法防护所有NVIDIA 驱动漏洞但它确实防护了 Linux 中一大类通用漏洞。应用不只使用 GPU而是把 GPU 作为包含第三方库的更大应用的一部分。例如 Tensorflow 同样存在每个应用都会有的那类漏洞。以安全为优先来设计和实现应用是困难的而在新兴的 AI 领域安全往往让位于抢占市场速度同时也有大量服务允许用户在厂商基础设施上运行外部用户的代码。对于这些及其他用例gVisor 非常适合作为更大安全计划的一部分。小结与下一步启用 GPU 支持只需--nvproxyDocker 场景下docker run --runtimerunsc --gpusall即可运行未修改的 CUDA 应用。新环境优先走 CDIcontainerd ≥ 1.7、CRI-O可免去 legacy shimnvidia-container-toolkit包仍需保留。驱动版本是硬性匹配用runsc nvproxy list-supported-drivers确认官方支持范围非认证版本需--nvproxy-allow-unsupported-driver并自行承担风险。能力白名单默认compute,utility按需以--nvproxy-allowed-driver-capabilities谨慎扩增graphics/video或显式启用profiling/rdma等特权能力。失败排查先从 ioctl_sniffer 与带nvproxy: handler is undefined *前缀的调试日志入手。安全上牢记gVisor 阻断了通用内核攻击面但 NVIDIA 驱动的漏洞仍需靠及时升级驱动来封堵。如需继续深入可进一步阅读调试指南、平台选择、安全模型以及 nvproxy 核心实现 pkg/sentry/devices/nvproxy 与能力定义 nvconf/caps.go。【免费下载链接】gvisorApplication Kernel for Containers项目地址: https://gitcode.com/GitHub_Trending/gv/gvisor创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
上一篇/下一篇内容由系统自动关联
返回资讯列表 →