如何在 GitLab CI 中通过 Gatling 的 Maven 插件自动生成性能报告并做回归对比
解读
面试官问的是“一条命令能跑起来”之后的工程化落地:
- 让 GitLab CI 在每次代码合并请求(MR)或定时任务中自动触发 Gatling 脚本;
- 跑完后把 HTML/JSON 报告自动归档,供产品、开发、测试在线查看;
- 把本次关键指标(P99、均值、错误率、TPS)与上一次主干构建做差值对比,若劣化超过阈值则流水线失败,并给出可读的回归告警。
国内公司普遍用私有 GitLab + Maven 仓库,CI Runner 以 Docker-in-Docker 或 Shell 模式部署在 K8s/裸金属上,因此答案必须兼顾“私有网络无公网包拉取”“大报告上传慢”“阈值策略可配置”三大痛点。
知识点
- Gatling Maven 插件生命周期:test -> gatling:execute,生成 target/gatling/ 目录。
- GitLab CI 产物(artifacts)与流水线缓存(cache)区别:artifacts 跨 job 持久化,cache 跨流水线加速依赖。
- GitLab Pages 或 GitLab 自带 artifacts 浏览:国内私有 GitLab 常关 Pages,需把报告打成 zip 上传到自建对象存储或 Nginx 目录。
- 回归对比思路:
a) 解析 simulation.log 或 global_stats.json 提取指标;
b) 把指标以 JSON 落库(文件、MinIO、InfluxDB、Postgres 均可);
c) 本次构建读取“基准”指标,计算差值百分比;
d) 通过 JUnit XML 或 exit code 让 GitLab CI 识别失败。 - 阈值策略:相对阈值(+10%)、绝对阈值(P99>500 ms)、错误率=0,支持变量注入以便开发在 MR 中临时调整。
- 资源控制:Maven 指定 -Dgatling.core.directory.resources=/opt/conf 把脚本与数据文件分离,CI 镜像提前放入基础数据,减少每次拉取 2 GB 流量。
- 国内镜像加速:settings.xml 里把 central 换成阿里云,Docker 镜像基于 maven:3.9-eclipse-temurin-17 预装 Gatling 依赖,降低 Runner 下载时间。
答案
以下示例基于私有 GitLab 16.x、Runner 以 Docker 模式注册到项目,Maven 3.9,Gatling 3.10,脚本放在 src/test/scala 目录。
步骤 1:pom.xml 关键片段
<plugin>
<groupId>io.gatling</groupId>
<artifactId>gatling-maven-plugin</artifactId>
<version>4.8.2</version>
<configuration>
<!-- 让 CI 通过 -Dgatling.simulationClass 动态传入 -->
<simulationClass>${gatling.simulationClass}</simulationClass>
<!-- 输出 JSON 方便后续解析 -->
<reportsOnly>false</reportsOnly>
</configuration>
<executions>
<execution>
<phase>integration-test</phase>
<goals><goal>execute</goal></goals>
</execution>
</executions>
</plugin>
步骤 2:阈值对比工具(示例用 Python,CI 镜像里预装)
#!/usr/bin/env python3
import json, sys, os
THRESHOLDS = {'p99': 10, 'mean': 10, 'errors': 0} # 百分比
baseline_file = 'baseline.json'
current_file = 'target/gatling/global_stats.json'
def load(f): return json.load(open(f))
base, curr = load(baseline_file), load(current_file)
code = 0
for k in THRESHOLDS:
delta = (curr[k] - base[k]) / base[k] * 100
if delta > THRESHOLDS[k]:
print(f'REGRESSION: {k} +{delta:.1f}% > {THRESHOLDS[k]}%')
code = 1
if code == 0:
# 把本次结果存为新的 baseline
json.dump(curr, open(baseline_file, 'w'), indent=2)
sys.exit(code)
步骤 3:.gitlab-ci.yml
variables:
MAVEN_OPTS: "-Dmaven.repo.local=$CI_PROJECT_DIR/.m2 -Dgatling.simulationClass=com.demo.StressSimulation"
BASELINE_BUCKET: "s3://perf-baseline/$CI_PROJECT_NAME"
stages: [performance, compare]
performance:
stage: performance
image: registry.xxx.com/perf/gatling:3.10-maven3.9 # 公司预置镜像
cache:
key: m2-$CI_COMMIT_REF_SLUG
paths: [.m2/]
script:
- mvn gatling:execute -q
- mv target/gatling/* target/report/
artifacts:
when: always
paths: [target/report/]
expire_in: 30 days
only: [merge_requests, schedules]
compare:
stage: compare
image: registry.xxx.com/perf/python:3.11
needs: [performance]
script:
- aws s3 cp $BASELINE_BUCKET/baseline.json baseline.json || echo "no baseline, will create"
- python3 scripts/diff.py
- aws s3 cp baseline.json $BASELINE_BUCKET/baseline.json
only: [merge_requests, schedules]
步骤 4:效果
- 每次 MR 会触发性能阶段,跑完产出报告;
- compare 阶段自动拉取上一条主干 baseline,若 P99 上涨超过 10% 或出现错误,流水线失败,MR 无法合并;
- 报告通过 artifacts 在线浏览,若公司关闭 Pages,可在 Nginx 目录挂载 /var/opt/gitlab/gitlab-rails/shared/artifacts,实现内网秒开。
拓展思考
- 多环境基准:测试、预发、生产配置不同,可在 baseline.json 里按 env 分片,CI 变量注入 ENV=testing 读取对应阈值。
- 趋势看板:把 global_stats.json 通过 Telegraf 写入 InfluxDB,Grafana 绘制近 30 天 P99 曲线,比只看单次回归更直观。
- 增量场景:只对修改过的接口做基准对比,解析 simulation.log 按请求名分组,避免“全局 P99 被新接口拉低”的误报。
- 云原生扩展:Runner 换成 K8s executor,使用 Helm 把 Gatling 脚本打成 ConfigMap,Pod 弹性伸缩到 4-8 并发节点,压测万级 TPS 时不再单机瓶颈。
- 合规与审计:在 CI 里加入“性能门禁”标签,合并记录留存于 GitLab 审计事件,方便后续监管抽查。