基于Docker的Selenium Grid分布式测试环境搭建与实战指南

1. 项目概述:为什么我们需要Docker化的Selenium Grid?

如果你和我一样,在团队里负责UI自动化测试,肯定经历过这样的场景:测试用例越来越多,跑一次全量回归动辄几个小时,开发等着上线,测试这边还在吭哧吭哧地跑脚本,效率瓶颈卡得死死的。更头疼的是环境问题,本地Chrome版本是120,测试服务器上是115,一个看似简单的脚本,在本地跑得飞起,一到服务器就各种元素定位失败、浏览器崩溃。为了解决这些问题,我花了大量时间研究并最终落地了基于Docker的Selenium Grid分布式测试环境。这不仅仅是把Selenium Grid塞进容器那么简单,而是一套从环境一致性、资源弹性伸缩到测试执行效率的完整解决方案。

简单来说,这个项目就是用Docker容器技术来部署和管理Selenium Grid。Selenium Grid本身是一个用于分布式测试的“大脑”,它由一个Hub(中心调度器)和多个Node(执行节点)组成。而Docker则把每个Hub和Node都打包成一个独立的、轻量级的“集装箱”。这样做的好处是颠覆性的:你不再需要为每台测试机器手动安装Java、浏览器驱动、配置环境变量;你可以像搭积木一样,在几分钟内拉起一个包含多种浏览器、多个版本的测试集群;当测试负载增大时,你可以快速扩容节点,测试完成后又能立刻销毁,资源利用率极高。对于需要频繁回归、多浏览器兼容性测试或者追求持续集成/持续交付(CI/CD)流水线效率的团队来说,这几乎是目前最优雅的实践。

2. 环境整体架构与核心组件选型

在动手之前,我们必须把整个架构的脉络理清楚。一个典型的Docker化Selenium Grid环境,其核心是“一中心,多节点”的星型结构。所有的测试请求都发往中心的Hub,由Hub根据测试脚本中指定的浏览器类型、版本等能力(Capabilities)进行匹配,并调度到符合条件的Node节点上执行。

2.1 核心组件深度解析

1. Selenium Grid Hub: 调度中心Hub是整个集群的指挥中枢。它不执行任何测试,只负责三件事:接收来自测试脚本(通过WebDriver协议)的请求;维护一个所有已注册Node节点的能力注册表;根据请求中的DesiredCapabilities(比如browserName: chrome,version: 120.0)进行匹配,将任务路由到最合适的Node。你可以把它理解为一个智能的负载均衡器。在Docker部署中,我们使用官方镜像selenium/hub

2. Selenium Grid Node: 执行终端Node是实际干活儿的“工人”。每个Node容器内部都预装好了特定浏览器(如Chrome、Firefox、Edge)及其对应的WebDriver。一个Node可以配置允许多个并发会话(Session),这取决于机器的硬件资源。例如,一个拥有4核CPU和8G内存的Node,可能同时运行3-4个浏览器实例。我们使用selenium/node-chrome,selenium/node-firefox等镜像来创建Node。

3. Docker: 环境标准化与隔离层Docker的价值在这里体现得淋漓尽致。首先,它提供了极致的环境一致性selenium/node-chrome:latest这个镜像在任何安装了Docker的机器上运行起来,内部的Chrome和Chromedriver版本、依赖库都是完全一样的,彻底杜绝了“在我机器上是好的”这类问题。其次,它实现了资源隔离。每个Node容器拥有独立的文件系统、进程空间和网络接口,测试之间互不干扰。最后,它带来了部署的敏捷性。一行docker run命令就能启动一个节点,结合Docker Compose或Kubernetes,可以实现环境的秒级创建与销毁。

4. VNC: 可视化监控与调试利器这是很多初学者会忽略,但实际排查问题时救命的功能。Docker-Selenium镜像默认集成了noVNCx11vnc服务,并将5900端口暴露出来。通过VNC客户端(如RealVNC Viewer)连接Node容器的5900端口,你可以实时看到容器内浏览器正在执行的操作画面。当测试失败时,你不再需要靠日志去猜“页面到底长什么样”,直接连上去看,是元素没加载出来,还是弹窗遮挡了,一目了然。这对于调试脚本和验证环境是否正常至关重要。

2.2 与传统部署方式的对比

为了让你更清楚为什么选择Docker方案,我列了一个对比表格,这是我在技术选型时做的核心分析:

对比维度传统物理机/虚拟机部署Docker容器化部署Docker方案优势分析
环境准备需在每台机器手动安装JDK、浏览器、驱动,配置环境变量、PATH。过程繁琐,易出错。只需安装Docker,通过拉取官方镜像一键启动。部署效率提升90%以上。从以小时计缩短到以分钟计。
环境一致性难以保证所有机器环境(浏览器版本、驱动版本、系统库)完全一致。镜像即环境,保证绝对一致。彻底解决环境差异导致的测试不稳定问题
资源占用每个浏览器实例都是一个完整的系统进程,资源占用高。容器共享主机内核,启动更快,内存和CPU开销更小。更高的资源利用率,同一台机器可以运行更多并发会话。
隔离性测试运行在同一系统环境中,可能相互影响(如Cookie、缓存)。每个会话在独立的容器环境中运行,完全隔离。测试结果更纯净可靠,避免了交叉污染。
扩展与伸缩需要准备新机器,重复繁琐的安装配置过程,扩容周期长。通过命令或编排工具(Compose/K8s)秒级扩容新节点。动态弹性伸缩,轻松应对测试高峰。
清理与复用环境清理复杂,浏览器缓存、用户数据可能残留。测试结束,删除容器即可,环境瞬间恢复纯净。下次测试,启动全新容器。环境可瞬时重置,实现真正的“一次构建,到处运行”。

从我实际迁移的经验来看,Docker方案最大的收益不是技术上的“酷”,而是工程效率上的“稳”和“快”。团队新成员入职,再也不用花一两天配环境了;CI/CD流水线里,测试环境作为临时资源随用随建,跑完即焚,成本控制得非常好。

3. 从零开始搭建分布式测试环境

理论讲完了,我们进入实战环节。我会带你从最干净的状态开始,一步步搭建起一个可用的分布式环境。假设我们有两台Linux服务器(或虚拟机):

  • Hub主机:IP为192.168.1.100, 运行Hub容器。
  • Node主机:IP为192.168.1.101, 运行多个浏览器Node容器。

3.1 基础环境准备:在所有机器上安装Docker

这是第一步,也是唯一需要手动安装的软件。以Ubuntu系统为例:

# 1. 更新软件包索引并安装必要依赖 sudo apt-get update sudo apt-get install -y apt-transport-https ca-certificates curl software-properties-common # 2. 添加Docker官方GPG密钥 curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - # 3. 添加Docker稳定版仓库 sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" # 4. 再次更新并安装Docker CE(社区版) sudo apt-get update sudo apt-get install -y docker-ce # 5. 启动Docker服务并设置开机自启 sudo systemctl start docker sudo systemctl enable docker # 6. (可选)将当前用户加入docker组,避免每次使用sudo sudo usermod -aG docker $USER # 执行此命令后,需要退出当前终端重新登录生效

注意:生产环境请务必参考Docker官方文档安装指定版本,并配置镜像加速器(如阿里云、腾讯云镜像加速器),否则拉取镜像会非常慢。

安装完成后,在每台机器上运行docker --versionsudo docker run hello-world验证安装是否成功。

3.2 启动Selenium Grid Hub

在Hub主机 (192.168.1.100) 上执行以下命令:

docker run -d -p 4442-4444:4442-4444 --name selenium-hub selenium/hub:latest

命令参数拆解与避坑指南:

  • -d: 后台运行容器。
  • -p 4442-4444:4442-4444: 端口映射。这是最容易出错的地方。Selenium Grid 4 使用了新的架构,通信基于事件总线(Event Bus)。
    • 4442事件发布端口。Node节点向这个端口发送“我在这里”的心跳和状态信息。
    • 4443事件订阅端口。Hub和Node都监听这个端口来接收事件。
    • 4444Grid控制台和WebDriver标准端口。你的测试脚本通过http://hub_ip:4444来访问Hub,同时这也是Grid可视化控制台的访问端口。
    • 必须将这三个端口都映射出来,否则Node无法注册到Hub,测试脚本也无法连接。
  • --name selenium-hub: 给容器起个名字,方便管理。
  • selenium/hub:latest: 使用官方Hub的最新镜像。

启动后,访问http://192.168.1.100:4444,你应该能看到Selenium Grid的控制台页面。目前页面是空的,因为还没有Node注册上来。

3.3 启动浏览器Node节点

现在在Node主机 (192.168.1.101) 上启动Node。我们先启动一个Chrome节点。

docker run -d -p 5900:5900 -p 5555:5555 \ --shm-size="2g" \ -e SE_EVENT_BUS_HOST=192.168.1.100 \ -e SE_EVENT_BUS_PUBLISH_PORT=4442 \ -e SE_EVENT_BUS_SUBSCRIBE_PORT=4443 \ -e SE_NODE_HOST=192.168.1.101 \ -e SE_NODE_MAX_SESSIONS=6 \ -e SE_NODE_OVERRIDE_MAX_SESSIONS=true \ -e SE_VNC_NO_PASSWORD=1 \ --name selenium-node-chrome-1 \ selenium/node-chrome:latest

关键参数详解与配置心得:

  1. 端口映射 (-p)

    • -p 5900:5900: 将容器的VNC服务端口映射到主机,方便我们通过VNC Viewer进行可视化监控。
    • -p 5555:5555:Node的注册和通信端口。Grid 4中,Node通过这个端口与Hub通信。虽然命令中映射了,但实际注册依赖的是下面的事件总线环境变量。
  2. 共享内存大小 (--shm-size)

    • --shm-size="2g"这是一个至关重要的参数,不设置大概率会导致浏览器崩溃。Chrome和Firefox等浏览器会使用/dev/shm(共享内存)来缓存页面数据。Docker容器默认的共享内存很小(通常64MB),不足以支撑浏览器运行。设置为2g是一个经验值,对于复杂的单页应用(SPA),你可能需要增加到4g。如果测试中浏览器频繁崩溃或无响应,首先检查这个值。
  3. 事件总线配置 (SE_EVENT_BUS_*)

    • SE_EVENT_BUS_HOST=192.168.1.100: 指定Hub主机的IP地址。这里不能填localhost127.0.0.1,因为容器内的localhost指向它自己。必须填Hub对外的、Node主机能访问到的真实IP。
    • SE_EVENT_BUS_PUBLISH_PORT=4442/SE_EVENT_BUS_SUBSCRIBE_PORT=4443: 必须与启动Hub时映射的端口对应。
  4. Node自身配置 (SE_NODE_*)

    • SE_NODE_HOST=192.168.1.101: 当前Node节点自身的IP地址。Hub需要通过这个地址回连Node。同样需要是对外IP。
    • SE_NODE_MAX_SESSIONS=6:最大会话数。这决定了这个Node容器能同时运行多少个浏览器实例(即并发数)。这个值不是越大越好,需要根据主机的CPU和内存资源来设定。我的经验公式是:最大会话数 ≈ (主机可用内存 / 每个浏览器会话预估内存) * 0.8。例如,主机有8G内存,每个Chrome会话约消耗1G,那么可以设置为6。设置过高会导致内存耗尽,浏览器崩溃。
    • SE_NODE_OVERRIDE_MAX_SESSIONS=true: 必须设置为true,上述MAX_SESSIONS配置才会生效。
  5. VNC配置 (SE_VNC_NO_PASSWORD)

    • SE_VNC_NO_PASSWORD=1: 设置VNC连接无需密码。仅限内网测试环境使用!如果是公网或对安全有要求的环境,务必移除此参数或设置复杂密码,否则有安全风险。

启动后,等待十几秒,刷新Hub控制台 (http://192.168.1.100:4444),你应该能看到一个Chrome节点已经注册成功,并显示其支持的能力(如浏览器名、版本等)。

启动更多节点:在同一台Node主机上,你可以继续启动更多节点,但要注意端口冲突。Docker不允许映射相同的主机端口。

# 启动第二个Chrome节点,需要改变主机端口,例如5901和5556 docker run -d -p 5901:5900 -p 5556:5555 \ --shm-size="2g" \ -e SE_EVENT_BUS_HOST=192.168.1.100 \ -e SE_EVENT_BUS_PUBLISH_PORT=4442 \ -e SE_EVENT_BUS_SUBSCRIBE_PORT=4443 \ -e SE_NODE_HOST=192.168.1.101 \ -e SE_NODE_MAX_SESSIONS=6 \ -e SE_NODE_OVERRIDE_MAX_SESSIONS=true \ -e SE_VNC_NO_PASSWORD=1 \ --name selenium-node-chrome-2 \ selenium/node-chrome:latest # 启动一个Firefox节点 docker run -d -p 5902:5900 -p 5557:5555 \ --shm-size="2g" \ -e SE_EVENT_BUS_HOST=192.168.1.100 \ -e SE_EVENT_BUS_PUBLISH_PORT=4442 \ -e SE_EVENT_BUS_SUBSCRIBE_PORT=4443 \ -e SE_NODE_HOST=192.168.1.101 \ -e SE_NODE_MAX_SESSIONS=4 \ # Firefox资源占用可能不同 -e SE_NODE_OVERRIDE_MAX_SESSIONS=true \ -e SE_VNC_NO_PASSWORD=1 \ --name selenium-node-firefox-1 \ selenium/node-firefox:latest

3.4 使用Docker Compose编排多节点环境

手动一条条敲docker run命令管理多个节点非常低效。更专业的做法是使用docker-compose.yml文件来定义和启动整个集群。这在本地开发或单机模拟多节点时尤其方便。

创建一个docker-compose.yml文件:

version: '3.8' services: selenium-hub: image: selenium/hub:latest container_name: selenium-hub ports: - "4442-4444:4442-4444" environment: - SE_EVENT_BUS_PUBLISH_PORT=4442 - SE_EVENT_BUS_SUBSCRIBE_PORT=4443 chrome-node-1: image: selenium/node-chrome:latest container_name: chrome-node-1 shm_size: "2g" depends_on: - selenium-hub environment: - SE_EVENT_BUS_HOST=selenium-hub - SE_EVENT_BUS_PUBLISH_PORT=4442 - SE_EVENT_BUS_SUBSCRIBE_PORT=4443 - SE_NODE_MAX_SESSIONS=4 - SE_NODE_OVERRIDE_MAX_SESSIONS=true - SE_VNC_NO_PASSWORD=1 ports: - "5900:5900" # VNC for chrome-node-1 chrome-node-2: image: selenium/node-chrome:latest container_name: chrome-node-2 shm_size: "2g" depends_on: - selenium-hub environment: - SE_EVENT_BUS_HOST=selenium-hub - SE_EVENT_BUS_PUBLISH_PORT=4442 - SE_EVENT_BUS_SUBSCRIBE_PORT=4443 - SE_NODE_MAX_SESSIONS=4 - SE_NODE_OVERRIDE_MAX_SESSIONS=true - SE_VNC_NO_PASSWORD=1 ports: - "5901:5900" # VNC for chrome-node-2 firefox-node: image: selenium/node-firefox:latest container_name: firefox-node shm_size: "2g" depends_on: - selenium-hub environment: - SE_EVENT_BUS_HOST=selenium-hub - SE_EVENT_BUS_PUBLISH_PORT=4442 - SE_EVENT_BUS_SUBSCRIBE_PORT=4443 - SE_NODE_MAX_SESSIONS=3 - SE_NODE_OVERRIDE_MAX_SESSIONS=true - SE_VNC_NO_PASSWORD=1 ports: - "5902:5900" # VNC for firefox-node

然后在文件所在目录执行docker-compose up -d,所有服务(1个Hub,3个Node)就会按依赖顺序自动启动。使用docker-compose ps查看状态,docker-compose down一键停止并清理所有容器。这种方式极大地简化了环境的管理。

4. 编写并执行分布式自动化测试脚本

环境搭好了,接下来就是让测试脚本跑起来。核心思想是:你的脚本不再直接操作本地浏览器,而是将指令发送给远端的Grid Hub。

4.1 Python + pytest 实现并行测试

Python生态中,pytest+pytest-xdist插件是实现并发测试的黄金组合。pytest-xdist可以自动将测试用例分发给多个工作进程并行执行。

第一步:准备测试脚本 (test_grid_demo.py)

import pytest from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.common.desired_capabilities import DesiredCapabilities import time # 定义Hub的地址 HUB_URL = "http://192.168.1.100:4444/wd/hub" @pytest.fixture(scope="function") def driver(request): """ 为每个测试函数创建一个远程WebDriver实例。 通过request.param可以实现参数化,传递不同的浏览器能力。 """ # 这里简单示例,固定使用Chrome。实际可以参数化。 capabilities = { "browserName": "chrome", "browserVersion": "latest", # 指定"latest"或具体版本如"120.0" "platformName": "LINUX", # 节点通常是Linux容器 "se:options": { "enableVNC": True, # 启用VNC,方便调试(如果镜像支持) "enableVideo": False # 是否录制视频,False为不录制 } } # 创建远程驱动 driver = webdriver.Remote(command_executor=HUB_URL, options=webdriver.ChromeOptions().from_capabilities(capabilities)) driver.implicitly_wait(10) # 设置隐式等待 yield driver # 测试结束后,退出浏览器,释放Node上的会话资源 driver.quit() def test_search_baidu(driver): """测试用例1:访问百度并搜索""" driver.get("https://www.baidu.com") search_box = driver.find_element(By.ID, "kw") search_box.send_keys("Selenium Grid Docker") search_box.submit() time.sleep(2) # 等待结果加载,实际应用中应用显式等待 assert "Selenium" in driver.title or "Selenium" in driver.page_source print(f"Test 'test_search_baidu' executed on session: {driver.session_id}") def test_access_github(driver): """测试用例2:访问GitHub""" driver.get("https://github.com") assert "GitHub" in driver.title print(f"Test 'test_access_github' executed on session: {driver.session_id}") # 更多测试用例... def test_ui_interaction(driver): driver.get("https://the-internet.herokuapp.com/dynamic_controls") checkbox = driver.find_element(By.XPATH, "//input[@type='checkbox']") checkbox.click() assert checkbox.is_selected() print(f"Test 'test_ui_interaction' executed on session: {driver.session_id}")

第二步:使用pytest-xdist并行执行

安装必要库:pip install pytest pytest-xdist selenium

执行以下命令:

pytest test_grid_demo.py -v -n auto
  • -v: 输出详细日志。
  • -n auto:pytest-xdist的核心参数。auto表示自动检测CPU核心数并创建对应数量的工作进程。你也可以指定数字,如-n 3表示创建3个进程。

执行过程解析:

  1. pytest-xdist会启动多个工作进程(例如,你的机器是4核,可能启动4个进程)。
  2. 每个进程独立运行,都会执行driverfixture,从而各自向Hub (http://192.168.1.100:4444) 发起创建浏览器会话的请求。
  3. Hub收到请求后,查看其能力要求(browserName: chrome),从注册的Node中找到一个空闲的、支持Chrome的节点,并将创建会话的指令转发给该Node。
  4. Node容器内启动一个Chrome浏览器实例,并将浏览器WebDriver的远程调试地址返回给Hub,再传回给你的测试进程。
  5. 此后,你的测试脚本中的所有driver.xxx()操作,都会通过HTTP请求发送到Hub,再由Hub转发给对应的Node执行。
  6. 所有测试进程并行运行,大大缩短了总执行时间。

在Hub的控制台 (http://192.168.1.100:4444) 的 Sessions 标签页,你可以实时看到正在运行的会话。通过VNC连接到对应Node的5900端口,可以亲眼看到浏览器在自动执行操作。

4.2 高级配置:指定浏览器版本与视频录制

在实际项目中,我们经常需要测试特定版本的浏览器。Docker-Selenium提供了丰富的镜像标签。

# 拉取指定版本的Chrome节点镜像 docker pull selenium/node-chrome:120.0 # 在docker run命令中使用这个镜像名即可

在测试脚本中,通过browserVersion来指定:

capabilities = { "browserName": "chrome", "browserVersion": "120.0", # 指定精确版本 "platformName": "LINUX", }

启用视频录制:这对于在CI/CD中调试失败的测试非常有用。Docker-Selenium镜像支持在测试结束后自动录制并保存视频。

# 启动Node时,挂载一个主机目录用于保存视频,并开启视频录制功能 docker run -d ... \ -v /path/on/host/videos:/opt/selenium/video \ # 挂载视频目录 -e SE_RECORD_VIDEO=true \ # 开启录制 -e SE_VIDEO_FILE_NAME="test_video.mp4" \ # 视频文件名模板 ... selenium/node-chrome:latest

在测试脚本的能力中,也可以控制是否录制:

capabilities = { "browserName": "chrome", "se:options": { "enableVNC": True, "enableVideo": True # 请求录制视频 } }

5. 生产环境进阶:稳定性、监控与CI/CD集成

把环境跑起来只是第一步,要让它稳定、可靠地服务于生产,还需要考虑更多。

5.1 容器健康检查与重启策略

默认情况下,容器内的进程崩溃,容器就停止了。我们需要让Docker自动重启失败的容器。

# 在docker run命令中添加重启策略 docker run -d \ --restart unless-stopped \ # 除非手动停止,否则总是重启 ... \ selenium/node-chrome:latest

更推荐在docker-compose.yml中配置:

services: selenium-hub: image: selenium/hub:latest restart: unless-stopped healthcheck: # 健康检查 test: ["CMD", "curl", "-f", "http://localhost:4444/status"] interval: 30s timeout: 10s retries: 3 start_period: 40s

5.2 使用Docker网络优化通信

之前我们使用IP地址来配置SE_EVENT_BUS_HOSTSE_NODE_HOST,这在多主机部署时没问题,但在单机或Docker Compose部署时,使用Docker自定义网络更简洁可靠。

# docker-compose.yml version: '3.8' networks: selenium-grid: driver: bridge services: selenium-hub: image: selenium/hub:latest container_name: selenium-hub ports: - "4442-4444:4442-4444" networks: - selenium-grid environment: - SE_EVENT_BUS_PUBLISH_PORT=4442 - SE_EVENT_BUS_SUBSCRIBE_PORT=4443 chrome-node: image: selenium/node-chrome:latest container_name: chrome-node shm_size: "2g" depends_on: - selenium-hub networks: - selenium-grid environment: - SE_EVENT_BUS_HOST=selenium-hub # 直接使用服务名,Docker负责DNS解析 - SE_EVENT_BUS_PUBLISH_PORT=4442 - SE_EVENT_BUS_SUBSCRIBE_PORT=4443 - SE_NODE_HOST=chrome-node # 也使用容器名 - SE_NODE_MAX_SESSIONS=4 deploy: # 使用deploy配置资源限制(需要Docker Swarm模式,或Compose特定版本) resources: limits: cpus: '1.0' memory: 4G

5.3 与CI/CD工具集成(以Jenkins为例)

在Jenkins流水线中,我们可以将Selenium Grid环境作为动态资源管理。

方案一:Pipeline中动态启动在Jenkinsfile的post阶段或使用docker-compose插件,在测试开始前启动Grid,测试结束后清理。

pipeline { agent any stages { stage('Prepare Grid') { steps { sh ''' docker-compose -f docker-compose.grid.yml up -d sleep 30 # 等待Grid完全启动 ''' } } stage('Run Tests') { steps { sh 'pytest tests/ --grid-url=http://jenkins-host:4444 -n auto' } } stage('Cleanup') { steps { sh 'docker-compose -f docker-compose.grid.yml down' } } } }

方案二:使用独立的Grid服务对于测试任务频繁的团队,可以维护一个长期运行的Selenium Grid集群。Jenkins任务直接使用这个集群的地址。这就需要更完善的监控和运维手段。

5.4 常见问题排查与性能调优实录

在实际使用中,我踩过不少坑,这里总结几个最典型的:

问题1:Node节点注册失败,Hub控制台看不到节点。

  • 排查思路
    1. 检查网络:在Node主机上执行ping <hub_ip>telnet <hub_ip> 4442telnet <hub_ip> 4443,确保端口可通。
    2. 检查环境变量:确认SE_EVENT_BUS_HOSTSE_NODE_HOST设置的是正确的、可路由的IP地址,不是localhost
    3. 查看容器日志docker logs -f <node_container_name>。最常见的错误日志是连接超时或拒绝连接,指向网络或端口问题。
    4. 检查Hub日志docker logs -f selenium-hub,看是否有接收到注册请求。

问题2:测试执行时浏览器崩溃或无响应。

  • 首要怀疑--shm-size设置过小。这是Docker运行GUI应用的头号杀手。立即尝试将其增加到--shm-size="4g"
  • 其他可能
    • 主机内存不足。使用docker stats查看容器内存占用,确保主机有足够Swap或物理内存。
    • Node的SE_NODE_MAX_SESSIONS设置过高,超过了主机承载能力。适当调低此值。
    • 浏览器镜像版本有Bug。尝试更换一个不同的版本标签,如从latest换到120.0

问题3:测试脚本执行速度慢,不如本地直接跑。

  • 网络延迟:Hub、Node、测试执行机之间的网络延迟会增加每个WebDriver命令的耗时。尽量让它们处于同一个局域网或可用区。
  • 会话复用:对于大量短测试,创建和销毁浏览器会话开销很大。考虑使用driver.quit()只关闭窗口而不删除会话(需要Grid和脚本配合),或者使用Selenium的sessionTimeout配置延长空闲会话存活时间,供后续测试复用。
  • 视频录制开销:如果开启了enableVideo,录制和编码视频会消耗大量CPU。非必要不开启,或仅在失败时触发录制。

问题4:如何管理不同版本的浏览器镜像?

  • 策略:不要总是使用latest标签。在项目的docker-compose.yml或部署脚本中,固定具体的版本标签,如selenium/node-chrome:120.0。建立一个内部镜像仓库,定期从官方拉取所需版本,并打上自己的标签(如mycompany/node-chrome:120.0-stable),在内部使用,这样可以避免因官方镜像更新带来的意外变更。

搭建和维护一个健壮的Docker化Selenium Grid环境,就像运维一个微服务集群。它带来的效率提升是巨大的,但同时也要求我们具备一定的容器化和网络知识。从手动部署到Compose编排,再到考虑Kubernetes上的动态调度,每一步都是对测试基础设施的升级。我的体会是,前期多花时间把基础打牢,解决好网络、资源、监控这些底层问题,后期在编写和维护自动化测试用例时就会无比顺畅,才能真正把精力聚焦在业务测试本身,让自动化测试成为研发流程中可靠高效的环节,而不是一个脆弱的负担。