OpenSandbox Kotlin/Java SDK 实战指南:沙箱生命周期、命令流式执行、文件操作与客户端池化
【免费下载链接】OpenSandboxSecure, Fast, and Extensible Sandbox runtime for AI agents.项目地址: https://gitcode.com/GitHub_Trending/ope/OpenSandbox
OpenSandbox 面向 AI Agent 场景提供了多语言 SDK,其中 Kotlin SDK(同时可直接被 Java 调用)是连接 OpenSandbox Server、创建并管理安全沙箱的核心入口。本篇基于 Kotlin/Java SDK 官方文档 与仓库中sdks/sandbox/kotlin/的实际源码,完整覆盖安装、快速上手、生命周期钩子、命令/文件操作、连接与重试配置、出口网络策略和 Credential Vault,以及实验性的客户端侧SandboxPool池化机制,帮助你在 Java/Kotlin 应用中以可复制、可运行的方式接入 OpenSandbox。
1. 安装
Kotlin SDK 以com.alibaba.opensandbox:sandbox坐标发布,Gradle(Kotlin DSL)与 Maven 两种方式均可引入:
// Gradle (Kotlin DSL) dependencies { implementation("com.alibaba.opensandbox:sandbox:{latest_version}") }<!-- Maven --> <dependency> <groupId>com.alibaba.opensandbox</groupId> <artifactId>sandbox</artifactId> <version>{latest_version}</version> </dependency>如果需要分布式部署客户端池,仓库还包含可选模块sandbox-pool-redis(对应 Maven 坐标com.alibaba.opensandbox:sandbox-pool-redis),其实现位于 RedisPoolStateStore.kt;此外同目录下的code-interpreter子模块提供代码解释器的高层封装 CodeInterpreter.kt。
2. 快速上手:创建沙箱并执行命令
前提:运行示例前需保证 OpenSandbox 服务已启动,启动方式见 Getting Started。
import com.alibaba.opensandbox.sandbox.Sandbox; import com.alibaba.opensandbox.sandbox.config.ConnectionConfig; import com.alibaba.opensandbox.sandbox.domain.exceptions.SandboxException; import com.alibaba.opensandbox.sandbox.domain.models.execd.executions.Execution; public class QuickStart { public static void main(String[] args) { // 1. Configure connection ConnectionConfig config = ConnectionConfig.builder() .domain("api.opensandbox.io") .apiKey("your-api-key") .build(); // 2. Create a Sandbox using try-with-resources try (Sandbox sandbox = Sandbox.builder() .connectionConfig(config) .image("ubuntu") .build()) { // 3. Execute a shell command Execution execution = sandbox .commands() .run("echo 'Hello Sandbox!'"); // 4. Print output System.out.println(execution.getLogs().getStdout().get(0).getText()); // 5. Cleanup (sandbox.close() called automatically) // Note: kill() must be called explicitly if you want to terminate the remote sandbox instance immediately sandbox.kill(); } catch (SandboxException e) { // Handle Sandbox specific exceptions System.err.println("Sandbox Error: [" + e.getError().getCode() + "] " + e.getError().getMessage()); System.err.println("Request ID: " + e.getRequestId()); } catch (Exception e) { e.printStackTrace(); } } }从源码结构看,Sandbox类是整个 SDK 的主入口,见 Sandbox.kt:它是一个AutoCloseable,构造函数聚合了Sandboxes(生命周期)、Filesystem、Commands、Health、Metrics、Egress、CredentialVault、IsolationService、Diagnostics等服务对象,并支持可选的customHealthCheck回调。因此sandbox.close()只负责清理客户端资源,远程沙箱实例必须通过显式kill()终止——这正是示例中两者并列出现的原因。异常处理上,SandboxException携带error.code、error.message与requestId,方便与服务端日志对账。
3. 生命周期钩子(Lifecycle Hooks)
可以在Sandbox.Builder上配置生命周期钩子:preStart在 entrypoint 启动前完成,periodic钩子在启动完成后按调度周期执行。
import com.alibaba.opensandbox.sandbox.domain.models.sandboxes.LifecycleHook; import com.alibaba.opensandbox.sandbox.domain.models.sandboxes.PeriodicLifecycleHook; import com.alibaba.opensandbox.sandbox.domain.models.sandboxes.SandboxLifecycle; SandboxLifecycle lifecycle = SandboxLifecycle.builder() .preStart(LifecycleHook.builder() .command("sh", "-c", "echo ready > /tmp/prestart.done") .timeoutSeconds(120) .build()) .periodic(PeriodicLifecycleHook.builder() .name("checkpoint") .schedule("@every 5m") .command("sh", "-c", "date -u >> /tmp/checkpoints.log") .timeoutSeconds(120) .build()) .build(); Sandbox sandbox = Sandbox.builder() .connectionConfig(config) .image("ubuntu:24.04") .lifecycle(lifecycle) .build();超时约束由 Server 端校验:preStart接受 1–10800 秒,periodic接受 1–300 秒,两者缺省均为 60 秒。这一点可以在服务端 Schema 中得到印证,schema.py 中timeoutSeconds字段分别声明了ge=1, le=10800(preStart)与ge=1, le=300(periodic)。关于触发时机、失败行为与运行时支持范围的完整说明,见 Lifecycle Hooks 指南。
4. 使用示例
4.1 生命周期管理:续期、暂停与恢复
// Renew the sandbox // This resets the expiration time to (current time + duration) sandbox.renew(Duration.ofMinutes(30)); // Pause execution (suspends all processes) sandbox.pause(); // Resume execution sandbox.resume(); // Get current status SandboxInfo info = sandbox.getInfo(); System.out.println("State: " + info.getStatus().getState()); System.out.println("Expires: " + info.getExpiresAt()); // null when manual cleanup mode is usedrenew(timeout)的语义是把过期时间重置为“当前时间 + duration”,对应 Sandbox.kt 中的renew(timeout: Duration)实现。若希望创建永不过期(手动清理模式)的沙箱,传入timeout(null)即可,此时getInfo().getExpiresAt()返回 null:
Sandbox manual = Sandbox.builder() .connectionConfig(config) .image("ubuntu") .timeout(null) .build();注意 Builder 的默认值并非无穷大:从 Sandbox.kt 可以看到timeout默认Duration.ofSeconds(600)(即 10 分钟),readyTimeout默认Duration.ofSeconds(30),健康检查轮询间隔healthCheckPollingInterval默认 200 ms——这与文档中“超时默认 10 分钟、就绪等待默认 30 秒”的说明一致。
4.2 自定义健康检查
默认的 ready 检查是 ping;你也可以传入 lambda 覆盖判断逻辑(例如等待某个端口可访问)。注意:自定义检查内部的超时需要你自行控制,SDK 无法中途打断它。
Sandbox sandbox = Sandbox.builder() .connectionConfig(config) .image("nginx:latest") // Custom check: Wait for port 80 to be accessible .healthCheck(sbx -> { try { // 1. Get the external mapped address for port 80 SandboxEndpoint endpoint = sbx.getEndpoint(80); // 2. Perform your connection check (e.g. HTTP request, Socket connect) // return checkConnection(endpoint.getEndpoint()); return true; } catch (Exception e) { return false; } }) .build();getEndpoint(port)解析的是沙箱端点的对外可达地址;SDK 内部对 execd 与 egress 分别使用默认端口 44772 与 18080(见 Constants.kt)。
4.3 命令执行与流式输出
通过ExecutionHandlers可以实时消费 stdout/stderr 与完成事件:
// Create handlers for streaming output ExecutionHandlers handlers = ExecutionHandlers.builder() .onStdout(msg -> System.out.println("STDOUT: " + msg.getText())) .onStderr(msg -> System.err.println("STDERR: " + msg.getText())) .onExecutionComplete(complete -> System.out.println("Command finished in " + complete.getExecutionTimeInMillis() + "ms") ) .build(); // Execute command with handlers RunCommandRequest request = RunCommandRequest.builder() .command("for i in {1..5}; do echo \"Count $i\"; sleep 0.5; done") .handlers(handlers) .build(); sandbox.commands().run(request);如果不想走 shell 解析、以 argv 形式原生执行程序,可以直接传参数列表。在 Linux 上,下面的示例会原样打印字面量$HOME,并保持hello world为单个参数:
sandbox.commands().run(RunCommandRequest.builder() .argv(List.of("printf", "%s\n", "$HOME", "hello world")) .build());原生 argv 执行依赖更新版本的 execd,可执行文件查找与平台行为见 execd 命令执行模式说明。需要留意的是:SSE/流式请求会绕过 SDK 的自动重试(下文第 5.2 节),因为流式请求体无法安全重放。
4.4 文件操作:写、读、搜索、删除
sandbox.files()覆盖了写文件、读文件、按模式搜索和批量删除:
// 1. Write file sandbox.files().write(List.of( WriteEntry.builder() .path("/tmp/hello.txt") .data("Hello World") .mode(644) .build() )); // 2. Read file String content = sandbox.files().readFile("/tmp/hello.txt", "UTF-8", null); System.out.println("Content: " + content); // 3. List/Search files List<EntryInfo> files = sandbox.files().search( SearchEntry.builder() .path("/tmp") .pattern("*.txt") .build() ); files.forEach(f -> System.out.println("Found: " + f.getPath())); // 4. Delete file sandbox.files().deleteFiles(List.of("/tmp/hello.txt"));文件服务的接口定义位于 Filesystem.kt,write采用WriteEntry列表支持批量写入并可为每个文件单独指定mode。
4.5 管理面操作(SandboxManager)
SandboxManager用于管理面任务:列出既有沙箱、按状态过滤、批量终止等。
SandboxManager manager = SandboxManager.builder() .connectionConfig(config) .build(); import com.alibaba.opensandbox.sandbox.domain.models.sandboxes.SandboxState; // ... // List running sandboxes PagedSandboxInfos sandboxes = manager.listSandboxInfos( SandboxFilter.builder() .states(SandboxState.RUNNING) .pageSize(10) .page(1) .build() ); sandboxes.getSandboxInfos().forEach(info -> { System.out.println("Found sandbox: " + info.getId()); // Perform admin actions manager.killSandbox(info.getId()); }); // Try-with-resources will automatically call manager.close() // manager.close();管理类实现见 SandboxManager.kt,它同样实现了AutoCloseable,建议放入 try-with-resources 中。
4.6 客户端侧沙箱池(SandboxPool,实验特性)
SandboxPool在客户端维护一个“已就绪”沙箱的闲置缓冲区,从而把冷启动成本前置,降低acquire()的获取延迟。
⚠ 实验特性:
SandboxPool仍在根据生产反馈快速演进,后续版本可能引入不兼容变更。
基础用法:
import com.alibaba.opensandbox.sandbox.pool.SandboxPool; import com.alibaba.opensandbox.sandbox.pool.SandboxPoolManager; import com.alibaba.opensandbox.sandbox.domain.pool.PoolCreationSpec; import com.alibaba.opensandbox.sandbox.domain.pool.PoolDestroyOptions; import com.alibaba.opensandbox.sandbox.domain.pool.AcquirePolicy; import com.alibaba.opensandbox.sandbox.infrastructure.pool.InMemoryPoolStateStore; SandboxPool pool = SandboxPool.builder() .poolName("demo-pool") .ownerId("worker-1") .maxIdle(3) .warmupCreateQps(10) .warmupConcurrency(128) .warmupReadyTimeout(Duration.ofSeconds(45)) .warmupHealthCheckInitialDelay(Duration.ofSeconds(2)) .stateStore(new InMemoryPoolStateStore()) // single-node store .connectionConfig(config) .creationSpec( PoolCreationSpec.builder() .image("ubuntu:22.04") .entrypoint(java.util.List.of("tail", "-f", "/dev/null")) .extension("storage.id", "dataset-001") .build() ) .build(); pool.start(); Sandbox sb = pool.acquire(Duration.ofMinutes(10), AcquirePolicy.FAIL_FAST); try { sb.commands().run("echo pool-ok"); } finally { sb.kill(); sb.close(); } pool.shutdown(true);分阶段预热调度
Kotlin 版本的池化以固定的 1 秒节奏做 reconcile(reconcileInterval(...)API 已被移除;源码中该节奏由 SandboxPool.kt 的RECONCILE_INTERVAL_MS = 1_000L常量固定)。在此节奏下:
warmupCreateQps(...)(默认10)限制每个 tick 允许的新预热创建数;warmupConcurrency(...)(默认128)独立限制创建后的健康检查与 prepare 并发度;- 内置的预热创建只做一次 HTTP 尝试,不遵循常规传输重试策略,也不对 HTTP 429 做特殊节流;自定义
PooledSandboxCreator必须使用context.createConnectionConfig并尊重context.skipHealthCheck以保持这些语义;acquire()触发的直接创建行为不变。
创建后的流水线是显式分阶段的:
- 创建一个沙箱(不经过 Builder 的内联就绪等待循环);
- 等待
warmupHealthCheckInitialDelay(默认 0)后,每warmupHealthCheckPollingInterval(默认 500 ms)检查一次就绪,直到warmupReadyTimeout(默认 30 s);到期时仍会做最后一次检查; - 执行一次
warmupSandboxPreparer;若配置了warmupPostPrepareHealthCheck,则按相同轮询间隔重试,直到warmupPostPrepareHealthCheckTimeout(默认 30 s),且不会重跑 preparer; - 续期沙箱 TTL 并把 ID 提交进闲置缓冲区。
诊断状态方面:degradedThreshold(默认3)仍控制HEALTHY → DEGRADED的诊断状态,但 Kotlin 实现不再使用指数退避暂停补池,snapshot().backoffActive恒为false。
AcquirePolicy 语义
AcquirePolicy决定“闲置缓冲区为空”或“首个闲置候选未通过就绪检查”时的行为:
| 策略 | 跨闲置候选重试 | 耗尽后兜底 |
|---|---|---|
FAIL_FAST | 否 | 抛PoolEmptyException/PoolAcquireFailedException |
DIRECT_CREATE(默认) | 否 | 通过 lifecycle API 直接创建新沙箱 |
RETRY_NEXT_IDLE | 最多尝试maxAcquireRetries个闲置沙箱 | 抛异常 |
RETRY_NEXT_IDLE_THEN_CREATE | 最多尝试maxAcquireRetries个闲置沙箱 | 直接创建新沙箱 |
当池内可能混有健康与陈旧沙箱时(例如冷启动很慢的自定义模板、网络抖动遗留的不可达闲置实例),建议选用RETRY_NEXT_IDLE*变体。每个失败候选最多消耗acquireReadyTimeout,因此要用maxAcquireRetries(默认3)限定重试次数。
池生命周期语义
acquire()仅允许在池状态为RUNNING时调用;- 状态为
DRAINING/STOPPED时,acquire()抛PoolNotRunningException; - 池命名空间正在销毁或已销毁时,
acquire()抛PoolDestroyedException,且不会回退到直接创建; maxIdle是“就绪闲置沙箱”的目标/上限,不是对借出沙箱或AcquirePolicy.DIRECT_CREATE所建沙箱的全局上限;ownerId是锁持有者标识(节点/进程 ID),并非池标识;省略时 SDK 自动生成基于 UUID 的默认值;- 需要在“预热就绪成功后、进入闲置池之前”做准备工作时使用
warmupSandboxPreparer(...);若 prepare 后的服务需要独立验证窗口,再加warmupPostPrepareHealthCheck(...),重试不会重跑 preparer。
预热性能观测
为追踪预热路径,启用ConnectionConfig.builder().enableTracing(true)并在应用中加入 OpenTelemetry SDK 与 exporter。每次预热会产生一条 trace(pool.warmup根 span,外加create/readiness_check/prepare/post_prepare_check/renew/commit各阶段 span),并把trace_id/span_id发布到 SLF4J MDC,因此可以通过sandbox_id在日志中检索对应预热过程。详细设计见 SDK Tracing(Pool Warmup)。
分布式部署
分布式部署时,使用可选的com.alibaba.opensandbox:sandbox-pool-redis模块,或自行实现PoolStateStore接口。Redis 模块接收调用方管理的 Jedis 客户端,Redis 连接的配置与生命周期仍归你的应用所有。共享同一池命名空间的节点必须使用相同的沙箱创建与预热定义;修改该定义时应更换poolName或命名空间。Kotlin 实现会在独立于分阶段预热的节奏上续租主锁,间隔不大于primaryLockTtl的三分之一;若提交前租约 epoch 变化,任务会被丢弃。
分布式模式下的补充行为:
resize(maxIdle)可在任意节点调用;调用在目标值写入共享状态库后即返回,当前主节点在周期性 reconcile 中执行补池或收缩。需要排空分布式闲置缓冲区时用resize(0)并等待snapshot().idleCount == 0;releaseAllIdle()只是尽力而为的清理。releaseAllIdle()保持串行清理;releaseAllIdle(concurrency)提供有界并行清理,concurrency必须为正数,且该重载会等待每个排空 ID 都完成尽力而为的 kill 尝试。SandboxPoolManager.destroy(poolName)是更强的管理操作:写入DESTROYING围栏、排空可见闲置 ID、尽力 kill 闲置沙箱、清理持久化池状态,最后写入带 TTL 的DESTROYED墓碑以防止旧节点重建同名池命名空间。若排空或持久状态清理无法完成,destroy()抛PoolDestroyIncompleteException并将命名空间保持DESTROYING围栏状态;重试destroy()可继续完成清理。
无需构造旧SandboxPool对象即可销毁旧池命名空间的运维示例:
SandboxPoolManager poolManager = SandboxPoolManager.builder() .stateStore(redisStore) .connectionConfig(config) .ownerId("deploy-job-123") .build(); poolManager.destroy( "old-pool", new PoolDestroyOptions() );池化的核心实现分布在 SandboxPool.kt、SandboxPoolManager.kt 与 pool 领域模型目录,测试覆盖了限流、共享连接、异步预热等场景(如 SandboxPoolRateLimitTest.kt)。
5. 配置详解
5.1 连接配置(ConnectionConfig)
ConnectionConfig管理 API Server 的连接参数,完整参数表如下(含环境变量的对应关系):
| 参数 | 说明 | 默认值 | 环境变量 |
|---|---|---|---|
apiKey | 鉴权用 API Key | 必填 | OPEN_SANDBOX_API_KEY |
domain | 沙箱服务端点域名 | 必填(或 localhost:8080) | OPEN_SANDBOX_DOMAIN |
protocol | HTTP 协议(http/https) | http | - |
requestTimeout | API 请求超时 | 30 秒 | - |
debug | 开启 HTTP 请求调试日志 | false | - |
headers | 自定义 HTTP 头 | 空 | - |
connectionPool | 共享的 OkHttp ConnectionPool | SDK 按实例自建 | - |
retryPolicy | 非流式请求的自动重试策略(见 自动重试) | 启用(RetryPolicy()) | - |
useServerProxy | 以沙箱 Server 为 execd/endpoint 请求的代理(客户端无法直连沙箱时使用) | false | - |
disableMetrics | 禁用 SDK 创建延迟遥测(见 SDK Telemetry) | false | OPENSANDBOX_DISABLE_METRICS |
enableTracing | 为池预热启用 OpenTelemetry 追踪(见 SDK Tracing) | false | - |
环境变量回退逻辑在源码中可直接确认,ConnectionConfig.kt 定义了OPEN_SANDBOX_API_KEY、OPEN_SANDBOX_DOMAIN、OPENSANDBOX_DISABLE_METRICS三个常量,Builder 未显式设置apiKey/domain时按此回退。
// 1. Basic configuration ConnectionConfig config = ConnectionConfig.builder() .apiKey("your-key") .domain("api.opensandbox.io") .requestTimeout(Duration.ofSeconds(60)) .build(); // 2. Advanced: Shared Connection Pool // If you create many Sandbox instances, sharing a connection pool is recommended to save resources. // SDK default keep-alive is 30 seconds for its own pools. ConnectionPool sharedPool = new ConnectionPool(50, 30, TimeUnit.SECONDS); ConnectionConfig sharedConfig = ConnectionConfig.builder() .apiKey("your-key") .domain("api.opensandbox.io") .headers(Map.of( "X-Custom-Header", "value", "X-Request-ID", "trace-123" )) .connectionPool(sharedPool) // Inject shared pool .build();SDK 遥测说明:
Sandbox.builder()...build()默认会把创建延迟上报到POST /v1/metrics/events。调用ConnectionConfig.builder().disableMetrics(true)或导出OPENSANDBOX_DISABLE_METRICS=1可关闭,详见 SDK Telemetry。
5.2 自动重试
SDK 会自动重试瞬时故障:ConnectionConfig会在 SDK 的非流式 HTTP 客户端上安装RetryInterceptor(策略类型为com.alibaba.opensandbox.sandbox.transport.RetryPolicy)。
默认行为:
- 默认启用。幂等方法(
GET/HEAD/PUT/DELETE/OPTIONS)在429、502、503以及发送前传输失败(DNS、TCP 连接、TLS 握手)时重试; POST/PATCH默认不会因状态码重试(请求可能已在服务端生效);但发送前传输失败(尚未写出任何字节)仍会重试;- 最多
3次重试,采用 decorrelated-jitter 指数退避,并尊重服务端Retry-After头(上限 60 s); - SSE/流式请求完全绕过自动重试,因为其响应体无法安全重放。SSE 客户端同时禁用 OkHttp 内置连接恢复,防止流式命令 POST 被重放。
这些默认值在 RetryPolicy.kt 中可以直接核对:maxRetries默认DEFAULT_MAX_RETRIES = 3、initialBackoff默认 500 ms、maxBackoff默认 30 s、jitter默认JitterMode.DECORRELATED,另有可选的perAttemptTimeout与overallDeadline;RetryDecision.kt 与 RetryInterceptor.kt 分别实现退避计算与“更紧的单次/整体超时”的裁决逻辑。
行为变更提示:SDK 策略重试默认开启,相比更早的 SDK 版本会提高 HTTP 尝试次数与尾延迟。如需关闭,使用
RetryPolicy.disabled(),非流式请求将回退到 OkHttp 原有的内置连接恢复。
import com.alibaba.opensandbox.sandbox.transport.RetryPolicy; import com.alibaba.opensandbox.sandbox.transport.StatusCode; import java.time.Duration; import java.util.Set; // Disable SDK-policy retries and retain OkHttp's built-in connection recovery. ConnectionConfig config = ConnectionConfig.builder() .apiKey("your-key") .domain("api.opensandbox.io") .retryPolicy(RetryPolicy.disabled()) .build(); // Custom policy: more retries, an overall wall-clock deadline, and an opt-in to // retry POST/PATCH on 503 (only safe if your endpoints are idempotent). ConnectionConfig tuned = ConnectionConfig.builder() .apiKey("your-key") .domain("api.opensandbox.io") .retryPolicy(new RetryPolicy( /* maxRetries */ 5, /* initialBackoff */ Duration.ofMillis(500), /* maxBackoff */ Duration.ofSeconds(30), /* backoffMultiplier */ 2.0, /* jitter */ com.alibaba.opensandbox.sandbox.transport.JitterMode.DECORRELATED, /* retryableStatusCodesIdempotent */ RetryPolicy.DEFAULT_IDEMPOTENT_STATUS, /* retryableStatusCodesNonIdempotent */ Set.of(StatusCode.SERVICE_UNAVAILABLE), /* perAttemptTimeout */ null, /* overallDeadline */ Duration.ofSeconds(20), /* onRetry */ null)) .build();重试行为有专门的单测保障,如 RetryPolicyTest.kt 与 RetryInterceptorTest.kt。
5.3 沙箱创建配置
Sandbox.builder()支持的创建参数及默认值:
| 参数 | 说明 | 默认值 |
|---|---|---|
image | Docker 镜像 | 必填 |
timeout | 自动终止超时 | 10 分钟 |
entrypoint | 容器 entrypoint 命令 | ["tail", "-f", "/dev/null"] |
resource | CPU 与内存限制 | {"cpu": "1", "memory": "2Gi"} |
env | 环境变量 | 空 |
metadata | 自定义元数据标签 | 空 |
extensions | 透传给服务端的扩展参数 | 空 |
networkPolicy | 可选的出口网络策略(egress) | - |
credentialProxy | 可选的 Credential Vault 代理启动配置 | - |
readyTimeout | 等待沙箱就绪的最长时间 | 30 秒 |
注意:
opensandbox.io/前缀的 metadata key 是系统保留标签,服务端会拒绝此类自定义 metadata。
完整示例(含出口网络策略):
import com.alibaba.opensandbox.sandbox.domain.models.sandboxes.NetworkPolicy; import com.alibaba.opensandbox.sandbox.domain.models.sandboxes.NetworkRule; Sandbox sandbox = Sandbox.builder() .connectionConfig(config) .image("python:3.11") .timeout(Duration.ofMinutes(30)) .resource(map -> { map.put("cpu", "2"); map.put("memory", "4Gi"); }) .env("PYTHONPATH", "/app") .metadata("project", "demo") .extension("storage.id", "dataset-001") .networkPolicy( NetworkPolicy.builder() .defaultAction(NetworkPolicy.DefaultAction.DENY) .addEgress( NetworkRule.builder() .action(NetworkRule.Action.ALLOW) .target("pypi.org") .build() ) .build() ) .build();Builder 中readyTimeout还带参数校验:必须为正数,否则抛出异常(见 Sandbox.kt)。
5.4 运行时出口策略更新
运行时的 egress 读取与 patch 直连沙箱的 egress sidecar:SDK 先解析沙箱 18080 端口的 endpoint,再调用 sidecar 的/policyAPI。Patch 采用合并语义:
- 传入规则优先于同
target的既有规则; - 其他
target的既有规则保持不变; - 单个 patch 载荷内部,同一
target的第一条规则生效; - 当前
defaultAction保持不变。
NetworkPolicy policy = sandbox.getEgressPolicy(); sandbox.patchEgressRules( List.of( NetworkRule.builder().action(NetworkRule.Action.ALLOW).target("www.github.com").build(), NetworkRule.builder().action(NetworkRule.Action.DENY).target("pypi.org").build() ) );sidecar 侧的策略服务器实现见 policy_server.go 与 policy 包。
5.5 Credential Vault
Credential Vault 让 egress sidecar 在出站时注入凭证,从而把真实密钥排除在沙箱环境变量、命令、文件和日志之外。用法:创建沙箱时设置credentialProxyEnabled(true),再通过sandbox.credentialVault()写入凭证与绑定关系。
import com.alibaba.opensandbox.sandbox.domain.models.sandboxes.Credential; import com.alibaba.opensandbox.sandbox.domain.models.sandboxes.CredentialAuth; import com.alibaba.opensandbox.sandbox.domain.models.sandboxes.CredentialBinding; import com.alibaba.opensandbox.sandbox.domain.models.sandboxes.CredentialMatch; import com.alibaba.opensandbox.sandbox.domain.models.sandboxes.CredentialVaultCreateRequest; import com.alibaba.opensandbox.sandbox.domain.models.sandboxes.NetworkPolicy; import com.alibaba.opensandbox.sandbox.domain.models.sandboxes.NetworkRule; import java.util.List; Sandbox sandbox = Sandbox.builder() .connectionConfig(config) .image("python:3.11") .networkPolicy( NetworkPolicy.builder() .defaultAction(NetworkPolicy.DefaultAction.DENY) .addEgress( NetworkRule.builder() .action(NetworkRule.Action.ALLOW) .target("api.example.com") .build() ) .build() ) .credentialProxyEnabled(true) .build(); sandbox.credentialVault().create( CredentialVaultCreateRequest.builder() .credentials( List.of( Credential.builder() .name("api-token") .inlineSource("<token>") .build() ) ) .bindings( List.of( CredentialBinding.builder() .name("api-token") .match( CredentialMatch.builder() .schemes(CredentialMatch.Scheme.HTTPS) .hosts("api.example.com") .paths("/v1/*") .build() ) .auth(CredentialAuth.apiKey("x-api-key", "api-token")) .build() ) ) .build() );凭证匹配由CredentialMatch(scheme/host/path 三元组)与CredentialAuth(如apiKey(header, credentialName))共同决定。更完整的鉴权类型、绑定建议以及 Git/curl 示例见 Credential Vault 指南;sidecar 侧的凭证注入实现位于 credentialvault 包。
6. 验证与深入路径
- 端到端:仓库
tests/java/目录提供 Java E2E 套件(生命周期、命令、文件系统、池化、Credential Vault、错误处理等),可作为行为基准参照; - 单元/集成测试:Kotlin SDK 自身测试集中在
sdks/sandbox/kotlin/sandbox/src/test/kotlin/,例如 SandboxTest.kt、SandboxManagerTest.kt、InMemoryPoolStateStoreTest.kt; - 服务端行为:生命周期钩子的超时校验、egress
/policyAPI 与 metrics 上报端点分别可在 schema.py、policy_server.go 中继续追查; - 相关 OSEP 设计文档:客户端池化见 OSEP 0005 与 OSEP 0021,Credential Vault 见 OSEP 0012,出口控制见 OSEP 0001。
7. 小结
OpenSandbox Kotlin/Java SDK 以Sandbox(执行面)与SandboxManager(管理面)为双入口,配合ConnectionConfig的环境变量回退、自动重试与遥测开关,覆盖从沙箱创建、钩子、命令流式执行、文件操作到出口策略与凭证注入的完整链路;实验性SandboxPool则以 1 秒固定节奏的 reconcile、分阶段预热和AcquirePolicy策略把冷启动成本前置,且可通过sandbox-pool-redis扩展到多节点。建议在集成时优先核对timeout(默认 10 分钟)、readyTimeout(默认 30 秒)与重试策略这三组默认值是否符合你的部署环境,并保留requestId用于故障排查。
【免费下载链接】OpenSandboxSecure, Fast, and Extensible Sandbox runtime for AI agents.项目地址: https://gitcode.com/GitHub_Trending/ope/OpenSandbox
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考