尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

自建E2B沙箱:AI Agent代码执行隔离实战

自建E2B沙箱:AI Agent代码执行隔离实战 如果你正在做 AI Agent 开发尤其是让 Agent 写代码、执行代码、操作文件这种场景“沙箱”这件事迟早会找上你。我做数据处理 Agent 时一开始图省事直接在业务服务器的宿主机上跑模型生成的 Python 脚本结果有一次脚本失控把所有临时目录写满了连日志组件都跟着崩了差点把线上服务拖下水。从那以后我给自己团队定了一条红线代码执行必须隔离。选型阶段我对比了几种方案最后落到了 E2B 上又因为要保留内网部署能力和统一运维入口把整套环境搭在了阿里云计算巢上。这篇文章我会把整套“自建 E2B”实战过程完整讲一遍为什么选 E2B、为什么用计算巢、架构怎么拆、部署怎么做、Agent 怎么接进去以及真实环境里容易踩的坑。内容偏工程实现有一定 AI Agent 开发经验或者正在做私有化部署的读者可以直接照着操作如果是刚入门的同学也可以把前面架构部分当背景知识看。1. 为什么要在云计算巢上自己搭一套 E2B 沙箱1.1 从一次脚本失控说起先讲讲那个让我痛下决心的现场。当时团队做一个自动化报表 Agent让模型生成统计脚本再直接调定时任务在服务器上执行。前两周都很正常直到有一次模型为了“更严谨地分析数据”生成了一段递归扫描目录的脚本加上循环里没有文件大小判断直接把临时目录塞了几十个 GB。等我们发现时连系统清理任务都没法正常跑了只能靠重启换回恢复窗口。概括起来其实是一个很关键的道理大模型本身是概率系统它生成的代码没法保证 100% 安全。可能是逻辑错误把资源耗尽可能是依赖包里有恶意脚本也可能是提示词注入让你执行了不该执行的命令。我以前听人说“大模型写代码一般不会乱来”实际经验告诉我这个想法非常危险。AI Agent 的代码执行如果不做隔离就像让一个陌生人在你家里厨房随意动火着火只是时间问题。所以我的结论是把 Agent 生成的代码丢到沙箱里跑不是过度设计而是上生产环境之前的基本底线。沙箱能拦截很多潜在风险但沙箱本身也有不同实现方式这就要求选型时看清楚技术底子。1.2 E2B 是什么它解决了什么问题E2B 的定位简单说就是“专门给 AI Agent 准备的沙箱运行时”。你可以在一个隔离的 Linux 环境里运行 Python、Node.js、Shell 命令安装依赖包读写文件发出网络请求。整个执行过程跟外面完全隔开跑完可以直接销毁沙箱也可以保留会话继续用。我知道很多人第一反应是“这和 Docker 容器有什么区别”。区别不小。Docker 容器只是给你一个隔离环境但 Agent 场景下你还需要回答这些问题沙箱以什么镜像为模板怎么快速拉起怎么把文件注入进去Agent 分几步执行时有没有状态代码输出怎么拿回来用完怎么回收E2B 提供的就是这一整套生命周期管理能力。它有自定义沙箱模板机制、统一的 API 调度、文件系统操作、代码执行接口甚至支持在同一个沙箱里连续执行多步任务这对 Agent 多轮工具调用非常关键。我也想过用 FaaS 来实现同样的效果但很快放弃了。云函数虽然也是隔离执行但回话有痛点镜像体积限制太大你要装一个几十个包的 Python 环境很费劲冷启动时间长任务稍微复杂就会超时而且很多 FaaS 是无状态的你要跑一个有状态的会话实现难度直接翻倍。还有一类是基于容器或微虚拟机 Darryl 沙箱的产品类似 Daytona、ppio 等它们更适合做开发环境而不是面向 Agent 的代码执行底座。所以综合下来E2B 是匹配度最高的。1.3 选择阿里云计算巢的三个现实理由第一是可控性。Agent 生成的代码经常会接触内部数据如果依赖外部托管的沙箱服务数据链路绕到公网这件事在大多数公司内部都过不了合规。自建 E2B 以后控制面和沙箱运行节点都在自己的云环境里数据全程在内网流转安全性上放心很多。第二是运维体验。阿里云计算巢本身是一个应用交付与运维平台它能通过模板把一组云资源编排在一起包含 VPC、安全组、负载均衡、ECS 实例、监控告警等。我们可以把 E2B 相关的服务包装成模板在计算巢里一键部署后续扩容、改参数、看监控都在同一个界面完成比手动 SSH 上去改 Docker 配置要省太多心力。第三是成本。托管版沙箱按调用次数和时长计费高频调用时开销会很吓人。自建以后主要成本就是底层 ECS 计算资源、对象存储和带宽只要把沙箱池的规模控制好费用通常能压到托管版的 40% 到 60%。对于每天几千次调用的场景这个差值已经相当可观了。2. E2B 架构拆解部署前必须搞清楚的几个部分2.1 四个核心组件要自建 E2B得先理解它的整体结构。我把它拆成四个部分这样部署时心里有数。控制面 API 服务负责处理来自 Agent 后端的请求包括沙箱创建、销毁、模板列表、鉴权等。它相当于“大脑”所有 Agent 调用都先打到这一层。沙箱模板可以理解为“沙箱的镜像”里面预装了 Python、Node.js、常用依赖包。Agent 启动沙箱时指定用哪个模板就能在对应的环境里执行任务。模板可以自己用 Dockerfile 构建然后推送到镜像仓库。数据面计算节点真正跑沙箱的一批机器可以是 ECS 实例上的一组容器也可以是 Kubernetes 集群里的 Pod。控制面收到创建沙箱的请求后会调度数据面节点拉起一个隔离执行环境。客户端 SDK / CLISDK 提供给 Agent 后端集成支持 Python、JavaScript 等语言CLI 用来构建和上传沙箱模板。官方叫法是 E2B SDK 和 E2B CLI。这四个部分缺一不可。控制面保证 API 入口稳定模板保证环境可复现数据面保证执行隔离SDK 保证 Agent 能快速对接。2.2 一次完整的沙箱调用是怎么流转的我直接把一次调用链路撸成一条线大家在部署排障时可以用来对照。Agent 后端收到用户请求按业务逻辑判断需要执行一段动态代码。Agent 后端调用 E2B SDK 的Sandbox.create()传入模板名称和超时时间。SDK 把这个请求发送给控制面 API 服务。控制面校验 API Key并检查模板是否存在。控制面调度到数据面节点在该节点上启动一个隔离的执行环境。SDK 收到沙箱 ID返回给 Agent 后端。Agent 后端用这个沙箱 ID 执行代码、写文件、跑命令。任务完成后Agent 后端调用sandbox.kill()或者sandbox.close()销毁沙箱。这里面最容易出问题的点是第四步和第五步。模板不存在、数据面节点资源不够、网络权限没放开都会造成沙箱创建超时。后面我专门列了一节排错内容。2.3 自托管部署的网络与存储规划自建 E2B 的时候网络和存储规划不能省。下面是我觉得最合理的默认方案。在 VPC 内划分两个子网一个放控制面服务一个放数据面计算节点。控制面子网只对 Agent 后端所在的网段开放端口数据面子网不直接暴露公网只允许控制面节点访问。这样即便沙箱被恶意代码侵入攻击面也被限制在数据面这一层影响范围可控。存储有两块要准备。第一块是对象存储用来临时存放沙箱输出文件和模板构建产物第二块是镜像仓库用来存放沙箱模板镜像。如果你的代码包里包含公司内部数据建议把镜像仓库也放在内网别推到公共仓库。还有一点值得提醒如果数据面节点选择的是支持 KVM 的裸金属实例可以使用 E2B 基于微虚拟机的沙箱类型如果只是普通 ECS 实例尽量选基于容器运行时的方式否则嵌套虚拟化的性能问题会非常尴尬。这个细节后面在常见问题里还会再提。3. 计算巢部署 E2B从零到第一个沙箱3.1 开通计算巢前需要准备的东西在动手部署之前先花十分钟把账号侧的东西准备好不然后面会卡壳。需要用到的阿里云资源有这些一个主账号或者 RAM 子账号开通计算巢服务账号下的 RAM 授权给计算巢让它能调用 ECS、VPC、SLB、ROS 等产品的 API规划好 VPC 和交换机最好事先确定控制面与数据面的网段准备好 SSH 秘钥部署完成后需要登录 ECS 做初始化配置。授权这里提醒一句尽量别直接给主账号太高权限最省事的方式是创建一个专门用的 RAM 角色把计算巢要用到的权限绑定到角色上。这样权限边界清楚后面审计也方便。3.2 在计算巢中创建服务实例计算巢的部署逻辑是你先准备一个服务模板模板里声明了要创建哪些云资源以及这些资源的依赖关系然后由计算巢调度 ROS 去真正创建。实际操作时可以从计算巢市场上选择一个现成的 E2B 服务模板。如果没有合适的公开模板也可以自己上传模板。我先说通用流程。登录计算巢控制台进入“服务实例”页面点击创建服务实例。选择服务模板填入部署区域例如华东 2上海或华北 2北京。配置 VPC 和交换机选择你刚才规划好的网段。选择控制面 ECS 实例规格。起步配置建议 4 核 8GB如果 Agent 调用频率高可以上 8 核 16GB。配置数据面节点数量。数据面可以是一台 ECS也可以是一组 ECS 加入 Kubernetes 集群推荐先用一台 8 核 16GB 的 ECS 把链路跑通。填写计算巢需要的基础信息比如服务名称、版本号然后点击部署。部署过程通常需要 5 到 10 分钟计算巢会自动等待所有资源创建完成。界面上能看到每个资源的创建状态哪一步失败会直接显示错误原因这个体验比自己在命令行里排着看舒服很多。3.3 初始化 E2B 控制面模板部署完成后ECS 已经创建出来了但控制面的容器还需要手动初始化。一般来说模板会在 ECS 上预置一个 Docker Compose 文件我们登录到控制面 ECS 执行启动命令就行。我建议的顺序是先确认环境变量文件是否正确主要包含控制面监听端口、API 秘钥、对象存储的 Bucket 名称、镜像仓库地址等。然后执行docker compose up -d再检查所有容器状态docker compose ps正常情况下会看到控制面 API 容器、数据库容器、缓存容器都是 running 状态。接着验证控制面 API 是否监听在预置端口上curl -X POST http://localhost:8000/sandboxes \ -H Authorization: Bearer $E2B_API_KEY如果返回了创建沙箱的异步响应或者空列表说明控制面基本正常。注意这里千万不要急着跳到业务代码先把控制面对数据面的调度链路跑通。数据面节点上的工作也别忘了。登录数据面 ECS确认沙箱运行时服务已经启动并且控制面的私网地址可以访问到数据面的管理端口。这一步是全网最容易卡住的瓶颈很多沙箱创建超时的报错都来自这里。3.4 用 Python SDK 跑通第一个沙箱控制面初始化之后我一般会在本地写一段最简代码验证整体链路。from e2b import Sandbox # 创建沙箱默认模板会预装 Python 运行时 sbx Sandbox(timeout60) # 在沙箱里执行一段 Python 代码 result sbx.run_code(import sys; print(sys.version)) print(result.stdout) # 写入一个文件再读出来确认文件系统正常 sbx.filesystem.write(/tmp/hello.txt, hello from e2b) content sbx.filesystem.read(/tmp/hello.txt) print(content) # 执行 shell 命令 proc sbx.commands.run(ls -la /tmp) print(proc.stdout) # 用完释放资源 sbx.kill()代码很短但这几步已经覆盖了 Agent 最核心的请求创建沙箱、执行代码、操作文件、执行命令、关闭沙箱。如果你在本地跑通了这段代码说明整套自建 E2B 链路已经通了。如果报错连不上控制面优先检查本地到控制面 ECS 公网/内网地址的网络连通性以及安全组是否放开了对应端口。4. 把沙箱接进 AI AgentSDK、MCP 与 LangGraph4.1 在 Agent 工程里封装沙箱工具跑通 SDK 只是第一步真正在 Agent 工程里用起来需要把沙箱调用封装成工具函数。比如在 LangChain 或 LangGraph 里Agent 是一个图结构节点之间通过工具调用来推进任务。我通常会把 E2B 的创建和执行逻辑封装成一个工具函数输入是一段代码或命令输出是执行结果。def run_in_sandbox(code: str) - str: sandbox Sandbox() try: result sandbox.run_code(code) if result.error: return f执行出错: {result.error} return result.stdout finally: sandbox.kill()封装的目的只有一个让模型不需要关心沙箱的创建和销毁。Agent 只需要知道“我有一段代码要执行”调用工具拿到结果。这个是工程分层的意义所在。4.2 通过 MCP 协议暴露沙箱能力MCPModel Context Protocol最近在 AI Agent 集成里很火。它可以理解为 AI 应用和外部工具之间的标准化接口。如果你想在 Claude Desktop、Claude Code 或者自研的 MCP 客户端里直接用上 E2B可以写一个简单的 MCP Server把沙箱封装成 MCP 工具。用 Python 写其实不难比如基于 FastMCPfrom mcp.server.fastmcp import FastMCP from e2b import Sandbox mcp FastMCP(e2b-sandbox) mcp.tool() def execute_python(code: str) - str: 在隔离沙箱中执行 Python 代码并返回运行结果 sandbox Sandbox() try: res sandbox.run_code(code) if res.error: return ferror: {res.error} return res.stdout or (no output) finally: sandbox.kill() if __name__ __main__: mcp.run()启动这个 MCP Server 之后Claude Code 这类支持 MCP 的客户端会把你声明的execute_python注册成一个可调用工具。模型在使用过程中会自动生成 Python 代码然后调用这个工具在沙箱里执行。我在实际使用中遇到过一个问题Claude Code 的沙箱有时会“起不来”排查下来很多原因不是 MCP Server 的问题而是自建 E2B 的后端控制面没有把数据面节点地址写入白名单导致 MCP Server 发起创建沙箱请求时直接超时。4.3 在 LangGraph 中编排沙箱执行LangGraph 更擅长编排多步骤任务流它把一次 Agent 会话抽象成一个图每个图执行完一步就更新状态。我举一个实际例子写一个“数据分析 Agent”用户提问题Agent 生成 SQL 或 Python 数据分析代码并且在沙箱里执行。在 Graph 里数据清洗节点和执行节点分别调用不同的工具但执行节点会用到 E2B。from langgraph.graph import StateGraph, END class State(TypedDict): question: str generated_code: str execution_result: str def generate_code(state: State): # 调模型生成代码 code llm.invoke(f根据问题写出 Python 代码{state[question]}) return {generated_code: code} def execute_code(state: State): result run_in_sandbox(state[generated_code]) return {execution_result: result} graph StateGraph(State) graph.add_node(generate, generate_code) graph.add_node(execute, execute_code) graph.add_edge(generate, execute) graph.add_edge(execute, END)这个结构非常简单但已经有生产雏形了。如果把沙箱换成宿主机执行那代码一旦出错后果就像我开头讲的那样。用沙箱之后最多就是沙箱实例内资源被消耗宿主机完全不受影响。5. 常见故障实录与排查思路5.1 沙箱创建超时这是自建 E2B 最常见的报错。控制面 API 能用但一创建沙箱就超时。我总结的排查路径如下。先看数据面节点资源是否充足内存不足时沙箱引擎无法分配资源最直接的命令是free -h df -h然后看控制面和数据面之间的网络连通性。很多超时都源于安全组没有放行端口。nc -zv data-plane-ip port再看沙箱模板镜像是否能被数据面节点拉取。如果镜像仓库是私有的需要在数据面节点配置拉取凭证。这一步的经验是把超时时间调大一点比如从默认 30 秒调到 60 秒先确认链路再优化性能。如果调大超时后沙箱能创建成功那说明不是链路问题而是资源或镜像拉取太慢。5.2 冷启动慢沙箱冷启动慢高概率是模板镜像太大。我有一次往模板里装了完整的 CUDA 工具包镜像体积直接到 8GB每次拉起都要几十秒。后来把 CUDA 相关的动态库单独拆出来做基础镜像冷启动时间降到了 5 秒以内。另外一个缓解办法是做沙箱预置池。让控制面在空闲时间预先创建一批沙箱放着Agent 发起请求时直接拿一个空闲沙箱来用整个过程接近秒开。这个和数据库连接池的思路非常像。需要注意预置池会持续占用资源所以要把池子上限卡死否则可能白白跑着一堆空闲实例。5.3 嵌套虚拟化带来的性能坑我前面提到过如果使用了 E2B 基于微虚拟机的沙箱类型数据面节点必须有 /dev/kvm 支持。阿里云大部分普通 ECS 实例没有开放的嵌套虚拟化所以你在 ECS 上直接跑微 VM 模式大概率会起不来。解决方案有两个一是换实例规格选择支持裸金属或嵌套虚拟化的规格二是切换沙箱运行时类型使用容器模式。容器模式虽然没有微 VM 那么硬的隔离边界但在普通 ECS 上能稳定运行而且配合 Seccomp、Namespace 等内核安全机制覆盖绝大多数 Agent 代码执行场景已经足够了。5.4 沙箱里的代码访问不到内网服务有时候 Agent 需要在沙箱内调用内网的 API但发现连不通。这多半是 VPC 路由没打通。沙箱进程虽然在数据面节点里但它可能在独立的网络命名空间内需要开启端口转发或者把数据面节点放在能访问内网服务的子网中。我的做法是在数据面节点上配置内网 DNS并把沙箱进程的网络模式设置为主机模式。这样沙箱内发起的请求默认走宿主机的网络路由内网服务就能自然访问到。代价是隔离性稍微减弱所以只建议在受控的内网环境里这么干。5.5 沙箱内存和磁盘被塞满Agent 生成的代码有时会出现死循环或者不停写文件。E2B 对沙箱有配额限制但默认值不一定适合你的场景。我在配置里显式设置了每个沙箱的 CPU 上限、内存上限和磁盘上限。sbx Sandbox( memory_mb1024, cpu_millis1000, disk_mb2048, )这样即使代码失控也只是沙箱内部资源耗尽不会影响数据面节点上的其他沙箱。还有一点要养成习惯任务结束必须在 finally 里调用kill()不然沙箱会一直占用资源直到触发超时回收。5.6 常见问题速查表现象优先排查点处理方式沙箱创建超时控制面到数据面网络、数据面资源安全组放行端口调大超时时间冷启动慢镜像大小、数据面节点 I/O精简镜像、开沙箱预置池微 VM 模式起不来/dev/kvm 是否存在换裸金属实例或切容器模式Agent 内网服务不通VPC 路由、网络模式切换主机网络模式、配置内网 DNS沙箱资源被耗尽配额设置显式设置内存/Mil CPU 配额6. 压测、调优与成本控制6.1 先做一次小规模压测部署完之后我建议不要直接上生产先做一轮压测。我用的办法是写一段并发创建脚本同时创建 10 个沙箱每个沙箱执行一次计算任务后销毁。from concurrent.futures import ThreadPoolExecutor from e2b import Sandbox def worker(_): sbx Sandbox() res sbx.run_code(sum(range(1000000))) sbx.kill() return res.stdout with ThreadPoolExecutor(max_workers10) as ex: results list(ex.map(worker, range(10)))如果这 10 个沙箱都能在合理时间内创建并完成计算控制面基本没问题。如果你打算支持更高的并发需要关注控制面 ECS 的内存和 CPU 使用率内存不够时容器会频繁 OOM。6.2 沙箱预置池怎么配最合适预置池的值太大浪费资源太小又起不到作用。我的经验是按“高峰期并发数”的 50% 来预置。假设你的 Agent 峰值会同时拉 20 个沙箱那就预置 10 个。另外给池内沙箱加一个无任务存活时间比如 10 分钟没有任务就自动销毁避免深夜挂着一批空转沙箱。这样既保证体验又控制成本。6.3 成本账单里最容易漏掉的三笔钱自建 E2B 的账单除了 ECS 实例费用还有三笔容易被忽略。对象存储的费用。沙箱输出文件如果直接上传到 OSS随着任务量增长存储费用和请求费用都在涨。解决办法是定期清理临时目录设置生命周期规则自动删除超过 7 天的文件。镜像仓库的流量费用。模板更新频率高的话每次构建和拉取镜像都会消耗流量。建议把构建好的镜像放在与数据面节点同一个地域的镜像仓库里避免跨地域流量费。安全组和负载均衡如果选了公网 SlB注意带宽计费方式。按固定带宽和按流量计费差别很大高频调用建议用按使用流量并设置带宽上限防止被短时间内大量沙箱请求打到天价账单。6.4 从裸版到生产级的最后一步裸版 E2B 指的就是只拉起沙箱执行代码的最小模型。要变成生产级至少还要补齐下面几件事。给控制面 API 加一层网关鉴权不能把 API Key 直接暴露在客户端。记录每一次沙箱创建、执行、销毁的操作日志。把控制面 API 的 Prometheus 指标接上监控告警。在 Agent 侧做重试机制避免单次沙箱创建失败导致整个任务中断。为不同的 Agent 任务配置独立的模板避免任务 A 被安装的依赖污染任务 B 的环境。这几件事做完整套系统才敢说能扛住真实业务流量。最后分享一点个人体会自建 E2B 这件事技术上不复杂真正的复杂度在“隔离边界”和“资源效率”之间的平衡。你想要更强的隔离就得多花钱多耗资源你想更省钱就得在容器模式和配额控制上做更多功课。没有标准答案只有适合你业务场景的答案。如果让我给一个最实用的建议那就是刚开始自建时别追求大而全先用一台控制面 ECS 加一台数据面 ECS 把链路跑通用最简模板跑通一个真实案例再去考虑预置池、生产级监控和成本优化。链路通了以后再一层层加东西难度会小很多。这套环境我现在已经跑了差不多半年期间虽然也踩过不少坑但整体稳定。后续我还会探索两个方向一个是把沙箱模板的构建流程做成 CI/CD 自动化另一个是研究在数据面节点上混合调度容器沙箱和微 VM 沙箱按任务安全级别自动选择隔离方案。如果你也在做类似的自建沙箱欢迎一起交流。
返回列表