在云端跑浏览器从来不是一件轻松的事。无头浏览器吃内存、占 CPU、冷启动慢,规模一大就容易成为整个系统的瓶颈。Browser Run 这次把底层从原来的运行环境整体迁移到 Cloudflare Containers,不只是换了宿主机——调度模型、资源上限、发布节奏都跟着变了。
为什么迁移
Browser Run 的核心场景是:在服务器端启动一个真实浏览器实例,执行页面渲染、截图、抓取、自动化测试等任务。这类任务有几个天然难点:
- 资源消耗大:一个 Chromium 实例轻轻松松吃掉 200–400 MB 内存,多实例并发时资源争抢严重。
- 冷启动代价高:浏览器进程从启动到可用往往需要几秒,如果调度层不能快速分配容器,用户就等着。
- 弹性难做:流量峰值时需要快速扩容,低谷时又不能让大量空闲实例烧钱。
旧架构在这些点上都有瓶颈——使用上限卡得紧、高峰期排队明显、发布新功能要走长流程。迁移到 Cloudflare Containers,本质上是把"在哪跑"和"怎么调度"这两个问题一起换了底座。
Cloudflare Containers 带来了什么
Cloudflare Containers 是 Cloudflare 近期推出的容器运行平台,核心特征:
- 边缘调度:容器不是集中在少数数据中心,而是可以跑在 Cloudflare 全球网络的多个节点上,离用户更近,延迟更低。
- 快速启停:容器启动时间在毫秒到秒级,比传统 VM 调度快得多,适合浏览器实例这种"用完即走"的短生命周期任务。
- 资源隔离更干净:每个容器有独立的 CPU/内存配额,不会因为邻居实例跑重任务就被挤占。
Browser Run 利用这些特性做了几件事:
- 提高并发上限:容器调度快了,同一时刻能分配更多浏览器实例,用户不再频繁撞到 rate limit。
- 降低排队延迟:请求进来后更快拿到可用容器,整体响应时间缩短。
- 发布节奏加快:容器镜像构建和分发流程更自动化,从代码合并到上线的时间大幅缩短。
- 可靠性提升:单个容器崩溃不会波及邻居,调度层可以快速在其他节点补起新实例。
实践:用 Browser Run API 跑一次页面截图
下面是一个可以直接跑的 Python 示例,调用 Browser Run 的 REST API 对指定页面截图并保存为本地文件。你需要先在 Cloudflare 控制台获取 Browser Run 的 API Token。
import requests
import base64
import sys
# 替换为你自己的 Browser Run API Token
API_TOKEN = "your_browser_run_api_token_here"
# 目标页面 URL
TARGET_URL = "https://example.com"
resp = requests.post(
"https://browser.run.cloudflare.com/v1/screenshot",
headers={
"Authorization": f"Bearer {API_TOKEN}",
"Content-Type": "application/json",
},
json={
"url": TARGET_URL,
"options": {
"fullPage": True,
"format": "png",
# 可选:设定视口大小
"viewport": {"width": 1280, "height": 720},
},
},
timeout=30,
)
if resp.status_code != 200:
print(f"请求失败,状态码: {resp.status_code}")
print(resp.text)
sys.exit(1)
data = resp.json()
# 返回结果中包含 base64 编码的截图
image_bytes = base64.b64decode(data["image"])
output_path = "screenshot.png"
with open(output_path, "wb") as f:
f.write(image_bytes)
print(f"截图已保存到 {output_path},大小 {len(image_bytes)} 字节")
运行前确保安装了 requests:
pip install requests
如果你想批量跑多个页面,只需要把 TARGET_URL 替换成列表,循环调用即可。迁移到 Containers 之后,并发上限提高,批量场景下排队概率明显降低。
用 Cloudflare Worker 编排 Browser Run
更进阶的做法是把 Browser Run 嵌进 Cloudflare Worker,实现边缘触发、边缘返回,省掉客户端到中心服务器的往返。下面是一个最小 Worker 脚本:
// worker.js — Cloudflare Worker 入口
export default {
async fetch(request, env) {
const url = new URL(request.url);
const target = url.searchParams.get("url") || "https://example.com";
const resp = await fetch("https://browser.run.cloudflare.com/v1/screenshot", {
method: "POST",
headers: {
"Authorization": `Bearer ${env.BROWSER_RUN_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
url: target,
options: { fullPage: false, format: "png" },
}),
});
if (!resp.ok) {
return new Response(`Browser Run error: ${resp.status}`, { status: resp.status });
}
const data = await resp.json();
const imageBytes = atob(data.image);
const uint8 = new Uint8Array(imageBytes.length);
for (let i = 0; i < imageBytes.length; i++) uint8[i] = imageBytes.charCodeAt(i);
return new Response(uint8, {
headers: { "Content-Type": "image/png", "Cache-Control": "public, max-age=3600" },
});
},
};
对应的 wrangler.toml:
name = "browser-screenshot-worker"
main = "worker.js"
compatibility_date = "2024-09-01"
[vars]
# 不要把 token 硬编码在这里,用 wrangler secret put BROWSER_RUN_TOKEN
部署步骤:
# 安装 wrangler(Cloudflare CLI)
npm install -g wrangler
# 登录
wrangler login
# 设置 secret
wrangler secret put BROWSER_RUN_TOKEN
# 部署
wrangler deploy
部署后访问 https://your-worker.workers.dev/?url=https://target-site.com 即可直接拿到 PNG 截图。Worker 和 Browser Run 都跑在 Cloudflare 边缘网络,整条链路不需要绕回中心机房。
迁移的取舍与注意事项
迁移到 Containers 不是没有代价,值得关注的几个点:
- 冷启动仍然存在:容器启动比 VM 快,但浏览器进程本身的初始化时间没变。如果你的场景对首次响应极度敏感,考虑预热池或保活策略。
- 成本模型变了:Containers 按容器运行时长计费,短任务更划算,但如果浏览器实例长时间空闲等待,费用可能比旧方案高。评估你的实际并发模式再做决定。
- 调试手段:容器环境比传统服务器更"黑盒",出问题时日志和监控要依赖 Cloudflare 提供的仪表盘,不能 SSH 进去翻文件。
- 区域覆盖:Cloudflare Containers 的节点覆盖还在扩展,某些地区可能暂时没有容器节点,请求会被路由到稍远的节点,延迟略高。
上手清单
如果你已经在用或打算用 Browser Run,迁移后可以这样验证收益:
- 跑一次基准测试:用上面的 Python 脚本对同一页面连续请求 20 次,记录平均耗时和失败率,和旧环境对比。
- 压测并发上限:用 10–50 并发同时请求截图,观察是否还出现排队或 429 响应。
- 检查区域延迟:从不同地理位置(或用不同区域的 Worker)发起请求,看响应时间分布。
- 监控成本:在 Cloudflare 仪表盘看 Containers 的运行时长和费用,和旧方案账单对比。
Browser Run 迁移到 Cloudflare Containers,核心收益是"快"和"弹性"——调度快、发布快、扩容快。对于任何需要在云端跑浏览器的场景,这意味着更少的排队、更低的超时率、更短的迭代周期。如果你还没试过,上面的代码可以直接跑起来感受差异。