Перейти к содержимому
К программе курса
k6: нагрузочное тестирование как система
17 / 17100%

Финальный проект: внедрите k6 в свой проект

60 минут

Цель финального проекта

К концу этого урока у вас будет:

  • ✅ Работающий smoke-тест, интегрированный в CI/CD
  • ✅ Load-тест с baseline для ключевого user journey
  • ✅ Настроенная интеграция с Prometheus/Grafana (или альтернатива)
  • ✅ Документированный процесс Go/No-Go для релизов
  • ✅ План дальнейшего развития (3-6 месяцев)

Этот проект — не теоретическое упражнение. Внедряйте k6 в ваш реальный проект параллельно с прохождением шагов. В конце вы получите working solution, который сразу приносит пользу.

🏆 Критерии оценки: уровни достижений

Выберите уровень, который соответствует вашим целям и ресурсам. Каждый уровень — полноценное достижение, которое приносит реальную пользу.

🥉 Bronze — Минимально жизнеспособное внедрение

Подходит для: малых команд, MVP-проектов, первого опыта с k6

Обязательные требования:

  • ✅ Smoke-тест покрывает 3+ критических endpoints
  • ✅ Тест интегрирован в CI/CD (запускается на каждом PR)
  • ✅ Thresholds привязаны к реальным SLO или временным целям
  • ✅ Команда знает, как интерпретировать результаты smoke-теста
  • ✅ Есть документация: что тестируем, как запускать локально

Время внедрения: 1-2 недели Польза: Предотвращение критических регрессий перед деплоем

Bronze достаточно, чтобы предотвратить 70% инцидентов, связанных с производительностью. Не стремитесь сразу к Gold — лучше сделать Bronze качественно.

🥈 Silver — Рекомендуемый уровень

Подходит для: продакшн-проектов с регулярными релизами, команд 5+ человек

Включает Bronze +:

  • ✅ Load-тест для ключевого user journey (checkout, регистрация, поиск)
  • ✅ Baseline зафиксирован и сохранен в Git/S3
  • ✅ Автоматическое сравнение с baseline (скрипт или CI/CD stage)
  • ✅ Интеграция с observability: Prometheus + Grafana или альтернатива
  • ✅ Grafana dashboard доступен команде и задокументирован
  • ✅ Процесс Go/No-Go задокументирован (критерии, ответственные, template отчета)
  • ✅ Load-тест запускается перед каждым major release

Время внедрения: 3-4 недели Польза: Обнаружение регрессий до production, данные для capacity planning, прозрачный процесс релиза

🥇 Gold — Advanced уровень

Подходит для: high-load проектов, e-commerce, финтех, enterprise

Включает Silver +:

  • ✅ Покрытие тестами 3+ критических user journeys
  • ✅ Stress-тест для поиска breakpoint (запускается перед big sale/маркетинговыми кампаниями)
  • ✅ Soak-тест (4-8 часов) раз в месяц для поиска утечек памяти
  • ✅ Масштабирование генераторов: k6-operator в Kubernetes или distributed execution
  • ✅ Интеграция трейсов (traceparent header) для корреляции k6 ↔ APM
  • ✅ Автоматический fail в CI/CD при деградации > 10% к baseline
  • ✅ Multi-region тестирование (если приложение geo-distributed)
  • ✅ Continuous load testing в production (канарейка, shadow traffic)

Дополнительно (опционально):

  • ✅ Custom metrics для бизнес-KPI (conversion rate, cart abandonment)
  • ✅ Chaos engineering: k6 + Chaos Mesh для тестов отказоустойчивости
  • ✅ Библиотека паттернов и best practices для команды
  • ✅ Метрики ROI: prevented incidents, сэкономленное время/деньги

Время внедрения: 2-3 месяца Польза: Максимальная уверенность в стабильности, предотвращение крупных инцидентов, обоснование инфраструктурных расходов

🎯 Как выбрать свой уровень

Начните с Bronze, если:

  • У вас нет опыта с k6
  • Команда < 5 человек
  • Проект в стадии MVP/beta
  • Нет выделенного Performance Engineer

Стремитесь к Silver, если:

  • Проект в production с регулярными релизами
  • У вас были инциденты из-за производительности
  • Нужны данные для capacity planning
  • Команда готова инвестировать 3-4 недели

Выбирайте Gold, если:

  • High-load проект (> 10K RPS в пиках)
  • Downtime стоит дорого (финтех, e-commerce)
  • Есть SRE/Performance Engineer в команде
  • Уже есть mature observability (Prometheus, Grafana, трейсинг)

Не пытайтесь прыгнуть сразу на Gold! Это приведет к burnout и забрасыванию проекта. Лучше качественно внедрить Bronze за 2 недели, чем потратить месяц на недоделанный Gold.

Фаза 1: Smoke-тест в CI/CD (неделя 1)

Шаг 1.1: Определите критические endpoints

**Задание:** Составьте список критических API endpoints вашего приложения
 
Критерии критичности:
 
- [ ] Endpoint используется в 80% пользовательских сценариев
- [ ] Деградация этого endpoint влияет на revenue
- [ ] Endpoint участвует в критическом business flow (регистрация, оплата, авторизация)
 
**Мой список критических endpoints:**
 
1. POST /auth/login — авторизация
2. GET /api/products — каталог товаров
3. POST /api/cart/add — добавление в корзину
4. POST /api/orders/create — создание заказа
5. ...

Шаг 1.2: Напишите первый smoke-тест

// tests/smoke.js
import http from "k6/http";
import { check, sleep } from "k6";
 
export const options = {
  scenarios: {
    smoke: {
      executor: "shared-iterations",
      iterations: 10,
      vus: 2,
      maxDuration: "2m",
    },
  },
  thresholds: {
    http_req_failed: ["rate<0.05"], // < 5% ошибок
    http_req_duration: ["p(95)<1000"], // p95 < 1s (временный, уточните позже)
    checks: ["rate>0.95"], // > 95% проверок прошли
  },
};
 
const BASE_URL = __ENV.BASE_URL || "https://staging.yourapp.com";
 
export default function () {
  // Замените на ваши критические endpoints
 
  // 1. Health check
  let res = http.get(`${BASE_URL}/health`);
  check(res, {
    "health check is 200": (r) => r.status === 200,
  });
 
  // 2. Критический endpoint 1
  res = http.get(`${BASE_URL}/api/products?limit=10`);
  check(res, {
    "products list is 200": (r) => r.status === 200,
    "has products": (r) => {
      const body = JSON.parse(r.body || "{}");
      return body.products && body.products.length > 0;
    },
  });
 
  // 3. Критический endpoint 2 (с авторизацией)
  const token = __ENV.TEST_TOKEN; // Передайте через env
  res = http.post(
    `${BASE_URL}/api/cart/add`,
    JSON.stringify({ productId: "test-1", quantity: 1 }),
    {
      headers: {
        "Content-Type": "application/json",
        Authorization: `Bearer ${token}`,
      },
    }
  );
  check(res, {
    "cart add is 200": (r) => r.status === 200 || r.status === 201,
  });
 
  sleep(1);
}

Шаг 1.3: Интегрируйте в CI/CD

Выберите ваш CI/CD и адаптируйте примеры ниже

GitHub Actions:

# .github/workflows/smoke-test.yml
name: Smoke Test
 
on:
  pull_request:
    branches: [main, develop]
  push:
    branches: [main]
 
jobs:
  smoke-test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
 
      - name: Run k6 smoke test
        uses: grafana/k6-action@v0.3.1
        with:
          filename: tests/smoke.js
        env:
          BASE_URL: ${{ secrets.STAGING_URL }}
          TEST_TOKEN: ${{ secrets.TEST_TOKEN }}
 
      - name: Upload results
        if: always()
        uses: actions/upload-artifact@v3
        with:
          name: k6-results
          path: summary.json

GitLab CI:

# .gitlab-ci.yml
smoke-test:
  stage: test
  image: grafana/k6:latest
  script:
    - k6 run tests/smoke.js --summary-export summary.json
  artifacts:
    when: always
    paths:
      - summary.json
    expire_in: 30 days
  variables:
    BASE_URL: $STAGING_URL
    TEST_TOKEN: $TEST_TOKEN
  only:
    - merge_requests
    - main

Чек-лист фазы 1:

  • Smoke-тест написан и покрывает 3-5 критических endpoints
  • Тест запускается локально: k6 run tests/smoke.js
  • Тест интегрирован в CI/CD pipeline
  • Тест падает при нарушении thresholds (exit code 1)
  • Команда знает, как интерпретировать результаты smoke-теста

Milestone 1: Вы предотвращаете deploy, который сломает критические endpoints!

Фаза 2: Load-тест с baseline (недели 2-3)

Шаг 2.1: Выберите ключевой user journey

Пример: "Browse → Add to Cart → Checkout"

// tests/load-checkout.js
import http from "k6/http";
import { check, sleep } from "k6";
import { SharedArray } from "k6/data";
 
// Загружаем тестовые данные
const products = new SharedArray("products", function () {
  return JSON.parse(open("./fixtures/products.json"));
});
 
export const options = {
  scenarios: {
    load_test: {
      executor: "ramping-vus",
      startVUs: 0,
      stages: [
        { duration: "2m", target: 10 }, // Warmup
        { duration: "5m", target: 50 }, // Ramp-up
        { duration: "10m", target: 50 }, // Plateau (основной тест)
        { duration: "2m", target: 0 }, // Ramp-down
      ],
      gracefulRampDown: "30s",
    },
  },
  thresholds: {
    // Уточните на основе ваших SLO
    "http_req_duration{flow:checkout}": ["p(95)<800", "p(99)<1500"],
    "http_req_failed{flow:checkout}": ["rate<0.01"],
    "checks{flow:checkout}": ["rate>0.97"],
  },
};
 
const BASE_URL = __ENV.BASE_URL || "https://staging.yourapp.com";
 
export default function () {
  const product = products[Math.floor(Math.random() * products.length)];
 
  // 1. Browse products
  let res = http.get(`${BASE_URL}/api/products`, {
    tags: { flow: "browse" },
  });
  check(res, { "browse OK": (r) => r.status === 200 });
  sleep(2); // Think time
 
  // 2. View product details
  res = http.get(`${BASE_URL}/api/products/${product.id}`, {
    tags: { flow: "browse" },
  });
  check(res, { "product details OK": (r) => r.status === 200 });
  sleep(1);
 
  // 3. Add to cart
  res = http.post(
    `${BASE_URL}/api/cart/add`,
    JSON.stringify({ productId: product.id, quantity: 1 }),
    {
      headers: {
        "Content-Type": "application/json",
        Authorization: `Bearer ${__ENV.TEST_TOKEN}`,
      },
      tags: { flow: "checkout" },
    }
  );
  check(res, { "add to cart OK": (r) => r.status === 200 || r.status === 201 });
  sleep(2);
 
  // 4. Checkout
  res = http.post(
    `${BASE_URL}/api/orders/create`,
    JSON.stringify({
      paymentMethod: "test",
      shippingAddress: "Test Street 1",
    }),
    {
      headers: {
        "Content-Type": "application/json",
        Authorization: `Bearer ${__ENV.TEST_TOKEN}`,
      },
      tags: { flow: "checkout" },
    }
  );
  check(res, {
    "checkout OK": (r) => r.status === 200 || r.status === 201,
    "has orderId": (r) => {
      const body = JSON.parse(r.body || "{}");
      return body.orderId !== undefined;
    },
  });
 
  sleep(5); // Think time after purchase
}

Шаг 2.2: Соберите baseline

# 1. Запустите тест на чистом стенде
k6 run tests/load-checkout.js --summary-export baseline.json
 
# 2. Сохраните baseline в Git
git add baseline.json
git commit -m "feat: add load test baseline for checkout flow"
git push
 
# 3. Документируйте условия baseline
echo "## Baseline для checkout flow
 
**Дата:** $(date)
**Версия:** v1.2.3
**Условия:**
- Стенд: staging (4 CPU, 8GB RAM)
- БД: PostgreSQL 15, пустая (только fixtures)
- Сценарий: 50 VU, 10 минут plateau
 
**Результаты:**
- p95: 450ms
- p99: 850ms
- Error rate: 0.2%
- Checks: 98.5%
" > baseline-checkout.md

Шаг 2.3: Автоматическое сравнение с baseline

// scripts/compare-baseline.js
import fs from "fs";
 
const baseline = JSON.parse(fs.readFileSync("baseline.json", "utf8"));
const current = JSON.parse(fs.readFileSync("summary.json", "utf8"));
 
const metrics = ["http_req_duration", "http_req_failed"];
let hasRegression = false;
 
for (const metric of metrics) {
  const baseVal = baseline.metrics[metric].values["p(95)"];
  const currVal = current.metrics[metric].values["p(95)"];
  const diff = ((currVal - baseVal) / baseVal) * 100;
 
  console.log(`${metric} p95: ${currVal}ms (${diff.toFixed(1)}% vs baseline)`);
 
  if (Math.abs(diff) > 10) {
    console.error(`⚠️  Regression detected: ${diff.toFixed(1)}%`);
    hasRegression = true;
  }
}
 
process.exit(hasRegression ? 1 : 0);

Чек-лист фазы 2:

  • Load-тест написан для ключевого user journey
  • Baseline собран и задокументирован (версия, условия, результаты)
  • Baseline сохранен в Git/S3
  • Скрипт сравнения с baseline работает
  • В CI/CD добавлен stage для load-теста (опционально, можно запускать вручную)

Milestone 2: Вы можете обнаружить регрессию производительности до production!

Фаза 3: Observability (недели 3-4)

Шаг 3.1: Настройте отправку метрик

Вариант A: Prometheus (если есть)

k6 run tests/load-checkout.js \
  --out prometheus-remote-write=http://prometheus:9090/api/v1/write \
  --summary-export summary.json

Вариант B: Grafana Cloud (бесплатный tier)

# 1. Зарегистрируйтесь: https://grafana.com/auth/sign-up/create-user
# 2. Получите Prometheus remote write URL и токен
# 3. Запустите тест:
K6_PROMETHEUS_REMOTE_URL=https://prometheus-xxx.grafana.net/api/prom/push \
K6_PROMETHEUS_INSECURE_SKIP_TLS_VERIFY=false \
k6 run tests/load-checkout.js \
  --out experimental-prometheus-rw

Вариант C: InfluxDB + Grafana (Docker)

# docker-compose.yml
version: '3'
services:
  influxdb:
    image: influxdb:2.7
    ports:
      - "8086:8086"
    environment:
      DOCKER_INFLUXDB_INIT_MODE: setup
      DOCKER_INFLUXDB_INIT_USERNAME: admin
      DOCKER_INFLUXDB_INIT_PASSWORD: adminpassword
      DOCKER_INFLUXDB_INIT_ORG: myorg
      DOCKER_INFLUXDB_INIT_BUCKET: k6
 
  grafana:
    image: grafana/grafana:latest
    ports:
      - "3000:3000"
 
# Запуск:
docker-compose up -d
 
# Тест:
k6 run tests/load-checkout.js --out influxdb=http://localhost:8086/k6

Шаг 3.2: Создайте Grafana dashboard

Импортируйте готовый дашборд k6:

  1. Откройте Grafana → Dashboards → Import
  2. ID дашборда: 2587 (k6 Load Testing Results)
  3. Выберите ваш Prometheus/InfluxDB datasource

Или создайте кастомный с ключевыми панелями:

  • http_req_duration{flow="checkout"} (p50, p95, p99)
  • http_req_failed{flow="checkout"} (error rate)
  • http_reqs (throughput RPS)
  • vus (active virtual users)

Чек-лист фазы 3:

  • Метрики k6 отправляются в Prometheus/InfluxDB/Grafana Cloud
  • Grafana dashboard создан и доступен команде
  • Dashboard содержит панели: latency (p95), error rate, RPS, VUs
  • Ссылка на dashboard добавлена в README/Wiki

Milestone 3: Вы видите метрики в реальном времени и можете быстро найти bottleneck!

Фаза 4: Процесс Go/No-Go (неделя 4)

Шаг 4.1: Документируйте критерии релиза

# Критерии Go/No-Go для релизов
 
## Smoke test (обязательно перед каждым PR)
 
- ✅ Go: checks > 95%, http_req_failed < 5%, p95 < 1s
- ⚠️ Review: checks 90-95% (проверить причину)
- ❌ No-Go: checks < 90% или http_req_failed > 10%
 
## Load test (обязательно перед major release)
 
- ✅ Go: p95 в SLO, error rate < 1%, деградация к baseline < 10%
- ⚠️ Review: деградация 10-20% (обсудить с владельцем продукта)
- ❌ No-Go: p95 вне SLO, error rate > 2%, деградация > 20%
 
## Stress test (перед big sale / маркетинговая кампания)
 
- ✅ Go: нашли breakpoint > ожидаемого пика × 1.5
- ⚠️ Review: breakpoint близко к пику (нужен plan масштабирования)
- ❌ No-Go: breakpoint < ожидаемого пика
 
## Ответственные
 
- За запуск тестов: DevOps / QA
- За анализ результатов: Tech Lead / Performance Engineer
- За решение Go/No-Go: Product Owner + Tech Lead

Шаг 4.2: Создайте template отчета

# Load Test Report: Checkout Flow
 
**Дата:** 2024-01-15
**Версия:** v1.3.0
**Тип теста:** Load (ramping-vus, 50 VU, 10 min)
**Dashboard:** https://grafana.yourapp.com/d/k6-checkout
 
## Результаты
 
| Метрика     | Baseline | Current | Diff  | Status    |
| ----------- | -------- | ------- | ----- | --------- |
| p95 latency | 450ms    | 520ms   | +15%  | ⚠️        |
| p99 latency | 850ms    | 980ms   | +15%  | ⚠️        |
| Error rate  | 0.2%     | 0.3%    | +50%  | ✅ (< 1%) |
| Checks      | 98.5%    | 98.1%   | -0.4% | ✅        |
| RPS         | 85       | 82      | -3%   | ✅        |
 
## Анализ
 
**Bottleneck:** Рост latency на 15% к baseline
 
**Причина:**
 
- http_req_waiting вырос с 400ms до 480ms
- CPU сервиса стабильный (40%)
- БД latency выросла с 50ms до 120ms
- Обнаружено: N+1 query в новом endpoint `/api/orders/items`
 
**Триангуляция:**
 
- Grafana: http_req_waiting ↑ 20%
- Traces (Jaeger): span `orders.getItems` = 80% времени
- Logs: "Slow query: SELECT \* FROM order_items WHERE..."
 
## Решение
 
⚠️ **Review рекомендован**
 
**Рекомендация:**
 
- Оптимизировать N+1 query (JOIN вместо loop)
- Или: принять деградацию +15% (укладывается в SLO < 800ms)
 
**Решение владельца продукта:** [Заполнить]
 
**Действия:**
 
- [ ] Создать задачу на оптимизацию БД: JIRA-123
- [ ] Запланировать повторный тест после фикса
- [ ] Обновить baseline если принимаем деградацию

Чек-лист фазы 4:

  • Критерии Go/No-Go задокументированы и согласованы с командой
  • Template отчета создан
  • Процесс понятен: кто запускает, кто анализирует, кто принимает решение
  • Команда знает, где искать результаты (Grafana dashboard, summary.json, отчет)

Milestone 4: У вас есть четкий, прозрачный процесс принятия решений о релизе!

Фаза 5: Roadmap (3-6 месяцев)

Месяц 1-2: Стабилизация

  • Smoke-тест проходит стабильно на всех PR
  • Load-тест запускается перед каждым релизом
  • Baseline обновляется раз в спринт
  • Команда привыкает использовать результаты для Go/No-Go

Месяц 3-4: Расширение покрытия

  • Добавить stress-тест для критических сценариев
  • Добавить soak-тест (4-8 часов) раз в месяц
  • Покрыть тестами top-5 user journeys (не только checkout)
  • Интегрировать трейсы (traceparent) для корреляции с APM

Месяц 5-6: Масштабирование

  • Внедрить k6-operator в Kubernetes (если нужны большие нагрузки)
  • Настроить распределенные тесты (multi-region)
  • Автоматическое сравнение с baseline в CI/CD (fail если > 10%)
  • Канарейка в production (ограниченный RPS под feature flag)

Долгосрочно

  • Каталог паттернов: что сработало, что нет
  • Lessons learned: инциденты, которые предотвратили благодаря k6
  • Метрики ROI: сколько денег/времени сэкономили
  • Обучение новых членов команды (этот курс + внутренние best practices)

Финальный чек-лист проекта

Практика: что сделать до следующего урока

1.Фаза 1: Smoke-тест в CI/CD

  • ✅ Smoke-тест написан (3-5 критических endpoints)
  • ✅ Интегрирован в CI/CD (GitHub Actions / GitLab CI)
  • ✅ Тест падает при нарушении thresholds
  • ✅ Команда понимает, как читать результаты

2.Фаза 2: Load-тест с baseline

  • ✅ Load-тест написан для ключевого user journey
  • ✅ Baseline собран и задокументирован
  • ✅ Скрипт сравнения с baseline работает
  • ✅ Процесс запуска load-теста перед релизом установлен

3.Фаза 3: Observability

  • ✅ Метрики отправляются в Prometheus/InfluxDB/Grafana
  • ✅ Dashboard создан и доступен команде
  • ✅ Ссылка на dashboard в README

4.Фаза 4: Процесс Go/No-Go

  • ✅ Критерии Go/No-Go задокументированы
  • ✅ Template отчета создан
  • ✅ Ответственные назначены
  • ✅ Команда знает процесс принятия решений

5.Фаза 5: Roadmap на 3-6 месяцев

  • ✅ План развития составлен
  • ✅ Задачи добавлены в backlog
  • ✅ Определены метрики успеха (ROI, prevented incidents)

Поздравляем! 🎉

Вы завершили курс "k6: нагрузочное тестирование как система" и внедрили k6 в реальный проект!

Что вы получили:

  • 🎯 Работающий процесс нагрузочного тестирования
  • 📊 Метрики и дашборды для принятия решений
  • 🛡️ Защиту от регрессий производительности
  • 📈 Roadmap для масштабирования

Следующие шаги:

  1. Запустите первый smoke-тест в CI/CD и дождитесь успешного прохождения
  2. Соберите baseline для критического user journey
  3. Поделитесь результатами с командой
  4. Продолжайте улучшать покрытие и автоматизацию

Помните: нагрузочное тестирование — это не разовая активность, а непрерывный процесс. Обновляйте тесты вместе с продуктом, делитесь находками, учитесь на инцидентах.

Удачи в production! May your p95 be ever low and your RPS ever high! 🚀

🎉 Поздравляем с завершением курса!

Поделитесь опытом и получите промокод на бесплатный доступ к любому premium-материалу на выбор

Бесплатный доступ к любому premium-материалу на выбор

🎓

Ваш опыт поможет другим студентам

📧

Промокод придет на email в течение 24 часов

Минимум 50 символов

0/50