无服务器部署与 CI CD 自动化
11.9 无头模式
Tip
在服务器的暗夜里,有一辆没有显示器的赛车在狂奔。
1. 为什么要学这个?
日常场景: 你想写一个 E2E 测试,验证用户点击登录后是否跳到了首页。 或者你想做一个服务,自动给每个博客文章生成一张截图(Open Graph Image)。 但在 CI 服务器 (GitHub Actions) 或 云函数 (Serverless) 上,没有显示器,没有显卡。 普通的 Chrome 浏览器根本启动不起来。
你需要 Headless Browser (无头浏览器) —— 一个只在内存里渲染,不画在屏幕上的浏览器。
2. 核心概念:Headless
通俗定义
Headless Browser 就像一辆幽灵车。
- 内核: 拥有完整的 Blink/V8 引擎,能解析 JS,能渲染 CSS。
- 区别: 它不产生像素输出 (No GUI)。它直接在内存里操作 DOM 树。
- 优势: 速度极快(不需要 GPU 绘图),资源占用少。
常用工具
- Playwright / Puppeteer: 司機。
- Headless Chrome: 车。
3. 解决方案 (HOW)
1. 开启 Headless 模式 (Playwright)
Playwright 默认就是无头的。如果你想看它跑,要手动关掉。
// playwright.config.ts
import { defineConfig } from '@playwright/test';
export default defineConfig({
use: {
// true = 无头 (默认,适合 CI)
// false = 有头 (适合本地调试,看浏览器自己动)
headless: process.env.CI ? true : false,
},
});
2. Serverless 上的挑战 (The 50MB Limit)
如果你想在 Vercel Serverless Function 里跑 Puppeteer (比如用来生成截图),你会遇到大坑。
- 问题: 完整的 Chromium 浏览器大约 150MB+。
- 限制: Vercel/AWS Lambda 的包大小限制通常是 50MB。
- 解法: 使用专门压缩过的
chrome-aws-lambda(现在叫@sparticuz/chromium)。
运行机制图
graph TD
Script["测试脚本 (Node.js)"] -->|"指令: page.goto()"| Protocol["DevTools Protocol"]
subgraph Browser ["Headless Chrome (内存中)"]
Protocol --> JS["V8 引擎 (跑 JS)"]
Protocol --> Layout["排版引擎 (算位置)"]
Protocol --> Paint["绘画引擎 (不输出)"]
end
Paint --"screenshot()"--> Image["截图.png"]
Layout --"evaluate()"--> Data["提取文字"]
style Browser fill:#eceff1,stroke:#b0bec5
style Image fill:#fff9c4,stroke:#fbc02d
4. 真实案例
Story
"消失的方块字"
某团队用 Headless Chrome 在 Linux 服务器上自动生成分享海报。
本地 macOS 测试一切正常,生成的图片精美绝伦。
上线后,生成的每一张图片,中文全是 □□□ (豆腐块)。
原因:Linux 服务器 (Ubuntu/Debian) 默认只装了英文字体,没装中文字体。Headless 浏览器渲染时找不到字体,就画了方块。
解法:在 Dockerfile 里安装 fonts-noto-cjk,或者在 Serverless 里加载自定义字体文件。
Vibe 心法: 云端也是一台电脑,但它是一台赤裸的电脑。 不要假设它有字体,不要假设它有屏幕,甚至不要假设它有 GPU。显式声明每一个依赖,是驾驭这台幽灵战车的前提。
5. 本章小结
- 无头:CI 环境必须用 Headless 模式。
- 调试:本地开发可以用 Headed 模式看过程。
- 环境差异:警惕字体缺失和时区问题。