FluxSandbox沙箱调度引擎
特性介绍
FluxSandbox是高性能Kubernetes沙箱调度引擎,作为OpenSandbox的fluxruntime后端,面向AI Agent工作负载提供低时延、高吞吐的沙箱生命周期管理能力。
FluxSandbox不直接暴露API,以OpenSandbox为使用入口。用户使用OpenSandbox SDK创建沙箱,由OpenSandbox Server通过gRPC转发给FluxSandbox完成调度与生命周期管理,整体链路为:
用户(SDK/REST API)→ OpenSandbox Server(flux runtime)→ FluxSandbox Controller → Scheduler → Agent Pod → 沙箱运行时沙箱运行时(即沙箱实例的实际载体)有两种模式:
- E2B运行时(默认):沙箱为microVM实例,由节点上的E2B Sandbox Runtime与Orchestrator创建。支持暂停/恢复/快照等高级能力,沙箱基于E2B模板创建后秒级启动。
- containerd运行时:沙箱为节点containerd上的裸容器,适用于标准Kubernetes集群。不支持暂停/恢复/快照。
应用场景
- AI Agent运行环境:提供可秒级获取、按需回收的隔离运行沙箱,承载Agent的命令执行、代码运行等任务。
- 大规模沙箱池:通过SandboxGroup声明容量与资源规格,系统自动维持预热缓冲,应对高并发创建请求。
- 统一资源管控:沙箱资源规格由SandboxGroup统一声明,避免调用方自定义。
- 环境保存与复用:对运行中的沙箱创建快照,基于快照快速创建新沙箱,实现环境模板化克隆与故障回退(仅E2B运行时)。
- 快照预热:将快照主动分发缓存到多个超节点,加速从快照创建沙箱(仅E2B运行时)。
能力范围
- 声明式管理沙箱组容量(SandboxGroup CRD),自动扩缩Agent Pod。
- 沙箱自动调度与生命周期管理(创建、运行、暂停、恢复、回收)。
- 预热缓冲(sandboxBuffer),降低沙箱获取时延。
- 快照管理:创建、查询、删除快照,并从快照恢复沙箱(仅E2B运行时)。
- 快照预热:将快照主动分发缓存到多个超节点,加速从快照创建沙箱(仅E2B运行时)。
- 多副本分片调度,支持横向扩展。
- 资源超分与多资源类型:
sandboxResources的requests/limits分离声明,CPU与内存可按比例超分提升单节点沙箱部署密度;支持hugepages-2Mi等资源名占位(E2B运行时大页内存)。 - 节点级孤儿沙箱自动清理。
运行时模式选择
表1 两种运行时模式对比
| 维度 | E2B运行时(默认) | containerd运行时 |
|---|---|---|
| 沙箱形态 | Firecracker microVM。 | 裸容器。 |
| 沙箱镜像语义 | E2B模板名(如ubuntu-22-04-custom),模板需预先构建。 | 标准容器镜像引用(如python:3.11)。 |
entrypoint | 不注入沙箱,microVM进程由模板决定。 | 作为容器主进程入口,生效。 |
env | 不注入沙箱。 | 注入容器,生效。 |
| 资源规格 | 由SandboxGroup的sandboxResources声明(Agent Pod按槽位预留)。 | 由SandboxGroup的sandboxResources声明,containerd按cgroup限额执行。 |
| 暂停/恢复 | 支持。 | 不支持。 |
| 快照(创建/恢复/预热) | 支持。 | 不支持。 |
| 命令执行承载 | 沙箱内envd守护进程。 | 沙箱内注入的execd守护进程。 |
| 节点前置 | 部署E2B Sandbox Runtime与Orchestrator。 | 安装containerd。 |
| 管控面前置 | 部署E2B Template Manager。 | 无。 |
| SandboxGroup配置 | 默认,无需额外设置。 | 需显式设置agentEnv.RUNTIME_TYPE=containerd与execdImage。 |
说明:
本手册后续章节默认以E2B运行时进行说明。若使用containerd运行时,请关注各章节中的containerd运行时特殊说明。
基本概念
表2 基本概念
| 概念 | 说明 |
|---|---|
| SandboxGroup | 沙箱组,用户直接创建的CRD资源,定义一组沙箱的资源规格与容量。状态就绪(Ready)后即可创建沙箱。 |
| SubSandboxGroup | SandboxGroup的自动分片,由Controller拆分生成并分配给各调度器实例,用户无需手动创建。 |
| Agent Pod | 承载沙箱实例的工作负载Pod,由调度器按SandboxGroup容量自动拉起与回收。 |
| Sandbox | 单个沙箱实例,运行在Agent Pod所在节点上,通过OpenSandbox SDK使用。 |
| OpenSandbox Server | 用户请求入口,将SDK请求以fluxruntime方式转发给FluxSandbox。 |
| E2B模板 | E2B运行时的沙箱镜像,内置envd守护进程,创建请求中的image即模板名。 |
| 快照(Snapshot) | 沙箱某一时刻完整状态的持久化记录,可用于创建承载相同状态的新沙箱。仅E2B运行时支持。 |
| 超节点(SuperPod) | 快照预热的目标节点池,缓存快照以加速从快照创建沙箱,存储余量经MoonCake上报。 |
实现原理
用户通过SDK创建沙箱时,OpenSandbox Server根据请求中的extensions.sandboxGroup字段路由到对应的SandboxGroup,经FluxSandbox Controller转发给所属调度器分片,调度器从该组的Agent Pod中选择一个创建沙箱实例。SandboxGroup的容量字段(缓冲、单Pod沙箱上限等)驱动Agent Pod的自动扩缩,sandboxResources决定每个沙箱占用的资源规格。
E2B运行时下,调度器在创建前会根据image(模板名)向E2B Template Manager解析模板元数据,再由节点上的E2B Sandbox Runtime拉起microVM沙箱;containerd运行时下,Agent直接通过节点containerd创建裸容器并注入execd。
安装
前提条件
- Kubernetes集群为v1.28及以上版本,可通过
kubectl访问。 - E2B运行时(默认):
- Worker节点已部署E2B Sandbox Runtime(每节点一个实例,侦听
unix:///run/cri-multiplex.sock)与E2B Orchestrator; - 管控面已部署E2B Template Manager,并准备了可用的E2B模板(见准备E2B模板)。
- Worker节点已部署E2B Sandbox Runtime(每节点一个实例,侦听
- 已安装Helm 3.14及以上版本(推荐使用Helm部署)。
- OpenSandbox Server镜像与Chart可直接从官方源获取(见部署OpenSandbox Server);FluxSandbox四个组件镜像(controller、scheduler、watcher、agent)已推送到集群可访问的registry(从源码构建时执行
make docker-build与make docker-push)。 - 使用SDK创建沙箱时,本地需安装Python 3.10+。
【containerd运行时】
使用containerd运行时时,Worker节点只需安装containerd,无需部署E2B组件,但也无法使用E2B相关能力(暂停/恢复/快照/快照预热),相关章节可跳过。部署时的Chart参数差异见部署FluxSandbox中的containerd说明。
部署OpenSandbox Server
沙箱以OpenSandbox为使用入口,需部署OpenSandbox Server并将其runtime配置为flux。Chart与Server镜像均可直接从官方仓库获取。
创建配置文件
config.toml,runtime类型设为flux并指向FluxSandbox Controller:toml[server] host = "0.0.0.0" port = 80 max_sandbox_timeout_seconds = 86400 workers = 15 thread_pool_size = 200 timeout_keep_alive = 120 limit_concurrency = 0 backlog = 65535 loop = "uvloop" http = "httptools" [log] level = "INFO" [runtime] type = "flux" execd_image = "opensandbox/execd:v1.0.20" [storage] allowed_host_paths = [] volume_default_size = "1Gi" [flux_sandbox] endpoint = "flux-sandbox-controller.flux-system.svc:9091" timeout_seconds = 60 use_tls = false说明:
execd_image为Server的必填配置项;[ingress]块由Chart自动生成(默认direct模式),配置文件中不要重复携带。Chart的Service端口固定为80,port需保持为80。- 若需从源码构建镜像(备选方式):克隆openFuyao/opensandbox仓库后执行
docker build -f server/Dockerfile.flux -t opensandbox-server:flux .,并将下文server.image.repository/tag指向该镜像。注意Dockerfile.flux依赖BuildKit特性,节点无docker buildx时建议直接使用官方镜像。
使用Chart部署Server:
在线部署(集群节点可访问镜像仓库):
helm install opensandbox-server \
oci://cr.openfuyao.cn/charts/opensandbox-server --version 0.2.1-of.1 \
-n flux-system --create-namespace \
--set namespaceOverride=flux-system \
--set server.image.repository=openfuyao-0qfqnk.swr-pro.myhuaweicloud.com/openfuyao/opensandbox/flux-server \
--set server.image.tag=0.2.1-of.1 \
--set server.replicaCount=1 \
--set 'server.env[0].name=OPENSANDBOX_INSECURE_SERVER' \
--set 'server.env[0].value=YES' \
--set-file configToml=config.toml说明:
helm只负责下载Chart包(模板),Server镜像由节点kubelet在创建Pod时按imagePullPolicy(缺省IfNotPresent)自动从镜像仓库拉取,无需手动导入。
离线部署(集群节点无法访问镜像仓库):
在能访问外网的机器上下载Chart包,并拉取、导出Server镜像:
# 1. 下载Chart包
helm pull oci://cr.openfuyao.cn/charts/opensandbox-server --version 0.2.1-of.1
# → opensandbox-server-0.2.1-of.1.tgz
# 2. 拉取Server镜像
docker pull openfuyao-0qfqnk.swr-pro.myhuaweicloud.com/openfuyao/opensandbox/flux-server:0.2.1-of.1
# 3. 导出为离线镜像包
docker save -o opensandbox-server-image.tar \
openfuyao-0qfqnk.swr-pro.myhuaweicloud.com/openfuyao/opensandbox/flux-server:0.2.1-of.1
# 4. 传输到目标集群
scp opensandbox-server-0.2.1-of.1.tgz opensandbox-server-image.tar root@<node-ip>:/root/在目标节点导入镜像:
ctr -n k8s.io images import /root/opensandbox-server-image.tar
crictl images | grep flux-server在控制节点安装。与在线部署相比仅追加一个server.image.pullPolicy=Never:离线节点上kubelet只查本地镜像,镜像名需与导入节点的完全一致,缺失时报ErrImageNeverPull:
helm install opensandbox-server /root/opensandbox-server-0.2.1-of.1.tgz \
-n flux-system --create-namespace \
--set namespaceOverride=flux-system \
--set server.image.repository=openfuyao-0qfqnk.swr-pro.myhuaweicloud.com/openfuyao/opensandbox/flux-server \
--set server.image.tag=0.2.1-of.1 \
--set server.image.pullPolicy=Never \
--set server.replicaCount=1 \
--set 'server.env[0].name=OPENSANDBOX_INSECURE_SERVER' \
--set 'server.env[0].value=YES' \
--set-file configToml=config.toml说明:(在线/离线部署通用)
-n flux-system必须显式携带:不带-n时release记录落在当前上下文的默认命名空间。- 卸载或删除Deployment与Service后,
serviceaccount/opensandbox-server与configmap/opensandbox-server-config会残留(带helm所有权注解),需一并删除后再重装。OPENSANDBOX_INSECURE_SERVER=YES:必须设置,缺失会导致server worker反复退出。
部署FluxSandbox
方式一:Helm部署(推荐)
CRD随Chart自动安装。使用官方镜像时,只需设置global.imageRegistry前缀。
在线部署(集群节点可访问镜像仓库):
helm install flux-sandbox \
oci://cr.openfuyao.cn/charts/flux-sandbox --version 26.9.0 \
--namespace flux-system --create-namespace \
--set global.imageRegistry=cr.openfuyao.cn/openfuyao \
--set watcher.args.criSocket="/run/cri-multiplex.sock"说明:
helm只负责下载Chart包(模板),容器镜像由各节点kubelet在创建Pod时按imagePullPolicy(缺省IfNotPresent)自动从镜像仓库拉取,无需手动导入。私有仓库需先执行helm registry login再安装。
离线部署(集群节点无法访问镜像仓库):
在能访问外网的机器上下载Chart包,并拉取、导出四个组件镜像:
# 1. 下载Chart包
helm pull oci://cr.openfuyao.cn/charts/flux-sandbox --version 26.9.0
# → flux-sandbox-26.9.0.tgz
# 2. 拉取四个组件镜像
IMAGES="
cr.openfuyao.cn/openfuyao/flux-sandbox/controller:26.9.0
cr.openfuyao.cn/openfuyao/flux-sandbox/scheduler:26.9.0
cr.openfuyao.cn/openfuyao/flux-sandbox/agent:26.9.0
cr.openfuyao.cn/openfuyao/flux-sandbox/watcher:26.9.0
"
for img in $IMAGES; do docker pull "$img"; done
# 3. 导出为离线镜像包
docker save -o flux-sandbox-images.tar $IMAGES
# 4. 传输到目标集群
scp flux-sandbox-26.9.0.tgz flux-sandbox-images.tar user@your-cluster-ip:/root/在每个worker节点导入镜像(controller/scheduler可能调度到任意worker节点,watcher为DaemonSet每节点一个,Agent Pod也可能落在任意worker节点):
# 集群运行时为containerd时,导入到k8s.io命名空间
ctr -n k8s.io images import /root/flux-sandbox-images.tar
# 验证导入成功
crictl images | grep flux-sandbox
# docker运行时节点改为:docker load -i /root/flux-sandbox-images.tar在控制节点安装。与在线部署相比仅追加三个pullPolicy=Never,镜像tag使用默认latest(--set *.image.tag需与实际导入节点的镜像tag一致):
helm install flux-sandbox /root/flux-sandbox-26.9.0.tgz \
--namespace flux-system --create-namespace \
--set global.imageRegistry=cr.openfuyao.cn/openfuyao \
--set controller.image.pullPolicy=Never \
--set scheduler.image.pullPolicy=Never \
--set watcher.image.pullPolicy=Never \
--set watcher.args.criSocket="/run/cri-multiplex.sock"说明:
离线部署必须保留global.imageRegistry并追加pullPolicy=Never:镜像tag为latest时kubelet可能按Always处理而访问registry,离线节点会报ImagePullBackOff;Never策略下kubelet只查本地镜像,镜像名需与导入节点的完全一致,缺失时报ErrImageNeverPull。
单节点部署需缩减scheduler副本数时(Chart默认部署3副本),追加--set scheduler.replicaCount=1,该参数会同步驱动controller的--expected-scheduler-replicas。其他参数见表3常用Helm values。
containerd运行时:纯containerd环境(无E2B组件)建议关闭E2B配置注入,并调整watcher的扫描socket:
helm install flux-sandbox ./charts/flux-sandbox \
--namespace flux-system --create-namespace \
--set global.imageRegistry=cr.openfuyao.cn/openfuyao \
--set e2b.enabled=false \
--set watcher.args.containerdSocket=/run/containerd/containerd.sock说明:
e2b.enabled=false后组件不再挂载E2B配置卷;watcher默认面向E2B集群(criSocket=""、containerdSocket=""),containerd集群需按上例调整为对应的socket路径。
常用定制项:
表3 常用Helm values
| 参数 | 缺省值 | 说明 |
|---|---|---|
global.imageRegistry | "" | 所有镜像的registry前缀 |
global.imagePullSecrets | [] | 镜像拉取凭据 |
scheduler.replicaCount | 3 | 调度器分片数 |
e2b.enabled | true | 是否注入E2B Template Manager配置;纯containerd环境设置为false |
e2b.apiEndpoint | http://api.e2b.svc.cluster.local:3000 | E2B Template Manager地址 |
e2b.existingSecret | "" | 存放E2B config.json的Secret名,推荐用Secret管理E2B凭据 |
e2b.hostPath | /root/.e2b | 遗留方式:宿主机上存放config.json的目录 |
watcher.args.criSocket | /run/cri-multiplex.sock | watcher扫描的E2B CRI socket;置空禁用E2B孤儿扫描 |
watcher.args.containerdSocket | "" | watcher扫描的containerd socket;containerd集群设为/run/containerd/containerd.sock |
*.nodeSelector / *.tolerations | {} | 各组件调度约束 |
完整values说明见flux-sandbox Chart README。
方式二:原始清单
# 创建命名空间
kubectl create namespace flux-system
# 安装CRD
make crd-apply
# 部署各组件
kubectl apply -f config/controller/
kubectl apply -f config/scheduler/
kubectl apply -f config/sandbox-watcher/验证部署
kubectl wait --for=condition=Ready pod -n flux-system -l app=flux-sandbox-controller --timeout=120s
kubectl wait --for=condition=Ready pod -n flux-system -l app=flux-sandbox-scheduler --timeout=120sE2B运行时还应确认Template Manager配置就绪:组件日志中出现Template Manager初始化成功的信息。若未配置E2B凭据,组件会以降级方式启动(日志告警);组件启动后,通过E2B类型的SandboxGroup创建沙箱时,创建请求将报错"E2B runtime requires template manager but it is not configured"。
使用前准备
完成部署后,需创建SandboxGroup并准备沙箱模板(E2B运行时)或容器镜像(containerd运行时),随后即可通过OpenSandbox SDK使用沙箱。
创建SandboxGroup
SandboxGroup定义一组沙箱的资源规格和容量,是用户唯一需要创建的CRD资源。Controller会将其拆分为SubSandboxGroup(分片)分配给各调度器实例,由调度器拉起Agent Pod。沙箱组状态变为Ready后即可创建沙箱。
执行
kubectl apply -f -创建SandboxGroup :bashkubectl apply -f - <<EOF apiVersion: sandbox.flux.io/v1alpha1 kind: SandboxGroup metadata: name: sg-1u1g namespace: flux-system spec: capacity: agentPodMin: 1 agentPodMax: 1 sandboxBufferMin: 1 maxSandboxesPerPod: 5 sandboxResources: requests: { cpu: "1", memory: "2Gi" } limits: { cpu: "1", memory: "2Gi" } EOF容量字段说明:
表4 capacity字段说明
字段 必填 说明 agentPodMin/agentPodMax是 Agent Pod数量的下限/上限,系统在此区间自动扩缩。 sandboxBufferMin/sandboxBufferMax否 预热缓冲空闲沙箱数量的下限/上限,用于降低沙箱获取时延。 maxSandboxesPerPod是 单个Agent Pod上允许共存的最大沙箱数量。 拆分机制举例:假设SandboxGroup配置
agentPodMax: 200、maxSandboxesPerPod: 5,Controller启动参数--max-pod-per-sub-sandbox-group使用默认值100,系统会自动将该SandboxGroup拆分为2个SubSandboxGroup分片(命名形如<SandboxGroup名>-0、<SandboxGroup名>-1),并分配给两个调度器实例:- 每个分片最多管理100个Agent Pod,最大容量为100 × 5 = 500个Sandbox;
- 整个SandboxGroup最大容量为200 × 5 = 1000个Sandbox;
agentPodMax不能被--max-pod-per-sub-sandbox-group整除时向上取整,最后一个分片承接余量(如agentPodMax: 250时拆分为3个分片,最后一个分片管理50个Agent Pod)。
若想调整可创建的Sandbox数量上限:
- 调整SandboxGroup spec中的
agentPodMax或maxSandboxesPerPod,总容量 = agentPodMax × maxSandboxesPerPod。maxSandboxesPerPod调大时,Agent Pod会按sandboxResources为更多槽位预留资源、单Pod资源请求线性上升,一般优先调整agentPodMax。 - 启动参数
--max-pod-per-sub-sandbox-group(Helm部署对应valuescontroller.args.maxPodPerSubSandboxGroup,默认100)不改变总容量,只控制拆分粒度:调大可减少分片数,调小可将分片摊到更多调度器实例上均衡负载。若不希望拆分,将其调大到不小于agentPodMax即可(如上例设为200)。
等待SandboxGroup就绪:
bashkubectl wait --for=condition=Ready sandboxgroup sg-1u1g -n flux-system --timeout=120s
containerd运行时:使用containerd运行时时,SandboxGroup需显式声明运行时类型与execd镜像(execd承载SDK的命令执行,必填):
spec:
agentEnv:
RUNTIME_TYPE: "containerd"
execdImage: "opensandbox/execd:v1.0.20"说明:
运行时类型由agentEnv.RUNTIME_TYPE控制(缺省即E2B);execdImage仅对containerd运行时生效,E2B运行时下设置无效。
配置沙箱资源:超分与hugepages
sandboxResources的requests与limits各司其职:
- limits:沙箱实例的资源上限(containerd运行时按cgroup限额执行;E2B运行时以模板规格为准),同时决定Agent Pod的资源硬顶(limits × maxSandboxesPerPod)。
- requests:节点调度账本的占位值。Kubernetes调度Agent Pod时只按requests扣减节点可分配资源,因此requests小于limits时节点可容纳更多Agent Pod——这就是资源超分。
- requests缺省(整体不配置)等价于requests = limits,即不超分,上文
sg-1u1g示例即此形态。
CPU超分示例(limits为沙箱真实规格2核,requests按1:4超分):
sandboxResources:
requests: # 调度账本占位:500m × 4槽 = 2核/Pod
cpu: "500m"
memory: "300Mi"
limits: # 沙箱上限:2核 × 4槽 = 8核/Pod(cgroup硬顶)
cpu: "2"
memory: "300Mi"超分比例 = limits / requests。实测参考:同一节点同样申请48个Agent Pod,不超分仅13个可调度(节点CPU账本不足),CPU 1:4超分后48个全部调度,部署密度约3.7倍。沙箱实例的资源上限两种配置完全相同,超分改变的是节点账本容量而非单沙箱体验;全部沙箱同时满载时会发生CPU争抢(限流降速而非失败),适合负载错峰的场景。
配置规则:
- requests非空时须与limits声明完全相同的资源键集合,且每个资源满足
0 < requests ≤ limits,违反时SandboxGroup创建被拒绝并给出明确原因(status条件ResourceSpecValid)。 sandboxResources创建后不可变:任何修改(包括改回原值)都会被API Server在写入时直接拒绝,需要变更规格时删除并重建SandboxGroup。
hugepages配置(仅E2B运行时):
E2B沙箱的microVM内存由模板制作时的ram-mb决定,模板启用大页时须在SandboxGroup中声明对应的大页资源,使节点大页内存进入调度账本:
sandboxResources:
requests: # 大页不可超分:requests必须等于limits
cpu: "500m"
memory: "300Mi" # microVM之外的非大页开销,建议固定300Mi
hugepages-2Mi: "2Gi" # 与模板ram-mb一致(如2048Mi)
limits:
cpu: "2"
memory: "300Mi"
hugepages-2Mi: "2Gi"- 资源名必须使用复数形式
hugepages-2Mi(单数hugepage-2Mi会被拒绝并提示正确写法);还支持hugepages-1Gi等其他页大小,数量须为页大小的整数倍。 hugepages-*不可超分(requests必须等于limits),节点沙箱数量上限由节点大页池容量决定(如64Gi大页、每沙箱2Gi时上限32个)。- 大页声明必须与模板
ram-mb保持一致:不一致时创建沙箱会被直接拒绝,并提示修正SandboxGroup,防止资源账目与实际消耗不一致。 - containerd运行时不消费大页,
RUNTIME_TYPE: "containerd"与hugepages-*的组合在SandboxGroup创建/更新时即被拒绝。 - 节点须预先开启大页(内核启动参数预留
hugepages),Kubernetes会将配置了大页的Agent Pod调度到具备大页资源的节点。
注意:
内存超分会降低Agent Pod的QoS等级(Guaranteed降为Burstable),节点内存压力下Burstable Pod更易被驱逐,且驱逐单位是整个Agent Pod(其上全部沙箱一起消失)。建议以CPU超分为主,内存保守或1:1;E2B运行时的大页部分始终1:1。
准备E2B模板
E2B运行时下,创建沙箱时传入的image是E2B模板名(而非容器镜像引用)。模板决定了沙箱的操作系统环境、预装软件与microVM内的进程,模板需满足:
- 模板已通过E2B模板机制构建并注册到Template Manager可访问的E2B环境中,模板名全局可用(如
ubuntu-22-04-custom)。 - 模板内置envd守护进程,承载SDK的命令执行、文件操作等能力(官方基础模板已内置)。
- 沙箱内实际运行的进程与环境以模板定义为准(创建请求中的
entrypoint、env不会注入microVM)。
创建请求中的模板名不存在时,Template Manager解析失败,创建请求返回错误。
containerd运行时:image是标准容器镜像引用,需确保镜像在所有Worker节点可拉取:
# 沙箱运行时镜像(SDK示例使用python:3.11)
docker pull python:3.11
# execd镜像(SandboxGroup中execdImage指定)
docker pull opensandbox/execd:v1.0.20说明:
镜像需推送到集群可访问的registry,或在所有Worker节点上预拉取。
使用沙箱
前提条件
- 已完成FluxSandbox部署、SandboxGroup创建与OpenSandbox Server接入。
- 本地已安装OpenSandbox Python SDK(适配版,安装方式见下),并获取Server访问地址。
安装OpenSandbox Python SDK(适配版)
本仓库(openFuyao/opensandbox)对官方SDK做了适配(含与FluxSandbox Server对齐的快照API等),且未发布到PyPI。直接执行pip install opensandbox会安装PyPI官方包,缺少上述适配,须按以下两种方式之一安装。
方式一:有网环境,从GitCode仓库直接安装
pip install "git+https://gitcode.com/openFuyao/opensandbox.git@of-dev/v0.2.1#subdirectory=sdks/sandbox/python"说明:
#subdirectory=sdks/sandbox/python不能省略,SDK源码位于仓库子目录中。
方式二:离线环境,离线wheel包安装
在有网机器上构建wheelhouse。构建用的Python小版本与系统架构须与内网机器一致(如均为Python 3.12 + linux x86_64),因为依赖pydantic-core是编译型wheel,绑定Python版本与平台:
git clone -b of-dev/v0.2.1 https://gitcode.com/openFuyao/opensandbox.git
cd opensandbox
# SDK本体与全部运行时依赖一起打进wheelhouse目录
pip wheel -w wheelhouse ./sdks/sandbox/python
# 打包并传输到内网机器
tar czf opensandbox-sdk-wheelhouse.tar.gz wheelhouse
scp opensandbox-sdk-wheelhouse.tar.gz root@<内网机器IP>:/root/在内网机器上离线安装:
tar xzf opensandbox-sdk-wheelhouse.tar.gz
pip install --no-index --find-links=wheelhouse wheelhouse/opensandbox-*.whl说明:
--no-index强制pip仅从wheelhouse目录取包,不访问任何软件源。- 有网机器上构建时须保留
.git目录,否则无法从git tag推导版本号。
验证安装
pip show opensandbox版本号带dev与+g<commit-id>后缀(如0.1.14.dev26+g6066bc24)即为适配版;版本号为干净的0.1.x则说明安装的是PyPI官方包,须重新安装。
python -c "
from opensandbox import Sandbox # 异步SDK入口
from opensandbox.sync.sandbox import SandboxSync # 同步SDK入口
print(hasattr(Sandbox, 'create_snapshot')) # 应输出 True
print(hasattr(SandboxSync, 'create_snapshot')) # 应输出 True
"两个输出均为True,说明快照API适配已就位。
注意:
适配版本号(0.1.14.devNN+g…)低于PyPI官方包版本(0.1.16),不要执行pip install -U opensandbox对该包升级,否则会被官方包覆盖,导致快照等适配能力缺失;升级适配版请按上述方式重新构建安装。
创建沙箱参数说明
创建沙箱统一通过OpenSandbox SDK/REST API,FluxSandbox不对外暴露独立接口。参数必选/可选规则如下(校验由OpenSandbox Server执行,image模式与snapshot_id模式规则不同):
表5 创建沙箱参数(image模式)
| 参数 | 必选 | 默认值 | 说明 |
|---|---|---|---|
image | 必选(与snapshot_id二选一) | - | E2B模板名(containerd运行时为容器镜像引用)。 |
entrypoint | 必选 | SDK默认["tail", "-f", "/dev/null"] | 沙箱主进程入口。REST直接调用不传返回400;SDK不传使用默认值。E2B运行时不注入microVM,仅containerd运行时生效。 |
resource_limits(REST字段名resourceLimits) | 必选 | SDK默认{"cpu": "1", "memory": "2Gi"} | 资源规格。REST直接调用不传返回400;SDK不传使用默认值。实际生效规格以SandboxGroup的sandboxResources为准(见下)。 |
extensions.sandboxGroup | 必选 | - | 目标SandboxGroup名称,组必须已就绪(Ready)。缺失返回400。 |
timeout | 可选 | SDK默认10分钟;REST不传则不自动过期 | 沙箱自动过期时长,到期自动回收。不得低于60秒,且不能超过Server配置的上限(max_sandbox_timeout_seconds)。 |
env | 可选 | {} | 环境变量。仅containerd运行时注入沙箱;E2B运行时不注入。 |
metadata | 可选 | {} | 用户自定义标签,键值需符合Kubernetes label规范。 |
resource_requests(REST字段名resourceRequests) | 可选 | 同resource_limits | 资源请求值,缺省时以resource_limits代替。实际生效规格以SandboxGroup为准。 |
platform / network_policy / volumes / secure_access / credential_proxy | 可选 | - | 平台约束、出网策略、存储挂载等高级配置,按OpenSandbox通用语义使用。 |
表6 创建沙箱参数(snapshot_id模式,仅E2B运行时)
| 参数 | 必选 | 说明 |
|---|---|---|
snapshot_id | 必选(与image二选一) | 源快照ID,快照必须处于Ready状态,否则返回404/409。 |
entrypoint | 可选 | 不传时服务端自动使用["tail", "-f", "/dev/null"]。 |
image / resource_limits | 可选 | image由快照记录自动注入;resource_limits实际生效规格以SandboxGroup为准。 |
extensions.sandboxGroup / timeout / 其余参数 | 同image模式 | - |
说明:
entrypoint与resource_limits的必选说明:REST必填用于Server参数校验,但FluxSandbox以SandboxGroup配置为实际生效值,请求中的参数不决定实际规格。如需调整规格,请修改对应SandboxGroup。- 同一请求中
image与snapshot_id必须恰好提供一个,否则返回400。
使用限制
- 创建沙箱时必须通过
extensions.sandboxGroup指定目标SandboxGroup,否则请求被拒绝(400)。 - 沙箱资源规格由SandboxGroup的
sandboxResources统一决定,创建时传入的资源参数不生效(但仍需满足Server的参数校验,见表5)。 - E2B运行时下,
entrypoint与env不注入沙箱,沙箱内进程与环境由E2B模板决定。 - 暂停、恢复与快照能力仅E2B运行时支持;containerd运行时调用相关接口返回不支持的错误。
- 仅
Running状态的沙箱可以创建快照;同一沙箱同一时刻仅允许一个快照操作。 sandboxResources创建后不可变:修改会被API Server在写入时直接拒绝(含改回原值),需删除并重建SandboxGroup。- E2B运行时声明
hugepages-*时,值必须与模板ram-mb一致且requests等于limits,否则创建沙箱被拒绝;containerd运行时不支持hugepages-*资源。
快速开始
连接OpenSandbox Server,创建沙箱:
import asyncio
from datetime import timedelta
from opensandbox import Sandbox
from opensandbox.config import ConnectionConfig
async def main():
config = ConnectionConfig(
domain="<server-address>:80", # OpenSandbox Server地址(Chart默认Service端口80)
)
sandbox = await Sandbox.create(
"ubuntu-22-04-custom", # E2B模板名(containerd运行时传镜像引用,如"python:3.11")
connection_config=config,
timeout=timedelta(minutes=30),
extensions={"sandboxGroup": "sg-1u1g"}, # 必填:路由到SandboxGroup
skip_health_check=True,
)
print("created:", sandbox.id)
info = await sandbox.get_info()
print("state:", info.status.state)
asyncio.run(main())删除沙箱:
python3 kill_sandbox.py <沙箱ID>import asyncio, sys
from datetime import timedelta
from opensandbox.config import ConnectionConfig
from opensandbox.sandbox import Sandbox
async def main(sandbox_id):
config = ConnectionConfig(domain="<server-address>", api_key="")
sb = await Sandbox.get(sandbox_id, connection_config=config)
await sb.kill()
await sb.close()
print(f"沙箱 {sandbox_id} 已销毁")
asyncio.run(main(sys.argv[1]))完整执行命令见envd_manual_verify.md。
创建沙箱
从模板/镜像创建:
sandbox = await Sandbox.create(
"ubuntu-22-04-custom", # image:E2B模板名(或容器镜像引用)
connection_config=config,
entrypoint=["tail", "-f", "/dev/null"], # 可选,SDK默认即此值
env={"FOO": "bar"}, # 环境变量(仅containerd运行时注入)
timeout=timedelta(minutes=30), # 自动过期时间;REST不传则不自动过期
extensions={"sandboxGroup": "sg-1u1g"},
)从快照创建(恢复快照时的完整状态,秒级就绪;仅E2B运行时):
sandbox = await Sandbox.create(
connection_config=config,
snapshot_id="<snapshot-id>", # 与image二选一
timeout=timedelta(minutes=30),
extensions={"sandboxGroup": "sg-1u1g"},
)说明:
image与snapshot_id必须二选一传入。快照必须处于Ready状态,不存在或未就绪的快照会返回404/409。从快照创建时无需传image与entrypoint,服务端自动使用快照记录的镜像与默认entrypoint。
containerd运行时使用示例
以containerd运行时为例的完整使用流程:创建沙箱 → 执行命令 → 流式输出 → 文件读写 → 状态查询。示例要求:
- SandboxGroup为containerd运行时(见创建SandboxGroup中的containerd说明),execd镜像已配置;
- 沙箱镜像
python:3.11与execd镜像opensandbox/execd:v1.0.20在Worker节点可拉取。
本地通过port-forward访问OpenSandbox Server:
kubectl port-forward -n flux-system svc/opensandbox-server 8080:80完整示例代码:
import asyncio
from datetime import timedelta
from opensandbox import Sandbox
from opensandbox.config import ConnectionConfig
from opensandbox.models.execd import ExecutionHandlers
from opensandbox.models.filesystem import WriteEntry, SearchEntry
async def main():
config = ConnectionConfig(
domain="localhost:8080",
api_key="your-secret-key",
use_server_proxy=True,
)
# ---------- 全新沙箱完整流程 ----------
async with await Sandbox.create(
"python:3.11",
connection_config=config,
timeout=timedelta(minutes=30), # 注:SDK 0.1.16 此参数未传到 server
extensions={"sandboxGroup": "sg-1u1g"},
) as sandbox:
print(f"sandbox id: {sandbox.id}")
# 1. 执行命令 + exit code
result = await sandbox.commands.run("python -c 'print(1 + 1)'")
print(f"exit_code={result.exit_code}, out={result.logs.stdout[0].text.strip()}")
# 2. 流式输出(SDK 0.1.16 要求 async 回调,on_stdout/on_stderr 都要给)
async def on_stdout(msg):
print(f"STDOUT: {msg.text}")
async def on_stderr(msg):
print(f"STDERR: {msg.text}")
handlers = ExecutionHandlers(on_stdout=on_stdout, on_stderr=on_stderr)
await sandbox.commands.run("for i in 1 2 3; do echo $i; done", handlers=handlers)
# 3. 文件操作:写入 / 读取 / 查找
await sandbox.files.write_files([
WriteEntry(path="/tmp/hello.txt", data="Hello World", mode=644)
])
content = await sandbox.files.read_file("/tmp/hello.txt")
print(f"file content: {content}")
files = await sandbox.files.search(SearchEntry(path="/tmp", pattern="*.txt"))
print(f"search: {[f.path for f in files]}")
# 4. 查询状态
info = await sandbox.get_info()
print(f"state: {info.status.state}, expires: {info.expires_at}")
asyncio.run(main())说明:
- SDK 0.1.16的
timeout参数未透传到server,沙箱不会按该值自动过期;需要自动过期时通过REST请求体的timeout字段(秒,最小60)控制。- SDK 0.1.16的流式回调要求
on_stdout/on_stderr均为async函数且缺一不可,否则收不到输出。api_key仅在使用了鉴权的Server部署下需要;use_server_proxy=True表示经Server代理访问沙箱。
管理快照
快照捕获沙箱在某一时刻的完整状态,可用于环境保存、批量克隆与故障回退。快照创建为同步操作,返回即携带最终状态(Ready或Failed),且创建过程中沙箱保持Running,失败时沙箱自动恢复运行。
【containerd运行时】 快照能力仅E2B运行时支持,本节不适用。
通过SandboxManager管理快照(创建、查询、列举、删除),通过Sandbox.create(snapshot_id=...)从快照恢复沙箱:
import asyncio
from datetime import timedelta
from opensandbox import Sandbox, SandboxManager
from opensandbox.config import ConnectionConfig
from opensandbox.models.sandboxes import SnapshotFilter
config = ConnectionConfig(domain="<server-address>:80") # OpenSandbox Server地址
async def main():
# ---------- 创建快照 ----------
# 沙箱须处于Running状态,创建为同步操作,返回即携带最终状态(Ready或Failed)
async with await SandboxManager.create(connection_config=config) as manager:
snap = await manager.create_snapshot(
sandbox_id="<sandbox-id>", # 必填:源沙箱ID,沙箱须处于Running
name="my-snapshot", # 可选:快照名称
)
print(f"snapshot: {snap.id}, state: {snap.status.state}")
# ---------- 查询单个快照 ----------
info = await manager.get_snapshot(snap.id) # 必填:快照ID
print(f"state: {info.status.state}, reason: {info.status.reason}")
# ---------- 列举快照(支持按源沙箱、状态过滤与分页) ----------
snaps = await manager.list_snapshots(
SnapshotFilter(
sandbox_id="<sandbox-id>", # 可选:按源沙箱ID过滤
states=["READY"], # 可选:按状态过滤(READY/FAILED)
page_size=20, # 可选:每页数量,默认20
page=1, # 可选:页码,从1开始
)
)
for s in snaps.snapshot_infos:
print(f"{s.id}: {s.status.state}")
# ---------- 删除快照 ----------
await manager.delete_snapshot(snap.id) # 必填:快照ID
print(f"deleted: {snap.id}")
# ---------- 从快照恢复沙箱 ----------
# 快照必须处于Ready状态;E2B沙箱无execd进程,SDK自动跳过端点解析与健康检查
# snapshot_id与image二选一,恢复时只传snapshot_id,image由快照记录自动注入
sandbox = await Sandbox.create(
snapshot_id=snap.id, # 必填:源快照ID(与image二选一)
connection_config=config, # 必填:Server连接配置
timeout=timedelta(minutes=30), # 可选:自动过期时间,不传则不自动过期
resource={"cpu": "1", "memory": "2Gi"}, # 可选:资源规格(实际以SandboxGroup为准)
extensions={"sandboxGroup": "sg-1u1g"}, # 必填:目标SandboxGroup名称
)
print(f"restored sandbox: {sandbox.id}")
info = await sandbox.get_info()
print(f"state: {info.status.state}")
await sandbox.close()
asyncio.run(main())说明:
快照记录持久化存储,OpenSandbox Server重启后仍可查询和使用。被删除的快照无法再用于创建沙箱。快照创建后可通过预热将其分发到超节点缓存,进一步降低从快照创建沙箱的时延,详见快照预热。
查询与管理存量沙箱
from opensandbox.manager import SandboxManager
from opensandbox.models.sandboxes import SandboxFilter
async with await SandboxManager.create(connection_config=config) as manager:
# 列举运行中的沙箱
sandboxes = await manager.list_sandbox_infos(
SandboxFilter(states=["RUNNING"], page_size=10)
)
for info in sandboxes.sandbox_infos:
print(f"Found sandbox: {info.id}")
# 终止指定沙箱
await manager.kill_sandbox(info.id)沙箱状态说明
表7 沙箱状态流转
| 状态 | 说明 |
|---|---|
| PENDING | 创建中,尚未就绪。 |
| RUNNING | 运行中,可执行命令、操作文件、创建快照。 |
| PAUSING | 暂停中(瞬态),期间不接受新的暂停/恢复请求。 |
| PAUSED | 已暂停,进程挂起,资源占位已释放。 |
| RESUMING | 恢复中(瞬态)。 |
| TERMINATED | 已终止。 |
核心流转:PENDING → RUNNING ⇄(暂停/恢复)PAUSED → TERMINATED。
表8 快照状态
| 状态 | 说明 |
|---|---|
| Ready | 快照可用,可基于它创建沙箱。 |
| Failed | 快照创建失败,源沙箱已自动恢复运行。 |
常见错误说明
表9 常见错误场景
| 场景 | 错误码 | 处理建议 |
|---|---|---|
创建沙箱未传extensions.sandboxGroup | 400 | 补充sandboxGroup字段,值为目标SandboxGroup名称。 |
创建沙箱未传image/snapshot_id,或两者同时传入 | 400 | 两者恰好提供一个。 |
image模式下未传entrypoint或resource_limits(REST直接调用) | 400 | 补充必选参数;或使用SDK(有默认值)。 |
timeout低于60秒或超过Server配置上限 | 400 | 调整timeout取值。 |
| 指定的E2B模板不存在(E2B运行时) | 500 | 确认模板已构建并注册到E2B环境,模板名与请求一致。 |
| 指定的容器镜像不存在(containerd运行时) | 500 | 确认镜像已推送到集群可访问的registry或在节点预拉取。 |
| 查询/删除不存在的沙箱或快照 | 404 | 核对ID是否正确、资源是否已被删除。 |
| 对非Running沙箱创建快照 | 409 | 先恢复沙箱到Running状态。 |
| 快照进行中重复发起快照 | 409 | 等待当前快照操作完成。 |
| 对运行中沙箱执行恢复 | 409 | 仅已暂停(PAUSED)的沙箱需要恢复。 |
| 暂停/恢复期间重复操作 | 409 | 状态处于PAUSING/RESUMING瞬态,等待流转完成。 |
| 暂停/恢复/快照操作返回不支持 | - | 该SandboxGroup使用containerd运行时,相关能力仅E2B运行时支持。 |
| 暂停失败(PauseFailed) | - | 检查目标Agent Pod是否就绪后重试。 |
快照预热
【containerd运行时】 快照预热依赖E2B快照能力,仅E2B运行时可用,本节不适用。
快照预热将已有快照主动分发并缓存到多个超节点,使基于该快照创建沙箱时无需现场拉取,降低冷启动时延。预热子系统内嵌于Controller进程,通过Controller的HTTP接口调用(默认:8080,API前缀/api/v1),适合外部集群管理系统对接。
表10 预热接口总览
| 方法 | 路径 | 用途 |
|---|---|---|
| POST | /api/v1/snapshot-warm-tasks | 创建预热任务(异步执行) |
| GET | /api/v1/snapshot-warm-tasks/{task_id} | 查询任务预热进度(分页) |
| GET | /api/v1/snapshots/cache-details | 查询各超节点已缓存的快照(分页) |
| GET | /api/v1/superpods/storage-details | 查询各超节点存储余量 |
所有响应携带X-Request-ID响应头(透传请求中的同名header,未传则自动生成),可用于问题追踪。错误响应统一为{"error": {"code", "message", "retryable"}}结构。
开启预热
预热子系统默认关闭,需在Controller启动参数中显式开启,以下参数均为必填,缺失任一将导致启动失败:
--warm-enabled=true
--warm-postgres-dsn=<PostgreSQL连接串> # 任务与预热详情持久化
--warm-etcd-endpoints=<MoonCake etcd地址> # 存储余量查询
--warm-superpod-ids=<逗号分隔的超节点ID列表> # 预热目标池
--warm-e2b-api-endpoint=<E2B API-Server地址> # 预热后端为e2b时必填预热后端通过--warm-backend-type指定,默认e2b,可选node(需配合--warm-node-port,默认44772)。数据库建表脚本见flux-sandbox仓库warm-migrations/0001_warm.sql。
创建预热任务
curl -X POST http://<controller>:8080/api/v1/snapshot-warm-tasks \
-H 'Content-Type: application/json' \
-d '{"snapshot_ids": ["snap-001", "snap-002", "snap-003"]}'响应201 Created:
{
"task_id": "warm-task-20260908-1786230000000000000-1",
"state": "WAIT",
"accepted_snapshot_count": 3,
"created_at": "2026-09-08T02:00:00Z"
}任务异步执行,状态流转:WAIT(已入队)→ WARMING(执行中)→ SUCCEED / FAILED。每个快照按负载均衡分配到若干超节点(默认5个),预热失败的超节点自动重试3次(间隔10秒),仅重试失败的超节点;任务整体超时30分钟。
snapshot_ids必填,不能含空串或重复项;任务按提交的完整数组执行,不支持分批。请求体上限64 MiB(约10万个ID),超限返回400。
查询预热进度
curl 'http://<controller>:8080/api/v1/snapshot-warm-tasks/<task_id>?page_size=100&page_num=1'响应中的summary反映整任务进度:
{
"total": 3,
"success_count": 2,
"failed_count": 0,
"in_progress_count": 1
}- 任务是否结束以
success_count + failed_count == total判断。 snapshots数组按提交顺序分页返回各快照的预热结果:warm_success=true表示该快照全部目标预热完成;warmed_superpod_ids列出已实际缓存该快照的超节点(预热中/失败的快照也会列出已成功的部分)。page_size默认1000、上限10000(越界返回400);page_num从1起。任务ID不存在返回404。
查询缓存分布与存储余量
# 各超节点已缓存的快照(只统计实际预热成功的超节点)
curl 'http://<controller>:8080/api/v1/snapshots/cache-details?page_size=1000&page_num=1'
# 各超节点MoonCake存储余量,可用于预热前容量评估
curl http://<controller>:8080/api/v1/superpods/storage-details- 缓存详情按超节点分组返回,包含
snapshot_id、category、cached_at等字段。 - 存储余量数据直读MoonCake etcd上报:
observed_at距今超过60秒视为过期,该超节点health=UNAVAILABLE;已配置的超节点始终会出现在响应中(无上报或数据过期的显示为UNAVAILABLE)。
典型调用流程
BASE=http://<controller>:8080/api/v1
# 1. 提交预热任务
TASK_ID=$(curl -s -X POST $BASE/snapshot-warm-tasks \
-H 'Content-Type: application/json' \
-d '{"snapshot_ids": ["snap-001", "snap-002"]}' | jq -r .task_id)
# 2. 轮询进度,直到全部快照出结果
curl -s "$BASE/snapshot-warm-tasks/$TASK_ID?page_size=1000" | jq .summary
# 3. 查看缓存分布与超节点存储水位
curl -s "$BASE/snapshots/cache-details" | jq .
curl -s "$BASE/superpods/storage-details" | jq .预热常见错误
表11 预热常见错误
| 错误码 | 触发场景 | 可重试 |
|---|---|---|
| 400 INVALID_ARGUMENT | 请求体非法或超限;snapshot_ids为空、含空串、含重复;分页参数越界 | 否 |
| 404 NOT_FOUND | 任务ID不存在 | 否 |
| 503 UNAVAILABLE | PostgreSQL / MoonCake etcd暂不可用 | 是 |
retryable=true的错误可稍后原样重试。
使用限制
- 预热接口无认证,部署时需通过网络策略限制访问面。
- 任务创建后不支持取消或删除;失败快照需重新提交任务(幂等:已成功的快照不会被重复预热下发)。
- 当前通过API提交的快照均按
REGULAR类别处理,每个快照默认预热到5个超节点。
查看运行状态
# 查看SandboxGroup状态与容量
kubectl get sandboxgroup -n flux-system -o wide
# 查看沙箱实例
kubectl get sandbox -n flux-systemSandboxGroup就绪后status.phase为Ready,可通过kubectl describe sandboxgroup查看Conditions定位异常。沙箱运行状态通过SDK的get_info或管理接口感知。
升级与卸载
# 升级
helm upgrade flux-sandbox ./charts/flux-sandbox --namespace flux-system -f my-values.yaml
# 卸载
helm uninstall flux-sandbox --namespace flux-system说明:
CRD不随helm uninstall删除(Helm的既定行为)。如需删除,执行kubectl delete -f charts/flux-sandbox/crds/,注意这会级联删除所有沙箱相关CR。
附录
SandboxGroup Spec字段参考
表12 SandboxGroup Spec字段
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
capacity | object | 是 | 容量声明,字段见表4(agentPodMin、agentPodMax、maxSandboxesPerPod必填)。 |
sandboxResources | object | 是 | 每沙箱占用的资源规格(requests/limits),是资源规格的唯一权威来源。requests为节点调度账本占位,可小于limits实现CPU/内存超分(缺省等于limits即不超分);limits为沙箱实例与Agent Pod的资源上限。支持hugepages-2Mi等资源名(E2B运行时大页,须与模板ram-mb一致且requests等于limits)。创建后不可变。 |
agentEnv | map | 否 | 注入Agent Pod的环境变量,如RUNTIME_TYPE: "containerd"。 |
execdImage | string | 否(containerd运行时下必填) | execd镜像地址。containerd运行时下必填(设置后自动注入execd init容器);E2B运行时无需设置。 |
agentPodScheduling | object | 否 | Agent Pod的节点调度约束(tolerations/nodeSelector/affinity),可将沙箱组定向到特定节点池。 |
schedulingPolicy | object | 否 | 沙箱在Agent Pod间的放置策略(strategy、weights、topologyAffinity),可选的高级配置。 |
SandboxGroup Status字段参考
表13 SandboxGroup Status字段
| 字段 | 说明 |
|---|---|
phase | 当前生命周期阶段,就绪后为Ready。 |
observedGeneration | Controller已观测到的代数,可用于判断变更是否已生效。 |
subSandboxGroupsCount | 已拆分的分片数量。 |
totalAgentPods | 当前Agent Pod总数。 |
totalCapacity | 当前沙箱总容量。 |
conditions | 描述组状态的条件,用于排障定位。 |
SDK操作与REST API对照
非Python用户或网关集成场景可直接调用OpenSandbox Server REST API:
表14 SDK操作与REST API对照
| 操作 | REST API |
|---|---|
| 创建沙箱 | POST /v1/sandboxes(202) |
| 查询沙箱 | GET /v1/sandboxes/{id} |
| 列举沙箱 | GET /v1/sandboxes |
| 删除沙箱 | DELETE /v1/sandboxes/{id} |
| 更新沙箱元数据 | PATCH /v1/sandboxes/{id}/metadata |
| 续期沙箱 | POST /v1/sandboxes/{id}/renew-expiration |
| 暂停沙箱 | POST /v1/sandboxes/{id}/pause |
| 恢复沙箱 | POST /v1/sandboxes/{id}/resume |
| 获取端口访问地址 | GET /v1/sandboxes/{id}/endpoints/{port} |
| 创建快照 | POST /v1/sandboxes/{id}/snapshots |
| 查询快照 | GET /v1/snapshots/{id} |
| 列举快照 | GET /v1/snapshots |
| 删除快照 | DELETE /v1/snapshots/{id} |
创建沙箱请求示例(字段与表5/表6对应):
curl -s -X POST "http://${SERVER}/v1/sandboxes" \
-H "Content-Type: application/json" \
-d "{
\"image\":{\"uri\":\"${TEMPLATE_OR_IMAGE}\"},
\"entrypoint\":[\"tail\",\"-f\",\"/dev/null\"],
\"resourceLimits\":{\"cpu\":\"1\",\"memory\":\"2Gi\"},
\"extensions\":{\"sandboxGroup\":\"${SG}\"},
\"timeout\":3600
}"说明:
- REST直接调用时,
image模式下entrypoint与resourceLimits为必填(SDK会自动填充默认值);timeout不传表示沙箱不自动过期,单位为秒,最小60。- SDK参数使用下划线命名(如
resource_limits),REST请求体字段使用驼峰命名(如resourceLimits)。
FAQ
SandboxGroup创建后一直未就绪
可能原因
- Controller或Scheduler组件未正常运行。
- Agent Pod无法调度(节点资源不足、污点未容忍)。
解决办法
执行
kubectl describe sandboxgroup <name> -n flux-system查看Conditions定位原因,并检查组件与Agent Pod状态:kubectl get pods -n flux-system。创建沙箱返回400,提示
extensions['sandboxGroup'] is required创建请求必须携带
extensions.sandboxGroup字段指定目标SandboxGroup,且该组已就绪(Ready)。创建沙箱返回400,提示entrypoint或resourceLimits必填
REST直接调用时
image模式下entrypoint、resourceLimits为必选参数(SDK有默认值,不会触发);按表5补充后重试。注意这两个值不影响实际生效规格,实际规格由SandboxGroup决定。E2B运行时创建沙箱失败,报"E2B runtime requires template manager but it is not configured"
E2B运行时依赖E2B Template Manager解析模板。检查:
- chart的
e2b.enabled为true(默认); - 已通过
e2b.existingSecret(或e2b.hostPath)提供包含teamApiKey的E2Bconfig.json; e2b.apiEndpoint指向的Template Manager服务可达。
- chart的
E2B运行时创建沙箱失败,提示模板不存在
创建请求的
image必须是已构建注册的E2B模板名。确认模板已在E2B环境中存在,且名称完全一致。containerd运行时创建沙箱失败
标准K8s环境中使用containerd运行时需完成:SandboxGroup设置
agentEnv.RUNTIME_TYPE=containerd与execdImage;沙箱镜像与execd镜像可拉取。详见各章节的containerd运行时说明。SDK创建沙箱时传入的资源参数不生效
沙箱资源规格由SandboxGroup的
sandboxResources统一决定,是唯一权威来源,调用方传入的资源参数会被忽略(但仍需通过Server参数校验)。如需调整规格,请修改对应SandboxGroup。E2B沙箱内没有看到传入的entrypoint或环境变量
属预期行为。E2B运行时下
entrypoint、env不注入microVM,沙箱内进程与环境由E2B模板决定;需要定制环境请通过模板构建。containerd运行时下两者均生效。沙箱创建时镜像拉取失败(containerd运行时)
确认沙箱运行镜像(如
python:3.11)和execd镜像已推送到集群可访问的registry,或在所有Worker节点上预拉取。E2B运行时不涉及镜像拉取,模板在构建阶段已固化。从快照创建沙箱返回409
快照必须处于
Ready状态。查询快照状态确认后重试;Failed状态的快照不可用。暂停/恢复/快照接口返回不支持
这些能力仅E2B运行时支持。若SandboxGroup使用containerd运行时(
RUNTIME_TYPE=containerd),请改用E2B运行时的SandboxGroup。暂停沙箱后资源占用是否释放
是。沙箱进入
PAUSED状态后释放资源占位,同容量的新沙箱可以立即创建;恢复时基于暂停时生成的快照重建运行状态。快照创建失败会影响原沙箱吗
不会。快照创建过程中沙箱保持
Running;创建失败时沙箱自动恢复运行,快照状态置为Failed。沙箱内侦听的端口如何从外部访问
通过
sandbox.get_endpoint(port)获取访问地址后直接连接,详见访问沙箱端口。执行
helm uninstall后CRD仍然存在这是Helm的既定行为。如需彻底清理,手动执行
kubectl delete -f charts/flux-sandbox/crds/,注意会级联删除所有沙箱相关CR。预热接口返回503 UNAVAILABLE
预热子系统依赖的PostgreSQL或MoonCake etcd暂不可用,可稍后原样重试;持续出现时检查预热相关启动参数与依赖服务连通性。
预热任务能否取消
不支持取消或删除任务。个别快照预热失败时需重新提交任务,已成功的快照不会被重复预热下发。