饮墨

子安饮墨馀三斗,留与卿儿作赋来

5 步用 Grafana k6 实现云原生服务压测,精准定位性能瓶颈

痛点

你的 Kubernetes 集群扛住了日常流量,但大促或突发高峰时 Pod 频繁 OOMKill、响应延迟飙升。传统压测工具(JMeter、Locust)要么笨重难以容器化,要么产出的报告和云原生监控体系割裂——压测结果在 JMeter GUI 里,而真实指标在 Grafana 里,排查时两头切换效率极低。

核心矛盾: 压测工具与可观测性体系脱节,无法在同一视角下同时看到「施压曲线」和「服务响应指标」。

方案

Grafana k6 — 用 JavaScript 编写压测脚本,原生支持将指标输出到 Prometheus/Grafana,天然融入云原生可观测性栈。核心优势:

  • 单二进制,无 JVM 依赖,容器镜像仅 ~40MB
  • 脚本即代码,可 Git 管理、CI/CD 集成
  • 原生 Prometheus Remote Write,压测指标直接进 Grafana 大盘
  • 支持阈值断言(Thresholds),CI 中自动 pass/fail

实操步骤

第 1 步:安装 k6

# Linux (amd64)
curl -sSL https://github.com/grafana/k6/releases/download/v0.56.0/k6-v0.56.0-linux-amd64.tar.gz | tar xz
sudo mv k6-v0.56.0-linux-amd64/k6 /usr/local/bin/

# 验证
k6 version

容器化方式:

docker run --rm -i grafana/k6:0.56.0 run - < script.js

第 2 步:编写压测脚本

创建 load-test.js

import http from 'k6/http';
import { check, sleep } from 'k6';

export const options = {
  stages: [
    { duration: '30s', target: 50 },   // 30s 内爬升到 50 VU
    { duration: '1m',  target: 200 },   // 1min 内爬升到 200 VU
    { duration: '30s', target: 0 },     // 30s 内降至 0
  ],
  thresholds: {
    http_req_duration: ['p(95)<500'],   // P95 延迟 < 500ms
    http_req_failed:   ['rate<0.01'],   // 错误率 < 1%
  },
};

export default function () {
  const res = http.get('http://your-service.default.svc.cluster.local/api/health');
  check(res, {
    'status is 200': (r) => r.status === 200,
    'latency < 300ms': (r) => r.timings.duration < 300,
  });
  sleep(1);
}

第 3 步:对接 Prometheus + Grafana

启动 k6 时开启 Prometheus Remote Write 输出:

K6_PROMETHEUS_RW_SERVER_URL=http://prometheus:9090/api/v1/write \
K6_PROMETHEUS_RW_TREND_AS_NATIVE_HISTOGRAM=true \
k6 run --out experimental-prometheus-rw load-test.js

指标会自动写入 Prometheus,在 Grafana 中导入官方 Dashboard(ID: 18030)即可实时查看:

  • VU 并发数曲线
  • 请求速率(req/s)
  • P50 / P95 / P99 延迟分布
  • 错误率变化

第 4 步:集成到 CI/CD Pipeline

GitLab CI 示例:

load-test:
  stage: test
  image: grafana/k6:0.56.0
  script:
    - k6 run --out experimental-prometheus-rw load-test.js
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
  allow_failure: false  # thresholds 不通过则 MR 失败

k6 的 thresholds 机制会在指标超标时返回非零退出码,CI 自动标记失败,无需额外脚本判断。

第 5 步:在 K8s 中分布式执行(可选)

对于需要上万 VU 的场景,使用 k6-operator:

helm repo add grafana https://grafana.github.io/helm-charts
helm install k6-operator grafana/k6-operator -n k6-operator-system --create-namespace

创建 TestRun CRD:

apiVersion: k6.io/v1alpha1
kind: TestRun
metadata:
  name: peak-load-test
spec:
  parallelism: 4          # 4 个 Runner Pod 并行
  script:
    configMap:
      name: load-test-script
      file: load-test.js
  arguments: --out experimental-prometheus-rw

4 个 Pod 各跑 200 VU,总并发 800,施压能力线性扩展。

避坑

1. Prometheus Remote Write 超时丢数据

k6 默认 batch 间隔 1s,高并发时 Prometheus 写入压力大。调大 buffer:

K6_PROMETHEUS_RW_PUSH_INTERVAL=5s k6 run --out experimental-prometheus-rw load-test.js

2. K8s 内部压测时 DNS 解析成瓶颈

大量 VU 同时解析 Service 域名会打满 CoreDNS。解决方案:在压测脚本中直接用 ClusterIP,或提前扩容 CoreDNS 副本数:

kubectl -n kube-system scale deployment coredns --replicas=5

3. thresholds 设置过严导致 CI 频繁失败

P95 < 200ms 在共享集群上几乎不可能稳定达标。建议区分环境:

const ENV = __ENV.TEST_ENV || 'staging';
export const options = {
  thresholds: {
    http_req_duration: [ENV === 'prod' ? 'p(95)<300' : 'p(95)<800'],
  },
};

总结

Grafana k6 的核心价值是压测即可观测 — 施压指标和服务指标在同一个 Grafana 大盘上实时关联,精准定位「是压测端瓶颈还是服务端瓶颈」。结合 CI/CD thresholds 机制,性能回归在合并前就能拦截。对于已经建立 Prometheus + Grafana 监控体系的团队,k6 是目前融合度最高的云原生压测方案。

您还没有登录,请登录后发表评论。