版本:v26.09

FluxSandbox沙箱调度引擎 ​

特性介绍 ​

FluxSandbox是高性能Kubernetes沙箱调度引擎,作为OpenSandbox的fluxruntime后端,面向AI Agent工作负载提供低时延、高吞吐的沙箱生命周期管理能力。

FluxSandbox不直接暴露API,以OpenSandbox为使用入口。用户使用OpenSandbox SDK创建沙箱,由OpenSandbox Server通过gRPC转发给FluxSandbox完成调度与生命周期管理,整体链路为:

text
用户(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)后即可创建沙箱。
SubSandboxGroupSandboxGroup的自动分片,由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模板)。
  • 已安装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镜像均可直接从官方仓库获取。

  1. 创建配置文件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时建议直接使用官方镜像。
  2. 使用Chart部署Server:

在线部署(集群节点可访问镜像仓库):

bash
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镜像:

bash
# 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/

在目标节点导入镜像:

bash
ctr -n k8s.io images import /root/opensandbox-server-image.tar
crictl images | grep flux-server

在控制节点安装。与在线部署相比仅追加一个server.image.pullPolicy=Never:离线节点上kubelet只查本地镜像,镜像名需与导入节点的完全一致,缺失时报ErrImageNeverPull:

bash
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前缀。

在线部署(集群节点可访问镜像仓库):

bash
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包,并拉取、导出四个组件镜像:

bash
# 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节点):

bash
# 集群运行时为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一致):

bash
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:

bash
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.replicaCount3调度器分片数
e2b.enabledtrue是否注入E2B Template Manager配置;纯containerd环境设置为false
e2b.apiEndpointhttp://api.e2b.svc.cluster.local:3000E2B Template Manager地址
e2b.existingSecret""存放E2B config.json的Secret名,推荐用Secret管理E2B凭据
e2b.hostPath/root/.e2b遗留方式:宿主机上存放config.json的目录
watcher.args.criSocket/run/cri-multiplex.sockwatcher扫描的E2B CRI socket;置空禁用E2B孤儿扫描
watcher.args.containerdSocket""watcher扫描的containerd socket;containerd集群设为/run/containerd/containerd.sock
*.nodeSelector / *.tolerations{}各组件调度约束

完整values说明见flux-sandbox Chart README。

方式二:原始清单 ​

bash
# 创建命名空间
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/

验证部署 ​

bash
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=120s

E2B运行时还应确认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后即可创建沙箱。

  1. 执行kubectl apply -f -创建SandboxGroup :

    bash
    kubectl 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部署对应values controller.args.maxPodPerSubSandboxGroup,默认100)不改变总容量,只控制拆分粒度:调大可减少分片数,调小可将分片摊到更多调度器实例上均衡负载。若不希望拆分,将其调大到不小于agentPodMax即可(如上例设为200)。
  2. 等待SandboxGroup就绪:

    bash
    kubectl wait --for=condition=Ready sandboxgroup sg-1u1g -n flux-system --timeout=120s

containerd运行时:使用containerd运行时时,SandboxGroup需显式声明运行时类型与execd镜像(execd承载SDK的命令执行,必填):

yaml
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超分):

yaml
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中声明对应的大页资源,使节点大页内存进入调度账本:

yaml
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内的进程,模板需满足:

  1. 模板已通过E2B模板机制构建并注册到Template Manager可访问的E2B环境中,模板名全局可用(如ubuntu-22-04-custom)。
  2. 模板内置envd守护进程,承载SDK的命令执行、文件操作等能力(官方基础模板已内置)。
  3. 沙箱内实际运行的进程与环境以模板定义为准(创建请求中的entrypoint、env不会注入microVM)。

创建请求中的模板名不存在时,Template Manager解析失败,创建请求返回错误。

containerd运行时:image是标准容器镜像引用,需确保镜像在所有Worker节点可拉取:

bash
# 沙箱运行时镜像(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仓库直接安装 ​
bash
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版本与平台:

bash
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/

在内网机器上离线安装:

bash
tar xzf opensandbox-sdk-wheelhouse.tar.gz
pip install --no-index --find-links=wheelhouse wheelhouse/opensandbox-*.whl

输入图片说明说明:

  • --no-index强制pip仅从wheelhouse目录取包,不访问任何软件源。
  • 有网机器上构建时须保留.git目录,否则无法从git tag推导版本号。
验证安装 ​
bash
pip show opensandbox

版本号带dev与+g<commit-id>后缀(如0.1.14.dev26+g6066bc24)即为适配版;版本号为干净的0.1.x则说明安装的是PyPI官方包,须重新安装。

bash
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,创建沙箱:

python
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())

删除沙箱:

bash
python3 kill_sandbox.py <沙箱ID>
python
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。

创建沙箱 ​

从模板/镜像创建:

python
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运行时):

python
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:

bash
kubectl port-forward -n flux-system svc/opensandbox-server 8080:80

完整示例代码:

python
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=...)从快照恢复沙箱:

python
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重启后仍可查询和使用。被删除的快照无法再用于创建沙箱。快照创建后可通过预热将其分发到超节点缓存,进一步降低从快照创建沙箱的时延,详见快照预热。

查询与管理存量沙箱 ​

python
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.sandboxGroup400补充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启动参数中显式开启,以下参数均为必填,缺失任一将导致启动失败:

text
--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。

创建预热任务 ​

bash
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:

json
{
  "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。

查询预热进度 ​

bash
curl 'http://<controller>:8080/api/v1/snapshot-warm-tasks/<task_id>?page_size=100&page_num=1'

响应中的summary反映整任务进度:

json
{
  "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。

查询缓存分布与存储余量 ​

bash
# 各超节点已缓存的快照(只统计实际预热成功的超节点)
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)。

典型调用流程 ​

bash
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 UNAVAILABLEPostgreSQL / MoonCake etcd暂不可用是

retryable=true的错误可稍后原样重试。

使用限制 ​

  • 预热接口无认证,部署时需通过网络策略限制访问面。
  • 任务创建后不支持取消或删除;失败快照需重新提交任务(幂等:已成功的快照不会被重复预热下发)。
  • 当前通过API提交的快照均按REGULAR类别处理,每个快照默认预热到5个超节点。

查看运行状态 ​

bash
# 查看SandboxGroup状态与容量
kubectl get sandboxgroup -n flux-system -o wide

# 查看沙箱实例
kubectl get sandbox -n flux-system

SandboxGroup就绪后status.phase为Ready,可通过kubectl describe sandboxgroup查看Conditions定位异常。沙箱运行状态通过SDK的get_info或管理接口感知。

升级与卸载 ​

bash
# 升级
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字段

字段类型必填说明
capacityobject是容量声明,字段见表4(agentPodMin、agentPodMax、maxSandboxesPerPod必填)。
sandboxResourcesobject是每沙箱占用的资源规格(requests/limits),是资源规格的唯一权威来源。requests为节点调度账本占位,可小于limits实现CPU/内存超分(缺省等于limits即不超分);limits为沙箱实例与Agent Pod的资源上限。支持hugepages-2Mi等资源名(E2B运行时大页,须与模板ram-mb一致且requests等于limits)。创建后不可变。
agentEnvmap否注入Agent Pod的环境变量,如RUNTIME_TYPE: "containerd"。
execdImagestring否(containerd运行时下必填)execd镜像地址。containerd运行时下必填(设置后自动注入execd init容器);E2B运行时无需设置。
agentPodSchedulingobject否Agent Pod的节点调度约束(tolerations/nodeSelector/affinity),可将沙箱组定向到特定节点池。
schedulingPolicyobject否沙箱在Agent Pod间的放置策略(strategy、weights、topologyAffinity),可选的高级配置。

SandboxGroup Status字段参考 ​

表13 SandboxGroup Status字段

字段说明
phase当前生命周期阶段,就绪后为Ready。
observedGenerationController已观测到的代数,可用于判断变更是否已生效。
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对应):

bash
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 ​

  1. SandboxGroup创建后一直未就绪

    可能原因

    • Controller或Scheduler组件未正常运行。
    • Agent Pod无法调度(节点资源不足、污点未容忍)。

    解决办法

    执行kubectl describe sandboxgroup <name> -n flux-system查看Conditions定位原因,并检查组件与Agent Pod状态:kubectl get pods -n flux-system。

  2. 创建沙箱返回400,提示extensions['sandboxGroup'] is required

    创建请求必须携带extensions.sandboxGroup字段指定目标SandboxGroup,且该组已就绪(Ready)。

  3. 创建沙箱返回400,提示entrypoint或resourceLimits必填

    REST直接调用时image模式下entrypoint、resourceLimits为必选参数(SDK有默认值,不会触发);按表5补充后重试。注意这两个值不影响实际生效规格,实际规格由SandboxGroup决定。

  4. E2B运行时创建沙箱失败,报"E2B runtime requires template manager but it is not configured"

    E2B运行时依赖E2B Template Manager解析模板。检查:

    • chart的e2b.enabled为true(默认);
    • 已通过e2b.existingSecret(或e2b.hostPath)提供包含teamApiKey的E2B config.json;
    • e2b.apiEndpoint指向的Template Manager服务可达。
  5. E2B运行时创建沙箱失败,提示模板不存在

    创建请求的image必须是已构建注册的E2B模板名。确认模板已在E2B环境中存在,且名称完全一致。

  6. containerd运行时创建沙箱失败

    标准K8s环境中使用containerd运行时需完成:SandboxGroup设置agentEnv.RUNTIME_TYPE=containerd与execdImage;沙箱镜像与execd镜像可拉取。详见各章节的containerd运行时说明。

  7. SDK创建沙箱时传入的资源参数不生效

    沙箱资源规格由SandboxGroup的sandboxResources统一决定,是唯一权威来源,调用方传入的资源参数会被忽略(但仍需通过Server参数校验)。如需调整规格,请修改对应SandboxGroup。

  8. E2B沙箱内没有看到传入的entrypoint或环境变量

    属预期行为。E2B运行时下entrypoint、env不注入microVM,沙箱内进程与环境由E2B模板决定;需要定制环境请通过模板构建。containerd运行时下两者均生效。

  9. 沙箱创建时镜像拉取失败(containerd运行时)

    确认沙箱运行镜像(如python:3.11)和execd镜像已推送到集群可访问的registry,或在所有Worker节点上预拉取。E2B运行时不涉及镜像拉取,模板在构建阶段已固化。

  10. 从快照创建沙箱返回409

    快照必须处于Ready状态。查询快照状态确认后重试;Failed状态的快照不可用。

  11. 暂停/恢复/快照接口返回不支持

    这些能力仅E2B运行时支持。若SandboxGroup使用containerd运行时(RUNTIME_TYPE=containerd),请改用E2B运行时的SandboxGroup。

  12. 暂停沙箱后资源占用是否释放

    是。沙箱进入PAUSED状态后释放资源占位,同容量的新沙箱可以立即创建;恢复时基于暂停时生成的快照重建运行状态。

  13. 快照创建失败会影响原沙箱吗

    不会。快照创建过程中沙箱保持Running;创建失败时沙箱自动恢复运行,快照状态置为Failed。

  14. 沙箱内侦听的端口如何从外部访问

    通过sandbox.get_endpoint(port)获取访问地址后直接连接,详见访问沙箱端口。

  15. 执行helm uninstall后CRD仍然存在

    这是Helm的既定行为。如需彻底清理,手动执行kubectl delete -f charts/flux-sandbox/crds/,注意会级联删除所有沙箱相关CR。

  16. 预热接口返回503 UNAVAILABLE

    预热子系统依赖的PostgreSQL或MoonCake etcd暂不可用,可稍后原样重试;持续出现时检查预热相关启动参数与依赖服务连通性。

  17. 预热任务能否取消

    不支持取消或删除任务。个别快照预热失败时需重新提交任务,已成功的快照不会被重复预热下发。