☰
Kubernetes 上的可扩展 Selenium Grid 部署实战:从 Hub 主从架构到浏览器节点弹性扩缩
2026/10/7 2:30:02 网站建设 项目流程
  • 示例工程

【免费下载链接】examples

Kubernetes application example tutorials

项目地址:https://gitcode.com/gh_mirrors/examp/examples
点击查看免费下载

本文基于 kubernetes examples 仓库中_archived/selenium目录的完整示例,讲解如何把 Selenium Grid 以主从(master/worker)模型部署到 Kubernetes,用于解决 CI 流水线中 Selenium 资源争用的问题。读者将掌握:Selenium Hub 与浏览器节点的部署配置、三种验证 Hub 的方式、Python 端执行远程测试任务、按需横向扩缩节点,以及通过 VNC 调试挂起测试的完整流程。

为什么要把 Selenium 部署到 Kubernetes

Selenium 是业界主流的浏览器自动化工具,主要用于 Web 应用的自动化测试。当 Selenium 被引入 CI 流水线后,多个构建任务同时跑测试时,常常会围绕有限的 Selenium 资源(浏览器实例、测试执行环境)产生争用:要么排队等待,要么互相挤占资源导致测试不稳定。

本示例给出的解法是:把 Selenium Grid 搬进 Kubernetes,利用其调度与弹性能力,把浏览器执行环境做成可按需扩容、用完即弃的资源池。Selenium Grid 采用典型的主从模型:

  • Selenium Hub(主节点):网格的唯一入口,负责接收测试请求并把任务分发给空闲的 worker。整个网格只需要一个 Hub,但示例用 Deployment 保证它始终在运行。
  • Selenium Nodes(工作节点):真正跑浏览器的 worker(注意这里的 node 是 Selenium 概念,与 Kubernetes 节点无关)。示例提供 Chrome 与 Firefox 两类节点,数量可按需伸缩。

前置条件与资源规划

在动手之前,需要满足以下条件:

  • 一个可用的 Kubernetes 集群,以及配置好的kubectl客户端(具体搭建方式可参考 Kubernetes 官方 Getting Started Guides)。
  • 如果希望快速获得一个托管集群,Google Container Engine(GKE)是较便捷的选择。
  • 集群资源要求:要完整跑通本示例直到扩缩容部分,集群至少需要4 CPU 和 6 GB RAM。从仓库中的部署文件可以看到,Hub 与每个浏览器节点都声明了memory: 1000Mi、cpu: 0.5的资源上限(见 selenium-hub-deployment.yaml 与 selenium-node-chrome-deployment.yaml),加上节点默认 2 副本,资源消耗是可预估的,请据此规划节点规格。

部署文件全景解析:Hub 与服务

仓库_archived/selenium目录下共包含 6 个文件,其中 4 个 YAML 是部署的核心,本文先看 Hub 相关的两个。

Selenium Hub 的 Deployment 配置

selenium-hub-deployment.yaml 使用apps/v1的 Deployment 来托管 Hub,关键配置如下:

apiVersion: apps/v1 kind: Deployment metadata: name: selenium-hub labels: app: selenium-hub spec: replicas: 1 selector: matchLabels: app: selenium-hub template: metadata: labels: app: selenium-hub spec: containers: - name: selenium-hub image: selenium/hub:4.0 ports: - containerPort: 4444 - containerPort: 4443 - containerPort: 4442 resources: limits: memory: "1000Mi" cpu: ".5" livenessProbe: httpGet: path: /wd/hub/status port: 4444 initialDelaySeconds: 30 timeoutSeconds: 5 readinessProbe: httpGet: path: /wd/hub/status port: 4444 initialDelaySeconds: 30 timeoutSeconds: 5

逐项说明:

  • 镜像:selenium/hub:4.0,即 Selenium Grid 4 官方镜像。
  • 端口语义:容器暴露三个端口——4444是 WebDriver 命令入口(/wd/hub);4442是事件总线发布(publish)端口;4443是事件总线订阅(subscribe)端口。Selenium 4 中 Hub 内部的事件总线是节点注册与任务分发的通道。
  • 健康探针:分别配置了livenessProbe(存活探针,判定容器是否需要重启)与readinessProbe(就绪探针,判定流量是否可以进入),均通过 HTTP GET/wd/hub/status检查 4444 端口,initialDelaySeconds: 30表示容器启动 30 秒后才开始探测,timeoutSeconds: 5表示探测超时时间为 5 秒。这两个探针是网格可用性的关键保障。

Selenium Hub 的 Service 配置

浏览器节点需要知道如何找到 Hub 才能注册,为此示例创建了 selenium-hub-svc.yaml:

apiVersion: v1 kind: Service metadata: name: selenium-hub labels: app: selenium-hub spec: ports: - port: 4444 targetPort: 4444 name: port0 - port: 4443 targetPort: 4443 name: port1 - port: 4442 targetPort: 4442 name: port2 selector: app: selenium-hub type: NodePort sessionAffinity: None

要点:

  • selector: app: selenium-hub与 Deployment 的labels匹配,服务把流量路由到 Hub Pod。
  • 三个端口(4444/4443/4442)与容器的三个端口一一对应,port与targetPort均为相同值。
  • 类型为NodePort:既能让集群内节点通过内部 DNS 名称selenium-hub访问,也能让集群外(如浏览器节点所在的其他网络)通过节点端口访问。
  • sessionAffinity: None表示不做会话粘滞,请求可分发到任意后端。

创建 Hub 与服务的命令

在仓库根目录执行以下命令即可完成部署(示例文件归档在_archived/selenium目录下):

kubectl create --filename=_archived/selenium/selenium-hub-deployment.yaml kubectl create --filename=_archived/selenium/selenium-hub-svc.yaml

第一条命令创建 Hub 的 Deployment,第二条创建节点注册用的 Service。如果更习惯声明式管理,也可以将create换成apply。

验证 Hub 部署的三种方式

部署完成后,可以通过连接 Hub 的 Web 控制台来验证它是否正常工作。根据你的网络环境,有三种验证路径。

方式一:Kubernetes 节点可达——直连 NodePort

如果 Kubernetes 节点可以从你的网络直接访问,通过kubectl describe svc selenium-hub可以查到节点端口号。下面的代码段利用 kubectl 的模板功能自动提取 NodePort 与第一个节点名称,然后直接 curl:

export NODEPORT=`kubectl get svc --selector='app=selenium-hub' --output=template --template="{{ with index .items 0}}{{with index .spec.ports 0 }}{{.nodePort}}{{end}}{{end}}"` export NODE=`kubectl get nodes --output=template --template="{{with index .items 0 }}{{.metadata.name}}{{end}}"` curl http://$NODE:$NODEPORT

如果返回 Hub 的页面内容,说明服务已正确路由到 Hub Pod。

方式二:Kubernetes 节点不可达——kubectl port-forward

如果节点无法从你的网络访问,可以通过 kubectl 代理访问。先拿到 Hub Pod 的名称:

export PODNAME=`kubectl get pods --selector="app=selenium-hub" --output=template --template="{{with index .items 0}}{{.metadata.name}}{{end}}"` kubectl port-forward $PODNAME 4444:4444

在另一个终端中验证:

curl http://localhost:4444

port-forward会把本机 4444 端口转发到 Pod 的 4444 端口,无需暴露任何 Service。

方式三:使用 Google Container Engine——暴露公网地址

如果集群运行在 Google Container Engine 上,还可以把 Hub 通过公网暴露出去。文档同时提醒:这对多数场景都是个坏主意(安全性差),但确实可以这样做:

kubectl expose deployment selenium-hub --name=selenium-hub-external --labels="app=selenium-hub,external=true" --type=LoadBalancer

等待几分钟,selenium-hub-external服务会从 gcloud 获得一个负载均衡 IP。当kubectl get svc selenium-hub-external显示两个 IP 后,运行以下代码段取出公网 IP 并验证:

export INTERNET_IP=`kubectl get svc --selector="app=selenium-hub,external=true" --output=template --template="{{with index .items 0}}{{with index .status.loadBalancer.ingress 0}}{{.ip}}{{end}}{{end}}"` curl http://$INTERNET_IP:4444/

此后你(以及互联网上的所有人)都可以在浏览器中访问$INTERNET_IP打开 Hub 控制台——这也是文档强调"坏主意"的原因。

部署 Firefox 与 Chrome 节点

Hub 就绪后,就可以部署 worker 了。示例默认部署 2 个 Chrome 节点与 2 个 Firefox 节点。

kubectl create --filename=_archived/selenium/selenium-node-chrome-deployment.yaml kubectl create --filename=_archived/selenium/selenium-node-firefox-deployment.yaml

Pod 启动后,它们会出现在 Selenium Hub 的界面中,完成注册。

Chrome 节点部署解析

selenium-node-chrome-deployment.yaml 的完整结构如下:

apiVersion: apps/v1 kind: Deployment metadata: name: selenium-node-chrome labels: app: selenium-node-chrome spec: replicas: 2 selector: matchLabels: app: selenium-node-chrome template: metadata: labels: app: selenium-node-chrome spec: volumes: - name: dshm emptyDir: medium: Memory containers: - name: selenium-node-chrome image: selenium/node-chrome:4.0 ports: - containerPort: 5555 volumeMounts: - mountPath: /dev/shm name: dshm env: - name: SE_EVENT_BUS_HOST value: "selenium-hub" - name: SE_EVENT_BUS_SUBSCRIBE_PORT value: "4443" - name: SE_EVENT_BUS_PUBLISH_PORT value: "4442" resources: limits: memory: "1000Mi" cpu: ".5"

几个值得注意的设计:

  • /dev/shm内存卷(dshm):定义了一个emptyDir卷,medium: Memory表示挂载为内存文件系统,并挂载到容器的/dev/shm。Chrome 在容器中运行时常因共享内存不足而崩溃,这一配置为浏览器提供了充足的内存化共享存储,是浏览器节点稳定运行的常见做法。
  • 事件总线连接环境变量:通过SE_EVENT_BUS_HOST=selenium-hub、SE_EVENT_BUS_SUBSCRIBE_PORT=4443、SE_EVENT_BUS_PUBLISH_PORT=4442三个环境变量,告诉节点到哪里注册自己。从部署配置可以看出,节点正是通过 Service DNS 名selenium-hub加上事件总线的发布/订阅端口与 Hub 建立通信的,这与前文 Hub Service 暴露的 4442/4443 端口严格对应。
  • 端口与资源:节点自身暴露 5555 端口(Selenium 节点默认通信端口),并声明memory: 1000Mi、cpu: .5的资源上限。

Firefox 节点部署解析

selenium-node-firefox-deployment.yaml 与 Chrome 版本结构完全一致,差异仅在镜像(selenium/node-firefox:4.0)、Deployment 名称与标签。它同样带有 dshm 内存卷、5555 端口、SE_EVENT_BUS_*三件套环境变量与相同的资源上限,这里不再重复贴出,读者可直接对照查看。

运行一次真实的 Selenium 任务

节点注册完成后,运行一个真实的 Selenium 任务来验证整个网格。

准备 Python 运行环境

先启动一个可交互的 Python 容器并进入其中(这一步相当于申请一台临时执行机):

kubectl run selenium-python --tty -i --image=python:slim bash

进入容器后安装 Selenium 客户端库:

pip install selenium

执行测试脚本

启动 Python 解释器:

python

然后粘贴仓库中 selenium-test.py 的内容(完整源码如下):

from selenium import webdriver def check_browser(browser): if browser == "CHROME": options = webdriver.ChromeOptions() elif browser == "FIREFOX": options = webdriver.FirefoxOptions() driver = webdriver.Remote( command_executor='http://selenium-hub:4444/wd/hub', options=options ) driver.get("http://www.google.com") assert "google" in driver.page_source driver.quit() print("Browser %s checks out!" % browser) check_browser("FIREFOX") check_browser("CHROME")

脚本逻辑解读:

  • 根据浏览器类型构造对应的ChromeOptions/FirefoxOptions。
  • 关键点在于webdriver.Remote:command_executor指向http://selenium-hub:4444/wd/hub——这是集群内通过 Service DNS 名访问 Hub 的标准方式,也是 Hub Service 存在的意义。
  • 任务内容为访问http://www.google.com并断言页面源码中包含google,通过则打印校验通过信息。
  • 脚本依次对 Firefox 与 Chrome 各执行一次,从而验证两类节点都能正常接单。

如果一切正常,你会看到如下输出:

>>> check_browser("FIREFOX") Browser FIREFOX checks out! >>> check_browser("CHROME") Browser CHROME checks out!

恭喜,你的 Selenium Hub 已经带着 Firefox 和 Chrome 节点稳定运行了。

按需扩缩浏览器节点

Selenium Grid 的可扩展性正是本示例的核心价值。当测试负载上来后,硬件资源就是唯一的瓶颈。一条命令即可把节点数扩到任意规模:

kubectl scale deployment selenium-node-firefox --replicas=10 kubectl scale deployment selenium-node-chrome --replicas=10

执行后你就有 10 个 Firefox 节点和 10 个 Chrome 节点了。Deployment 的声明式副本管理让扩容、缩容都只需改replicas一个数字,新节点会自动通过事件总线注册到 Hub。当然,扩容前请确认集群剩余资源满足各节点的资源上限(每个节点约 0.5 CPU / 1000Mi 内存)。

通过 VNC 调试挂起的测试

测试偶尔会挂起,此时往往需要亲眼看看浏览器到底停在哪个页面。示例中每个节点 Pod 都运行了 VNC 服务。由于不想为每个 Pod 都暴露一个 Service,且容器内的 VNC 密码较弱,官方推荐用port-forward代理连接。把POD_NAME替换为想连接的 Pod 名称:

kubectl port-forward $POD_NAME 5900:5900

然后用 VNC 客户端连接localhost:5900,密码为secret,即可实时看到该节点上浏览器正在执行的操作画面,这是定位挂起测试最直观的手段。

本示例改编自 SeleniumHQ 的 docker-selenium 项目,网格的浏览器镜像即来自该项目。

资源清理

测试完毕,删除本示例创建的所有资源:

kubectl delete deployment selenium-hub kubectl delete deployment selenium-node-chrome kubectl delete deployment selenium-node-firefox kubectl delete deployment selenium-python kubectl delete svc selenium-hub

如果按"方式三"创建过外部服务,还需要额外清理selenium-hub-external。至此,一套完整的、可弹性扩缩的 Selenium Grid 就完成了从部署、验证、执行、扩容到清理的闭环。

小结

本示例展示了一条清晰的 Kubernetes 化 Selenium 路径:用 Deployment 保障 Hub 与浏览器节点的高可用与可伸缩,用 Service 提供节点注册与访问入口,用探针保证网格健康,用kubectl scale实现按需扩容,用 VNC 提供测试排障手段。对于 CI 流水线中频繁出现的 Selenium 资源争用问题,这套主从架构 + 弹性扩容的方案给出了开箱即用的参考答案。相关部署文件与测试脚本均可在仓库_archived/selenium目录下直接查看与复用。

  • 示例工程

【免费下载链接】examples

Kubernetes application example tutorials

项目地址:https://gitcode.com/gh_mirrors/examp/examples
点击查看免费下载

相关推荐

上一篇:DeepSeek Harness 同 tick 回显修复:Session Intent 草稿在受控输入下的同步刷新机制
下一篇:GBFR-Logs:3步掌握《碧蓝幻想:Relink》最强DPS分析工具

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询