Kubernetes 上 AI 智能体沙箱的五层架构
The 5 essential layers of AI agent sandboxing on Kubernetes
在 Kubernetes 上运行 AI 智能体需要五层沙箱隔离:隔离层可选 runc、gVisor 或 Kata Containers,分别对应共享内核、受限内核接口和完整 VM 隔离;节点层需为所选运行时配置 containerd、KVM 与 guest kernel;访问层用 NetworkPolicy 默认拒绝出网、按任务发放短期凭证、只读挂载数据;请求层通过 RuntimeClass 让智能体 Pod 选用隔离运行时,并把 gVisor Sentry、Kata guest kernel 等额外开销计入调度与配额。
正文 · AI 翻译
一个被赋予自主运行代码权限的 AI 智能体,不可避免地会运行没有人审查过的代码。它可能安装由模型选择的软件包,运行几秒前才生成的脚本,并反复迭代,直到开发者的测试通过;这种风险仅限于一台笔记本电脑。对于一家公司而言,这种风险可能超出智能体自身的工作负载,波及部署在共享 Kubernetes 基础设施上的任何工作负载。集群配置可以限制容器的系统调用,但一个被允许的调用一旦触及内核漏洞,仍可能暴露底层节点。
随着智能体的激增,这一缺口已使工作负载隔离从一项小众的加固要求,转变为运行智能体的平台的一项基础需求。这正是我们在 Oracle Cloud Infrastructure(OCI)的用户请求我们帮助解决的问题。
正如云原生领域所预期的那样,社区已经开发出了解决方案。这些方案存在于五个层面:从 Pod 请求,向下到节点,再向上回到智能体的会话。平台团队可以组装这套技术栈,而不必重新发明它,而原生提供商的支持则简化了配置与维护。托管 Kubernetes 服务正是以这种方式提供沙箱化 Pod,但不同运行时对智能体工作负载的隔离方式各不相同。
以下各节将介绍各种选项及其权衡,然后展示这些组件如何组合在一起。
图 1. Pod 沙箱技术栈的五个层面,每一层都由开源组件构建。
1. 隔离层:选择你的风险边界
图 2. 同一个 Pod 在三种运行时下的情况,以及每种运行时围绕智能体代码划定的边界。
首先要在最小隔离、受限内核接口和完整 VM 隔离之间做出选择。每种选项都在安全性与性能之间有所权衡。
runc 通过共享宿主内核提供最高的性能。基于 Seccomp 的过滤限制了进程可以发起的系统调用。
gVisor 适用于受限内核接口已足够的用例。它的用户空间应用内核负责处理系统调用,而不是启动一个客户机 Linux 内核,因此工作负载通过一个小型接口访问宿主。gVisor 实现了 Linux 的一个子集,因此启动迅速且部署密度高。应用程序必须与 Sentry 实现的接口兼容,这可能会限制智能体的行为。
“一个带有硬件虚拟化的独立客户机内核,可以避免在系统调用模拟力有不逮时出现意外,从而不会意外地束缚住智能体。”
Kata Containers 适用于需要完整 VM 隔离的用例。每个 Pod 都在一个轻量级 VM 中运行,拥有自己的客户机内核,代价是 VM 启动开销和客户机内核内存。几乎所有能在容器中运行的东西都能在 Kata 中运行。智能体会用到包管理器、编译器、嵌套构建和无头浏览器。对于这类代码,带有硬件虚拟化的独立客户机内核可以避免在系统调用模拟力有不逮时出现意外,从而不会意外地束缚住智能体。
2. 节点:组装它并为其计入开销
图 3. 当 Pod 选择 Kata 运行时时,工作节点内部的情况。microVM 之外的一切都运行在宿主机上。
工作节点必须针对所选的沙箱化运行时进行配置。
runc 容器使用 Linux 命名空间来隔离进程、文件系统和网络视图,并使用 cgroups 来控制资源使用。它们共享宿主内核。节点需要通过 containerd 之类的运行时来配置 runc,但不需要客户机内核或虚拟机监控程序。
gVisor 在 Sentry 中处理工作负载的系统调用,同时由一个独立的 Gofer 进程来中介对宿主后端文件的访问。每个符合条件的节点都需要安装 gVisor 运行时并与 containerd 集成。
Kata Containers 将每个 Pod 放入一个轻量级 VM 中。工作负载的系统调用会发往客户机 Linux 内核,通过硬件支持的虚拟化与宿主隔离开来。这会增加开销,并且需要裸金属节点或支持嵌套虚拟化的 VM。在基于内核的虚拟机(KVM)环境中,节点需要 Kata 运行时 shim、受支持的虚拟机监控程序(VMM)、KVM 和硬件虚拟化,以及一个包含 kata-agent 的客户机内核和镜像。你必须维护该客户机镜像。一个内核 CVE 意味着要修补两个内核。对于需要灵活性和严格隔离的用例,这种负担可能是必要的。
3. 访问层:限制智能体的触及范围
沙箱保护宿主免受智能体的侵害,但无法保护它免受交给智能体的任何东西的侵害。内部的代码仍然可以删除可写的代码仓库、读取已挂载的密钥,或把数据发送到网络允许的任何地方。沙箱策略与沙箱本身同样重要:
网络:使用 NetworkPolicy 默认拒绝出网流量,然后仅允许任务所需的目的地,例如软件包镜像或模型端点。
凭证:发放限定于该任务的短期凭证,而不是将长期令牌挂载到 Pod 中。泄露的凭证会随任务一起过期。
存储:除非任务需要写入,否则以只读方式挂载代码仓库和数据,并为可写工作区提供其自己的卷。
远程工具:为 API 端点和 MCP 服务器提供它们自己的授权。它们运行在沙箱之外,因此沙箱的边界并不覆盖它们。
4. 请求:RuntimeClass
Kubernetes 允许用户保留标准 Pod API,同时让选定的 Pod(包括智能体)选择加入更隔离的运行时。RuntimeClass 让 Pod 选择其所需的安全与性能平衡,而不会给非智能体工作负载增加负担。
额外的隔离会影响资源消耗和 Pod 就绪状态。gVisor 的 Sentry 进程以及 Kata 的客户机内核、kata-agent 和 VMM 会增加内存和 CPU 开销。将实测开销添加到 RuntimeClass 中,以便 Kubernetes 调度器和资源配额将其计入。请测量您自己的工作负载,而不是照抄示例。
集群管理员创建与节点所配置运行时相匹配的 RuntimeClass。它告诉 Kubernetes 该运行时可用、Pod 如何选择它,以及它需要多少开销:
apiVersion: node.k8s.io/v1 kind: RuntimeClass metadata: name: kata handler: kata # 节点 containerd 配置中的运行时名称 overhead: podFixed: memory: "160Mi" # 示例值;请测量您自己的值 cpu: "250m" scheduling: nodeSelector: sandbox.example.com/kata: "true" Pod 通过其 spec 中的 runtimeClassName 字段选择加入: apiVersion: v1 kind: Pod metadata: name: agent-task spec: runtimeClassName: kata # 或 gvisor;省略则使用默认 runc containers: - name: workspace image: python:3.12
其他一切保持不变。不需要沙箱的工作负载省略该字段并使用默认运行时。一个集群可以将每个运行时暴露为单独的 RuntimeClass。
5. 会话层:Agent Sandbox
智能体的工作呈突发式。智能体运行代码,然后等待数分钟或数小时,以进行下一次与模型或人类的交互。保持 VM 运行可使其随时恢复工作,但会消耗容量。将其拆除可节省容量,但之后需要重建。热池提供了一种折中方案,但会预留容量。
“智能体的工作呈突发式。智能体运行代码,然后等待数分钟或数小时,以进行下一次与模型或人类的交互。”
由 Kubernetes SIG Apps 拥有的 Agent Sandbox 项目将这一选择标准化。其 Sandbox API 创建具有稳定身份和持久存储的有状态单 Pod 环境,并提供用于模板、声明和热池的扩展。它保持运行时无关,并将隔离交给所选的 RuntimeClass。
我们的方法:基于社区项目构建
社区项目帮助我们实现了目标:让用户使用他们已经熟悉的 Kubernetes 工具,在共享基础设施上运行不受信任的智能体代码。它们保持工作负载可移植:相同的 Pod spec 可在任何符合规范的集群上运行。安全修复和设计评审来自比任何单一供应商都更大的网络。而与 Agent Sandbox 集成的框架不需要新的 API。得益于社区,用户可以采用这些新工具,而无需重复造轮子。
我们很荣幸能成为这个充满活力的领域的一部分,并期待看到它的发展。
文章《Kubernetes 上 AI 智能体沙箱的五层架构》首发于 The New Stack。