
Onyx Sandbox 容器镜像全解析构建、部署与运行时内幕【免费下载链接】danswerOpen Source AI Platform - AI Chat with advanced features that works with every LLM项目地址: https://gitcode.com/GitHub_Trending/da/danswer本文聚焦 Onyx原 DanswerCraft 功能的沙箱容器镜像Sandbox Container Image以backend/onyx/server/features/build/sandbox/image/目录为核心系统讲解镜像的构建方式CI 与本地两种路径、应用版本对齐的部署策略、镜像内预置的全部组件Next.js 模板、Python venv、OpenCode CLI、快照守护进程等、会话运行时目录结构并结合 Dockerfile、entrypoint、防火墙初始化脚本与 sidecar 守护进程源码深入揭示镜像内部的启动流程、出口网络封锁与快照持久化机制。读完本文你将具备独立构建、发布、排查 Onyx 沙箱镜像的完整能力。一、镜像在 Onyx 架构中的位置Onyx 的 Craft 功能为每个用户提供一个隔离的沙箱环境用于运行 OpenCode 编码 AgentAgent 在其中构建 Web 应用、执行 Python 代码并访问知识库文件。这些沙箱的运行载体Kubernetes Pod 或 Docker 容器完全由本目录产出的容器镜像承载。Kubernetes 模式下每个用户一个 Pod由api_server通过 Kubernetes API 驱动 Pod 生命周期与kubectl exec见 kubernetes_sandbox_manager.pyDocker 模式下api_server挂载/var/run/docker.sock调用 Docker Engine API见 docker_sandbox_manager.py。两种后端共享同一份镜像其源码与构建产物全部位于 backend/onyx/server/features/build/sandbox/image。二、镜像目录结构速览image/ ├── Dockerfile # 主容器镜像定义多阶段构建 ├── .dockerignore # 裁剪构建上下文 ├── entrypoint.sh # 容器启动脚本opencode serve 重启循环 ├── sidecar-entrypoint.sh # sidecar 守护进程启动脚本 ├── firewall-init.sh # 出口防火墙 CA 信任初始化脚本 ├── browser-cli.sh # agent-browser 的 PATH 包装器 ├── sandbox_daemon/ # 沙箱内 push/快照守护进程烘焙进镜像 ├── opencode-plugins/ # 按会话打出口代理标签的插件烘焙进镜像 ├── templates/ │ └── outputs/ # Web 应用脚手架模板Next.js ├── initial-requirements.in # 沙箱预装 Python 包清单 └── initial-requirements.txt # 全量锁定的 Python 依赖一个容易被忽视的设计原则内置技能skills并不在镜像中。它们存放在 backend/onyx/skills/builtin 目录由 API Server 在会话建立时推送到沙箱绝不会被烘焙进镜像。镜像只预装技能运行所需的运行时工具LibreOffice、poppler、字体等。三、构建镜像CI 优先本地兜底3.1 通过 CI 发布推荐路径应用发布流水线会将沙箱镜像与应用镜像打上相同标签例如应用 tagv4.1.2对应onyxdotapp/sandbox:v4.1.2。CI 的工作机制如下.github/workflows/deployment.yml中的沙箱任务以本目录内容计算一个内容标签ctx-hash若ctx-hash已存在部署流水线只为既有 manifest 追加应用 tag/别名不重复构建若不存在则做一次多架构构建并推送ctx-hash随后再补加应用对齐标签。该工作流仅在需要回填或修复某个应用 tag时手动触发。不要为新建构建发布v0.1.x这类仅沙箱的 tag也不要发布 Docker Hub 的onyxdotapp/sandbox:devtag本地开发仍可构建onyxdotapp/sandbox:dev并直接加载进 kind 集群。3.2 本地构建集群运行在 x86_64 节点上因此沙箱镜像必须构建为 amd64 架构。仅构建 amd64最快cd backend/onyx/server/features/build/sandbox/image docker build --platform linux/amd64 -t onyxdotapp/sandbox:dev .回填或修复时的多架构构建docker buildx build --platform linux/amd64,linux/arm64 \ -t onyxdotapp/sandbox:app-tag \ --push .3.3 构建参数Build ARG与关键取舍从 Dockerfile 可以看到镜像构建是高度参数化的多阶段构建构建参数默认值说明BASE_IMAGE_REGISTRYdocker.io基础镜像仓库前缀CI 会覆盖为 ECR pull-through 缓存以避免 Docker Hub 限流ENABLE_SKILLStrue置为false可跳过技能运行时依赖LibreOffice、poppler、字体、pptxgenjs省约 700 MBENABLE_BROWSERtrue是否打入 Chromium 引擎与agent-browserCLIOPENCODE_VERSION1.18.19OpenCode CLI 版本AGENT_BROWSER_VERSION0.31.1agent-browser CLI 版本NPM_VERSION11.17.0Node 镜像阶段内升级的 npm 版本GH_CLI_VERSION2.94.0GitHub CLI 版本独立阶段下载并用 sha256 校验和验证值得注意的源码细节基础镜像最终镜像基于python:3.13-slimDebian 系Node.js 24 从node:24-trixie-slim阶段拷贝二进制bun 则从oven/bun:1.3.14阶段拷贝。npm 符号链接由于只拷贝了node与node_modules需要手动建立npm、npx、corepack的符号链接。Chromium 独立分层大的、少变的 Chromium 引擎单独一层后续仅升级AGENT_BROWSER_VERSION时只重拉小体积 CLI 层。不要执行agent-browser install它会抓取 Chrome-for-Testing无 arm64 构建镜像改用系统 Chromium 并通过AGENT_BROWSER_EXECUTABLE_PATH指定。uid/gid 1000 归一化构建脚本会把基础镜像中已有 uid/gid 1000 的用户重命名为sandbox保证非 root 运行身份统一。Bun 缓存预热构建期在/workspace/templates/outputs/web执行bun install --frozen-lockfile后删除node_modules从而预暖 Bun 的 tarball 缓存使每次会话启动的安装命中本地存储。四、部署新版本镜像与应用版本对齐部署匹配的应用 tag 即可部署面会自动从应用版本推导沙箱镜像Docker composeonyxdotapp/sandbox:${IMAGE_TAG}遵循普通应用镜像的IMAGE_TAG行为含默认latestHelmonyxdotapp/sandbox:${global.version}。Kubernetes 部署应优先使用不可变的应用发布 tag除非刻意选择移动 tagmoving tags。SANDBOX_CONTAINER_IMAGE是内部逃生舱口仅供本地测试、staging、云端及应急操作使用。其默认值定义在 configs.pySANDBOX_CONTAINER_IMAGE ( os.environ.get(SANDBOX_CONTAINER_IMAGE, ).strip() or onyxdotapp/sandbox:latest )五、镜像内烘焙了什么镜像预置内容与文档一致且均能在 Dockerfile 中逐一找到证据组件位置说明基础系统python:3.13-slimDebian 系Node.js 24 从node:24-trixie-slim拷贝Web 模板/workspace/templates/outputs/Next.js 应用脚手架COPY --exclude.next --excludenode_modules拷贝Python venv/workspace/.venv/由initial-requirements.txt全量锁定并--only-binary:all:安装OpenCode CLI/home/sandbox/.opencode/bin/以 sandbox 用户身份安装指定版本、--no-modify-pathonyx-cli/usr/local/bin/onyx-cliOnyx CLI用于搜索快照 sidecar 守护进程/workspace/sandbox_daemon/与/opt/sandbox_daemon/打包/恢复会话文件持久化由 api_server 经 Onyx FileStore 负责出口标签插件/workspace/opencode-plugins/按会话打代理标签浏览器封装/usr/local/bin/browseragent-browser 包装器见下文镜像内不烘焙技能API Server 在会话建立时把技能推送到/workspace/managed/skills/。5.1 Python 依赖清单initial-requirements.in是精选清单initial-requirements.txt是其全量锁定版本。前者内容如下cryptography defusedxml fastapi lxml matplotlib matplotlib-inline numpy onyx-cli openpyxl pandas pdfplumber pillow pydantic python-docx python-pptx requests uvicorn[standard]覆盖数据处理pandas/numpy/matplotlib、文档解析pdfplumber/python-docx/python-pptx/openpyxl、HTTPrequests与 Web 服务fastapi/uvicorn等场景。5.2 镜像暴露的端口与入口Dockerfile 末尾暴露三个端口并指定入口# 3000: Next.js dev server, 4096: opencode serve, 8731: push daemon EXPOSE 3000 4096 8731 ENTRYPOINT [/workspace/entrypoint.sh]3000Next.js 开发服务器4096OpenCode 服务8731push 守护进程端口见 contract.py。六、运行时目录结构一次会话如何铺开创建会话时Pod 内会搭建如下结构/workspace/ ├── managed/skills/ # 会话建立时推送内置 自定义技能 ├── opencode-data/ # Kubernetes 下的沙箱级全局 opencode 数据 ├── templates/ # 烘焙进镜像 └── sessions/ └── $session_id/ ├── .opencode/ │ └── skills # 符号链接 → /workspace/managed/skills ├── outputs/ # 从模板拷贝内含 Web 应用 ├── attachments/ # 用户上传文件 ├── AGENTS.md # 给 AI Agent 的指令 └── opencode.json # OpenCode 配置关键设计点技能通过符号链接暴露给每个会话避免重复拷贝outputs/从烘焙的模板拷贝保证每个会话有干净的初始工程AGENTS.md由 util/agent_instructions.py 基于AGENTS.template.md动态填充opencode.json由 util/opencode_config.py 动态生成含 MCP 支持与工具权限。七、启动链路源码剖析7.1 entrypoint.shopencode serve 重启循环entrypoint.sh 是容器主进程职责包括opencode 历史恢复根据OPENCODE_DATA_HOMEKubernetes 下由 sidecar 恢复后指向沙箱级共享卷设置XDG_DATA_HOME放宽 bash 工具超时OPENCODE_EXPERIMENTAL_BASH_DEFAULT_TIMEOUT_MS默认 180000ms180s避免图片生成等合法慢工具被 opencode 120s 默认超时掐断代理 CA 导入通过certutil把出口代理的 MITM CA 逐条导入 Chromium 的 NSS 数据库保证浏览器 HTTPS 可用失败不阻塞启动重启循环opencode serve --hostname 0.0.0.0 --port 4096 --print-logs崩溃后按指数退避1s → 最大 30s自动重启并妥善处理 SIGTERM/SIGINT 信号。7.2 firewall-init.sh出口网络封锁与 CA 信任firewall-init.sh 是沙箱安全的关键支持两种引导模式initcontainerK8s initContainer执行完步骤后退出 0entrypointdocker-compose 入口执行完步骤后用setpriv降权到 UID 1000 再 exec 真正的入口。它完成三步安装代理 CA把SANDBOX_PROXY_CA_BUNDLE_SRC默认/sandbox-ca/ca.crt装入系统信任库并生成SANDBOX_PROXY_CA_BUNDLE_DST默认/etc/ssl/sandbox/ca-bundle.crtiptables 出口封锁清空 OUTPUT 链、默认策略DROP仅放行lo、已建立的 conntrack 连接以及到SANDBOX_PROXY_HOST:SANDBOX_PROXY_PORT的 TCPIPv6 同步全封锁部分封锁即安全回归自校验检查 OUTPUT 链默认策略确为DROP、存在代理放行规则与 conntrack 规则失败即退出。entrypoint模式下还修复了 Docker 卷挂载点/workspace/sessions的属主chown 1000:1000并用setpriv --bounding-set-all清除能力边界集后降权。注意它显式设置HOME/home/sandbox USERsandbox——setpriv 不会刷新环境变量继承HOME/root会导致降权后的 Agent 因/root/.cache的 EACCES 而启动失败。7.3 sidecar 守护进程签名保护的 push 与快照sidecar-entrypoint.sh 以无限重启循环方式运行/workspace/.venv/bin/python -m sandbox_daemon.server。其核心实现位于 server.py是一个 FastAPI 服务提供文件系统列举/filesystem/list带路径穿越防护输出 manifest/outputs/manifestpush/push流式接收 tar.gz 并校验 SHA-256 与大小上限后安全解包、原子替换快照创建/恢复/snapshot/create无内容时返回 204与/snapshot/restoreopencode 历史创建、恢复、标记恢复三组端点。所有写操作都要求X-Push-Signature与X-Push-Timestamp头使用 Ed25519 公钥来自环境变量注入仅 sidecar 持有验证{timestamp}|{path}|{sha256}签名并检查 60 秒时间戳漂移防止未授权的数据写入。7.4 browser 包装器与会话标签插件browser-cli.sh 把agent-browser包装成 PATH 上的browser可执行文件剥除代理 URL 中的用户信息代理按源 IP 授权、自动追加--no-sandbox --proxy-server... --proxy-bypass-list127.0.0.1;localhost等参数并把当前会话 ID从 cwd 的/sessions/uuid解析传给agent-browser --session让浏览器实例与会话一一对应。opencode-plugins/session-proxy-tag.ts 是 OpenCode 插件在会话初始化时从?directory捕获会话 ID将其编码进HTTP(S)_PROXY的 userinfo形如https://session-id:xproxy使出口代理能把审批卡片精确路由到对应会话。HTTPS 下该信息随 CONNECT 的 Proxy-Authorization 传递hop-by-hop不会到达源站。该插件路径与 kubernetes_sandbox_manager.py 中的_OPENCODE_SESSION_TAG_PLUGIN_PATH /workspace/opencode-plugins/session-proxy-tag.ts严格对应。八、会话启动后的完整时序综合上述脚本一次沙箱会话的启动链路为Pod/容器创建K8s 下先运行 initContainerfirewall-init 的initcontainer模式完成 CA 安装、iptables 封锁与自校验compose 下由 entrypoint 模式完成同样步骤后setpriv降权sidecar 启动K8s原生可重启 init sidecar 运行sandbox_daemon.server恢复 opencode 历史并等待 API Server 就绪探测主进程启动entrypoint.sh导入代理 CA 到 Chromium NSS 后循环拉起opencode serve端口 4096会话铺开API Server 推送技能到/workspace/managed/skills/、从模板拷贝outputs/、挂载附件并注入AGENTS.md与opencode.jsonWeb 预览Agent 在会话目录执行bun install --frozen-lockfile命中镜像预暖缓存bun run devNext.js 开发服务器监听 3000 端口经 Service/DNS 或桥接网络代理对外提供预览快照与恢复sidecar 经签名 API 打包/恢复会话文件持久化由 api_server 通过 Onyx FileStore 完成沙箱与 sidecar 均不持有存储凭据。九、故障排查速查9.1 验证镜像是否存在于 Docker Hubcurl -s https://hub.docker.com/v2/repositories/onyxdotapp/sandbox/tags | jq .results[].name9.2 查看 Pod 实际使用的镜像kubectl get pod pod-name -n onyx-sandboxes -o jsonpath{.spec.containers[?(.namesandbox)].image}9.3 常见故障与排查要点症状排查要点Pod 停滞在PROVISIONINGK8skubectl logs -n onyx-sandboxes sandbox-{id}查主容器、-c sidecar查 sidecar 日志核对代理 host/CA 配置与 ServiceAccount 是否存在容器停滞在PROVISIONINGDockerapi_server是否挂载了/var/run/docker.sockonyx_craft_sandbox桥接网络是否存在docker logs sandbox-id8确认ONYX_SERVER_URL在桥接网络内可达且含 API 路径前缀Next.js 预览打不开查容器日志确认bun install成功kubectl exec ... -- ls /workspace/outputs/web确认模板已拷贝浏览器 HTTPS 异常确认代理 CA 已导入 Chromium NSSentrypoint 启动日志会打印导入结果十、安全设计小结从镜像构建到运行时安全贯穿始终沙箱容器以uid/gid 1000 非 root运行K8s 下使用受限 security contextDocker 下--security-opt no-new-privileges、--cap-drop ALL并通过setpriv --bounding-set-all清空能力集出口网络强制走代理iptables 默认丢弃所有 OUTPUT仅放行代理端点浏览器/CLI 的流量全部经由 egress proxy 审计与路由沙箱与 sidecar 均不持有 FileStore/S3/MinIO 凭据快照数据经签名 API 流式传给 api_server 持久化并按租户隔离存储路径写路径全部要求Ed25519 签名 时间戳防重放 SHA-256 完整性校验Docker 模式下沙箱仅加入专用onyx_craft_sandbox桥接网络compose 的 postgres/redis/minio/模型服务对沙箱不可达EC2 上的 IMDS169.254.169.254需在宿主机强制 IMDSv2 或加DOCKER-USERiptables 规则阻断安装器不会自动处理应用层无兜底。十一、相关资源沙箱系统总览架构、环境变量、K8s/Docker 映射sandbox/README.md镜像定义image/Dockerfile配置项与默认值build/configs.py内置技能会话建立时推送不入镜像backend/onyx/skills/builtin沙箱配置与解析的单元测试test_sandbox_backend_parsing.py、test_docker_manager_config.pyHelm 沙箱相关测试test_pod_spec.py、test_sandbox_image_prepuller.py【免费下载链接】danswerOpen Source AI Platform - AI Chat with advanced features that works with every LLM项目地址: https://gitcode.com/GitHub_Trending/da/danswer创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考