@ decocms/ sandbox- 控制器
运行工作室代理沙盒在Kubernetes上 服务于工作室外 服务于工作室 作为远程供应商.
| 属性 | 数值 |
|---|---|
| 工作空间 | @decocms/sandbox-controller (单位:千美元)packages/sandbox-controller(中文(简体) ) |
| 仁爱 | 公共图书馆,从 @decocms/sandbox |
| 运行时间 | 阿宝 |
| 分发 | 公共 npm 软件包 |
概览
沙盒控制器是一个库,不是服务. 一个主机进程—— deco's
分组, decocms/control-plane - 依赖它,建立一个
AgentSandboxProvider 并服务于沙盒 API.
录音室的 RemoteSandboxProvider (单位:千美元)@decocms/sandbox/provider/remote(电话)
当API STUDIO_SANDBOX_CONTROL_PLANE_URL 已设定; 没有它, Studio 运行
同一供应商在进程中.
软件包再导出代码 @decocms/sandbox 财务报告和审定财务报表 @decocms/shared, (中文(简体) ),
秘密地把它捆入一个有申报的模块.
责任
- 导出
AgentSandboxProvider,其选项和状态存储类型,以及kubeConfigForServer,用于本地代理的无证书的 kubeconfig, 已建 和软件包的客户端. - 导出
migrateSandboxControllerSchema,用于创建和升级 图书馆自己的sandbox_controllerPostgres schema, 以及里面的商店:postgresRunnerStateStore页:1RunnerStateStore(数量)scope保留多个 在一个数据库中将主机或环境的行和锁分开),以及postgresTenantPoolStore每个沙盒组的租户暖水池tenantPoolReader,用于提供者的缓存列表tenantPools。 。 。 . - 导出租户池模型 :
TenantPool财务报告和审定财务报表TenantPoolRepo他们 校验符( e)tenantPoolSchema, (中文(简体) ),tenantPoolFieldsSchema, (中文(简体) ),tenantPoolRepoSchema, (中文(简体) ),normalizeRepoUrl,以及renderTenantPoolWarmPools,它建立一个游泳池SandboxWarmPool对象供主机应用. - 导出
FreestyleSandboxProvider(散装箱作为 Freestyle VM)和SandboxProviderRouter服务于库伯内兹和弗里斯泰特 提供者; 见packages/sandbox。 。 。 . - 将电线合同导出
sandbox-api: 工具名称和计划,以及 监视路线. - 导出主机助手 :
sandboxTools(无框架工具定义),sandboxWatchResponse(称为SSE阶段),PushedCredentials(单位:千美元) 证书工作室推倒的记忆存储)和pushedCredentialOptions(提供商的薄荷钩 在商店.
使用量
import {
AgentSandboxProvider,
kubeConfigForServer,
migrateSandboxControllerSchema,
postgresRunnerStateStore,
postgresTenantPoolStore,
PushedCredentials,
pushedCredentialOptions,
renderTenantPoolWarmPools,
sandboxTools,
sandboxWatchResponse,
tenantPoolReader,
} from "@decocms/sandbox-controller";
// At boot, before any store serves a call. Each store opens its own pool.
await migrateSandboxControllerSchema({ url: databaseUrl });
const stateStore = postgresRunnerStateStore({ url: databaseUrl });
const poolStore = postgresTenantPoolStore({ url: databaseUrl });
// The host's admin surface writes pools and their repo allocations.
await poolStore.upsertPool("sandbox-cluster-1", {
name: "acme",
tenant: orgId,
size: 4,
});
const acme = await poolStore.setRepos("sandbox-cluster-1", "acme", [
{ repoUrl: "https://github.com/acme/site", replicas: 2 },
]);
// The host applies these; the provider only binds and warms their pods.
const warmPools = renderTenantPoolWarmPools(acme!, {
templateName: "studio-sandbox-prod",
});
const pools = tenantPoolReader(poolStore, "sandbox-cluster-1");
await pools.start();
// Memory only: a restart forgets it until Studio's next push.
const credentials = new PushedCredentials();
const provider = new AgentSandboxProvider({
// The host's local proxy to the cluster; it authenticates on its own.
kubeConfig: kubeConfigForServer("http://127.0.0.1:7777"),
// Reach daemons the host's way instead of an apiserver port-forward.
forwardPort: (pod, port) => openDaemonForward(pod, port),
stateStore,
// Warm-pool mode; tenant pools need it.
sentinelToken,
// Read on every ensure, push and reconcile tick: a synchronous snapshot.
tenantPools: pools.current,
// Mint hooks that read the store, and `persistCredentials: false`.
...pushedCredentialOptions(credentials),
});
// Wrap each definition in the host's tool type, behind the host's auth.
const tools = sandboxTools(() => provider, credentials);
// GET /api/sandbox/watch?handle=… behind the same auth.
const watch = (req: Request, handle: string) =>
sandboxWatchResponse(provider, handle, req.signal);
建筑
Studio ──MCP tools + SSE watch──▶ host (this library) ──▶ Kubernetes
Studio ──────── daemon and preview traffic ────────────▶ sandbox pod
主机回答沙盒守护进程在哪里, 哪个持卡人打开它; Studio
拨打守护进程本身( n)daemonAddress: "service" 解答分组
服务 URL. 控制平面没有集群证书:其库贝和
守护进程呼叫是沙盒操作 它的集群数据飞机代理运行
(单位:千美元)decocms/operator internal/agent/ops.go) (中文(简体) ).
租户暖水池
一个租户池是留给一个租户的温室容量: size 已建舱
从游泳池 image。租户是一个不透明的钥匙;供货商比较它
与索赔的租户密钥,即索赔用户的Org id. 重置为
顶部的可选分配 : { repoUrl, branch, workload, replicas }
带 replicas 并不断复制、安装和复制
运行 Repo 的 Dev 服务器 。 分配的复制品最多加到
size其余的都是空白的温室, 复制在捆绑上像通用的
游泳池的。 游泳池的吗. repoUrl 是一个无证书的 https 克隆 URL,已正常存储 :
小写主机, 没有用户信息, 没有线索 .git 或者说 /。 。 。 .
操作员绑定任何温室 SandboxWarmPool 索赔名称,以及
守护进程拒绝将一个被克隆的吊舱移动到另一个 repo, 因此每次分配
这是它自己的 SandboxWarmPool。 。 。 . renderTenantPoolWarmPools(pool, { templateName }) 返回每个分配符中的一个,命名
<pool>-<8 hex of repoUrl#branch>,和一个空白的,命名 <pool> 与
剩下的复制品,即使0点也做成 这样就应用了收缩器 他们都用
页:1 -medium 游泳池图像模板, 即一个有约束要求的模板
名称。 主机应用并删除这些对象; 库从来不写
他们.
一项索赔按其顺序对下列事项具有约束力:
repo,在索赔的分行(如果有),其他的分行;其租户的空白
舱; 通用池. 只有一个池子,它的图像是要求的图像
合格,没有空白复制品的游泳池被跳过。 调和器暖和
仅分配吊舱,每波德刷新和故障后退;空白吊舱是
由约束它的权利要求所配置。 供应商把池子当作
tenantPools,列表或函数,并在每次使用时读取函数,所以
更改在下一个保证和对齐时生效,而不重新启动.
调和器会忘记一个被删除的分配器的吊舱,并且会使其深度计为零.
markTenantPoolsDirty(repoUrl, ref) 刷新该 repo 上的配置
和树枝相接.
全权证书
主持人从不给工作室打电话. 只有工作室才能做薄荷(clone sorders,
org-fs API 键) 到达它有两个方法: SANDBOX_ENSURE 带着他们
保持有效多久,每10分钟工作室打电话 SANDBOX_LIST
(租户和回购户,以及每个游泳池的名称、租户、形象和回购分配,
(没有全权证书),然后 SANDBOX_CREDENTIALS_PUSH工作室处理
列表为不信任 : 它只对租户使用薄荷, 而用户是其中的一员
,并且仅用于连接或寄存器属于
那个鸟人 对于一个游泳池,它只从它自己的寄存器记录中取出薄荷
org 由该游泳池的租户命名,其克隆 URL 与所列 URL 相匹配
推一无相. 集合证书以下列方式送达: poolCloneUrls键入
租户和收回的URL,以及 pushedCredentialOptions 把他们交给
分配舱. STUDIO_SANDBOX_TENANT_POOLS 仅配置池
Studio的流程中供应商;带有控制平面,Studio忽略了它.
商店保存每个条目的过期, 从未替换一个寿命更长的条目
更短的,当一个入口的生命减少时, 以一无所有的方式回答再薄荷
离跑者要求的远 与 persistCredentials: false 坚持
状态不保留克隆令牌、子模块令牌或org-fs配置;恢复需要
他们从商店。 在主机重启后, 沙盒在下次启动前恢复
推力只有在10分钟后才能有证书.
存储
州立商店每个沙盒保留一行 sandbox_controller.runner_state, (中文(简体) ),
与 Studio 的同名语义库在进程商店. withLock 序列化
用交易范围为复制品提供一个沙盒
由用户和项目管理员键入的咨询锁定; 等待时间超过
90秒( 秒数 )lockWaitMs)获得可重试的"沙箱咨询锁"繁忙出错.
每个锁持有人都保持连接 整个供给,所以商店
打开自己的集合 maxConnections (违约4)而不是共享
东道方; close() 结束它。 主机运行 migrateSandboxControllerSchema 在靴子时;
店里从不独自迁移 迁移是天能的 并且持有
咨询锁,所以每个复制品可以同时运行. 应用版本为:
记录 sandbox_controller.migrations。 。 。 . state 嵌入短寿命
证书;永远不要登录.
游泳池店每游泳池一排 sandbox_controller.tenant_pools, (中文(简体) ),
单个的集群和名称,所以一个数据库可以服务于多个沙盒
组,每个分配单位一行 sandbox_controller.tenant_pool_repos, (中文(简体) ),
按池、 repo URL 和分支并与其池一起删除.
upsertPool(cluster, pool) 用 tenantPoolFieldsSchema 创建
或更新该名称的池,保留其原样,但抛出
TenantPoolConflictError 当这个名字属于另一个租户时: 它的温暖的舱
保持租户的退房. setRepos(cluster, name, repos) 用
tenantPoolRepoSchema 并替换一个池的所有分配
,当没有这样的池时,则无效回答。 都锁上泳池一行
掷出 TenantPoolCapacityError 而不是让分配额超过
尺寸. deletePool 回答是否删除了游泳池; list(cluster) 返回时
组群的池与它们的回放. tenantPoolReader(store, cluster) 装入集群的池
start()打开 refresh() 和每个 refreshMs (默认为30秒);
current() 同步回答上次加载的列表。 保存失败的负载
最后的列表, 以及一个在更新的列表被丢弃后完成的负载。 写作
从另一个复制到读者的下个勾点; 作者自己的
进程可以调用 refresh() 马上.
看着
监视路由中每写一个 SSE 保留评论
SANDBOX_WATCH_KEEPALIVE_MS。工作室读取表作为主持人的标志
生活:它重新连接了一条流水, 放弃了只给养电话
在宿主沉默了4个守口如瓶后.
发展
bun run --cwd=packages/sandbox-controller build # dist/index.js + index.d.ts
bun run --cwd=packages/sandbox-controller pack # dist/sandbox-controller.tgz
bun test packages/sandbox/server/provider/remote.test.ts
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/postgres \
bun test packages/sandbox/server/provider/postgres-state-store.integration.test.ts
本地主机可以依赖包装的柏油球
bun add @decocms/sandbox-controller@file:<path>/dist/sandbox-controller.tgz。 。 。 .
边界
- 主机拥有授权:工具没有自己的授权.
- 默认
daemonAddress: "forward",守护进程地址是 主机本身127.0.0.1前进,只能从同一机器上到达. - 来源于
packages/sandbox换个位置 改为a 被捆绑的源头在发布时碰倒了这个软件包的版本,由它发布. zod, (中文(简体) ),@opentelemetry/api财务报告和审定财务报表postgres是同级依赖关系: 主办单位zod解析导出的计划, 提供者的测量选项是@opentelemetry/api类型,并运行在主机上postgres。 。 。 .