self-hosted CI runner 有个尴尬的特点:它什么都记得。缓存、token,还有去年春天某人“临时”加上的部署密钥。对你自己的分支来说没问题。但一旦有陌生人提交 pull request——或者你自己的 AI 智能体写了一个还没人读过的补丁——问题就来了。
干净的做法是每次运行一台机器。克隆、安装、测试,然后把机器扔掉。
一次运行是什么样的
沙箱是一台 Firecracker microVM,大约一秒启动,内置 Python 3.12、Node.js 22、git 和 curl。出站网络可用,所以 git clone 和 pip install -r requirements.txt 照常工作。没有入站端口——运行期间任何东西都无法连入测试环境。
在 CI 任务里,整个流程就是一段短脚本。下面是用 Python SDK 写的版本,可从任何 CI 任务调用:
import os, sys
from eqvps import Sandbox
repo, ref = os.environ["REPO_URL"], os.environ["PR_SHA"]
with Sandbox.create(tariff="standard", ttl=1800) as sb:
sb.exec(f"git clone {repo} /root/app && cd /root/app && git checkout {ref}", timeout=55)
sb.exec("cd /root/app && pip install -r requirements.txt", timeout=55)
task = sb.exec("cd /root/app && python3 -m pytest -q", background=True)
result = task.wait(on_output=lambda out, err: print(out, end=""))
sys.exit(result.exit_code or 0)
token 以 EQVPS_API_KEY 的名字保存在 CI 的密钥里。CI 中的其他东西——部署密钥、云凭据——都不会进入沙箱。with 代码块结束时沙箱会被删除,即使任务中途崩溃也一样。
测试本身作为后台任务运行,因为单条同步命令会在 55 秒时停止。后台任务在运行时持续输出,可以一直运行到沙箱的 TTL——这里设为 30 分钟,防止卡住的测试推高账单。
你真正会碰到的限制
把这些说清楚,能帮你省下一个下午。
并发。 一个账户可以持有 20 个沙箱,但同时只执行 2 条命令。对 CI 来说,就是两个任务真正并行。同时到达的十个 PR 会排队。如果你的流水线把测试分到 16 个 worker 上,那么主流水线不适合用它——把主流水线留在你自己 VPS 上的 runner 上,沙箱用于不可信的那条通道。
内部没有容器。 沙箱不带 Docker。需要 Postgres 容器的单元测试和集成测试无法直接运行;基于 SQLite 或内存模拟对象的测试则可以。
文件。 通过 API 上传和下载,每个文件最大 5 MB。请在沙箱内用 git 拉取代码,而不是上传压缩包。
费用
你按秒为套餐的 vCPU 和内存付费,最少 60 秒,从预付余额扣除——无需订阅。
| 运行 | 套餐 | 大致费用 |
|---|---|---|
| Lint + 单元测试,1 分钟 | small(0.5 vCPU,1 GB) | $0.0006 |
| 完整测试套件,3 分钟 | standard(1 vCPU,2 GB) | $0.0033 |
| 构建 + 测试,10 分钟 | plus(2 vCPU,4 GB) | $0.022 |
在这个价位上,值得关心的不是费用,而是测试能否在你设定的时间内跑完。让 TTL 比你最慢的一次绿色运行再多留一些余量。
合理的分工
我们的建议:可信分支留在你那台带缓存的快速 runner 上。所有不是你写的东西——fork、外部贡献者、智能体生成的补丁——先经过沙箱。如果沙箱运行通过,且有人看过 diff,再把它提升到可信流水线。
这样你就有一条注重速度和缓存的通道,和一条注重干净机器的通道,而不必强迫一个工具同时扮演两种角色。
从沙箱概览和连接指南开始——新账户可获得 $1 的沙箱时长,足够跑几百次短测试。上面用到的所有方法都在 SDK 参考中,准确的计费规则见沙箱限制与计费。
相关阅读:用于代码审查和 PR 检查的沙箱和AI 智能体沙箱。
评论
暂无评论。来做第一个吧。