云沙箱模板支持通过阿里云 OpenAPI 创建、查询和删除,CreateTemplate 一次完成模板创建和首次构建,构建异步执行,轮询 GetTemplate 获取构建状态,模板就绪后即可用于创建沙箱。本文介绍如何安装 SDK、配置权限、创建模板并构建,以及如何配置镜像源、运行时网络和日志,帮助您以自动化方式构建和管理沙箱模板。
前提条件
已开通函数计算和云沙箱。
已在云沙箱控制台创建 Team,并获取 Team ID。
准备阿里云账号或 RAM 用户,并为调用身份配置模板 API 权限,参见下文权限配置。
确认模板所在地域,例如北京地域为
cn-beijing,全部地域参见支持地域。使用 E2B SDK 本地构建时,获取连接参数
E2B_API_KEY、E2B_API_URL和E2B_DOMAIN,SDK 会自动读取这三个环境变量。参数详情见 E2B SDK 接入参数说明。
权限配置
调用模板 API 的 RAM 身份需要以下权限:
{
"Version": "1",
"Statement": [
{
"Effect": "Allow",
"Action": [
"fcsandbox:CreateTemplate",
"fcsandbox:GetTemplate",
"fcsandbox:ListTemplates",
"fcsandbox:DeleteTemplate"
],
"Resource": "*"
}
]
}支持按 Team 精确授权 Template 资源:
acs:fcsandbox:<region>:<account-id>:teams/<team-id>/templates/*权限策略的配置方法参见配置 RAM 用户权限。E2B SDK 本地构建通过 API Key 鉴权,不涉及 RAM 权限配置。
安装 SDK
云沙箱 OpenAPI 当前支持 Python、TypeScript、Go 和 Java SDK。支持范围、安装方式和版本以阿里云 SDK & API 说明为准:
Python
python3 -m venv .venv
source .venv/bin/activate
pip install "alibabacloud_fcsandbox20260509"TypeScript
npm install @alicloud/fcsandbox20260509 @alicloud/openapi-clientGo
go mod init pop-template-demo
go get github.com/alibabacloud-go/fcsandbox-20260509Java
Maven 依赖(版本以 SDK 说明页面为准),在工程 pom.xml 中添加:
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>fcsandbox20260509</artifactId>
<version>1.4.0</version>
</dependency>新建工程时可使用以下最小 pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>demo</groupId>
<artifactId>pop-template-demo</artifactId>
<version>1.0.0</version>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>fcsandbox20260509</artifactId>
<version>1.4.0</version>
</dependency>
</dependencies>
</project>示例代码保存为工程下的 src/main/java/CreateTemplateDemo.java。
运行示例前设置以下环境变量:
export ALIBABA_CLOUD_ACCESS_KEY_ID="<YOUR-ACCESS-KEY-ID>"
export ALIBABA_CLOUD_ACCESS_KEY_SECRET="<YOUR-ACCESS-KEY-SECRET>"
# 使用 STS 临时凭证时还需要设置:
# export ALIBABA_CLOUD_SECURITY_TOKEN="<YOUR-SECURITY-TOKEN>"
export FCSANDBOX_REGION_ID="cn-beijing"
export FCSANDBOX_ENDPOINT="fcsandbox.cn-beijing.aliyuncs.com"
export FCSANDBOX_TEAM_ID="<team-id>"FCSANDBOX_ENDPOINT 不包含 https://。如果 SDK 已内置目标地域的 Endpoint,可以不设置该变量,由 SDK 根据 FCSANDBOX_REGION_ID 解析。
创建模板并构建
请求参数、返回参数和错误码以 OpenAPI 门户的 CreateTemplate - 创建模板 为准。
请求体 CreateTemplateInput 包含 name、team_id、runtime_config、build_config 四个字段,模板创建后立即开始首次构建:
runtime_config:最终模板的运行规格。cpu(核数)、memory_size(MB)、disk_size(MB)按需填写,缺省时使用默认规格;sandbox_config.image为必填的构建源镜像。build_config:构建方式。整段缺省时按源镜像自动推导——云沙箱官方镜像直接使用,不再构建;其他镜像自动执行 copy(处理并推送目标镜像)和 envdInject(注入沙箱运行依赖)。
镜像要求与各镜像源的约束和传参写法参见下文镜像要求与镜像源。
以下示例使用北京地域和云沙箱官方镜像(无需自备 ACR EE 实例)创建模板,轮询构建状态至就绪后删除模板:
Python
import os
import time
from alibabacloud_fcsandbox20260509 import models
from alibabacloud_fcsandbox20260509.client import Client as FCSandboxClient
from alibabacloud_tea_openapi import models as open_api_models
def require_env(name: str) -> str:
value = os.environ.get(name, "").strip()
if not value:
raise RuntimeError(f"缺少环境变量: {name}")
return value
config = open_api_models.Config(
access_key_id=require_env("ALIBABA_CLOUD_ACCESS_KEY_ID"),
access_key_secret=require_env("ALIBABA_CLOUD_ACCESS_KEY_SECRET"),
security_token=os.environ.get("ALIBABA_CLOUD_SECURITY_TOKEN"),
region_id=require_env("FCSANDBOX_REGION_ID"),
)
if os.environ.get("FCSANDBOX_ENDPOINT"):
config.endpoint = os.environ["FCSANDBOX_ENDPOINT"]
pop_client = FCSandboxClient(config)
team_id = require_env("FCSANDBOX_TEAM_ID")
from_image = "fc-e2b-registry.cn-beijing.cr.aliyuncs.com/runtime/code-interpreter-v1:v0.0.49"
# 1. 创建模板并提交首次构建,返回 templateID。
response = pop_client.create_template(
models.CreateTemplateRequest(
body=models.CreateTemplateInput(
name=f"pop-demo-{int(time.time())}",
team_id=team_id,
runtime_config=models.CreateTemplateRuntimeConfig(
sandbox_config=models.CreateTemplateSandboxConfig(image=from_image),
),
)
)
)
template_id = response.body.template_id
print(f"created templateID: {template_id}, request_id: {response.body.request_id}")
# 2. 轮询 GetTemplate 直到终态。
deadline = time.time() + 900
while True:
got = pop_client.get_template(template_id, models.GetTemplateRequest(team_id=team_id))
state = got.body.status.state
print(f"state: {state}")
if state in ("ready", "error"):
break
if time.time() > deadline:
raise SystemExit("构建超时")
time.sleep(5)
if state == "error":
raise SystemExit(f"构建失败: {got.body.status.reason.message}")
# 3. 模板就绪后即可用于创建沙箱;不再使用时删除模板清理资源。
pop_client.delete_template(template_id, models.DeleteTemplateRequest(team_id=team_id))
print(f"deleted templateID: {template_id}")TypeScript
import * as process from 'process';
import FCSandbox20260509, * as $FCSandbox20260509 from '@alicloud/fcsandbox20260509';
import * as $OpenApi from '@alicloud/openapi-client';
function requireEnv(name: string): string {
const value = (process.env[name] ?? '').trim();
if (!value) {
throw new Error(`缺少环境变量: ${name}`);
}
return value;
}
async function main() {
const config = new $OpenApi.Config({
accessKeyId: requireEnv('ALIBABA_CLOUD_ACCESS_KEY_ID'),
accessKeySecret: requireEnv('ALIBABA_CLOUD_ACCESS_KEY_SECRET'),
securityToken: (process.env['ALIBABA_CLOUD_SECURITY_TOKEN'] ?? '').trim() || undefined,
regionId: requireEnv('FCSANDBOX_REGION_ID'),
endpoint: (process.env['FCSANDBOX_ENDPOINT'] ?? '').trim() || undefined,
});
const popClient = new FCSandbox20260509(config);
const teamID = requireEnv('FCSANDBOX_TEAM_ID');
const fromImage = 'fc-e2b-registry.cn-beijing.cr.aliyuncs.com/runtime/code-interpreter-v1:v0.0.49';
// 1. 创建模板并提交首次构建,返回 templateID。
const created = await popClient.createTemplate(new $FCSandbox20260509.CreateTemplateRequest({
body: new $FCSandbox20260509.CreateTemplateInput({
name: `pop-demo-${Math.floor(Date.now() / 1000)}`,
teamID,
runtimeConfig: new $FCSandbox20260509.CreateTemplateRuntimeConfig({
sandboxConfig: new $FCSandbox20260509.CreateTemplateSandboxConfig({ image: fromImage }),
}),
}),
}));
const templateID = created.body.templateID;
console.log(`created templateID: ${templateID}, request_id: ${created.body.requestId}`);
// 2. 轮询 GetTemplate 直到终态。
const deadline = Date.now() + 900_000;
let state = '';
let reasonMessage = '';
while (true) {
const got = await popClient.getTemplate(templateID,
new $FCSandbox20260509.GetTemplateRequest({ teamID }));
state = got.body.status.state;
console.log(`state: ${state}`);
reasonMessage = got.body.status.reason?.message ?? '';
if (state === 'ready' || state === 'error') {
break;
}
if (Date.now() > deadline) {
throw new Error('构建超时');
}
await new Promise(resolve => setTimeout(resolve, 5000));
}
if (state === 'error') {
throw new Error(`构建失败: ${reasonMessage}`);
}
// 3. 模板就绪后即可用于创建沙箱;不再使用时删除模板清理资源。
await popClient.deleteTemplate(templateID,
new $FCSandbox20260509.DeleteTemplateRequest({ teamID }));
console.log(`deleted templateID: ${templateID}`);
}
main();Go
package main
import (
"fmt"
"os"
"strings"
"time"
openapiutil "github.com/alibabacloud-go/darabonba-openapi/v2/utils"
fcsandbox20260509 "github.com/alibabacloud-go/fcsandbox-20260509/client"
"github.com/alibabacloud-go/tea/tea"
)
// requireEnv 读取必填环境变量,缺失时直接 panic。
func requireEnv(name string) string {
value := strings.TrimSpace(os.Getenv(name))
if value == "" {
panic(fmt.Sprintf("缺少环境变量: %s", name))
}
return value
}
func main() {
config := &openapiutil.Config{
AccessKeyId: tea.String(requireEnv("ALIBABA_CLOUD_ACCESS_KEY_ID")),
AccessKeySecret: tea.String(requireEnv("ALIBABA_CLOUD_ACCESS_KEY_SECRET")),
RegionId: tea.String(requireEnv("FCSANDBOX_REGION_ID")),
}
if token := strings.TrimSpace(os.Getenv("ALIBABA_CLOUD_SECURITY_TOKEN")); token != "" {
config.SecurityToken = tea.String(token)
}
if endpoint := strings.TrimSpace(os.Getenv("FCSANDBOX_ENDPOINT")); endpoint != "" {
config.Endpoint = tea.String(endpoint)
}
popClient, err := fcsandbox20260509.NewClient(config)
if err != nil {
panic(err)
}
teamID := requireEnv("FCSANDBOX_TEAM_ID")
fromImage := "fc-e2b-registry.cn-beijing.cr.aliyuncs.com/runtime/code-interpreter-v1:v0.0.49"
// 1. 创建模板并提交首次构建,返回 templateID。
created, err := popClient.CreateTemplate(&fcsandbox20260509.CreateTemplateRequest{
Body: &fcsandbox20260509.CreateTemplateInput{
Name: tea.String(fmt.Sprintf("pop-demo-%d", time.Now().Unix())),
TeamID: tea.String(teamID),
RuntimeConfig: &fcsandbox20260509.CreateTemplateRuntimeConfig{
SandboxConfig: &fcsandbox20260509.CreateTemplateSandboxConfig{
Image: tea.String(fromImage),
},
},
},
})
if err != nil {
panic(err)
}
templateID := tea.StringValue(created.Body.TemplateID)
fmt.Printf("created templateID: %s, request_id: %s\n",
templateID, tea.StringValue(created.Body.RequestId))
// 2. 轮询 GetTemplate 直到终态。
deadline := time.Now().Add(900 * time.Second)
state := ""
reasonMessage := ""
for {
got, err := popClient.GetTemplate(tea.String(templateID), &fcsandbox20260509.GetTemplateRequest{
TeamID: tea.String(teamID),
})
if err != nil {
panic(err)
}
state = tea.StringValue(got.Body.Status.State)
fmt.Printf("state: %s\n", state)
if got.Body.Status.Reason != nil {
reasonMessage = tea.StringValue(got.Body.Status.Reason.Message)
}
if state == "ready" || state == "error" {
break
}
if time.Now().After(deadline) {
panic("构建超时")
}
time.Sleep(5 * time.Second)
}
if state == "error" {
panic(fmt.Sprintf("构建失败: %s", reasonMessage))
}
// 3. 模板就绪后即可用于创建沙箱;不再使用时删除模板清理资源。
if _, err := popClient.DeleteTemplate(tea.String(templateID), &fcsandbox20260509.DeleteTemplateRequest{
TeamID: tea.String(teamID),
}); err != nil {
panic(err)
}
fmt.Printf("deleted templateID: %s\n", templateID)
}Java
import com.aliyun.fcsandbox20260509.Client;
import com.aliyun.fcsandbox20260509.models.CreateTemplateBuildConfig;
import com.aliyun.fcsandbox20260509.models.CreateTemplateCopyAction;
import com.aliyun.fcsandbox20260509.models.CreateTemplateInput;
import com.aliyun.fcsandbox20260509.models.CreateTemplateLogConfig;
import com.aliyun.fcsandbox20260509.models.CreateTemplateRegistryAuthConfig;
import com.aliyun.fcsandbox20260509.models.CreateTemplateRegistryCertConfig;
import com.aliyun.fcsandbox20260509.models.CreateTemplateRegistryConfig;
import com.aliyun.fcsandbox20260509.models.CreateTemplateRegistryNetworkConfig;
import com.aliyun.fcsandbox20260509.models.CreateTemplateRequest;
import com.aliyun.fcsandbox20260509.models.CreateTemplateRuntimeConfig;
import com.aliyun.fcsandbox20260509.models.CreateTemplateSandboxConfig;
import com.aliyun.fcsandbox20260509.models.CreateTemplateVPCConfig;
import com.aliyun.fcsandbox20260509.models.DeleteTemplateRequest;
import com.aliyun.fcsandbox20260509.models.GetTemplateRequest;
import com.aliyun.fcsandbox20260509.models.GetTemplateResponse;
import com.aliyun.fcsandbox20260509.models.ListTemplatesRequest;
import com.aliyun.fcsandbox20260509.models.ListTemplatesResponse;
import com.aliyun.fcsandbox20260509.models.PublicTemplate;
import com.aliyun.teaopenapi.models.Config;
public class CreateTemplateDemo {
private static String requireEnv(String name) {
String value = System.getenv(name);
if (value == null || value.isBlank()) {
throw new RuntimeException("缺少环境变量: " + name);
}
return value.trim();
}
public static void main(String[] args) throws Exception {
Config config = new Config()
.setAccessKeyId(requireEnv("ALIBABA_CLOUD_ACCESS_KEY_ID"))
.setAccessKeySecret(requireEnv("ALIBABA_CLOUD_ACCESS_KEY_SECRET"))
.setRegionId(requireEnv("FCSANDBOX_REGION_ID"));
String securityToken = System.getenv("ALIBABA_CLOUD_SECURITY_TOKEN");
if (securityToken != null && !securityToken.isBlank()) {
config.setSecurityToken(securityToken.trim());
}
String endpoint = System.getenv("FCSANDBOX_ENDPOINT");
if (endpoint != null && !endpoint.isBlank()) {
config.setEndpoint(endpoint.trim());
}
Client popClient = new Client(config);
String teamID = requireEnv("FCSANDBOX_TEAM_ID");
String fromImage = "fc-e2b-registry.cn-beijing.cr.aliyuncs.com/runtime/code-interpreter-v1:v0.0.49";
// 1. 创建模板并提交首次构建,返回 templateID。
CreateTemplateInput body = new CreateTemplateInput()
.setName("pop-demo-" + System.currentTimeMillis() / 1000)
.setTeamID(teamID)
.setRuntimeConfig(new CreateTemplateRuntimeConfig()
.setSandboxConfig(new CreateTemplateSandboxConfig()
.setImage(fromImage)));
String templateID = popClient
.createTemplate(new CreateTemplateRequest().setBody(body))
.getBody().getTemplateID();
System.out.printf("created templateID: %s%n", templateID);
// 2. 轮询 GetTemplate 直到终态。
long deadline = System.currentTimeMillis() + 900_000;
String state = "";
GetTemplateResponse got = null;
while (true) {
got = popClient.getTemplate(templateID,
new GetTemplateRequest().setTeamID(teamID));
state = got.getBody().getStatus().getState();
System.out.printf("state: %s%n", state);
if ("ready".equals(state) || "error".equals(state)) {
break;
}
if (System.currentTimeMillis() > deadline) {
throw new RuntimeException("构建超时");
}
Thread.sleep(5000);
}
if ("error".equals(state)) {
throw new RuntimeException("构建失败: "
+ got.getBody().getStatus().getReason().getMessage());
}
// 3. 模板就绪后即可用于创建沙箱;不再使用时删除模板清理资源。
popClient.deleteTemplate(templateID,
new DeleteTemplateRequest().setTeamID(teamID));
System.out.printf("deleted templateID: %s%n", templateID);
}
}运行示例:
Python
python create_template.pyTypeScript
npx tsx create_template.tsGo
go mod tidy
go run main.goJava
mvn compile exec:java -Dexec.mainClass="CreateTemplateDemo"构建为异步执行,轮询耗时取决于镜像大小,通常在数十秒到数分钟之间。
镜像要求
构建自定义镜像模板有两条路径,镜像要求相同,但凭证与网络配置能力不同:通过 OpenAPI 构建,调用 CreateTemplate 由服务端完成构建,适合自动化脚本、基础设施即代码(IaC)和平台化集成场景;通过 E2B SDK 本地构建,在本地运行 Template.build 提交构建,适合首次验证和个人开发。
推荐基础镜像:Ubuntu 20.04、Debian 12(bookworm)及以上(仅作推荐,不强制发行版)。自定义镜像需满足下表要求,按“硬性 / 条件 / 推荐”分级:
级别 | 要求 | 不满足时影响 |
硬性 | 架构为 | 模板构建直接失败 |
硬性 | ACR 镜像不得开启镜像加速选项 | 模板构建直接失败 |
条件 | 镜像内存在固定路径 |
|
硬性 |
| 沙箱无法初始化默认用户,容器启动失败 |
条件 | PATH 中存在 | Python |
条件 | PATH 中存在 | JavaScript |
条件 | 按需提供: | 对应的 Git、SSH、HTTPS、编译安装操作不可用 |
推荐 | 系统 PATH 至少包含 | 登录 Shell 常用命令可能找不到 |
向自己的镜像仓库推送镜像时,不要向同一个 tag 推送内容不同的镜像。每个不同的镜像都必须使用新的唯一 tag,例如使用版本号、日期或提交 ID,并让模板固定引用对应 tag。
镜像源
构建源镜像分为四类:公共镜像、ACR 企业版、公网 OCI 仓库、VPC 内私有 OCI 仓库。每类的准备、约束与传参写法如下;示例均为完整的创建模板调用,可直接替换本文“创建模板并构建”示例中的第 1 步,其余步骤(轮询、删除)保持不变。
公共镜像
使用云沙箱官方镜像无需准备 ACR EE 实例,无需凭证与网络配置。官方镜像在全部支持地域均有提供,地址格式为 fc-e2b-registry.<地域>.cr.aliyuncs.com/runtime/<镜像名>:<tag>,地域清单参见支持地域。以北京地域为例:
fc-e2b-registry.cn-beijing.cr.aliyuncs.com/runtime/base:v0.0.49
fc-e2b-registry.cn-beijing.cr.aliyuncs.com/runtime/code-interpreter-v1:v0.0.49约束:
只能引用已发布的官方 tag,不能向官方仓库推送镜像。
build_config缺省时官方镜像直接使用,不再触发构建。镜像地址的地域须与
FCSANDBOX_REGION_ID一致。
传参示例:
Python
response = pop_client.create_template(
models.CreateTemplateRequest(
body=models.CreateTemplateInput(
name=f"pop-demo-{int(time.time())}",
team_id=team_id,
runtime_config=models.CreateTemplateRuntimeConfig(
sandbox_config=models.CreateTemplateSandboxConfig(
image="fc-e2b-registry.cn-beijing.cr.aliyuncs.com/runtime/code-interpreter-v1:v0.0.49",
),
),
)
)
)TypeScript
const created = await popClient.createTemplate(new $FCSandbox20260509.CreateTemplateRequest({
body: new $FCSandbox20260509.CreateTemplateInput({
name: `pop-demo-${Math.floor(Date.now() / 1000)}`,
teamID,
runtimeConfig: new $FCSandbox20260509.CreateTemplateRuntimeConfig({
sandboxConfig: new $FCSandbox20260509.CreateTemplateSandboxConfig({
image: 'fc-e2b-registry.cn-beijing.cr.aliyuncs.com/runtime/code-interpreter-v1:v0.0.49',
}),
}),
}),
}));Go
created, err := popClient.CreateTemplate(&fcsandbox20260509.CreateTemplateRequest{
Body: &fcsandbox20260509.CreateTemplateInput{
Name: tea.String(fmt.Sprintf("pop-demo-%d", time.Now().Unix())),
TeamID: tea.String(teamID),
RuntimeConfig: &fcsandbox20260509.CreateTemplateRuntimeConfig{
SandboxConfig: &fcsandbox20260509.CreateTemplateSandboxConfig{
Image: tea.String("fc-e2b-registry.cn-beijing.cr.aliyuncs.com/runtime/code-interpreter-v1:v0.0.49"),
},
},
},
})
if err != nil {
panic(err)
}Java
CreateTemplateInput body = new CreateTemplateInput()
.setName("pop-demo-" + System.currentTimeMillis() / 1000)
.setTeamID(teamID)
.setRuntimeConfig(new CreateTemplateRuntimeConfig()
.setSandboxConfig(new CreateTemplateSandboxConfig()
.setImage("fc-e2b-registry.cn-beijing.cr.aliyuncs.com/runtime/code-interpreter-v1:v0.0.49")));
String templateID = popClient
.createTemplate(new CreateTemplateRequest().setBody(body))
.getBody().getTemplateID();ACR 企业版(ACR EE)
前置准备:
在与云沙箱相同的阿里云账号和地域下,创建 ACR EE 实例(经济版暂不支持)。
为 ACR EE 实例添加专有网络,确保云沙箱可以通过 VPC 拉取镜像。
创建命名空间与镜像仓库并推送镜像,得到支持 VPC 访问的内网镜像地址,例如:
test-registry-vpc.cn-beijing.cr.aliyuncs.com/runtime/python:3.12-v1网络约束:
ACR EE 实例已至少绑定一个专有网络 VPC。
该 VPC 下至少有一个 vSwitch 位于函数计算支持的可用区。
ACR EE 镜像仓库、VPC、vSwitch 与云沙箱需位于同一地域,本文示例默认使用北京地域。
ACR EE 访问控制与 VPC 配置已允许对应网络访问。
VPC 网段需使用 RFC 1918 规定的私有地址段,即
10.0.0.0/8、172.16.0.0/12或192.168.0.0/16。不支持公网私用,详情请参见VPC 常见问题。VPC 下需至少配置一个自建的安全组,且该安全组的出方向规则需放行 443/TCP 端口。
函数计算支持的可用区会随地域和产品能力调整,最新列表请参考配置网络。
常见失败:
镜像拉取失败:依次检查镜像地址、地域、命名空间、仓库权限、VPC 绑定和访问控制配置。使用私有 ACR EE 镜像时,网络配置错误比 SDK 参数错误更常见。
拉取网络 vSwitch 可用区不支持:处理方式参见下文常见问题vSwitch 所在可用区不支持。
拉取方式:最简单的方式是只传 image,平台按镜像地址推导 ACR EE 实例与仓库类型,使用实例临时授权拉取,并从实例绑定的 VPC 推导拉取网络。需要显式指定实例、凭证或拉取网络时,再传 acr_instance_id、registry_config.auth_config 与 registry_config.network_config;实例推导出的 vSwitch 落在不支持的可用区导致拉取失败时,必须用 network_config 显式指定 VPC、vSwitch 和安全组三项配置。
传参示例一:只传 image,实例、凭证与拉取网络全部缺省推导:
Python
response = pop_client.create_template(
models.CreateTemplateRequest(
body=models.CreateTemplateInput(
name=f"pop-demo-{int(time.time())}",
team_id=team_id,
runtime_config=models.CreateTemplateRuntimeConfig(
sandbox_config=models.CreateTemplateSandboxConfig(
image="test-registry-vpc.cn-beijing.cr.aliyuncs.com/runtime/python:3.12-v1",
),
),
)
)
)TypeScript
const created = await popClient.createTemplate(new $FCSandbox20260509.CreateTemplateRequest({
body: new $FCSandbox20260509.CreateTemplateInput({
name: `pop-demo-${Math.floor(Date.now() / 1000)}`,
teamID,
runtimeConfig: new $FCSandbox20260509.CreateTemplateRuntimeConfig({
sandboxConfig: new $FCSandbox20260509.CreateTemplateSandboxConfig({
image: 'test-registry-vpc.cn-beijing.cr.aliyuncs.com/runtime/python:3.12-v1',
}),
}),
}),
}));Go
created, err := popClient.CreateTemplate(&fcsandbox20260509.CreateTemplateRequest{
Body: &fcsandbox20260509.CreateTemplateInput{
Name: tea.String(fmt.Sprintf("pop-demo-%d", time.Now().Unix())),
TeamID: tea.String(teamID),
RuntimeConfig: &fcsandbox20260509.CreateTemplateRuntimeConfig{
SandboxConfig: &fcsandbox20260509.CreateTemplateSandboxConfig{
Image: tea.String("test-registry-vpc.cn-beijing.cr.aliyuncs.com/runtime/python:3.12-v1"),
},
},
},
})
if err != nil {
panic(err)
}Java
CreateTemplateInput body = new CreateTemplateInput()
.setName("pop-demo-" + System.currentTimeMillis() / 1000)
.setTeamID(teamID)
.setRuntimeConfig(new CreateTemplateRuntimeConfig()
.setSandboxConfig(new CreateTemplateSandboxConfig()
.setImage("test-registry-vpc.cn-beijing.cr.aliyuncs.com/runtime/python:3.12-v1")));
String templateID = popClient
.createTemplate(new CreateTemplateRequest().setBody(body))
.getBody().getTemplateID();传参示例二:acr_instance_id、auth_config、network_config 都传,显式指定实例、凭证与拉取网络:
Python
response = pop_client.create_template(
models.CreateTemplateRequest(
body=models.CreateTemplateInput(
name=f"pop-demo-{int(time.time())}",
team_id=team_id,
runtime_config=models.CreateTemplateRuntimeConfig(
sandbox_config=models.CreateTemplateSandboxConfig(
image="test-registry-vpc.cn-beijing.cr.aliyuncs.com/runtime/python:3.12-v1",
acr_instance_id="cri-xxxxxx",
registry_config=models.CreateTemplateRegistryConfig(
auth_config=models.CreateTemplateRegistryAuthConfig(
user_name="<username>",
password="<password>",
),
network_config=models.CreateTemplateRegistryNetworkConfig(
vpc_id="vpc-xxxxxx",
v_switch_id="vsw-xxxxxx",
security_group_id="sg-xxxxxx",
),
),
),
),
)
)
)TypeScript
const created = await popClient.createTemplate(new $FCSandbox20260509.CreateTemplateRequest({
body: new $FCSandbox20260509.CreateTemplateInput({
name: `pop-demo-${Math.floor(Date.now() / 1000)}`,
teamID,
runtimeConfig: new $FCSandbox20260509.CreateTemplateRuntimeConfig({
sandboxConfig: new $FCSandbox20260509.CreateTemplateSandboxConfig({
image: 'test-registry-vpc.cn-beijing.cr.aliyuncs.com/runtime/python:3.12-v1',
acrInstanceId: 'cri-xxxxxx',
registryConfig: new $FCSandbox20260509.CreateTemplateRegistryConfig({
authConfig: new $FCSandbox20260509.CreateTemplateRegistryAuthConfig({
userName: '<username>',
password: '<password>',
}),
networkConfig: new $FCSandbox20260509.CreateTemplateRegistryNetworkConfig({
vpcId: 'vpc-xxxxxx',
vSwitchId: 'vsw-xxxxxx',
securityGroupId: 'sg-xxxxxx',
}),
}),
}),
}),
}),
}));Go
created, err := popClient.CreateTemplate(&fcsandbox20260509.CreateTemplateRequest{
Body: &fcsandbox20260509.CreateTemplateInput{
Name: tea.String(fmt.Sprintf("pop-demo-%d", time.Now().Unix())),
TeamID: tea.String(teamID),
RuntimeConfig: &fcsandbox20260509.CreateTemplateRuntimeConfig{
SandboxConfig: &fcsandbox20260509.CreateTemplateSandboxConfig{
Image: tea.String("test-registry-vpc.cn-beijing.cr.aliyuncs.com/runtime/python:3.12-v1"),
AcrInstanceId: tea.String("cri-xxxxxx"),
RegistryConfig: &fcsandbox20260509.CreateTemplateRegistryConfig{
AuthConfig: &fcsandbox20260509.CreateTemplateRegistryAuthConfig{
UserName: tea.String("<username>"),
Password: tea.String("<password>"),
},
NetworkConfig: &fcsandbox20260509.CreateTemplateRegistryNetworkConfig{
VpcId: tea.String("vpc-xxxxxx"),
VSwitchId: tea.String("vsw-xxxxxx"),
SecurityGroupId: tea.String("sg-xxxxxx"),
},
},
},
},
},
})
if err != nil {
panic(err)
}Java
CreateTemplateInput body = new CreateTemplateInput()
.setName("pop-demo-" + System.currentTimeMillis() / 1000)
.setTeamID(teamID)
.setRuntimeConfig(new CreateTemplateRuntimeConfig()
.setSandboxConfig(new CreateTemplateSandboxConfig()
.setImage("test-registry-vpc.cn-beijing.cr.aliyuncs.com/runtime/python:3.12-v1")
.setAcrInstanceId("cri-xxxxxx")
.setRegistryConfig(new CreateTemplateRegistryConfig()
.setAuthConfig(new CreateTemplateRegistryAuthConfig()
.setUserName("<username>")
.setPassword("<password>"))
.setNetworkConfig(new CreateTemplateRegistryNetworkConfig()
.setVpcId("vpc-xxxxxx")
.setVSwitchId("vsw-xxxxxx")
.setSecurityGroupId("sg-xxxxxx")))));
String templateID = popClient
.createTemplate(new CreateTemplateRequest().setBody(body))
.getBody().getTemplateID();公网 OCI 仓库
公网可达、TLS 证书正常的 OCI 仓库,无需 VPC 与证书配置。其中“公网可达”指函数计算能够从对应地域访问到该仓库,而不只是仓库对公网开放了域名。
约束:
只需
auth_config凭证两项(用户名/密码),无需network_config;凭证需要对该仓库有拉取权限。build_config缺省时构建产物会推送回同一仓库的新 tag,因此凭证还需要具备推送权限。network_config、cert_config仅在仓库证书需要跳过校验或有特殊网络要求时追加。
传参示例:
Python
response = pop_client.create_template(
models.CreateTemplateRequest(
body=models.CreateTemplateInput(
name=f"pop-demo-{int(time.time())}",
team_id=team_id,
runtime_config=models.CreateTemplateRuntimeConfig(
sandbox_config=models.CreateTemplateSandboxConfig(
image="registry.example.com/ns/repo:v1",
registry_config=models.CreateTemplateRegistryConfig(
auth_config=models.CreateTemplateRegistryAuthConfig(
user_name="<username>",
password="<password>",
),
),
),
),
)
)
)TypeScript
const created = await popClient.createTemplate(new $FCSandbox20260509.CreateTemplateRequest({
body: new $FCSandbox20260509.CreateTemplateInput({
name: `pop-demo-${Math.floor(Date.now() / 1000)}`,
teamID,
runtimeConfig: new $FCSandbox20260509.CreateTemplateRuntimeConfig({
sandboxConfig: new $FCSandbox20260509.CreateTemplateSandboxConfig({
image: 'registry.example.com/ns/repo:v1',
registryConfig: new $FCSandbox20260509.CreateTemplateRegistryConfig({
authConfig: new $FCSandbox20260509.CreateTemplateRegistryAuthConfig({
userName: '<username>',
password: '<password>',
}),
}),
}),
}),
}),
}));Go
created, err := popClient.CreateTemplate(&fcsandbox20260509.CreateTemplateRequest{
Body: &fcsandbox20260509.CreateTemplateInput{
Name: tea.String(fmt.Sprintf("pop-demo-%d", time.Now().Unix())),
TeamID: tea.String(teamID),
RuntimeConfig: &fcsandbox20260509.CreateTemplateRuntimeConfig{
SandboxConfig: &fcsandbox20260509.CreateTemplateSandboxConfig{
Image: tea.String("registry.example.com/ns/repo:v1"),
RegistryConfig: &fcsandbox20260509.CreateTemplateRegistryConfig{
AuthConfig: &fcsandbox20260509.CreateTemplateRegistryAuthConfig{
UserName: tea.String("<username>"),
Password: tea.String("<password>"),
},
},
},
},
},
})
if err != nil {
panic(err)
}Java
CreateTemplateInput body = new CreateTemplateInput()
.setName("pop-demo-" + System.currentTimeMillis() / 1000)
.setTeamID(teamID)
.setRuntimeConfig(new CreateTemplateRuntimeConfig()
.setSandboxConfig(new CreateTemplateSandboxConfig()
.setImage("registry.example.com/ns/repo:v1")
.setRegistryConfig(new CreateTemplateRegistryConfig()
.setAuthConfig(new CreateTemplateRegistryAuthConfig()
.setUserName("<username>")
.setPassword("<password>")))));
String templateID = popClient
.createTemplate(new CreateTemplateRequest().setBody(body))
.getBody().getTemplateID();VPC 内私有 OCI 仓库
Registry 域名仅 VPC 内可达的自建私有仓库,例如在 VPC 内的 ECS 上自建的 Harbor。
约束:
仓库必须以 HTTPS 提供服务且监听 443 端口,不支持自定义端口或 HTTP。
必须提供
registry_config.network_config(VPC 三项配置vpc_id/v_switch_id/security_group_id,同时提供)。使用自签证书的私有仓库还必须在
cert_config中声明跳过证书校验(insecure=True),否则构建在拉取源镜像阶段以 x509 证书错误失败。凭证通过
auth_config照常传入。
传参示例:
Python
response = pop_client.create_template(
models.CreateTemplateRequest(
body=models.CreateTemplateInput(
name=f"pop-demo-{int(time.time())}",
team_id=team_id,
runtime_config=models.CreateTemplateRuntimeConfig(
sandbox_config=models.CreateTemplateSandboxConfig(
image="registry.internal.example.com/ns/repo:v1",
registry_config=models.CreateTemplateRegistryConfig(
auth_config=models.CreateTemplateRegistryAuthConfig(
user_name="<username>",
password="<password>",
),
network_config=models.CreateTemplateRegistryNetworkConfig(
vpc_id="vpc-xxxxxx",
v_switch_id="vsw-xxxxxx",
security_group_id="sg-xxxxxx",
),
cert_config=models.CreateTemplateRegistryCertConfig(
insecure=True,
),
),
),
),
)
)
)TypeScript
const created = await popClient.createTemplate(new $FCSandbox20260509.CreateTemplateRequest({
body: new $FCSandbox20260509.CreateTemplateInput({
name: `pop-demo-${Math.floor(Date.now() / 1000)}`,
teamID,
runtimeConfig: new $FCSandbox20260509.CreateTemplateRuntimeConfig({
sandboxConfig: new $FCSandbox20260509.CreateTemplateSandboxConfig({
image: 'registry.internal.example.com/ns/repo:v1',
registryConfig: new $FCSandbox20260509.CreateTemplateRegistryConfig({
authConfig: new $FCSandbox20260509.CreateTemplateRegistryAuthConfig({
userName: '<username>',
password: '<password>',
}),
networkConfig: new $FCSandbox20260509.CreateTemplateRegistryNetworkConfig({
vpcId: 'vpc-xxxxxx',
vSwitchId: 'vsw-xxxxxx',
securityGroupId: 'sg-xxxxxx',
}),
certConfig: new $FCSandbox20260509.CreateTemplateRegistryCertConfig({
insecure: true,
}),
}),
}),
}),
}),
}));Go
created, err := popClient.CreateTemplate(&fcsandbox20260509.CreateTemplateRequest{
Body: &fcsandbox20260509.CreateTemplateInput{
Name: tea.String(fmt.Sprintf("pop-demo-%d", time.Now().Unix())),
TeamID: tea.String(teamID),
RuntimeConfig: &fcsandbox20260509.CreateTemplateRuntimeConfig{
SandboxConfig: &fcsandbox20260509.CreateTemplateSandboxConfig{
Image: tea.String("registry.internal.example.com/ns/repo:v1"),
RegistryConfig: &fcsandbox20260509.CreateTemplateRegistryConfig{
AuthConfig: &fcsandbox20260509.CreateTemplateRegistryAuthConfig{
UserName: tea.String("<username>"),
Password: tea.String("<password>"),
},
NetworkConfig: &fcsandbox20260509.CreateTemplateRegistryNetworkConfig{
VpcId: tea.String("vpc-xxxxxx"),
VSwitchId: tea.String("vsw-xxxxxx"),
SecurityGroupId: tea.String("sg-xxxxxx"),
},
CertConfig: &fcsandbox20260509.CreateTemplateRegistryCertConfig{
Insecure: tea.Bool(true),
},
},
},
},
},
})
if err != nil {
panic(err)
}Java
CreateTemplateInput body = new CreateTemplateInput()
.setName("pop-demo-" + System.currentTimeMillis() / 1000)
.setTeamID(teamID)
.setRuntimeConfig(new CreateTemplateRuntimeConfig()
.setSandboxConfig(new CreateTemplateSandboxConfig()
.setImage("registry.internal.example.com/ns/repo:v1")
.setRegistryConfig(new CreateTemplateRegistryConfig()
.setAuthConfig(new CreateTemplateRegistryAuthConfig()
.setUserName("<username>")
.setPassword("<password>"))
.setNetworkConfig(new CreateTemplateRegistryNetworkConfig()
.setVpcId("vpc-xxxxxx")
.setVSwitchId("vsw-xxxxxx")
.setSecurityGroupId("sg-xxxxxx"))
.setCertConfig(new CreateTemplateRegistryCertConfig()
.setInsecure(true)))));
String templateID = popClient
.createTemplate(new CreateTemplateRequest().setBody(body))
.getBody().getTemplateID();运行时网络
vpc_config 决定模板最终运行时沙箱所在的 VPC(vSwitch 支持多个),与构建阶段拉取源镜像的网络(registry_config.network_config)相互独立,按需分别配置。
关闭公网出口(internet_access=False)后,沙箱内访问公网会失败。传参示例:
Python
response = pop_client.create_template(
models.CreateTemplateRequest(
body=models.CreateTemplateInput(
name=f"pop-demo-{int(time.time())}",
team_id=team_id,
runtime_config=models.CreateTemplateRuntimeConfig(
internet_access=False,
vpc_config=models.CreateTemplateVPCConfig(
vpc_id="vpc-xxxxxx",
v_switch_ids=["vsw-xxxxxx"],
security_group_id="sg-xxxxxx",
),
sandbox_config=models.CreateTemplateSandboxConfig(
image="fc-e2b-registry.cn-beijing.cr.aliyuncs.com/runtime/code-interpreter-v1:v0.0.49",
),
),
)
)
)TypeScript
const created = await popClient.createTemplate(new $FCSandbox20260509.CreateTemplateRequest({
body: new $FCSandbox20260509.CreateTemplateInput({
name: `pop-demo-${Math.floor(Date.now() / 1000)}`,
teamID,
runtimeConfig: new $FCSandbox20260509.CreateTemplateRuntimeConfig({
internetAccess: false,
vpcConfig: new $FCSandbox20260509.CreateTemplateVPCConfig({
vpcId: 'vpc-xxxxxx',
vSwitchIds: ['vsw-xxxxxx'],
securityGroupId: 'sg-xxxxxx',
}),
sandboxConfig: new $FCSandbox20260509.CreateTemplateSandboxConfig({
image: 'fc-e2b-registry.cn-beijing.cr.aliyuncs.com/runtime/code-interpreter-v1:v0.0.49',
}),
}),
}),
}));Go
created, err := popClient.CreateTemplate(&fcsandbox20260509.CreateTemplateRequest{
Body: &fcsandbox20260509.CreateTemplateInput{
Name: tea.String(fmt.Sprintf("pop-demo-%d", time.Now().Unix())),
TeamID: tea.String(teamID),
RuntimeConfig: &fcsandbox20260509.CreateTemplateRuntimeConfig{
InternetAccess: tea.Bool(false),
VpcConfig: &fcsandbox20260509.CreateTemplateVPCConfig{
VpcId: tea.String("vpc-xxxxxx"),
VSwitchIds: []*string{tea.String("vsw-xxxxxx")},
SecurityGroupId: tea.String("sg-xxxxxx"),
},
SandboxConfig: &fcsandbox20260509.CreateTemplateSandboxConfig{
Image: tea.String("fc-e2b-registry.cn-beijing.cr.aliyuncs.com/runtime/code-interpreter-v1:v0.0.49"),
},
},
},
})
if err != nil {
panic(err)
}Java
CreateTemplateInput body = new CreateTemplateInput()
.setName("pop-demo-" + System.currentTimeMillis() / 1000)
.setTeamID(teamID)
.setRuntimeConfig(new CreateTemplateRuntimeConfig()
.setInternetAccess(false)
.setVpcConfig(new CreateTemplateVPCConfig()
.setVpcId("vpc-xxxxxx")
.setVSwitchIds(java.util.Arrays.asList("vsw-xxxxxx"))
.setSecurityGroupId("sg-xxxxxx"))
.setSandboxConfig(new CreateTemplateSandboxConfig()
.setImage("fc-e2b-registry.cn-beijing.cr.aliyuncs.com/runtime/code-interpreter-v1:v0.0.49")));
String templateID = popClient
.createTemplate(new CreateTemplateRequest().setBody(body))
.getBody().getTemplateID();日志配置
沙箱运行日志可通过 runtime_config 中的 log_config 投递到您账号下的 SLS,仅开放 project 与 logstore 两项,需与云沙箱位于同一地域:
Python
response = pop_client.create_template(
models.CreateTemplateRequest(
body=models.CreateTemplateInput(
name=f"pop-demo-{int(time.time())}",
team_id=team_id,
runtime_config=models.CreateTemplateRuntimeConfig(
log_config=models.CreateTemplateLogConfig(
project="<sls-project>",
logstore="<sls-logstore>",
),
sandbox_config=models.CreateTemplateSandboxConfig(
image="fc-e2b-registry.cn-beijing.cr.aliyuncs.com/runtime/code-interpreter-v1:v0.0.49",
),
),
)
)
)TypeScript
const created = await popClient.createTemplate(new $FCSandbox20260509.CreateTemplateRequest({
body: new $FCSandbox20260509.CreateTemplateInput({
name: `pop-demo-${Math.floor(Date.now() / 1000)}`,
teamID,
runtimeConfig: new $FCSandbox20260509.CreateTemplateRuntimeConfig({
logConfig: new $FCSandbox20260509.CreateTemplateLogConfig({
project: '<sls-project>',
logstore: '<sls-logstore>',
}),
sandboxConfig: new $FCSandbox20260509.CreateTemplateSandboxConfig({
image: 'fc-e2b-registry.cn-beijing.cr.aliyuncs.com/runtime/code-interpreter-v1:v0.0.49',
}),
}),
}),
}));Go
created, err := popClient.CreateTemplate(&fcsandbox20260509.CreateTemplateRequest{
Body: &fcsandbox20260509.CreateTemplateInput{
Name: tea.String(fmt.Sprintf("pop-demo-%d", time.Now().Unix())),
TeamID: tea.String(teamID),
RuntimeConfig: &fcsandbox20260509.CreateTemplateRuntimeConfig{
LogConfig: &fcsandbox20260509.CreateTemplateLogConfig{
Project: tea.String("<sls-project>"),
Logstore: tea.String("<sls-logstore>"),
},
SandboxConfig: &fcsandbox20260509.CreateTemplateSandboxConfig{
Image: tea.String("fc-e2b-registry.cn-beijing.cr.aliyuncs.com/runtime/code-interpreter-v1:v0.0.49"),
},
},
},
})
if err != nil {
panic(err)
}Java
CreateTemplateInput body = new CreateTemplateInput()
.setName("pop-demo-" + System.currentTimeMillis() / 1000)
.setTeamID(teamID)
.setRuntimeConfig(new CreateTemplateRuntimeConfig()
.setLogConfig(new CreateTemplateLogConfig()
.setProject("<sls-project>")
.setLogstore("<sls-logstore>"))
.setSandboxConfig(new CreateTemplateSandboxConfig()
.setImage("fc-e2b-registry.cn-beijing.cr.aliyuncs.com/runtime/code-interpreter-v1:v0.0.49")));
String templateID = popClient
.createTemplate(new CreateTemplateRequest().setBody(body))
.getBody().getTemplateID();目标镜像与构建复用
构建非官方镜像时,平台会临时创建一个函数计算函数,由它拉取源镜像、注入云沙箱运行所需的依赖,并推送为目标镜像;模板最终使用这个目标镜像创建沙箱运行环境。该临时函数创建在您自己的账号下,构建期间会产生少量函数计算费用。
目标镜像可以通过 build_config.copy.image 显式指定,例如推送到您镜像仓库的新 tag;目标仓库与源镜像所在仓库相同时无需重复提供目标侧凭证,推送到其他仓库时需要在 build_config.copy.registry_config 显式提供目标仓库的凭证与网络配置。多次构建指定完全相同的目标镜像时,平台会复用已推送的目标镜像:只有首次构建会推送,后续构建命中复用、不再推送新镜像,构建耗时显著缩短,且目标 tag 的 manifest digest 与首次构建一致。
传参示例(目标与源镜像同仓库;build_config.copy 提供时 enabled 必填):
Python
response = pop_client.create_template(
models.CreateTemplateRequest(
body=models.CreateTemplateInput(
name=f"pop-demo-{int(time.time())}",
team_id=team_id,
runtime_config=models.CreateTemplateRuntimeConfig(
sandbox_config=models.CreateTemplateSandboxConfig(
image="registry.example.com/ns/repo:v1",
registry_config=models.CreateTemplateRegistryConfig(
auth_config=models.CreateTemplateRegistryAuthConfig(
user_name="<username>",
password="<password>",
),
),
),
),
build_config=models.CreateTemplateBuildConfig(
copy=models.CreateTemplateCopyAction(
enabled=True,
image="registry.example.com/ns/repo:v1-fcsandbox",
),
),
)
)
)TypeScript
const created = await popClient.createTemplate(new $FCSandbox20260509.CreateTemplateRequest({
body: new $FCSandbox20260509.CreateTemplateInput({
name: `pop-demo-${Math.floor(Date.now() / 1000)}`,
teamID,
runtimeConfig: new $FCSandbox20260509.CreateTemplateRuntimeConfig({
sandboxConfig: new $FCSandbox20260509.CreateTemplateSandboxConfig({
image: 'registry.example.com/ns/repo:v1',
registryConfig: new $FCSandbox20260509.CreateTemplateRegistryConfig({
authConfig: new $FCSandbox20260509.CreateTemplateRegistryAuthConfig({
userName: '<username>',
password: '<password>',
}),
}),
}),
}),
buildConfig: new $FCSandbox20260509.CreateTemplateBuildConfig({
copy: new $FCSandbox20260509.CreateTemplateCopyAction({
enabled: true,
image: 'registry.example.com/ns/repo:v1-fcsandbox',
}),
}),
}),
}));Go
created, err := popClient.CreateTemplate(&fcsandbox20260509.CreateTemplateRequest{
Body: &fcsandbox20260509.CreateTemplateInput{
Name: tea.String(fmt.Sprintf("pop-demo-%d", time.Now().Unix())),
TeamID: tea.String(teamID),
RuntimeConfig: &fcsandbox20260509.CreateTemplateRuntimeConfig{
SandboxConfig: &fcsandbox20260509.CreateTemplateSandboxConfig{
Image: tea.String("registry.example.com/ns/repo:v1"),
RegistryConfig: &fcsandbox20260509.CreateTemplateRegistryConfig{
AuthConfig: &fcsandbox20260509.CreateTemplateRegistryAuthConfig{
UserName: tea.String("<username>"),
Password: tea.String("<password>"),
},
},
},
},
BuildConfig: &fcsandbox20260509.CreateTemplateBuildConfig{
Copy: &fcsandbox20260509.CreateTemplateCopyAction{
Enabled: tea.Bool(true),
Image: tea.String("registry.example.com/ns/repo:v1-fcsandbox"),
},
},
},
})
if err != nil {
panic(err)
}Java
CreateTemplateInput body = new CreateTemplateInput()
.setName("pop-demo-" + System.currentTimeMillis() / 1000)
.setTeamID(teamID)
.setRuntimeConfig(new CreateTemplateRuntimeConfig()
.setSandboxConfig(new CreateTemplateSandboxConfig()
.setImage("registry.example.com/ns/repo:v1")
.setRegistryConfig(new CreateTemplateRegistryConfig()
.setAuthConfig(new CreateTemplateRegistryAuthConfig()
.setUserName("<username>")
.setPassword("<password>")))))
.setBuildConfig(new CreateTemplateBuildConfig()
.setCopy(new CreateTemplateCopyAction()
.setEnabled(true)
.setImage("registry.example.com/ns/repo:v1-fcsandbox")));
String templateID = popClient
.createTemplate(new CreateTemplateRequest().setBody(body))
.getBody().getTemplateID();查询构建状态
通过 GetTemplate - 查询模板 查询单个模板的配置与构建状态。构建状态通过响应体的 status.state 获取,取值:
状态 | 说明 |
| 已受理,等待构建 |
| 构建进行中 |
| 构建成功,可用于创建沙箱 |
| 构建失败, |
进入 ready 或 error 终态后,响应包含 status.finished_at,即构建结束时间。
响应按实际配置回显字段,不使用平台默认值补齐;镜像拉取凭证等敏感字段不会回显。
管理模板
通过 ListTemplates - 查询模板列表 分页查询 Team 下的模板列表,按创建时间倒序返回;通过 DeleteTemplate - 删除模板 删除不再使用的模板:
Python
# 分页查询 Team 下的模板列表。
response = pop_client.list_templates(
models.ListTemplatesRequest(team_id=team_id)
)
for template in response.body.templates:
print(template.template_id, template.status.state, template.created_time)
# 还有下一页时,把 response.body.next_token 传入下一次请求的 next_token 继续查询。
# 删除模板。
pop_client.delete_template(template_id, models.DeleteTemplateRequest(team_id=team_id))TypeScript
// 分页查询 Team 下的模板列表。
const response = await popClient.listTemplates(
new $FCSandbox20260509.ListTemplatesRequest({ teamID }));
for (const template of response.body.templates ?? []) {
console.log(template.templateID, template.status.state, template.createdTime);
}
// 还有下一页时,把 response.body.nextToken 传入下一次请求的 nextToken 继续查询。
// 删除模板。
await popClient.deleteTemplate(templateID,
new $FCSandbox20260509.DeleteTemplateRequest({ teamID }));Go
// 分页查询 Team 下的模板列表。
resp, err := popClient.ListTemplates(&fcsandbox20260509.ListTemplatesRequest{
TeamID: tea.String(teamID),
})
if err != nil {
panic(err)
}
for _, template := range resp.Body.Templates {
fmt.Println(tea.StringValue(template.TemplateID),
tea.StringValue(template.Status.State),
tea.StringValue(template.CreatedTime))
}
// 还有下一页时,把 resp.Body.NextToken 传入下一次请求的 NextToken 继续查询。
// 删除模板。
_, err = popClient.DeleteTemplate(tea.String(templateID), &fcsandbox20260509.DeleteTemplateRequest{
TeamID: tea.String(teamID),
})
if err != nil {
panic(err)
}Java
// 分页查询 Team 下的模板列表。
ListTemplatesResponse response = popClient.listTemplates(
new ListTemplatesRequest().setTeamID(teamID));
for (PublicTemplate template : response.getBody().getTemplates()) {
System.out.printf("%s %s %s%n", template.getTemplateID(),
template.getStatus().getState(), template.getCreatedTime());
}
// 还有下一页时,把 response.getBody().getNextToken() 传入下一次请求的 nextToken 继续查询。
// 删除模板。
popClient.deleteTemplate(templateID,
new DeleteTemplateRequest().setTeamID(teamID));模板处于任意状态(含构建中)都可删除,删除会终止进行中的构建。
删除成功后模板名立即释放,可创建同名新模板。
平台内置模板(如
base)不允许删除。刚创建的模板可能延迟数秒才会出现在列表中,属于索引同步的正常现象,
GetTemplate不受影响。
使用限制
所有模板接口都必须传入 Team ID,缺失时返回
400 InvalidParameter。请求体采用严格 JSON 校验:未知字段、重复 key、显式
null都会返回400;请求体上限 1 MiB。同一 Team 中模板名称不能重复,重复创建返回
409 TemplateAlreadyExists。没有独立的更新接口,更新模板需要先删除旧模板再重新创建。
通过 E2B SDK 本地构建
E2B SDK 在本地定义模板并提交构建,适合首次验证和个人开发。安装依赖:
python3 -m venv .venv
source .venv/bin/activate
pip install e2b==2.32.0 e2b-code-interpreter==2.8.1 python-dotenv创建 .env 文件:
# 使用前请替换为自己的 API Key
E2B_API_KEY=e2b_xxx
FROM_IMAGE="fc-e2b-registry.cn-beijing.cr.aliyuncs.com/runtime/code-interpreter-v1:v0.0.49"
# 北京生产环境地址示例值
E2B_API_URL=https://api.cn-beijing.e2b.fc.aliyuncs.com
E2B_DOMAIN=cn-beijing.e2b.fc.aliyuncs.comFROM_IMAGE 默认为云沙箱官方镜像,无需凭证与网络配置;其他官方镜像地址参见上文镜像源。使用需要鉴权或网络配置的私有镜像源时,请改用本文 OpenAPI 方式创建模板。
运行脚本,将镜像构建为模板,并创建沙箱验证:
#!/usr/bin/env python3
import os
import time
from dotenv import load_dotenv
from e2b import Template, default_build_logger
from e2b_code_interpreter import Sandbox
load_dotenv()
FROM_IMAGE = os.getenv("FROM_IMAGE", "").strip()
RUN_CODE = "print('hello')"
# SDK 自动读取 E2B_API_KEY / E2B_API_URL / E2B_DOMAIN 环境变量。
build = Template.build(
Template().from_image(FROM_IMAGE),
name=f"template-{int(time.time())}",
cpu_count=2,
memory_mb=2048,
on_build_logs=default_build_logger(),
)
sandbox = Sandbox.create(template=build.template_id)
try:
print(f"template_id: {build.template_id}")
print(f"build_id: {build.build_id}")
print(f"sandbox_id: {sandbox.sandbox_id}")
print(f"sandbox_domain: {sandbox.sandbox_domain}")
print(f"envd_api_url: {sandbox.envd_api_url}")
execution = sandbox.run_code(RUN_CODE)
stdout = "".join(execution.logs.stdout or [])
stderr = "".join(execution.logs.stderr or [])
print(f"run_code code: {RUN_CODE}")
print(f"run_code stdout: {stdout.strip()}")
print(f"run_code stderr: {stderr.strip()}")
print(f"run_code error: {execution.error}")
if execution.error is not None:
raise RuntimeError(f"run_code 执行失败: {execution.error}")
if stdout.strip() != "hello":
raise RuntimeError(f"run_code stdout 不符合预期: {stdout!r}")
finally:
print(f"killing sandbox: {sandbox.sandbox_id}")
sandbox.kill()
print("sandbox killed")使用 CLI 查看模板并创建沙箱
如果已经安装 E2B CLI,可以通过模板列表确认构建状态:
export E2B_API_KEY=e2b_xxx
export E2B_API_URL=https://api.cn-beijing.e2b.fc.aliyuncs.com
export E2B_DOMAIN=cn-beijing.e2b.fc.aliyuncs.com
e2b template list模板状态为 ready 后,可以使用模板名称或 template_id 创建沙箱。模板名称可以是上一步本地构建产出的 template-{时间戳},my-code-interpreter-v1 为示例名:
e2b sandbox create my-code-interpreter-v1进入沙箱后建议先验证关键依赖:
env | sort | head
python3 --version
which python3模板名称建议
建议在模板名称中包含业务名、基础镜像、关键依赖版本或日期,例如 agent-python313-20260531。生产环境避免覆盖正在使用的模板,先创建新模板并灰度验证。
E2B SDK 本地构建适合使用官方镜像快速验证。构建拉取网络、运行时 VPC 等配置需要在创建模板时设置,请改用本文 OpenAPI 方式创建模板,使用 registry_config.network_config、vpc_config 字段,参见上文镜像源与运行时网络。
常见问题
排查接口问题时,优先查看 SDK 异常对象中的 code、message 和 request_id;联系技术支持时请提供 request_id。
返回 InvalidParameter 错误码
检查 Team ID 是否缺失,以及请求体是否包含未知字段、重复 key、显式 null,或传入了暂不支持的 start_command、ready_command 字段。
返回 TemplateAlreadyExists 错误码
同一 Team 中已存在同名模板。可通过 ListTemplates 查询确认,不再使用后通过 DeleteTemplate 删除,或更换模板名称。
返回 TemplateNotFound 错误码
模板不存在、已删除,或不属于当前 Team。
返回 SignatureDoesNotMatch 错误
签名校验失败。常见原因为 AccessKey Secret 不正确、STS 临时凭证过期或本机时间偏差过大;使用官方 SDK 发起调用可避免手工构造签名带来的问题。
status.state 为 error
构建失败,查看 status.reason.message 中的具体原因,常见为镜像拉取失败、镜像不满足要求或 VPC 网络配置错误。镜像源的约束与传参写法参见上文“镜像源”,vSwitch 可用区问题参见下文vSwitch 所在可用区不支持。
模板构建慢
优先检查自己的 ACR EE 镜像体积、网络链路、镜像层缓存和 ACR EE 私网访问配置。基础镜像越大,首次构建通常越慢。
如果只是排查模板构建链路是否正常,可以临时改用云沙箱官方镜像做对照验证。官方镜像已包含云沙箱运行依赖,通常更容易排除镜像适配问题。
vSwitch 所在可用区不支持
如果构建或运行过程中遇到 vSwitch is in unsupported zone,说明当前选择的 vSwitch 所在可用区不被函数计算支持。
处理方式:
根据错误信息确认当前 vSwitch 所在可用区。
从函数计算支持的可用区中选择一个目标可用区。
在同一个 VPC 中新建该可用区下的 vSwitch。
使用这个新的 vSwitch 配置函数计算或相关网络配置。
重新构建模板并创建沙箱验证。
同一个 VPC 内不同可用区的 vSwitch 默认内网互通。因此,即使业务资源在其他可用区,也可以在同一个 VPC 中新增一个函数计算支持可用区下的 vSwitch,用于完成函数计算侧网络接入。
沙箱创建成功但 run_code 失败
run_code 会调用镜像的 python3 命令执行代码,如果镜像不包含 python3 或相关依赖,可能会导致执行失败。建议先确认镜像是否包含代码解释器所需依赖。可以在沙箱中运行:
python3 --version
which python3
env | sort | head -50沙箱启动慢
如果沙箱启动较慢,可以先使用以下脚本将镜像压缩为单层镜像,再使用原镜像和压缩后的镜像进行 A/B 对照。测试时应保持模板规格、地域、并发量和镜像启动命令一致,并分别记录模板构建、沙箱创建和首次执行耗时。
脚本依赖 Docker 和 Python 3。运行前请确认源镜像已拉取到本地,并为目标镜像使用新的 tag。使用压缩后的镜像前请先完成验证。
#!/usr/bin/env bash
set -euo pipefail
PLATFORM="linux/amd64"
SRC="${1:?用法:$0 <源镜像> [目标 tag]}"
if [ "$#" -ge 2 ]; then
DST="$2"
elif [[ "$SRC" == *@* ]] || [[ "${SRC##*/}" != *:* ]]; then
echo "错误:源镜像使用 digest 或未指定 tag 时,请显式传入目标 tag。" >&2
exit 2
else
DST="${SRC}-flat"
fi
echo "==> 源镜像: $SRC"
echo "==> 目标镜像: $DST"
echo "==> 平台: $PLATFORM"
echo "==> 提取镜像元数据..."
RAW_CMD=$(docker inspect --format='{{json .Config.Cmd}}' "$SRC")
RAW_ENTRYPOINT=$(docker inspect --format='{{json .Config.Entrypoint}}' "$SRC")
RAW_ENV=$(docker inspect --format='{{json .Config.Env}}' "$SRC")
RAW_EXPOSE=$(docker inspect --format='{{json .Config.ExposedPorts}}' "$SRC")
RAW_WORKDIR=$(docker inspect --format='{{.Config.WorkingDir}}' "$SRC")
RAW_USER=$(docker inspect --format='{{.Config.User}}' "$SRC")
RAW_VOLUMES=$(docker inspect --format='{{json .Config.Volumes}}' "$SRC")
RAW_LABELS=$(docker inspect --format='{{json .Config.Labels}}' "$SRC")
RAW_STOPSIGNAL=$(docker inspect --format='{{.Config.StopSignal}}' "$SRC")
CHANGES=()
if [ "$RAW_CMD" != "null" ] && [ -n "$RAW_CMD" ]; then
CHANGES+=(--change "CMD $RAW_CMD")
fi
if [ "$RAW_ENTRYPOINT" != "null" ] && [ -n "$RAW_ENTRYPOINT" ]; then
CHANGES+=(--change "ENTRYPOINT $RAW_ENTRYPOINT")
fi
if [ "$RAW_ENV" != "null" ] && [ "$RAW_ENV" != "[]" ] && [ -n "$RAW_ENV" ]; then
while IFS= read -r env_entry; do
CHANGES+=(--change "ENV ${env_entry%%=*}=\"${env_entry#*=}\"")
done < <(echo "$RAW_ENV" | python3 -c '
import json
import sys
for entry in json.load(sys.stdin):
print(entry)
')
fi
if [ "$RAW_EXPOSE" != "null" ] && [ "$RAW_EXPOSE" != "{}" ] && [ -n "$RAW_EXPOSE" ]; then
while IFS= read -r port; do
CHANGES+=(--change "EXPOSE $port")
done < <(echo "$RAW_EXPOSE" | python3 -c '
import json
import sys
for key in json.load(sys.stdin):
print(key)
')
fi
if [ -n "$RAW_WORKDIR" ]; then
CHANGES+=(--change "WORKDIR $RAW_WORKDIR")
fi
if [ -n "$RAW_USER" ]; then
CHANGES+=(--change "USER $RAW_USER")
fi
if [ "$RAW_VOLUMES" != "null" ] && [ "$RAW_VOLUMES" != "{}" ] && [ -n "$RAW_VOLUMES" ]; then
while IFS= read -r volume; do
CHANGES+=(--change "VOLUME $volume")
done < <(echo "$RAW_VOLUMES" | python3 -c '
import json
import sys
for key in json.load(sys.stdin):
print(key)
')
fi
if [ "$RAW_LABELS" != "null" ] && [ "$RAW_LABELS" != "{}" ] && [ -n "$RAW_LABELS" ]; then
while IFS= read -r label; do
CHANGES+=(--change "LABEL $label")
done < <(echo "$RAW_LABELS" | python3 -c '
import json
import sys
for key, value in json.load(sys.stdin).items():
print(f"{key}=\"{value}\"")
')
fi
if [ -n "$RAW_STOPSIGNAL" ]; then
CHANGES+=(--change "STOPSIGNAL $RAW_STOPSIGNAL")
fi
ORIG_LAYERS=$(docker inspect --format='{{len .RootFS.Layers}}' "$SRC")
echo "==> 原始镜像层数:$ORIG_LAYERS"
echo "==> 开始压缩镜像层..."
CONTAINER_ID=$(docker create --platform "$PLATFORM" "$SRC" /bin/true)
trap 'docker rm -f "${CONTAINER_ID:-}" >/dev/null 2>&1 || true' EXIT
docker export "$CONTAINER_ID" | docker import \
--platform "$PLATFORM" \
"${CHANGES[@]}" \
- "$DST"
NEW_LAYERS=$(docker inspect --format='{{len .RootFS.Layers}}' "$DST")
ORIG_SIZE=$(docker inspect --format='{{.Size}}' "$SRC" | awk '{printf "%.0f MB", $1/1024/1024}')
NEW_SIZE=$(docker inspect --format='{{.Size}}' "$DST" | awk '{printf "%.0f MB", $1/1024/1024}')
ORIG_ARCH=$(docker inspect --format='{{.Architecture}}' "$SRC")
NEW_ARCH=$(docker inspect --format='{{.Architecture}}' "$DST")
echo ""
echo "==> 完成"
echo " 原始镜像:$ORIG_LAYERS 层,$ORIG_SIZE,架构=$ORIG_ARCH"
echo " 压缩镜像:$NEW_LAYERS 层,$NEW_SIZE,架构=$NEW_ARCH"
echo " Tag:$DST"使用示例
将上述脚本保存为 flatten-image.sh 并赋予执行权限。以下示例将源镜像压缩为单层镜像,并生成新的目标 tag:
chmod +x flatten-image.sh
./flatten-image.sh \
registry.example.com/runtime/python:3.12-v1 \
registry.example.com/runtime/python:3.12-v1-flat脚本执行完成后,确认输出的镜像层数和大小符合预期,再将目标镜像推送到镜像仓库,并在构建模板时使用目标镜像地址:
docker push registry.example.com/runtime/python:3.12-v1-flat如果压缩后的镜像在相同条件下启动明显更快,建议在后续构建镜像时合并构建步骤,减少镜像层数。