Skip to main content
Back to course
k6: нагрузочное тестирование как система
2 / 1712%

Методология: цели, SLA/SLO и выбор типов тестов

30 минут

📋 Вы точно готовы?

Перед началом этого урока убедитесь:

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

  • ✅ Прочитали Урок 01: Введение
  • ✅ Понимаете базовые термины: latency (p95/p99), RPS, error rate
  • ✅ Знакомы с концепцией SLA/SLO (хотя бы на уровне "что это")

Опционально:

  • Опыт работы с production метриками (Grafana, Prometheus, Datadog)
  • Понимание бизнес-контекста вашего приложения

Estimated time:

  • Чтение теории: 15 минут
  • Практические упражнения: 15 минут
  • Итого: 30 минут

Этот урок — фундамент всего курса. Не пропускайте его, даже если кажется "слишком теоретическим". Без стратегии все последующие тесты будут бесполезны.

Когда нагрузочное тестирование обязано быть

  • Пиковые события: big sale, маркетинговые кампании, product launch.
  • Риски инфраструктуры: миграция БД / брокера, смена API Gateway, переход на новый cloud region.
  • Регрессии производительности в проде: алерты p95/p99, рост error rate, жалобы клиентов.
  • Decision making: нужно число для capacity планирования и бюджетов (сколько машин/ГБ/соединений держать под пик).

Процесс нагрузочного тестирования как система

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

Карта тестов под бизнес-цели

СобытиеЦельТип тестаЧто фиксируем
Каждая фича / коммитНе ломаем базовые флоуSmoke (1–5 VU, минуты)Проход ключевых API, валидные ответы
Перед релизомПроверить SLO на реалистичном RPSLoad (плато 30–60 мин)p95/p99, ошибку < 0.5%, ресурсные метрики
Подготовка к big saleГде потолок?Stress / Breakpoint (рост до отказа)RPS/конкуренция при деградации, auto-scaling триггеры
Проверяем деградации со временемУтечки/ throttlingSoak (часы)Рост латентности, утечки памяти, GC паузы
Хрупкость к резким всплескамПоведение под sudden spikeSpike (резкий скачок)Лимиты очередей, circuit breaker, warmup

Тип теста задает требования к стенду: для stress/breakpoint нужен стенд, аналогичный прод (или прод с feature flag), иначе выводы неполные.

Цель → Тип → Executor → Критерий

ЦельТипExecutorКритерий
Найти потолокStress/Breakramping-arrival-rateТочка излома p99/error rate при RPS
Рекламный всплескSpikeconstant-vus с резким скачкомВремя восстановления < 30s, ошибки <1%
Релиз без регрессииLoadramping-vus (warmup → плато)p95/p99 в SLO, error rate <0.5%
Долгая стабильностьSoakconstant-arrival-rate (часы)Нет роста памяти/GC, p95 не ползет
Быстрая валидация фичиSmokeshared-iterationsChecks >97%, санитарка стенда

SLA, SLO и thresholds

  1. Формулируем SLO на языке пользователя и денег: «p95 POST /checkout < 600ms при 300 rps, error rate < 0.5%, аптайм 99.9%».
  2. Переносим в thresholds. Пример:
export const options = {
  scenarios: {
    checkout: {
      executor: "constant-arrival-rate",
      rate: 300, // rps
      timeUnit: "1s",
      duration: "30m",
      preAllocatedVUs: 50,
      maxVUs: 200,
    },
  },
  thresholds: {
    http_req_duration: ["p(95)<600", "p(99)<1000"],
    http_req_failed: ["rate<0.005"],
    "checks{flow:checkout}": ["rate>0.97"],
  },
};
  1. Оставляем error budget: если SLO 99.5%, бюджет ошибки = 0.5%. На него завязываем частоту регрессий и риск выката.
  2. Сразу описываем критерий решения: «если p95 > 600ms или error rate > 1% — релиз стоп, расследуем; если p95 в бюджете, но медленнее прошлой версии >10% — создаем задачу, релиз по решению владельца».

Кейс: ShopStack готовится к Black Friday

Исходные данные:

  • p95 /checkout = 480ms при 250 rps (Grafana, прод).
  • Прогноз BFCM: 300 rps (+20%).
  • Требование: конверсия не падает → p95 < 600ms, ошибки <0.5%.

Расчет thresholds:

  • Текущий p95 480ms + 25% запас → SLO 600ms.
  • Error budget при 99.5% = 0.5%.
  • Checks на критичные операции: 98% успехов.

Go/No-Go:

  • ✅ Go: p95 ≤ 600ms и errors ≤ 0.5%.
  • ⚠️ Review: p95 ≤ 650ms (+8%) или errors ≤ 1.0%.
  • ❌ No-Go: p95 > 650ms или errors > 1.0%.
thresholds: {
  "http_req_duration{flow:checkout}": ["p(95)<600", "p(99)<900"],
  http_req_failed: ["rate<0.005"],
  "checks{flow:checkout}": [{ threshold: "rate>0.98", abortOnFail: true }],
}

Baseline и сравнение релизов

  • Baseline = эталонный результат на стабильном стенде: сценарии + версии сервисов + конфиг окружения + дата сетапа.
  • Сравниваем всегда с baseline, а не “вчера казалось быстрее”. Храним summary (JSON) + ссылку на дашборд.
  • Готовый шаблон метрик для baseline:
    • p95/p99 по ключевым флоу
    • error rate
    • CPU/RAM, connection pool, queue depth
    • зависимые сервисы (БД, кеш, внешние API)
  • Сравнение релизов: k6 run ... --summary-export → кладем в артефакты CI → строим diff (p95 +%/-, error rate +%/-, ресурсные метрики).

Процесс в спринте

  • Каждый PR: smoke (1–3 VU, 2–5 минут) в CI, fail по thresholds.
  • Раз в неделю: load (реалистичный RPS) на stage, отчет в канал #perf с выводами.
  • Перед крупным релизом/акцией: stress/spike + soak на pre-prod или prod behind flag, решение Go/No-Go по заранее согласованным SLO.
  • Ретроспектива: обновляем baseline, что улучшилось/ухудшилось, куда вкладывать следующий спринт (оптимизации БД? кэш? лимиты в брокере?).

Error budget как инструмент

  • Monthly SLO 99.5% → бюджет 0.5% ≈ 21.9 часа простоя в месяц.
  • 50% бюджета (осталось >10.9ч): можно рисковать, запускать стресс.

  • 25–50%: только критические фиксы, аккуратные тесты.
  • <25%: стоп изменений, нагрузка минимальна.

Пример: инцидент съел 4 часа (18%). Осталось 17.9ч. Можно load-тест с риском 1ч, но не stress на 5+ часов.

Что измерять в resilience-тестах

  • Stress/Breakpoint: p99/error rate при росте RPS, saturation CPU/RAM/DB, глубина очередей.
  • Spike: время восстановления p95/p99 после резкого скачка, устойчивость очередей и кэша.
  • Soak: утечки памяти, рост GC-пауз, деградация кеш-хитов спустя часы нагрузки.

Метрики инфраструктуры (ShopStack)

  • Postgres: connections_waiting, deadlocks, slow_queries, index hit ratio.
  • Redis: memory_fragmentation_ratio, evicted_keys.
  • API Gateway: rate_limit_hits, circuit_breaker_transitions.
  • Kubernetes: pod_restarts, HPA_scaling_events, container_memory_working_set_bytes, tcp_retransmits.
  • Microservices: thread_pool_queue_size, gc_pause, connection_pool_exhaustion.

Пример автоматического сравнения с baseline

// baseline-comparison.js
export function handleSummary(data) {
  const baseline = {
    "http_req_duration{flow:checkout}": { p95: 450, p99: 800 },
    http_req_failed: { rate: 0.002 },
    "checks{flow:checkout}": { rate: 0.992 },
  };
 
  const current = data.metrics;
  const diff = {};
 
  Object.keys(baseline).forEach((metric) => {
    if (!current[metric]) return;
    const base = baseline[metric];
    const cur = current[metric].values;
    const baseVal = base.p95 ?? base.rate;
    const curVal = cur["p(95)"] ?? cur.rate;
    diff[metric] = {
      baseline: baseVal,
      current: curVal,
      diffPercent: ((curVal - baseVal) / baseVal) * 100,
    };
  });
 
  return { stdout: JSON.stringify(diff, null, 2) };
}

Выводите diff в CI: если p95 деградировал >10% или error rate вырос — отклоняем релиз.

Требования к стендам:

Тип тестаСтендДанныеМониторинг
SmokeDev/StageSyntheticBasic metrics
LoadStage/Pre-prodScaled productionFull observability
Stress/SoakPre-prod/Prod*Production-likeAPM + profiling
SpikePre-prodRealistic mixBusiness + tech metrics

*Prod — только под флагами и в «тихие» окна.

📊 Реальный кейс: E-commerce миграция на микросервисы

Контекст: Крупный интернет-магазин одежды (15M+ пользователей/месяц) мигрировал монолит на микросервисную архитектуру за 6 месяцев до сезона распродаж.

Проблема: За 3 недели до Black Friday команда обнаружила, что новая архитектура не тестировалась под реальной нагрузкой.

Что сделали:

  1. Установили базовый SLO на основе продовых метрик:

    • p95 /checkout < 500ms (было 420ms на монолите)
    • p99 /search < 800ms (было 650ms)
    • Error rate < 0.3% на всех критичных endpoints
    • Availability 99.95% (бюджет: ~22 минуты простоя/месяц)
  2. Запустили stress-тест с профилем Black Friday (прогноз: 3x обычной нагрузки):

    • 🔴 Провал на 2.1x нагрузке: p95 checkout вырос до 2.8s
    • 🔴 Error rate достиг 8% (лимит circuit breaker на новом API Gateway)
    • 🔴 Postgres connection pool исчерпался (100 коннектов vs 250 на монолите)
  3. Триангуляция через k6 + APM:

    • k6 показал: деградация начинается с /api/inventory/check
    • Jaeger traces: новый микросервис Inventory делал 15 запросов в БД вместо 1 (N+1 query)
    • Grafana (Postgres): waiting connections достиг максимума за 12 минут нагрузки
  4. Решение за 10 дней:

    • Оптимизировали запросы: batch query вместо N+1 → латентность упала с 280ms до 45ms
    • Подняли connection pool до 300 + добавили PgBouncer
    • Настроили rate limiting на API Gateway: burst 500 req/s с graceful degradation
    • Добавили Redis-кеш для inventory hot items (hit rate 78%)
  5. Повторный stress-тест:

    • ✅ Выдержали 3.5x нагрузку (запас 17% сверх прогноза)
    • ✅ p95 checkout = 480ms, p99 = 720ms
    • ✅ Error rate = 0.18%

Результат:

  • Black Friday прошел без инцидентов: peak 12.5K RPS, p95 checkout 510ms
  • Избежали потенциального downtime: по оценкам, 2-4 часа простоя = $2.8M убытков
  • ROI от нагрузочного тестирования: ~$250K (стоимость работ) vs $2.8M (риск) = 11x ROI

War story: Инженер, который обнаружил N+1 query через k6, получил bonus за спасение Black Friday. CEO на all-hands сказал: «Мы чуть не потеряли $3M, потому что никто не запустил k6 раньше. Теперь load testing — обязательный этап перед каждым major release».

Урок: Нагрузочное тестирование — это не про «посмотреть, что сломается», а про раннее обнаружение проблем с конкретной ценой риска. Формулируйте SLO через деньги: сколько стоит 1% error rate? Сколько теряем при +200ms латентности?

✅ Чек-лист завершения урока

После этого урока вы должны уметь:

Стратегия тестирования:

  • Выбрать правильный тип теста под бизнес-цель (Smoke/Load/Stress/Soak/Spike)
  • Сопоставить тип теста с подходящим executor'ом
  • Определить требования к стенду для каждого типа теста

SLA/SLO и метрики:

  • Сформулировать SLO на языке метрик (p95 < Xms, error rate < Y%)
  • Рассчитать error budget для вашего SLO
  • Перевести SLO в k6 thresholds с abortOnFail для критичных метрик

Baseline и процесс:

  • Создать baseline: зафиксировать версию, конфиг стенда, результаты
  • Понять процесс Go/No-Go: когда релизим, когда останавливаем, когда обсуждаем
  • Определить критерии сравнения с baseline (например, деградация >10% = No-Go)

Практическое задание:

  • Для вашего проекта: определите 1 критический user journey
  • Сформулируйте SLO для этого journey (на основе текущих метрик или целевых)
  • Выберите тип теста для проверки SLO перед релизом
  • Запишите критерии Go/No-Go в формате: ✅ Go / ⚠️ Review / ❌ No-Go

Если чек-лист пройден — вы готовы к уроку 03, где настроим стенды и соберем первый baseline.

🎯 Что дальше

Теперь, когда вы понимаете стратегию и умеете формулировать SLO, переходите к:

Следующий урок: Урок 03: Стенды, безопасность и базовая линия

Что вы изучите:

  • Как выбрать правильный стенд для каждого типа теста
  • Почему dev/stage/pre-prod НЕ эквивалентны для нагрузочного тестирования
  • Как создать baseline и не попасть в ловушку "нерепрезентативных данных"
  • Реальный кейс: финтех-стартап и катастрофа с тестом на production

Estimated time: 20 минут

Связанные уроки:

Методология: цели, SLA/SLO и выбор типов тестов — k6: нагрузочное тестирование как система — Potapov.me