Chrome 集群 K8s 部署:内存配置、/dev/shm 与优雅关闭
每个浏览器实例 200-500MB 内存、需要 /dev/shm、WebSocket 长连接管理、优雅关闭防孤儿进程——K8s 原生资源管理在浏览器实例面前基本失效。
浏览器和普通微服务的区别
如果你用部署常规微服务的经验来部署浏览器集群,会踩很多坑。核心原因是浏览器和普通服务有几个本质区别:
区别一:内存开销不平均
普通 Web 服务的每个实例分配固定的资源,波动很小。浏览器的内存消耗取决于打开的页面——一个空白页 150MB,一个复杂的 SPA 可能 500MB+。同一个浏览器实例在处理不同任务时,内存消耗可以相差 3 倍。
这导致 Kubernetes 的资源预留(Resource Request/Limit)难以设置:
resources:
requests:
memory: "256Mi" # 设小了,复杂页面时 OOM
cpu: "500m"
limits:
memory: "1Gi" # 设大了,资源利用率低
cpu: "1000m"区别二:/dev/shm 需求
Chrome 使用共享内存(/dev/shm)来在进程间传递数据。默认的 /dev/shm 大小是 64MB,而 Chrome 经常需要 256MB-512MB。不够就会崩溃。
# 需要显式设置 /dev/shm 大小
apiVersion: v1
kind: Pod
spec:
containers:
- name: chrome
image: chromedp/headless-shell:latest
volumeMounts:
- mountPath: /dev/shm
name: dshm
volumes:
- name: dshm
emptyDir:
medium: Memory
sizeLimit: "512Mi" # Chrome 需要这个区别三:进程清理
当 Pod 被删除时,Kubernetes 发送 SIGTERM 给主进程(Chrome)。但 Chrome 的子进程(Renderer、GPU)可能超过 10 秒的 terminationGracePeriodSeconds 还没退出。SIGKILL 后,子进程变成孤儿:
spec:
terminationGracePeriodSeconds: 60 # 给 Chrome 更多时间
containers:
- name: chrome
lifecycle:
preStop:
exec:
command:
- /bin/sh
- -c
- |
# 先通过 CDP 优雅关闭所有标签页
curl -s -X PUT http://localhost:9222/json/close 2>/dev/null || true
sleep 2
# 然后清理可能遗留的 Chrome 进程
pkill -f "chrome" || true资源管理最佳实践
内存配置
经验值(根据实际测试):
| 任务类型 | 内存请求 | 内存限制 | 备注 |
|---|---|---|---|
| 简单页面加载 | 256Mi | 512Mi | 静态页面、文档 |
| 中等 SPA | 512Mi | 1Gi | React/Vue 应用 |
| 复杂 SPA + 交互 | 1Gi | 2Gi | 需要大量 JS 执行 |
| 多标签页 | 2Gi | 4Gi | 单个浏览器多个标签页 |
HPA 的有意义配置
前面(A3)说过 CPU 启动尖峰的问题。对于浏览器集群,建议使用 Keda 事件驱动扩缩容:
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: browser-scaler
spec:
scaleTargetRef:
name: browser-worker
triggers:
- type: rabbitmq
metadata:
queueName: browser-tasks
queueLength: "5" # 每个 Pod 处理 5 个任务
- type: cron
metadata:
timezone: Asia/Shanghai
start: "0 9 * * *" # 上班时间扩容
end: "0 18 * * *" # 下班时间缩容Node 选择
浏览器适合使用内存优化的实例类型(如 AWS r6i series),因为它们对 CPU 的要求通常低于对内存的要求。同时,不建议在同一 Node 上混合运行浏览器 Pod 和其他业务 Pod——浏览器的资源波动会影响其他服务的稳定性。
StatefulSet vs Deployment
浏览器实例虽然携带状态(Cookie、localStorage),但并不需要 K8s 层面的 StatefulSet——因为 Pod 故障时不需要保留存储。使用 Deployment + 挂载临时存储即可:
apiVersion: apps/v1
kind: Deployment
metadata:
name: browser-pool
spec:
replicas: 10
template:
spec:
containers:
- name: chrome
volumeMounts:
- name: tmp
mountPath: /tmp
volumes:
- name: tmp
emptyDir:
medium: Memory # tmpfs,更快
sizeLimit: "1Gi"总结
K8s 上跑浏览器集群的关键点:
- /dev/shm 必须显式设置大小,否则 Chrome 会崩溃
- 内存请求/限制 需要根据任务类型分别配置
- 优雅关闭 需要增加 terminationGracePeriodSeconds 并加 preStop hook
- HPA 使用队列深度而不是 CPU/内存
- Node 隔离 浏览器 Pod 和其他业务 Pod 分开部署
需要企业代理方案?
我们可根据目标站点、并发规模与稳定性目标提供定制方案。