GitLab CI 中如何把性能报告上传到 Merge Request 并可视化 diff

解读

在国内 GitLab 私有化部署(社区版/企业版)场景下,面试官真正想验证的是:

  1. 你是否能把“跑完脚本→拿到数据→让研发在 MR 里一眼看到差异”做成自动化闭环,而不是手动贴图;
  2. 是否熟悉 GitLab Artifacts、JUnit XML、Markdown Report、GitLab Pages 以及 GitLab 14.9+ 推出的 Performance Report 规范;
  3. 能否把性能指标(RT、TPS、CPU、内存、GC、DB 慢 SQL 等)抽象成可版本对比的“数值”,并用 GitLab 原生 Widget 或自定义 Markdown Table 展示 diff;
  4. 是否理解“MR 阶段只做增量快速验证,全量基准在 nightly pipeline”这一国内主流节奏,避免把重型压测搬进 MR 造成排队阻塞。

知识点

  1. GitLab CI 报表体系
    • artifacts:reports:junit → 单元/接口测试,MR 的 Test Report Widget;
    • artifacts:reports:performance → 14.9+ 专用,支持 load-performance.json 格式,MR 自动生成 Performance Report Widget;
    • artifacts:reports:metrics → 15.1+ 支持任意 Prometheus Text 格式指标,MR 的 Metrics Report Widget;
    • artifacts:reports:markdown → 任意 Markdown 片段,MR 的“Expand”区域展示,可自定义 diff table;
  2. 性能数据格式
    • load-performance.json(GitLab 官方 schema,含 duration、requests、failedRequests、percentiles 等字段);
    • JUnit XML(可把 SLA 断言包装成 testCase,失败即红线);
    • Prometheus Exposition(0.0.4)文本,方便 metrics report 采集;
  3. diff 思路
    • 基线数据持久化:把 master 分支最近一次 green pipeline 的 load-performance.json 存成 GitLab Pages 或对象存储,供 MR pipeline 下载;
    • 数值 diff:jq 或 Python 脚本对比基线与当前,计算 Δ%、显著性标记(↑↓→);
    • 可视化:生成 Markdown Table 或 load-performance.json(只保留 diff 行),再上传到 reports:markdown / reports:performance;
  4. 国内常见坑
    • 社区版无 Performance Widget,需降级用 reports:markdown + Pages;
    • 大文件 artifacts 默认 1G 上限,压测原始日志需二次聚合后再上传;
    • 多子系统时,需把网关、服务 A、服务 B 的指标合并到同一 load-performance.json,否则 MR 只会展示第一个文件;
    • 私有化实例若关闭 Pages,可把基线 json 存到同一仓库的 gh-pages 分支(GitLab 也能托管)或内部 MinIO。

答案

以 JMeter + 企业版 GitLab 14.10 为例,给出可直接落地的 .gitlab-ci.yml 片段,实现“MR 自动展示性能 diff”。

variables:
  BASELINE_URL: "https://pages.gitlab.example.com/${CI_PROJECT_PATH}/baseline/load-performance.json"
  PERF_WORKSPACE: "$CI_PROJECT_DIR/perf"

stages:
  - perf_test
  - perf_report

# 1. 运行压测并生成 load-performance.json
perf:mr:
  stage: perf_test
  image: harbor.example.com/qa/jmeter:5.6
  only:
    - merge_requests
  script:
    - mkdir -p $PERF_WORKSPACE
    # 拉取基线
    - curl -s -o $PERF_WORKSPACE/baseline.json "$BASELINE_URL" || echo "no baseline, skip"
    # 执行 5 min 负载,只跑核心场景(登录+下单)
    - jmeter -n -t scripts/mr.jmx -l $PERF_WORKSPACE/result.jtl -e -o $PERF_WORKSPACE/site
    # 用 python 脚本把 jtl 转成 GitLab 官方格式
    - python3 scripts/jtl2gitlab.py $PERF_WORKSPACE/result.jtl $PERF_WORKSPACE/current.json
    # 生成 diff markdown
    - python3 scripts/diff.py $PERF_WORKSPACE/baseline.json $PERF_WORKSPACE/current.json > $PERF_WORKSPACE/diff.md
  artifacts:
    when: always
    paths:
      - perf/site/        # 详细 HTML 报告,供 Pages 长期存档
    reports:
      junit: perf/perf.xml            # 把 SLA 断言变成 testCase
      performance: perf/current.json  # MR 的 Performance Widget
      markdown: perf/diff.md          # 自定义 diff table
  cache:
    key: jmeter-plugins
    paths:
      - ~/.jmeter/

# 2. 把当前结果同步为新的基线(只有合并后才更新)
perf:baseline:
  stage: perf_report
  image: alpine
  only:
    - master
  script:
    - cp perf/current.json public/load-performance.json
  artifacts:
    paths:
      - public
    expire_in: 90 day
  cache: {}

配套脚本要点

  1. jtl2gitlab.py:把 JMeter 聚合报告转换成 GitLab load-performance.json 格式,关键字段
    • duration、requests、failedRequests、percentiles{“95”:xxx};
  2. diff.py:
    • 读取 baseline.json 与 current.json,计算 Δ%;
    • 若 95th RT 上涨超过 20% 或 TPS 下降超过 10%,在表格里用↑↓高亮;
    • 输出 Markdown 表格,上限 30 行,避免 MR 页面过长;
  3. SLA 断言:
    • 在 JMeter 中断言 95th RT < 800 ms、Error < 0.5%,失败时把 case 写入 perf.xml,MR 即现红叉,阻止合并。

效果

  • 研发在 MR 页面右侧看到“Performance”标签,点开即是 95th、99th、TPS、Error 的当前值与基线 diff;
  • 同时“Expand”区域显示 markdown 表格,一目了然哪条 API 退化;
  • 若指标超限,JUnit 红线直接阻断合并,倒逼代码在 MR 阶段就完成性能回退修复。

拓展思考

  1. 多环境基准:国内很多团队把 nightly 跑在“等比缩容”K8s 环境,而 MR 跑在“容器 1C2G”容器,二者水位不同。解决方法是把基线按“CPU 核数”归一化,diff 时统一折算成“RT/核”或“TPS/核”,避免误报警。
  2. 大促模型复用:把双 11 模型拆成“读多”“写多”“秒杀”三类子脚本,MR 阶段只跑“读多”10 分钟,nightly 再跑全量。通过 GitLab CI rules:changes 感知代码路径,自动选择对应子脚本,既快又准。
  3. 自定义指标:除了 RT/TPS,还可把 GC 次数、慢 SQL 条数、Redis 命中率也写进 load-performance.json 的 extendedMetrics 字段,GitLab 15+ 的 Performance Widget 会自动展开,真正做到“代码未动,指标先现”。
  4. 社区版 fallback:若公司只有社区版,可关闭 performance report,改用 artifacts:reports:markdown + GitLab Pages,把 diff.md 渲染成静态站点,在 MR 描述里用机器人账号自动评论链接,同样实现“可视化 diff”效果。