点击蓝字
关注我们
上个季度,我手里的 Playwright 回归测试套件,单台 GitHub Actions 执行机要跑 47 分钟。开发同学开始跳过合并前的校验环节,产品经理也来问:“不就几个测试吗,怎么比构建代码还慢?” 我知道套件体量不小,但直到认真测算过,才意识到我们浪费了这么多时间。
三周之后,同一套套件只需要 8 分 12 秒就能跑完。我们没换框架、没换云服务商、也没招专职 DevOps,只是把 Playwright 自带的两个功能 —— 分片(Sharding)和 Docker—— 做了精细化配置,而不是抱着 “试试再说” 的心态随便搭一下。
这篇文章我会把这套能把运行时长砍掉 83% 的 Playwright 分片 + Docker 配置完整讲给你,包含完整的 GitHub Actions YAML 配置、踩了两天坑才避开的误区,以及我只用一条 Slack 消息就说服技术经理审批通过的数据依据。
47 分钟为什么是致命问题
我们的套件有 312 条端到端测试,分布在 28 个测试文件里。单条测试平均耗时 8.5 秒,但单台执行机串行执行、加上 Playwright CI 默认的 workers: 1 配置,算下来就是:312 条用例 × 8.5 秒 = 2652 秒,大概 44 分钟。再加上环境准备、依赖安装、产物上传的时间,最终就是 47 分钟。
47 分钟的执行时长,会给团队带来这些问题:
●开发会合入有问题的代码:PR 校验要等 47 分钟,没人愿意耗着等。大家直接合代码下班,赌夜里不会出问题。
●不稳定问题会越积越多:套件跑的频次越低,遇到的环境波动就越多 —— 网络抖动、测试环境不稳定、资源争抢,都会带来随机失败的用例。
●CI 账单会快速膨胀:GitHub Actions 按分钟计费。一条 47 分钟的任务一天跑 20 次,一天就是 940 分钟。按团队版每分钟 0.008 美元算,一天 7.52 美元,单条工作流每月就要 225 美元。
但真正的代价不是钱,是团队对流水线的信任。当流水线比喝杯咖啡还慢,大家就不会再把它当成质量卡点,只会把它当走流程的形式。我接触过的几乎所有公司都有这个问题:套件越变越大,没人做优化,最后慢慢就没人用了。
截至 2026 年 4 月,Playwright 的 npm 月下载量已经达到 1.347 亿次,同期 Cypress 是 3040 万,selenium-webdriver 是 839 万,和 Selenium 差了 16 倍。体量这么大,高效执行就成了必修课,而分片就是最核心的优化手段。
Playwright 分片到底是什么
分片就是把你的测试套件拆成更小的块,每一块叫一个分片。每个分片在独立的机器上运行。如果把 312 条测试拆成 6 个分片,每个分片大概跑 52 条。硬件配置相同的情况下,总耗时就从 47 分钟降到了 8 分钟以内,因为所有分片是并行跑的。
Playwright 通过 --shard=x/y 参数来完成拆分。比如拆成 6 个分片的写法是:
npx playwright test --shard=1/6npx playwright test --shard=2/6npx playwright test --shard=3/6npx playwright test --shard=4/6npx playwright test --shard=5/6npx playwright test --shard=6/6
每条命令会拿到不同的测试子集。在 CI 环境里,你把这些命令放在不同的任务里,调度器 ——GitHub Actions、GitLab CI、Azure Pipelines 都行 —— 就会并发执行它们。
一定要分清分片和 worker 不是一回事:worker 是单台机器上共享 CPU 和内存的并发执行,分片是把测试分发到多台机器,每台都有独立的资源。两者可以叠加使用:6 个分片 × 每个分片 4 个 worker = 同时跑 24 条测试。我测出的 83% 提速,就是靠这套组合方案实现的。
分片均衡度:按文件拆分vs按用例拆分
Playwright 支持两种粒度的分片,选错了会导致分片负载不均,等于白做优化。
●不开 fullyParallel: true:Playwright 会把整个测试文件分配给单个分片。如果一个文件有 40 条测试,另一个只有 3 条,分到前者的分片就会严重过载。这是默认行为,对测试文件大小不均的团队很不友好。
●开启 fullyParallel: true:Playwright 会按单条测试粒度拆分。不管文件多大,每个分片分到的测试数量基本均等。我用的就是这个模式,也是 6 分片方案里各分片耗时差能控制在 30 秒以内的原因。
在 playwright.config.ts 里配置:
exportdefaultdefineConfig({fullyParallel: true,workers: process.env.CI ? 4 : undefined,});
workers: 4 表示每个分片在自己的执行机上同时跑 4 条测试。在 GitHub Actions 的 ubuntu-latest 配置(2 核 CPU、7 GB 内存)下,4 个 worker 是最优平衡点。开太多会导致内存不足、浏览器崩溃,开太少又浪费 CPU 性能。
合并分片报告
每个分片都会生成自己的报告。要拿到统一的 HTML 报告,可以用 Playwright 自带的 blob 报告器和 merge-reports 命令。我在 CI 里配置了 blob 报告,然后在下游任务里合并结果,最终就能得到一份包含全部 312 条测试、追踪日志和截图的完整 HTML 报告,和单机构建的结果完全一致。
在 playwright.config.ts 里配置:
reporter: process.env.CI ? 'blob' : 'html',
所有分片执行完成后,运行:
npx playwright merge-reports --reporter html ./all-blob-reports
执行后会生成标准的 playwright-report 目录,里面是合并后的完整结果。
Docker 的作用:稳定才能更快
单靠分片已经能提速了,但 Docker 才是让耗时稳定下来的关键。不用 Docker 的时候,各分片的运行时长波动能到 15%,有的分片 7 分钟跑完,有的要 10 分钟。根源就是环境差异:GitHub Actions 各执行机上的浏览器版本不一致、系统依赖不全、区域设置不统一。
我换成了 Playwright 官方 Docker 镜像之后,波动直接降到了 3% 以内。
我用的镜像
mcr.microsoft.com/playwright:v1.59.1-noble
这个镜像基于 Ubuntu 24.04 LTS,预装了 Chromium、Firefox、WebKit 和全部系统依赖,压缩后大小约 872 MB。第一次 CI 运行要花 2-3 分钟下载,后续运行缓存了镜像层之后,30 秒以内就能启动。
版本锁定是必须做的。如果你的 package.json 装的是 Playwright 1.59.1,但 Docker 镜像跑的是 1.58,Playwright 会找不到浏览器执行文件,报错信息还很隐晦。解决方法很简单:所有版本全部锁死。
关键 Docker 启动参数
三个参数直接决定了 Docker 里跑 Playwright 稳不稳:
●--init:正确处理 1 号进程,避免浏览器僵尸进程。不开的话,大套件跑久了会残留 Chromium 实例,把内存耗光。
●--ipc=host:Chromium 必需的配置。不开启共享内存的话,页面 JS 一多就会内存不足,报 RESULT_CODE_KILLED 错误直接崩溃。
●--user pwuser:只在测不受信任站点时需要。测自己业务的端到端测试,用 root 就行,还能省掉权限问题。
我在 GitHub Actions 里的容器配置是这样的:
container:image: mcr.microsoft.com/playwright:v1.59.1-nobleoptions: --init --ipc=host
如果你还在每次运行都执行 npx playwright install --with-deps 装浏览器,每个任务要多花 45-90 秒。Docker 镜像里已经预装了浏览器,6 个分片就能省下 6 × 60 秒 = 6 分钟的时间。
落地提速的完整配置方案
下面是我在生产环境在用的完整 GitHub Actions 工作流。它支持 6 任务分片、官方 Docker 镜像、blob 报告合并、产物上传。你可以直接复制,根据自己套件的大小调整分片数量就能用。
name: Playwright Tests(Sharded)on:push:branches: [main, master]pull_request:branches: [main, master]jobs:playwright-tests:timeout-minutes: 30runs-on: ubuntu-latestcontainer:image: mcr.microsoft.com/playwright:v1.59.1-nobleoptions: --init --ipc=hoststrategy:fail-fast: falsematrix:shardIndex: [1, 2, 3, 4, 5, 6]shardTotal: [6]steps:- uses: actions/checkout@v5- name: Cache Node modulesuses: actions/cache@v4with:path: ~/.npmkey: ${{ runner.os }}-node-${{ hashFiles('package-lock.json') }}restore-keys: |${{ runner.os }}-node-- name: Install dependenciesrun: npm ci- name: Run Playwright testsrun: npx playwright test --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }}- name: Upload blob reportif: ${{ !cancelled() }}uses: actions/upload-artifact@v4with:name: blob-report-${{ matrix.shardIndex }}path: blob-report/retention-days: 1merge-reports:if: ${{ !cancelled() }}needs: [playwright-tests]runs-on: ubuntu-lateststeps:- uses: actions/checkout@v5- uses: actions/setup-node@v5with:node-version: lts/*- name: Install dependenciesrun: npm ci- name: Download blob reportsuses: actions/download-artifact@v5with:path: all-blob-reportspattern: blob-report-*merge-multiple: true- name: Merge reportsrun: npx playwright merge-reports --reporter html ./all-blob-reports- name: Upload HTML reportuses: actions/upload-artifact@v4with:name: html-reportpath: playwright-report/retention-days: 14
未完待续
下篇将继续学习耗时测算逻辑、三个细节优化、成本测算与常见踩坑点

