@ chamvi/ sandbox-docker (中文(简体) )
每个工作空间一个容器,一个Docker主机上的chamvi代理.
根入内地,所以 apt 和每个软件包管理器工作; 严格在边缘: 少数能力, no-new-privileges, (中文(简体) ),
cpu, 内存和pid限制, 没有主机挂载但包缓存, GNU 内的所有命令 timeout。 。 。 .
目录
安装
npm install @chamvi/sandbox-docker @chamvi/sandbox @chamvi/core ai --save
仅限无害环境管理,节点.js >=26.0.0。 。 。 . @chamvi/core 财务报告和审定财务报表 @chamvi/sandbox 是同伴的依赖关系。 Docker API 通过 夹层 5(与 @types/dockerode,因为选项需要 Dockerode 客户端); 它拉入 docker-modem, (中文(简体) ), tar-fs, (中文(简体) ), @balena/dockerignore 和 gRPC 软件包 @grpc/grpc-js, (中文(简体) ), @grpc/proto-loader 财务报告和审定财务报表 protobufjs 作为自己的依附关系. 烟雾 是唯一的运行时间依赖.
使用量
import type { ISandboxProvider } from '@chamvi/sandbox';
import { createDockerSandbox } from '@chamvi/sandbox-docker';
export function createSandbox(): ISandboxProvider {
// DOCKER_HOST or /var/run/docker.sock; nothing connects before the first create()
return createDockerSandbox({
network: 'bridge',
limits: { memory: '1Gi' },
});
}
把提供者传给 createWorkspaceManager({ provider }) 从 @chamvi/sandbox创建它;第一个 create() 已经.
集装箱
每个工作空间都是容器 chamvi-ws-<id>数字 :
| 设置 | 数值 |
|---|---|
| 用户 | user默认值 0:0 (单位:千美元)DEFAULT_USER),也是每个命令中的 |
| 命令 | sh -c 睡觉直到 expiresAt (绝对期限,所以一个容器) find 在一个主机或守护进程重新启动后再次启动, 仍然在同一时间停止, 或者一次结束, 图像的切入点被替换( nameEntrypoint: []:容器本身停止在 expiresAt 即使没有收藏, 垃圾收藏也会移除 |
| 输入 | Init: true:docker-init是pid 1,并收获任何图像的孤儿过程(背景工作,一个执行超时的树木杀死),同样是没有init的,否则僵尸会填满的 PidsLimit |
| 根文件系统 | 可写;仅读取 readOnlyRoot |
| 能力 | 全部删除; root 用户返回 CHOWN, (中文(简体) ), DAC_OVERRIDE, (中文(简体) ), FOWNER, (中文(简体) ), FSETID, (中文(简体) ), SETUID, (中文(简体) ), SETGID, (中文(简体) ), KILL (单位:千美元)ROOT_CAPABILITIES(三); no-new-privileges:true没有特权 |
| 限制 | NanoCpus 从 cpu, (中文(简体) ), Memory 财务报告和审定财务报表 MemorySwap (不交换)从 memory, (中文(简体) ), PidsLimit 从 pids |
/workspace | 匿名量,随容器去除(remove({ force: true, v: true })(中文(简体) ) |
/home/sandbox | 家庭(WORKSPACE_HOME: 磁盘上的匿名音量, 保存在重启时, 用容器取出 |
/cache | 其自己的匿名体积,用容器取出 cache: { volume } 名称的音量,每个工作区都使用它 |
/tmp | a 吨数 tmpSize (默认 1 GiB, TMP_SIZE· ; exec, (中文(简体) ), nosuid, (中文(简体) ), nodev(中文(简体) ) |
| 网络 | network (默认) bridge(中文(简体) ) |
| 环境 | SANDBOX_ENV,缓存变量(CACHE_ENV和 env,没有来自主机 |
| 标签 | app.kubernetes.io/managed-by=chamvi, (中文(简体) ), app.kubernetes.io/component=sandbox, (中文(简体) ), chamvi.dev/run=<runId>, (中文(简体) ), chamvi.dev/workspace=<id>, (中文(简体) ), chamvi.dev/deployment=<deployment>, (中文(简体) ), chamvi.dev/repo-key=<hash> (有一个仓库), chamvi.dev/created-at 财务报告和审定财务报表 chamvi.dev/expires-at (划时代), chamvi.dev/repo, (中文(简体) ), chamvi.dev/ref, (中文(简体) ), chamvi.dev/image |
limits.storage 不被执行:默认音量没有大小(使用有配额的存储驱动程序,或Kubernetes供应商)。 命令通过 Docker exec API 在 /workspace; 文件读取时使用 cat 并用 head -c 从斯丁.
硬化模式数字 : createDockerSandbox({ user: '1000:1000', readOnlyRoot: true }) 给出早期版本的容器:uid 1000,一个只读取的根,根本没有能力. apt 和 root 安装则失败; 工具链仍然以 mise 安装入 Home 。 在退出前, 提供者向该用户打开缓存( 一个执行器作为根, 它拥有挂载点 Docker 使图像不包含) /cache,所以它会 /cache 和空顶层 CACHE_ENV 文件夹可以写, 没有任何功能, 因此任何图像都在那里工作 。 只有这些文件夹: root 工作空间会让 root 拥有的文件夹深入到缓存中( npm's) _cacache) 硬化的用户不能写,所以永远不要指非根 user 以美元计 cache.volume 一个 root 工作空间或另一个用户所写 .
图像
默认图像是 ghcr.io/chamvi/sandbox-dev:trixie (单位:千美元)DEFAULT_SANDBOX_IMAGE:德比安克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克, tini 财务报告和审定财务报表 爱,在运行时安装任何工具链(mise use -g node@26, (中文(简体) ), mise install为 linux/amd64 财务报告和审定财务报表 linux/arm64 从 images/sandbox-dev标记 : trixie (根据更改和每周重建)和 trixie-<short sha> (每个承诺一次,改变图像).
- 运行可能选择一个图像( Q)
create_workspace({ image })),只有当管理者有许可列表时(allowImage,或CHAMVI_SANDBOX_IMAGES内sandboxModule())),没有一个工具就没有image参数. pull: 'missing'(默认)拉出一幅图像 当守护进程缺少它,'always'在每一个创建之前(一个可变的标记,然后是它的重建),'never'从来没有。 它覆盖每个图像: 提供者的,经理的image和一次运行要求(create_workspace({ image })) (中文(简体) ).- 在生产中粘接一个文摘(
image: 'ghcr.io/chamvi/sandbox-dev@sha256:...'),或使用pull: 'always'。 。 。 . - 图像的切入点被替换,所以其切入点为命令的图像(
["git"]) 工作也一样. - 图像需要
sh, (中文(简体) ),git和一个 GNUtimeout这需要-k附加时cat, (中文(简体) ),head, (中文(简体) ),mkdir, (中文(简体) ),dirname, (中文(简体) ),cp, (中文(简体) ),mv财务报告和审定财务报表rm。 。 。 .create探测集装箱运行后前三个故障SandboxError在任何检出前(并去掉容器),指定缺少的内容.
缓存
每个工作空间都挂起 /cache 获得 XDG_CACHE_HOME, (中文(简体) ), npm_config_cache, (中文(简体) ), PIP_CACHE_DIR, (中文(简体) ), GOMODCACHE, (中文(简体) ), GOCACHE 财务报告和审定财务报表 COMPOSER_CACHE_DIR 指着它CACHE_ENV: 包只下载,从不使用工具链(将它的安装保存在Home,每个工作空间). 默认 /cache 是一个匿名的音量,每个工作空间,用它删除.
cache: { volume: 'chamvi-cache' } 在每个工作空间中挂载一个命名的音量, 因此同一软件包的第二个安装速度快; 它比每个工作空间都活取它( 用 docker volume rm 以重设). 风险:共享量是每个运行和代理在主机上共享的信任边界. 一个被抓取的恶意包可以坐到另一个运行的缓存; npm 验证完整性的散列, pip, Go 的构建缓存等更少. 只在彼此信任的运行之间共享一卷,每个信任级别共享一卷.
警卫
-
套接字访问为主机上根. 谁能和Docker套接字说话 可以启动一个特权的容器。 给 chamvi 进程一个 套接字代理服务器 只允许容器、执行和图像,或运行 无根嵌入器,而不是安装
/var/run/docker.sock进入它. -
不是网络边界. 打开
bridge一个工作空间到达主机网关(chamvi API、Postgres、MCP服务器)、其他容器和云元数据169.254.169.254。使用一个没有集装箱间交通的专用网络并设置防火墙:docker network create --opt com.docker.network.bridge.enable_icc=false chamvi-sandbox # then drop traffic from that bridge to the host gateway and 169.254.169.254 (iptables DOCKER-USER chain)通过
network: 'chamvi-sandbox'。 。 。 .network: 'none'完全切断网络;然后只有无回放的工作空间才能工作,因为克隆人运行在容器内. -
根入于内,不取于主. 一个工作空间在默认情况下以可写入的根文件系统作为根运行, 这样它就可以安装任何东西; 它仍然没有特权的容器, 只持有软件包管理器所需的能力, 保存
no-new-privileges不挂载任何宿主cache.volume是工作空间之间唯一共享的音量。 使用硬化模式( E)user, (中文(简体) ),readOnlyRoot) 容器中的根过多. -
即时注射:模型所运行的跟随其读取的. 缩小
allowRepo在经理和设置bash背后的核准 必要时.
暂停和中止
每个命令运行为 timeout -k 5 <seconds> <argv>; GNU 数据 timeout 所以背景背景的孙子孙子也走了 客户端警卫停止等待15秒EXEC_GRACE_MS)在超时后,报告出口137个,产出至今. 一个在背景工作开始时退出的命令,它用自己的退出代码在退出后持有大约2 s的输出返回.
中止( E)signal(中文(简体) ) 节点:多克克洛德从执行溪流中流出,命令一直持续到其 timeout 火灾或工作空间被处理。 每一次确定运行都会处理其工作空间(manager.attach(engine)),所以没有什么比跑步要长。 一个处理器会清除容器, 它杀死任何仍在运行.
一个创造 signal (由 release(runId) 财务报告和审定财务报表 manager.close()) 放弃其请求:图像牵引(守护进程可能自行完成),容器创建(无论怎样制作的守护进程都被移除)并启动. dockerode 默认不设置请求超时,所以挂起的守护进程会拖住一个创建或去除. manager.close() 之后放弃 timeoutMs (30 s) 并报告所剩的; 将每个请求绑定, 通过一个客户端, 暂停 : createDockerSandbox({ docker: new Docker({ timeout: 60_000 }) }) (以ms计,比您最慢的图像拉长).
垃圾收集
find(runId, { deployment }) 通过标签列出运行的容器, 再次启动一个被停止的容器( 主机或守护进程重新启动或中止; 其音量仍然保留着工作) , 也有一个被这个进程控住并发现丢失( 管理者忘记了一个已消失的工作空间而不是处理它) , 并且从标签中重建工作空间, 因此运行在重启后或在共享守护进程的另一复制件上找到其工作空间 . collectGarbage({ deployment, isSettled }) 将部署的容器与管理的标签一并列出,并逐一取出 chamvi.dev/expires-at 并,与 isSettled,其中,已结算运行的运行(此流程的开放工作空间首先被处理),包括崩溃流程和其他共享守护进程复制的运行。 与 suspend: true 财务报告和审定财务报表 isSuspendable 它会停止(从不去除)运行中等待足够长的运行容器,所以它们没有CPU或内存;下一个 find 以文件再次启动它们( e)/tmp 并失去背景工作)。 无 suspend (默认)没有容器被停止:一个沉睡的容器在多克身上没有任何保留. A 类 find 与 restore: false (经理释放) 开始无; 它的手柄只处理. 经理打来的 acquire 最多10分钟一次 无法拆卸的容器不会阻止通行:其他人走,一个 SandboxError 与计数和第一个失败 投出在结尾.
选项
createDockerSandbox(options?)数字 :
| 选项 | 类型 | 默认 | 说明 |
|---|---|---|---|
docker | Dockerode | new Docker() | 客户( A)DOCKER_HOST 或者说 /var/run/docker.sock(中文(简体) ) |
image | string | DEFAULT_SANDBOX_IMAGE | 工作空间图像; 在生产中刻入摘要 |
network | string | 'bridge' | 'bridge', (中文(简体) ), 'none' 或用户创建的网络 |
pull | 'missing' | 'always' | 'never' | 'missing' | 何时拉出图像( 每个图像, 一个运行请求) |
env | Record<string, string> | 无 | 之后添加 SANDBOX_ENV 财务报告和审定财务报表 CACHE_ENV |
limits | SandboxLimits | DEFAULT_LIMITS | cpu, (中文(简体) ), memory, (中文(简体) ), pids, (中文(简体) ), maxLifetimeMs (单位:千美元)storage 未执行) |
user | string | '0:0' | 集装箱用户(uid:gid 或名称);非源用户没有能力 |
readOnlyRoot | boolean | false | 只读的根文件系统 |
cache | { volume: string } | 无(每个工作区匿名量) | 一个名字 /cache 每个工作空间共享的体积( e)false 财务报告和审定财务报表 {} 拒绝) |
suspend | boolean | false | 停止收集中长期等待的容器(CHAMVI_SANDBOX_DOCKER_SUSPEND 与 sandboxModule()(中文(简体) ) |
tmpSize | string | '1Gi' | 面积 /tmp tmpfs 缩写 |
clock | IClock | systemClock | 过期和执勤警卫 |
配置错误抛出核心 InvalidAgentDefinitionError; 守护进程和容器失败 SandboxError编号.
API 密码
| 导出 | 仁爱 | 目的 |
|---|---|---|
createDockerSandbox | 函数 | 这个 ISandboxProvider (单位:千美元)name: 'docker'(中文(简体) ) |
DEFAULT_NETWORK | 常数 | 'bridge' |
CONTAINER_NAME_PREFIX | 常数 | 'chamvi-ws-' |
DEFAULT_USER | 常数 | '0:0' |
类型 : DockerSandboxOptions。 。 。 .
许可证
原载 Apache 许可证 2.0。 。 。 .