Chrome 集群 K8s 部署:内存配置、/dev/shm 与优雅关闭

每个浏览器实例 200-500MB 内存、需要 /dev/shm、WebSocket 长连接管理、优雅关闭防孤儿进程——K8s 原生资源管理在浏览器实例面前基本失效。

亿牛云技术团队2026年4月28日3 分钟阅读

浏览器和普通微服务的区别

如果你用部署常规微服务的经验来部署浏览器集群,会踩很多坑。核心原因是浏览器和普通服务有几个本质区别:

区别一:内存开销不平均

普通 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

资源管理最佳实践

内存配置

经验值(根据实际测试):

任务类型内存请求内存限制备注
简单页面加载256Mi512Mi静态页面、文档
中等 SPA512Mi1GiReact/Vue 应用
复杂 SPA + 交互1Gi2Gi需要大量 JS 执行
多标签页2Gi4Gi单个浏览器多个标签页

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 上跑浏览器集群的关键点:

  1. /dev/shm 必须显式设置大小,否则 Chrome 会崩溃
  2. 内存请求/限制 需要根据任务类型分别配置
  3. 优雅关闭 需要增加 terminationGracePeriodSeconds 并加 preStop hook
  4. HPA 使用队列深度而不是 CPU/内存
  5. Node 隔离 浏览器 Pod 和其他业务 Pod 分开部署

需要企业代理方案?

我们可根据目标站点、并发规模与稳定性目标提供定制方案。