切换语言
切换主题

自托管 Dev Sandbox:用 Docker 和 Go 搭一套带预览链接的开发环境

"sandboxed README 说明了 Go 控制平面、Docker、Traefik、SQLite、preview URL、idle stop 和生产硬化边界。"

"Docker 官方资源限制文档说明容器默认没有 CPU 和内存限制,需要显式配置约束。"

"Docker Sandboxes 官方文档把 microVM、独立 Docker daemon、网络代理和凭据隔离作为更强安全模型。"

"Traefik Docker provider 可通过 Docker labels 获取动态路由配置,用 Host rule 路由到容器服务。"

Preview URL 看着像一个很小的功能:容器里跑起 3000 端口,把链接发给用户就行。真正动手时,自托管 Dev Sandbox 会把端口冲突、域名路由、容器回收、文件持久化和 API 调度全部拉出来。AI 编程产品尤其容易踩这个坑:Agent 写完代码后,用户不想看日志,只想点开结果。

这里不能只写一段 docker run。更稳的做法是拆出四个东西:一台 Linux 主机,Docker 管容器,Go 控制平面管生命周期,Traefik 管预览域名,SQLite 记录状态。它适合受信任团队和早期产品验证;如果你要运行陌生用户的任意代码,容器方案只能算第一层,后面要认真考虑 microVM、独立主机或 Kubernetes。

适合很多短生命周期环境,不适合个人玩两个容器

个人项目里,两个容器、两个端口、一个 docker compose up -d 就够了。Dev Sandbox 开始有价值,通常是因为环境数量变多,生命周期变短,并且每个环境都要被一个外部系统调用。

场景是否适合自托管 Dev Sandbox原因
自己跑一个长期 demo不太需要Shell 脚本或 Compose 更简单
团队每个分支一个预览环境适合需要自动创建、路由、回收和状态记录
AI app builder 给用户生成小应用很适合Agent 要在隔离目录里写代码,并马上暴露 preview URL
公开运行陌生用户任意代码只适合做原型Docker 容器不是强隔离边界,生产要补 VM / microVM
多节点、大规模调度、复杂网络策略单机方案不够Kubernetes 或托管平台更稳

很多人第一反应是,为什么不让 Agent 直接写一段 shell script。这个问题很合理。脚本能解决“启动一个容器”,但解决不了“让 50 个环境同时活着、闲置后停掉、下一次访问自动唤醒、文件不丢、每个环境有稳定 URL、API 能被 SaaS 后端调用”。这些需求一叠上来,脚本就开始长出控制平面。

最小架构:Go 控制平面、Docker、Traefik 和 SQLite

tastyeffectco/sandboxes 的设计很克制:一个 Go 程序 sandboxd 负责发 Docker 命令,Traefik 根据容器 labels 做动态路由,SQLite 做状态库,工作区落在磁盘目录。没有 Kubernetes,没有独立数据库,也没有消息队列。

browser
  |
  v
Traefik  ---->  sandbox container  ----> dev server :3000
  ^                    ^
  |                    |
sandboxd --------------+
  |
  +-- SQLite: sandbox 状态、端口、任务
  +-- workspaces/: 每个 sandbox 一个持久化目录
  +-- reaper: idle stop / memory pressure stop

这套架构里有四个对象要分清。

控制平面不是业务容器

Go 控制平面只做生命周期:创建 sandbox、停止 sandbox、销毁 sandbox、执行命令、提交 agent task、读取文件。它最好保持薄,不要把业务构建逻辑全塞进去。复杂逻辑可以放到 sandbox 的基础镜像、任务队列或上层应用里。

Preview URL 不是随机端口

每个 sandbox 可以对外暴露 s-<id>-3000.preview.localhost 这样的地址。Traefik 通过 Docker labels 发现目标容器和目标端口,再按 Host rule 转发请求。这样用户看到的是稳定链接,而不是“你的端口是 30017,记得别和别人冲突”。

SQLite 是小系统的状态锚点

容器会重启,主机会重启,Traefik 也可能重载。SQLite 记录 sandbox ID、端口、状态、任务、工作区位置,控制平面启动后再和 Docker 实际状态做 reconcile。早期产品用 SQLite 没问题,前提是你接受单机边界,并做好备份。

从本地跑通:先验证 API、端口、域名解析

本地验证不要急着接 Agent。先确认控制平面能启动容器、Traefik 能转发、preview URL 能打开。sandboxes README 给出的 quick start 很直接:

git clone https://github.com/tastyeffectco/sandboxes.git
cd sandboxes
./install.sh

API=http://127.0.0.1:9090
curl "$API/healthz"

健康检查通了以后,再创建一个服务端口为 3000 的 sandbox:

ID=$(curl -s -XPOST "$API/sandbox" \
  -H 'content-type: application/json' \
  -d '{"ports":[3000]}' | sed -E 's/.*"id":"([^"]+)".*/\1/')

curl -s -XPOST "$API/sandbox/$ID/exec" \
  -H 'content-type: application/json' \
  -d '{"cmd":["bash","-lc","cd ~/workspace && python3 -m http.server 3000"]}'

然后打开:

http://s-<id>-3000.preview.localhost

*.localhost 在现代浏览器里会解析到本机,适合本地零 DNS 调试。换成真实域名时,要把 *.preview.yourdomain.com 指向主机,再让 Traefik 处理 TLS。不要把 127.0.0.1:9090 这种本地 API 原样暴露到公网。生产环境至少要打开 token 认证,并用防火墙或内网网关挡住控制平面端口。

预览 URL 的难点在路由、唤醒和持久化

普通端口转发只管“容器活着时怎么访问”。Dev Sandbox 还要处理“容器睡着了怎么办”“文件放哪里”“同一个用户回来还能不能继续”。这三个问题决定系统是不是能从 demo 走到内测。

路由:用域名承载环境身份

端口号是机器视角,域名是产品视角。s-<id>-3000.preview.example.com 里包含 sandbox ID 和目标端口,上层应用可以直接把这个链接展示给用户。Traefik 的 Docker provider 会读取容器 labels,按 Host rule 把请求送进对应容器。

排障时按这个顺序看:

  • DNS:wildcard 域名是否指向主机,或本地是否使用 *.localhost
  • Traefik:容器是否带了正确 labels,是否在同一个 Docker network。
  • 端口:应用是否真的监听 0.0.0.0:3000,不是只监听 127.0.0.1
  • readiness:应用刚启动时是否有等待页或重试策略。
  • TLS:真实域名是否配置 wildcard 证书,HTTP 到 HTTPS 是否一致。

唤醒:空闲停止不是删除环境

空闲 sandbox 可以 docker stop,这样释放内存,但工作区目录仍在磁盘上。下一次用户打开 preview URL 时,低优先级 catch-all 路由可以先把请求交给控制平面,控制平面启动容器、等待端口 ready,再让浏览器进入真实应用。

这个机制比“永远开着”省资源,也比“关了就没了”更像产品。代价是首次访问会有冷启动,所以页面上最好有 warming 状态,而不是让用户盯着 502。

持久化:bind mount 好用,但要知道边界

Docker 官方文档把 bind mount 放在源码和构建产物共享场景里很常见。Dev Sandbox 也会这么做:每个 sandbox 一个 host 目录,挂到容器里的工作目录。好处是容器删除后代码还在,坏处是 host 路径和权限会变成系统设计的一部分。

内测前至少要定三条规则:工作区目录不能和控制平面配置混在一起;删除 sandbox 和删除工作区要分成两个动作;备份只备工作区和 SQLite,不要指望从容器层里捞关键状态。

多租户底线:资源限制、Docker socket、API auth 和镜像缓存

Docker 官方资源限制文档的提醒很直接:容器默认没有资源约束,会按主机内核调度器允许的范围使用 CPU 和内存。多租户环境里这就是风险,一个用户的 npm install、构建或死循环可能拖慢整台机器。

可以从这份清单开始:

  • 每个 sandbox 设置内存、CPU 和 PIDs 限制。
  • 控制平面 API 默认只监听内网或本机,公网入口必须认证。
  • 预览链接默认是可分享链接,涉及敏感内容时要接 forward-auth。
  • 基础镜像预装常用工具,减少每次 sandbox 都重新拉包。
  • Docker Hub 拉取有官方限流和 fair use 规则,生产环境准备登录、镜像缓存或私有 registry。
  • sandbox 工作区单独挂载,不把 /var/run/docker.sock 交给用户容器。
  • 记录创建、停止、销毁、执行命令和 agent task 的审计日志。

一个 Compose 里的资源限制示例可以长这样:

services:
  sandbox-app:
    image: your-sandbox-base:latest
    deploy:
      resources:
        limits:
          cpus: "1.00"
          memory: 1G
          pids: 256

更大的安全点在 Docker socket。sandboxd 如果挂载 host Docker socket,它对主机就有很高权限。这个设计在“控制平面由你维护,用户只进入被创建出来的 sandbox 容器”时可以接受;如果用户能影响控制平面容器或拿到 Docker socket,风险就会越过普通容器边界。

什么时候升级到 microVM、Kubernetes 或托管平台

单机 Docker 方案的优点是便宜、好读、好改,缺点也很直接:单机容量有限,安全边界主要依赖容器隔离和主机治理,调度能力不如集群。

触发条件更合适的方向
运行陌生用户任意代码microVM、独立 VM、gVisor、Kata 或 Firecracker
Agent 需要完整 Docker 能力但不能接触 host daemonDocker 官方 Sandboxes 这类 microVM + 独立 daemon 模式
多主机调度、弹性扩容、统一网络策略Kubernetes
团队不想维护底层控制面托管 preview environment 平台
仍在验证产品需求单机 Docker + Go 控制平面

Docker 官方 Sandboxes 的安全模型值得拿来做参照:它把 AI agent 放进 microVM,每个 sandbox 有自己的 Docker daemon、文件系统和网络,主机 Docker daemon 不暴露给 sandbox。资源开销会更高,但隔离边界更清楚。

所以早期可以先用单机方案摸清产品循环:创建环境、让 Agent 写代码、打开预览、空闲回收、保留文件。等真实用户进来,再按风险升级隔离层。不要反过来,一开始为了还没出现的规模问题把团队拖进集群维护。

一份从 MVP 到内测的落地清单

可以按这个顺序推进,不需要一次做满:

  1. 选一台干净 Linux 主机,只跑 sandbox 相关服务,不混放数据库、CI runner 和生产业务。
  2. 配置 wildcard 预览域名,例如 *.preview.example.com,本地先用 *.localhost 验证。
  3. 跑通控制平面 API:create、exec、stop、destroy、healthz。
  4. 为 sandbox 基础镜像预装 Node.js、Python、Git、常用包管理器和你要支持的 agent CLI。
  5. 为每个 sandbox 加资源限制、空闲回收、工作区持久化和销毁策略。
  6. 打开 API auth,预览链接按业务需要加访问控制。
  7. 记录审计日志,监控主机内存、磁盘、容器数量、冷启动时间和 502 比例。
  8. 做备份和恢复演练:SQLite、工作区目录、.env,以及基础镜像构建脚本。

如果你的下一步是把代码预览接进 CI,可以看 GitHub Actions 自托管 Runner:私有环境部署完全指南。如果是把生成出来的 Next.js 应用长期托管起来,逃离 Vercel:Next.js Docker 自托管完全指南 更贴近后半段。公开域名和源站保护,则可以接着看 Cloudflare回源IP白名单配置

常见问题

Dev Sandbox 和普通 Docker Compose 有什么区别?

Compose 更像“我声明一组服务,然后启动它们”。Dev Sandbox 更像“产品后端按用户请求创建、停止、唤醒、销毁环境,并给每个环境一个 URL”。如果环境数量少、生命周期长,Compose 足够;如果环境是按用户、分支或 Agent task 动态创建,就需要控制平面。

为什么不用 Kubernetes?

如果你已经有集群、Ingress、镜像仓库、权限和监控,Kubernetes 很适合做标准化环境平台。问题是很多早期团队只是想验证 AI app builder 或内部预览环境,维护集群会比业务本身还重。单机 Docker 不是替代 K8s,而是把第一阶段做轻。

容器隔离可以跑陌生用户代码吗?

不建议直接这么做。容器适合受信任团队、内部用户或低风险 demo。陌生用户任意代码应该放到更强隔离里,例如 microVM、独立 VM、gVisor / Kata / Firecracker,或者至少按租户拆主机。

Preview URL 一定要 HTTPS 吗?

本地 *.localhost 可以先用 HTTP。真实公网域名建议上 HTTPS,尤其是会输入 token、表单或业务数据的时候。wildcard 证书能减少为每个 sandbox 单独签证书的麻烦。

空闲停止后文件会丢吗?

只要工作区是持久化目录,docker stop 不会删除文件。真正要小心的是 destroypurge 的语义:一个只删容器,一个连工作区也删。产品 UI 和 API 名称要把这两件事分清。

Docker Hub 限流会影响这种系统吗?

会。多环境频繁启动、构建和拉取镜像时,公网 registry 会变成不稳定因素。生产环境最好登录 Docker Hub,准备私有 registry 或镜像缓存,并把常用依赖放进基础镜像。

结论

自托管 Dev Sandbox 值得做,但不要把它想成一个“高级 docker run”。它是一套小平台:控制平面负责生命周期,反向代理负责 URL,状态库负责恢复,资源限制和安全策略负责不让一个环境拖垮整台机器。

最稳的路径是先用单机 Docker 做出产品闭环,再根据真实风险升级。用户少、代码可信、团队能接受单机边界时,Go + Docker + Traefik + SQLite 很够用;陌生用户、强隔离、多主机和合规治理出现后,就该把 microVM、Kubernetes 或托管平台放上桌。

参考资料

自托管 Dev Sandbox MVP 落地流程

从单机 Docker 验证到内测前安全检查的实践步骤。

⏱️ 预计耗时: 4 小时

  1. 1

    步骤1: 准备干净主机

    选一台只运行 sandbox 相关服务的 Linux 主机,不混放生产数据库、CI runner 或其他高价值服务。
  2. 2

    步骤2: 配置预览域名

    本地先用 `*.localhost` 验证,真实环境再把 `*.preview.example.com` 指向主机并配置 TLS。
  3. 3

    步骤3: 跑通控制平面 API

    至少验证 create、exec、stop、destroy、healthz,确保上层应用可以通过 API 管理环境。
  4. 4

    步骤4: 准备 sandbox 基础镜像

    在基础镜像中预装 Node.js、Python、Git、常用包管理器和需要支持的 Agent CLI,减少每次启动后的重复安装。
  5. 5

    步骤5: 加入资源限制和回收策略

    为每个 sandbox 设置 CPU、内存和 PIDs 限制,配置 idle stop、wake-on-request 和工作区持久化。
  6. 6

    步骤6: 锁住控制面和预览入口

    启用 API token、内网访问或防火墙规则;敏感预览链接接入 forward-auth 或业务登录态。
  7. 7

    步骤7: 补日志和监控

    记录创建、停止、销毁、执行命令和 Agent task,监控主机内存、磁盘、容器数量、冷启动时间和 502 比例。
  8. 8

    步骤8: 演练备份恢复

    备份 SQLite、工作区目录、`.env` 和基础镜像构建脚本,并确认能在新主机恢复。

常见问题

Dev Sandbox 和普通 Docker Compose 有什么区别?
Compose 更像声明一组固定服务后启动它们。Dev Sandbox 更像产品后端按用户请求创建、停止、唤醒、销毁环境,并给每个环境一个 preview URL。环境数量少、生命周期长时 Compose 足够;按用户、分支或 Agent task 动态创建环境时,需要控制平面。
为什么不用 Kubernetes?
如果你已经有集群、Ingress、镜像仓库、权限和监控,Kubernetes 很适合做标准化环境平台。很多早期团队只是想验证 AI app builder 或内部预览环境,单机 Docker 可以先把第一阶段做轻,等多主机和复杂治理出现后再升级。
Docker 容器隔离可以直接跑陌生用户代码吗?
不建议。容器适合受信任团队、内部用户或低风险 demo。陌生用户任意代码应该放进更强隔离,例如 microVM、独立 VM、gVisor、Kata、Firecracker,或者至少按租户拆主机。
Preview URL 一定要 HTTPS 吗?
本地 `*.localhost` 可以先用 HTTP。真实公网域名建议上 HTTPS,尤其是会输入 token、表单或业务数据时。wildcard 证书能减少为每个 sandbox 单独签证书的麻烦。
空闲停止后文件会丢吗?
只要工作区是持久化目录,`docker stop` 不会删除文件。要小心区分 destroy 和 purge:一个只删容器,一个连工作区也删。产品 UI 和 API 名称要把这两个动作分清。
Docker Hub 限流会影响 Dev Sandbox 吗?
会。多环境频繁启动、构建和拉取镜像时,公网 registry 会变成不稳定因素。生产环境最好登录 Docker Hub,准备私有 registry 或镜像缓存,并把常用依赖放进基础镜像。

12 分钟阅读 · 发布于: 2026年6月5日 · 修改于: 2026年6月8日

评论

使用 GitHub 账号登录后即可评论