Методология: цели, SLA/SLO и выбор типов тестов
📋 Вы точно готовы?
Перед началом этого урока убедитесь:
Обязательные знания:
- ✅ Прочитали Урок 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 на реалистичном RPS | Load (плато 30–60 мин) | p95/p99, ошибку < 0.5%, ресурсные метрики |
| Подготовка к big sale | Где потолок? | Stress / Breakpoint (рост до отказа) | RPS/конкуренция при деградации, auto-scaling триггеры |
| Проверяем деградации со временем | Утечки/ throttling | Soak (часы) | Рост латентности, утечки памяти, GC паузы |
| Хрупкость к резким всплескам | Поведение под sudden spike | Spike (резкий скачок) | Лимиты очередей, circuit breaker, warmup |
Тип теста задает требования к стенду: для stress/breakpoint нужен стенд, аналогичный прод (или прод с feature flag), иначе выводы неполные.
Цель → Тип → Executor → Критерий
| Цель | Тип | Executor | Критерий |
|---|---|---|---|
| Найти потолок | Stress/Break | ramping-arrival-rate | Точка излома p99/error rate при RPS |
| Рекламный всплеск | Spike | constant-vus с резким скачком | Время восстановления < 30s, ошибки <1% |
| Релиз без регрессии | Load | ramping-vus (warmup → плато) | p95/p99 в SLO, error rate <0.5% |
| Долгая стабильность | Soak | constant-arrival-rate (часы) | Нет роста памяти/GC, p95 не ползет |
| Быстрая валидация фичи | Smoke | shared-iterations | Checks >97%, санитарка стенда |
SLA, SLO и thresholds
- Формулируем SLO на языке пользователя и денег: «p95
POST /checkout< 600ms при 300 rps, error rate < 0.5%, аптайм 99.9%». - Переносим в 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"],
},
};- Оставляем error budget: если SLO 99.5%, бюджет ошибки = 0.5%. На него завязываем частоту регрессий и риск выката.
- Сразу описываем критерий решения: «если 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 вырос — отклоняем релиз.
Требования к стендам:
| Тип теста | Стенд | Данные | Мониторинг |
|---|---|---|---|
| Smoke | Dev/Stage | Synthetic | Basic metrics |
| Load | Stage/Pre-prod | Scaled production | Full observability |
| Stress/Soak | Pre-prod/Prod* | Production-like | APM + profiling |
| Spike | Pre-prod | Realistic mix | Business + tech metrics |
*Prod — только под флагами и в «тихие» окна.
📊 Реальный кейс: E-commerce миграция на микросервисы
Контекст: Крупный интернет-магазин одежды (15M+ пользователей/месяц) мигрировал монолит на микросервисную архитектуру за 6 месяцев до сезона распродаж.
Проблема: За 3 недели до Black Friday команда обнаружила, что новая архитектура не тестировалась под реальной нагрузкой.
Что сделали:
-
Установили базовый SLO на основе продовых метрик:
p95 /checkout< 500ms (было 420ms на монолите)p99 /search< 800ms (было 650ms)- Error rate < 0.3% на всех критичных endpoints
- Availability 99.95% (бюджет: ~22 минуты простоя/месяц)
-
Запустили stress-тест с профилем Black Friday (прогноз: 3x обычной нагрузки):
- 🔴 Провал на 2.1x нагрузке: p95 checkout вырос до 2.8s
- 🔴 Error rate достиг 8% (лимит circuit breaker на новом API Gateway)
- 🔴 Postgres connection pool исчерпался (100 коннектов vs 250 на монолите)
-
Триангуляция через k6 + APM:
- k6 показал: деградация начинается с
/api/inventory/check - Jaeger traces: новый микросервис Inventory делал 15 запросов в БД вместо 1 (N+1 query)
- Grafana (Postgres):
waiting connectionsдостиг максимума за 12 минут нагрузки
- k6 показал: деградация начинается с
-
Решение за 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%)
-
Повторный 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 минут
Связанные уроки:
- Урок 05: Первый smoke-тест — применим SLO в thresholds
- Урок 09: Метрики и thresholds — deep dive в метрики