@chamvi/ sandbox-k8s (英语)
每个工作空间一个舱, 对于chamvi代理在Kubernetes.
根植于此软件包管理器的工作, 少数能力, 没有服务账户符号, 资源限制,
在重新启动后或在另一个复制品上再次通过标签找到.
目录
安装
npm install @chamvi/sandbox-k8s @chamvi/sandbox @chamvi/core ai --save
仅限无害环境管理,节点.js >=26.0.0。 。 。 . @chamvi/core 财务报告和审定财务报表 @chamvi/sandbox 是同伴的依赖关系。 API 服务器通过 @kubernetes/client-node 页:1CoreV1Api 财务报告和审定财务报表 Exec 在一个WebSocket上。 它宣布 @types/node ^26 作为运行时间的依赖,所以一个关于更古老的节点类型的项目会得到一个嵌入式复制件;这与 skipLibCheck。 。 。 .
使用量
import type { KubeConfig } from '@kubernetes/client-node';
import type { ISandboxProvider } from '@chamvi/sandbox';
import { createKubernetesSandbox } from '@chamvi/sandbox-k8s';
export function createSandbox(kubeConfig: KubeConfig): ISandboxProvider {
// one pod per workspace in this namespace; nothing is contacted before the first create()
return createKubernetesSandbox({ namespace: 'agents', kubeConfig });
}
无 kubeConfig 提供者的电话 loadFromDefault(): chamvi运行于吊舱时的集群内服务账户,否则 KUBECONFIG 或者说 ~/.kube/config。将提供者传给 createWorkspaceManager({ provider }) 从 @chamvi/sandbox创建它与任何联系; create() 创建吊舱, 等待它运行( 每秒, 最多2分钟, 快速失效) ErrImagePull, (中文(简体) ), ImagePullBackOff, (中文(简体) ), InvalidImageName, (中文(简体) ), CreateContainerConfigError 财务报告和审定财务报表 CreateContainerError),然后检查出来.
非洲区域局
身份 chamvi 随需要运行, 只存在于沙盒名空间 :
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: chamvi-sandbox
namespace: agents
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["create", "get", "list", "delete"]
- apiGroups: [""]
resources: ["pods/exec"]
verbs: ["get", "create"]
# only with persistence
- apiGroups: [""]
resources: ["persistentvolumeclaims"]
verbs: ["create", "get", "list", "delete"]
方案安全: 默认情况下, pockes 以可写入的 root 文件系统和能力作为 root 运行 CHOWN, (中文(简体) ), DAC_OVERRIDE, (中文(简体) ), FOWNER, (中文(简体) ), FSETID, (中文(简体) ), SETUID, (中文(简体) ), SETGID 财务报告和审定财务报表 KILL安全级别 baseline 承认和 restricted 拒绝。 将沙盒命名空间相应标记:
kubectl label namespace agents pod-security.kubernetes.io/enforce=baseline
只在哪个地方 restricted 允许使用硬化模式, createKubernetesSandbox({ namespace, user: { uid: 1000, gid: 1000 }, readOnlyRoot: true }): 无根 runAsNonRoot 财务报告和审定财务报表 fsGroup,只读的根,没有能力(apt 然后失败; 工具链仍然安装到 /cache 以弥撒为主。 共享 cache.claimName 必须尊重 fsGroup (NFS风格的音量插件忽略了它)或作世界写作,否则被硬化的用户无法写入.
pods/exec 需要两个动词 : 客户端打开 exec WebSocket 用一个 GET, API 服务器授权为 get,而带有 WebSocket 升级授权更改的服务器则请求 create将沙盒命名空间除去Chamvi自己的,因此被吊舱从不与它的秘密共享命名空间.
舱
每个工作空间都是空的 chamvi-ws-<id> 一个集装箱 sandbox数字 :
| 设置 | 数值 |
|---|---|
| 专辑 | restartPolicy: Never, (中文(简体) ), automountServiceAccountToken: false, (中文(简体) ), enableServiceLinks: false, (中文(简体) ), shareProcessNamespace: true (暂停容器是pid 1 并收获任何图像的孤儿过程, 也是一个没有init, 否则僵尸会填补 kubelet 的 pid 极限), activeDeadlineSeconds = 工作空间的寿命,以秒计(lifetimeSeconds),所以吊舱结束于 expiresAt 即使没有收藏, terminationGracePeriodSeconds: 5 |
| Pod 担保 | runAsUser编号 :runAsGroup 从 user (默认 0 ),秒comp RuntimeDefault; 非根用户添加 runAsNonRoot 财务报告和审定财务报表 fsGroup |
| 集装箱安全 | allowPrivilegeEscalation: false没有特权, readOnlyRootFilesystem 从 readOnlyRoot (默认不实),所有能力都下降,用于根 ROOT_CAPABILITIES 添加回来 |
| 命令 | command: ['sh', '-c', SLEEP_UNTIL_SCRIPT, 'sh', '<expiresAt in epoch seconds>'] (睡觉直到) expiresAt,因此一个预定后期停放的起落架),替换了图像的入站点(一个其入站点也是命令工作的出站点) |
| 资源 | 请求同等限制 : cpu, (中文(简体) ), memory, (中文(简体) ), ephemeral-storage 从 storage |
/workspace | 空目录 sizeLimit 从 storage; 与 persistence (b) 工作空间的索赔 workspace(中文(简体) ) |
/tmp | 内存支持的空目录 tmpSize (默认 1 GiB, TMP_SIZE(中文(简体) ) |
/home/sandbox | 家庭(WORKSPACE_HOME: 节点磁盘上的一个空盘,计算在 ephemeral-storage; 与 persistence (b) 工作空间的索赔 home(中文(简体) ) |
/cache | readWrite Many 主张 cache.claimName,由每个工作区共享, 否则是空的 |
| 环境 | SANDBOX_ENV,缓存变量(CACHE_ENV和 env查姆维的舱里什么都没有 |
| 标签 | podLabels,则 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> (带有寄存器) (管理下的标签胜出; podLabels 命名一掷; a runId 没有标签值是一个 ValidationError,引擎运行代号为UUID) |
| 说明 | chamvi.dev/created-at 财务报告和审定财务报表 chamvi.dev/expires-at (划时代), chamvi.dev/repo, (中文(简体) ), chamvi.dev/ref, (中文(简体) ), chamvi.dev/image |
使用一个 pock 而不是 Job : exec 需要一个在创建时间已知的 pock 名称, 而 Job 的控制器, 重试和生成的名称 则不会添加任何 cock 光谱尚未执行的 .
limits.pids 库伯涅茨没有每波德字段: podPidsLimit 应用,所以设置在运行沙盒的节点上.
图像
默认图像是 ghcr.io/chamvi/sandbox-dev:trixie (单位:千美元)DEFAULT_SANDBOX_IMAGE: Debian, git, 为任何工具链构建工具, rigrep, jq and mise, 为 linux/amd64 财务报告和审定财务报表 linux/arm64 从 images/sandbox-dev; 标签 trixie (根据更改和每周重建)和 trixie-<short sha>。 。 。 . imagePullPolicy (提供者图像)默认为 'IfNotPresent'; 使用 'Always' 跟踪可变标记,或在生产中粘贴摘要(image: 'ghcr.io/chamvi/sandbox-dev@sha256:...') (中文(简体) ).
运行可能选择一个图像( Q)create_workspace({ image })),只有当管理者有许可列表时(allowImage,或 CHAMVI_SANDBOX_IMAGES 内 sandboxModule()(三); imagePullPolicy 适用于每一个图像, 提供者, 管理者 和一个运行要求。 图像需要 sh, (中文(简体) ), git 和一个 GNU timeout 这需要 -k数字 : create 一旦吊舱运行失败 就会探测 SandboxError 指定缺少的,在任何检出之前(并删除吊舱).
缓存
每个舱载 /cache 并获得缓存变量(CACHE_ENV数字 : XDG_CACHE_HOME, (中文(简体) ), npm_config_cache, (中文(简体) ), PIP_CACHE_DIR, (中文(简体) ), GOMODCACHE, (中文(简体) ), GOCACHE, (中文(简体) ), COMPOSER_CACHE_DIR; 包只下载, mise将工具链保存在Home). 无 cache 是一个空的 Dir (按其 ephemeral-storage)),所以安装工作,但每次开始冷. 与 cache: { claimName } 每个工作区都挂载相同的ReadWriteMany索赔(NFS, CephFS, EFS, Azure 文件),因此工作区共享权利要求的再利用相互的软件包下载(漏取,拆出或同时安装仍然可能再次取取;工具链停留每个工作区,在Home).
风险: 名称空间的每个运行和代理共享一个共享的主张. 一个被抓取的恶意包可以坐到另一个运行的缓存;npm验证完整性的散列,活页,Go的构建缓存等更少. 保持运行,这些运行不得在不同的要求上相互影响,或没有一项要求.
警卫
- 不是网络边界. 没有网络政策,DNS就将工作空间舱传送到集群中的每一项服务,包括chamvi API及其数据库,以及云元数据。 选择由
app.kubernetes.io/component=sandbox(或你的电话)podLabels并且拒绝所有进取,除非工作需要(GitHub, npm 注册)、chamvi命名空间和169.254.169.254明确否认。 赫尔姆海图将运送这项政策. - 根在内部,不在节点上: 默认的根用户只持有软件包管理功能, 没有特权升级, seccomp
RuntimeDefault,没有主机路径,也没有特权模式;被硬化的模式也会降低根. - 无全权证书:没有挂载服务账户令牌,没有注入服务链接,没有chamvi环境变量到达吊舱.
- 即时注射:模型所运行的跟随其读取的. 缩小
allowRepo在经理和设置bash背后的核准 必要时.
暂停和中止
每个命令运行为 timeout -k 5 <seconds> <argv>,表示整个进程组。 一个客户端警卫在超时后停止等15 s,并报告出口137 与产出至今. Kubernetes只在每个持有其输出的流程关闭后才发送一个执行官的退出状态,所以命令在stderr上报告其退出:一个退出时它开始的背景工作持有其输出(node server.js & echo started)在退出后使用自己的退出代码返回大约2 s,任务一直持续到被删除后. 中止( E)signal(中文(简体) ) 节点: 执行WebSocket关闭,命令运行到其 timeout 火灾或吊舱被删除。 每一次确定运行都会处理其工作空间(manager.attach(engine)) (中文(简体) ). 一个处理器删除了没有宽限期的吊舱.
Stdin只关闭在 v5.channel.k8s.io 执行协议(Kubernetes 1.30+) v4 它被写着,但从来没有关闭, 所以一个命令等待 Stdin EOF 挂起,直到它的超时. writeFile 不依赖它(head -c <bytes>) (中文(简体) ).
垃圾收集
find(runId, { deployment }) 通过标签列出运行的显示器并重建运行中运行的显示器的工作空间(一个显示器失败、完成或正在被删除的显示器已消失),所以运行在重启后或在另一个复制件上找到它的工作空间. collectGarbage({ deployment, isSettled }) 列出部署中带有所管理标签的显示器,并删除其过去的标签 chamvi.dev/expires-at 说明,处于阶段 Failed 或者说 Succeeded 并,与 isSettled,则已结算的运行(该流程的开放工作空间被先处理). 经理打来的 acquire 最多每10分钟一次.
持久性 (单位:千美元)persistence: { storageClassName?, size? }, (中文(简体) ), CHAMVI_SANDBOX_PERSISTENCE=true 与 sandboxModule(): create 提出ReadWriteOnce索赔 chamvi-ws-<id> (舱的标签和说明, size 默认 limits.storage, (中文(简体) ), storageClassName 默认星团),在起落架前,起落架在 /workspace 财务报告和审定财务报表 /home/sandbox与 isSuspendable 收藏删除一个运行中等待足够长并保留索赔的运行中吊舱; find 重建无运行状态的活体索赔的吊舱(从索赔中得出的图像和失效, env 财务报告和审定财务报表 limits 从提供者和经理, activeDeadlineSeconds 生命所剩无几 等待着 create 失败 SandboxError 当它没有开始。 撤销、终止和结清索赔也删去。 被停职:背景工作, /tmp,在两座山外写作,并写出一个空的Dir /cache一个节点本地存储类将重建的吊舱接入其节点. 无法移除的舱不能阻止通行:其他人去,一个去 SandboxError 与计数和第一个失败 投出在结尾.
选项
createKubernetesSandbox(options)数字 :
| 选项 | 类型 | 默认 | 说明 |
|---|---|---|---|
namespace | string | 所需经费 | 工作空间舱的命名空间 |
kubeConfig | KubeConfig | loadFromDefault() | 集群接入 |
image | string | DEFAULT_SANDBOX_IMAGE | 工作空间图像; 在生产中刻入摘要 |
imagePullPolicy | 'IfNotPresent' | 'Always' | 'IfNotPresent' | 每个工作空间图像; 'Always' 可变标记 |
env | Record<string, string> | 无 | 之后添加 SANDBOX_ENV 财务报告和审定财务报表 CACHE_ENV |
user | { uid, gid } | { uid: 0, gid: 0 } | 舱的用户; 一个非源用户没有能力, runAsNonRoot 财务报告和审定财务报表 fsGroup |
readOnlyRoot | boolean | false | 只读的根文件系统 |
cache | { claimName } | 无 | 共享的ReadWriteMany索赔 /cache (每舱空出一个Dir) |
persistence | { storageClassName?, size? } | 无 | 每一工作空间的索赔 /workspace 和家,所以一个等待运行的吊舱可以中止;一个习惯 client 然后需要索赔方法 |
tmpSize | string | '1Gi' | 内存大小 /tmp |
limits | SandboxLimits | DEFAULT_LIMITS | cpu, (中文(简体) ), memory, (中文(简体) ), storage, (中文(简体) ), maxLifetimeMs (单位:千美元)pids:见上文) |
podLabels | Record<string, string> | 无 | 额外标签, 例如, 用于网络政策; 可能不命名管理下的标签 |
startTimeoutMs | number | 120_000 | 多长时间 create 等待吊舱运行 |
pollMs | number | 1_000 | 等待时的状态民调间隔 |
clock | IClock | systemClock | 民意调查 过期 执行警卫 |
client | IPodClient | createPodClient() | 测试接合 |
配置错误抛出核心 InvalidAgentDefinitionError; API 和 pock 失败为 SandboxError编号.
API 密码
| 导出 | 仁爱 | 目的 |
|---|---|---|
createKubernetesSandbox | 函数 | 这个 ISandboxProvider (单位:千美元)name: 'k8s'(中文(简体) ) |
createPodClient | 函数 | 这个 IPodClient 结束 @kubernetes/client-node |
CONTAINER_NAME | 常数 | 'sandbox' |
POD_NAME_PREFIX | 常数 | 'chamvi-ws-' |
START_TIMEOUT_MS, (中文(简体) ), POLL_MS | 常数 | 120_000, (中文(简体) ), 1_000 |
FATAL_WAITING_REASONS | 常数 | 集装箱等待原因 create 一次失败 |
类型 : KubernetesSandboxOptions, (中文(简体) ), KubernetesPersistence, (中文(简体) ), PodUser, (中文(简体) ), IPodClient, (中文(简体) ), PodExecInput, (中文(简体) ), CreatePodClientInput。 。 。 .
许可证
原载 Apache 许可证 2.0。 。 。 .