部署

Docker 浏览器自动化:部署与扩展指南

在 Docker 容器中部署浏览器自动化,包含 Dockerfile 示例、Compose 扩展、卷挂载和生产最佳实践。

文档中心

想直接看维护中的产品文档?

这篇文章对应的主题已经有文档中心页面。需要规范流程、当前参数和长期参考时,优先看 docs。

简介

Docker 提供了生产 BotBrowser 部署所需的可重现性、隔离性和扩展原语。每个容器从已知状态启动,独立于其他容器运行,并可在任何支持 Docker 的基础设施上复制。不再有"在我的机器上可以运行"的问题。

本指南涵盖完整的 Docker 部署工作流:构建镜像、使用卷挂载管理配置文件和脚本、配置共享内存、使用 Docker Compose 扩展,以及应用生产加固。无论你是在本地运行单个容器还是在云中编排数十个 Worker,这里的模式都适用。

为什么 Docker 部署很重要

BotBrowser 依赖于特定的系统库、虚拟显示服务器和精心的环境配置。在裸机上,每台新服务器都需要相同的手动设置:安装依赖、配置 Xvfb、设置环境变量和验证安装。Docker 将所有这些打包到一个在任何地方都能一致部署的单个镜像中。

Docker 还提供进程隔离。每个容器运行自己的 BotBrowser 实例,有自己的文件系统、网络命名空间和资源限制。一个容器的内存泄漏不影响其他容器。崩溃的 Worker 可以通过 Docker 的重启策略自动重启,无需人工干预。

对于运行具有不同配置文件和代理的多个 BotBrowser 实例的团队,Docker Compose 或容器编排平台使得从单个配置文件定义、扩展和管理整个集群变得简单。

技术背景

Chrome 和共享内存

Chrome 大量使用 /dev/shm(共享内存)进行浏览器进程、GPU 进程和渲染进程之间的进程间通信。Docker 默认的 /dev/shm 分配为 64 MB,远远不够。没有至少 1-2 GB 的共享内存,Chrome 会崩溃或行为不稳定。

修复很简单:在 docker run 命令中添加 --shm-size=2g,或在 Docker Compose 文件中设置 shm_size: '2g'

容器中的显示服务器

与裸机服务器一样,Docker 中的 BotBrowser 需要 Xvfb 进行显示初始化。容器必须在启动 Chrome 之前启动 Xvfb,并设置 DISPLAY 环境变量。

用户数据目录

每个 BotBrowser 实例需要自己的 --user-data-dir。在 Docker 中,此目录默认位于容器内部,容器停止时会丢失。对于持久会话,挂载宿主卷以在容器重启之间保留浏览器状态。

镜像大小考量

包含所有依赖的 BotBrowser Docker 镜像通常为 500-800 MB。使用多阶段构建或最小化包列表可以减小这个大小。代价是每个缺失的库都是潜在的运行时失败。

<svg viewBox="0 0 700 280" xmlns="http://www.w3.org/2000/svg" style={{maxWidth: '100%', height: 'auto'}}> Docker Container Xvfb :10 Virtual Display BotBrowser Chrome + Profile Node.js Automation Script /dev/shm 2 GB Shared Mem Volume: /opt/profiles (host mounted) Volume: /data/sessions (persistent)

常见方案和局限性

单一整体容器

在一个容器中运行所有内容(Xvfb、BotBrowser、自动化脚本和监控)是最简单的方案,但限制了可扩展性。你无法独立扩展自动化脚本和浏览器,任何组件的问题都会导致整个容器崩溃。

每浏览器一容器

每个 BotBrowser 实例运行一个容器提供最佳隔离。每个容器有自己的配置文件、代理和用户数据目录。开销更高(每个容器包含完整的系统库集),但隔离使调试和扩展变得简单。

Sidecar 模式

一些部署使用运行 Xvfb 的 Sidecar 容器供多个浏览器容器共享。这减少了资源使用,但增加了网络复杂性并创建了单点故障。

从 Chromium 基础镜像构建

使用 Google 官方 Chromium Docker 镜像作为基础可以减少设置工作,但这些镜像可能不包含 BotBrowser 需要的所有库。从 Ubuntu 构建提供了对依赖列表的更多控制。

BotBrowser 的方案

BotBrowser 的跨平台配置文件系统特别适合 Docker 部署。相同的配置文件无论容器的基础镜像、内核版本或宿主机器如何,都产生相同的指纹。这消除了容器化浏览器部署中常见的不一致来源。

这里推荐的容器化策略使用 Ubuntu 22.04 作为基础,安装所有必需的依赖,将 BotBrowser 二进制文件和配置文件复制到镜像中,并在自动化脚本之前启动 Xvfb。此方案经过测试、可靠,并在各云提供商之间产生一致的结果。

配置和用法

基础 Dockerfile

FROM ubuntu:22.04

# 安装系统依赖
RUN apt-get update && apt-get install -y \
    wget ca-certificates fonts-liberation \
    libasound2 libatk-bridge2.0-0 libatk1.0-0 \
    libcups2 libdbus-1-3 libdrm2 libgbm1 \
    libgtk-3-0 libnspr4 libnss3 \
    libxcomposite1 libxdamage1 libxrandr2 \
    xdg-utils xvfb curl \
    && rm -rf /var/lib/apt/lists/*

# 安装 Node.js
RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \
    && apt-get install -y nodejs \
    && rm -rf /var/lib/apt/lists/*

# 设置显示环境
ENV DISPLAY=:10.0

# 复制 BotBrowser 二进制文件
COPY botbrowser/ /opt/botbrowser/
RUN chmod +x /opt/botbrowser/chrome

# 复制配置文件
COPY profiles/ /opt/profiles/

# 复制自动化脚本
WORKDIR /opt/app
COPY package.json package-lock.json ./
RUN npm ci --production
COPY scripts/ ./scripts/

# 健康检查
HEALTHCHECK --interval=30s --timeout=10s --retries=3 \
  CMD pgrep -x Xvfb && pgrep -f "chrome" || exit 1

# 启动 Xvfb 并运行自动化脚本
CMD Xvfb :10 -screen 0 1920x1080x24 -ac & \
    sleep 1 && \
    node scripts/main.js

构建和运行

docker build -t botbrowser-worker .
docker run --rm --shm-size=2g botbrowser-worker

--shm-size=2g 标志是必需的。没有它,Chrome 会因共享内存不足而崩溃或行为不稳定。

自动化脚本

// scripts/main.js
const { chromium } = require('playwright-core');

const PROFILE = process.env.PROFILE || '/opt/profiles/profile.enc';
const PROXY = process.env.PROXY || '';

async function main() {
  const args = [
    '--disable-setuid-sandbox',
    `--bot-profile=${PROFILE}`,
  ];

  if (PROXY) {
    args.push(`--proxy-server=${PROXY}`);
  }

  const browser = await chromium.launch({
    executablePath: '/opt/botbrowser/chrome',
    args: args,
    headless: true,
  });

  try {
    const context = await browser.newContext();
    const page = await context.newPage();
    await page.goto('https://example.com');
    console.log('Title:', await page.title());

    // 你的自动化逻辑

  } finally {
    await browser.close();
  }
}

// 处理优雅关闭
process.on('SIGTERM', async () => {
  console.log('Received SIGTERM, shutting down...');
  process.exit(0);
});

process.on('SIGINT', async () => {
  console.log('Received SIGINT, shutting down...');
  process.exit(0);
});

main().catch(console.error);

多 Worker 的 Docker Compose

version: '3.8'

services:
  worker-us:
    build: .
    shm_size: '2g'
    environment:
      - PROFILE=/opt/profiles/windows-chrome-131.enc
      - PROXY=socks5://user:pass@us-proxy.example.com:1080
    volumes:
      - ./profiles:/opt/profiles:ro
      - session-us:/data/session
    deploy:
      resources:
        limits:
          memory: 4g
          cpus: '2'
    restart: unless-stopped

  worker-eu:
    build: .
    shm_size: '2g'
    environment:
      - PROFILE=/opt/profiles/windows-chrome-132.enc
      - PROXY=socks5://user:pass@eu-proxy.example.com:1080
    volumes:
      - ./profiles:/opt/profiles:ro
      - session-eu:/data/session
    deploy:
      resources:
        limits:
          memory: 4g
          cpus: '2'
    restart: unless-stopped

volumes:
  session-us:
  session-eu:

启动集群:

docker compose up -d
docker compose logs -f

扩展特定 Worker:

docker compose up -d --scale worker-us=3

卷挂载

将配置文件作为只读卷挂载,这样你可以在不重建镜像的情况下更新它们:

docker run --rm --shm-size=2g \
  -v /host/profiles:/opt/profiles:ro \
  -v /host/sessions/worker-1:/data/session \
  -e PROFILE=/opt/profiles/profile.enc \
  -e PROXY=socks5://user:pass@proxy:1080 \
  botbrowser-worker

环境变量

使用环境变量进行每容器配置:

变量说明示例
PROFILE容器内配置文件路径/opt/profiles/win-chrome-131.enc
PROXY带凭据的代理 URLsocks5://user:pass@proxy:1080
DISPLAYX 显示(在 Dockerfile 中设置):10.0

入口脚本

对于更复杂的启动逻辑,使用入口脚本:

#!/bin/bash
# entrypoint.sh

# 启动 Xvfb
Xvfb :10 -screen 0 1920x1080x24 -ac &
XVFB_PID=$!

# 等待 Xvfb 就绪
for i in $(seq 1 10); do
  if xdpyinfo -display :10 > /dev/null 2>&1; then
    break
  fi
  sleep 0.5
done

# 运行主脚本
exec node /opt/app/scripts/main.js

更新 Dockerfile:

COPY entrypoint.sh /opt/app/
RUN chmod +x /opt/app/entrypoint.sh
ENTRYPOINT ["/opt/app/entrypoint.sh"]

验证

验证你的 Docker 设置:

# 构建镜像
docker build -t botbrowser-worker .

# 使用交互式 shell 运行以进行调试
docker run --rm --shm-size=2g -it botbrowser-worker bash

# 在容器内:
Xvfb :10 -screen 0 1920x1080x24 &
export DISPLAY=:10.0
/opt/botbrowser/chrome --version
ldd /opt/botbrowser/chrome | grep "not found"

不应出现 "not found" 行。运行测试导航:

docker run --rm --shm-size=2g botbrowser-worker

检查输出是否有预期的页面标题且没有错误消息。

最佳实践

始终设置 --shm-size=2g 或更高。 这是最常见的 Docker + Chrome 问题。默认的 64 MB 不够。

在容器中使用 --disable-setuid-sandbox Docker 容器通常没有 Chrome 沙箱所需的内核能力。

将配置文件作为只读卷挂载。 这将配置文件更新与镜像构建分开,并防止意外修改。

设置资源限制。 使用 Docker 的 --memory--cpus 标志防止任何容器消耗所有宿主资源。

处理 SIGTERM 以实现优雅关闭。 Docker 在停止容器时发送 SIGTERM。你的脚本应关闭浏览器并清理资源。

使用 .dockerignore 从构建上下文中排除 node_modules.git 和本地开发文件以减小镜像大小和构建时间。

使用版本号标记镜像。 使用 botbrowser-worker:1.2.0 而非 :latest 以实现可重现的部署。

常见问题

为什么 Chrome 在 Docker 中出现"内存不足"崩溃?

最常见的原因是 /dev/shm 大小不足。在 docker run 命令中添加 --shm-size=2g。如果容器本身内存受限,使用 --memory=4g 增加内存限制。

我可以使用 Alpine Linux 代替 Ubuntu 吗?

Chrome 的依赖基于 glibc。Alpine 使用 musl libc,这不兼容。请使用基于 Debian 或 Ubuntu 的镜像。

如何在不重建镜像的情况下更新配置文件?

将配置文件目录作为卷挂载:-v /host/profiles:/opt/profiles:ro。更新宿主上的文件并重启容器。

我可以暴露远程调试端口吗?

可以。使用 -p 9222:9222 并在 Chrome 的启动标志中添加 --remote-debugging-port=9222。在生产环境中要谨慎,因为调试端口提供对浏览器的完全控制。

如何查看容器日志?

使用 docker logs <container-id>docker compose logs <service-name>。你脚本的 console.log 输出会出现在这里。

应该使用什么基础镜像?

Ubuntu 22.04 LTS 是最经过测试和可靠的选择。Ubuntu 24.04 可以调整包名称后使用。Debian 12 (Bookworm) 也是一个可靠的选项。

我可以在一个容器中运行多个 BotBrowser 实例吗?

可以,但独立容器提供更好的隔离和资源管理。如果你确实在一个容器中运行多个实例,确保每个实例有自己的 --user-data-dir--remote-debugging-port

如何处理 Chrome 僵尸进程?

docker run 命令中添加 --init 使用 Docker 的 init 进程来回收孤儿子进程。或者在 Dockerfile 中使用 dumb-inittini

总结

Docker 提供了在生产环境中部署 BotBrowser 最可靠和可重现的方式。使用所有依赖构建一次镜像,使用卷挂载管理配置文件和持久数据,并使用 Docker Compose 或编排工具进行扩展。关键配置要求是足够的共享内存(--shm-size=2g)。

裸机设置请参阅Headless 服务器设置。性能调优请参阅BotBrowser 性能优化。CLI 标志参考请参阅CLI 配方

#Docker#部署#Server#Automation#Devops

让 BotBrowser 从研究走向生产

先用这些指南理解模型,再进入跨平台验证、隔离上下文和面向规模化的浏览器部署。