Почему нагрузочное тестирование проваливается даже после успешного релиза?
Релиз прошёл гладко. Через три дня — промо-акция, трафик вырастает в 10 раз. Через четыре часа после старта акции база данных встаёт на lock wait timeout, API возвращает 504, и поддержка тонет в тикетах. При этом staging прошёл нагрузочный тест «успешно» — потому что тестировали 50 пользователей, а реальный трафик дал 5000. Такая ситуация повторяется в каждом втором проекте, который к нам приходит. Мы видим одни и те же ошибки: тесты пишутся на один сценарий, не учитываются медленные запросы при cache miss, и полностью игнорируется поведение системы при отказе зависимых сервисов. Нагрузочное тестирование — это не галочка перед релизом. Это процесс нахождения точки, где система начинает деградировать, и понимания, почему именно там. Наши инженеры с 10-летним опытом в performance testing гарантируют, что после работы с нами вы точно будете знать предел своей системы.
k6: от простого скрипта до production-grade теста
k6 — наиболее удобный инструмент для написания нагрузочных тестов в виде кода. JavaScript-синтаксис, встроенные метрики, нативная интеграция с Grafana через k6 cloud или xk6-output-prometheus-remote.
Базовый скрипт решает простую задачу. Реальные проблемы начинаются, когда нужно симулировать сложные пользовательские сценарии.
Пример: тест с авторизацией и зависимыми запросами:
import http from 'k6/http'; import { check, sleep } from 'k6'; import { SharedArray } from 'k6/data'; const users = new SharedArray('users', function () { return JSON.parse(open('./users.json')); }); export const options = { scenarios: { warm_up: { executor: 'ramping-vus', startVUs: 0, stages: [ { duration: '2m', target: 100 }, { duration: '5m', target: 100 }, { duration: '2m', target: 0 }, ], }, spike: { executor: 'ramping-arrival-rate', startRate: 0, timeUnit: '1s', preAllocatedVUs: 500, stages: [ { duration: '30s', target: 500 }, { duration: '1m', target: 500 }, { duration: '30s', target: 0 }, ], startTime: '9m', }, }, thresholds: { http_req_duration: ['p(95)<500', 'p(99)<1500'], http_req_failed: ['rate<0.01'], }, }; export default function () { const user = users[Math.floor(Math.random() * users.length)]; const loginRes = http.post(`${__ENV.BASE_URL}/api/auth/login`, JSON.stringify({ email: user.email, password: user.password, }), { headers: { 'Content-Type': 'application/json' } }); check(loginRes, { 'login 200': (r) => r.status === 200 }); const token = loginRes.json('access_token'); if (!token) return; const headers = { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' }; const catalogRes = http.get(`${__ENV.BASE_URL}/api/catalog?page=1`, { headers }); check(catalogRes, { 'catalog 200': (r) => r.status === 200 }); sleep(Math.random() * 2 + 1); } Несколько важных деталей в этом скрипте. SharedArray загружает данные один раз на все VU, а не копирует в каждый — при 500 VU и файле на 10000 строк разница в памяти ощутима. ramping-arrival-rate симулирует реальную нагрузку по RPS, а не по количеству пользователей — это важно: 100 VU с sleep(1) дадут ~100 RPS, но 100 VU без sleep могут дать тысячи, что не соответствует реальному поведению.
Thresholds — главный инструмент pass/fail. p(95)<500 означает: 95% запросов должны укладываться в 500ms. Если порог не пройден, k6 завершается с кодом 1 — CI-пайплайн может это поймать.
k6 run --out influxdb=http://localhost:8086/k6 script.js — стриминг метрик в реальном времени. В связке с Grafana-дашбордом (ID 2587 из grafana.com) получаем live-картину теста: RPS, latency по перцентилям, количество ошибок, активные VU.
Что выбрать: JMeter, Gatling или k6?
| Инструмент | Язык скриптов | Потребление ресурсов | Масштабирование | Сообщество |
|---|---|---|---|---|
| k6 | JavaScript | Низкое (Go-based) | Встроенное, Kubernetes-native | Активное, open-source |
| JMeter | XML/Java GUI | Высокое (Java-потоки) | Distributed (jmeter-server) | Крупное, корпоративное |
| Gatling | Scala DSL | Среднее (JVM, но эффективнее JMeter) | Distributed (injectors) | Растущее, Java/Scala |
Выбор инструмента часто определяется не техническими характеристиками, а тем, кто пишет тесты: разработчики привыкли к коду (k6/Gatling), QA-команды — к GUI (JMeter). k6 потребляет примерно в 5 раз меньше памяти, чем JMeter при равном количестве виртуальных пользователей, что делает его лучшим выбором для непрерывного тестирования в CI/CD.
Chaos Engineering: сломать контролируемо, чтобы не сломалось неожиданно
Chaos Engineering — это следующий уровень после нагрузочного тестирования. Нагрузка проверяет, что система держит трафик. Chaos проверяет, что система правильно деградирует при отказе компонентов.
Chaos Engineering — наиболее зрелый инструмент для Kubernetes. Устанавливается как CRD и управляется через YAML или Web UI.
Сценарий: что происходит с приложением, когда один из трёх инстансов базы данных перестаёт отвечать?
apiVersion: chaos-mesh.org/v1alpha1 kind: NetworkChaos metadata: name: db-network-delay namespace: production spec: action: delay mode: one selector: namespaces: - production labelSelectors: app: postgresql delay: latency: "200ms" correlation: "25" jitter: "50ms" duration: "10m" scheduler: cron: "@every 1h" Этот манифест добавляет 200ms задержки сети к одному случайному поду с меткой app: postgresql раз в час на 10 минут. Запускается параллельно с нагрузочным тестом — смотрим, как меняется p(99) latency приложения.
PodChaos для проверки устойчивости к перезапускам:
apiVersion: chaos-mesh.org/v1alpha1 kind: PodChaos metadata: name: api-pod-kill spec: action: pod-kill mode: fixed-percent value: "33" selector: namespaces: [production] labelSelectors: app: api-service gracePeriod: 0 duration: "5m" 33% подов api-service убиваются мгновенно (без graceful shutdown) раз в прогоне. Ожидаемый результат: метрика ошибок временно растёт, но Kubernetes восстанавливает поды, и через 30–60 секунд ошибки уходят. Если ошибки не уходят — есть проблема с readiness probe или деплойментом.
Принцип GameDay: Chaos-эксперименты нужно проводить в рабочее время при участии команды, не ночью в одиночку. Цель — не сломать prod, а выработать мышечную память реакции на инциденты.
Что проверять с помощью Chaos Engineering в первую очередь?
- Отказ одной зоны доступности (NetworkChaos на весь namespace одной AZ)
- Нехватка памяти у ключевого сервиса (StressChaos с
memoryWorkers: 4, memoryBytes: "1GB") - Latency к внешнему API (HTTPChaos с задержкой на исходящие запросы)
- Заполнение диска (IOChaos + дополнительный контейнер, пишущий мусор)
Chaos Engineering — не про то, чтобы всё сломать. Это про то, чтобы знать заранее, что именно сломается и в каком порядке. Наши сертифицированные инженеры провели более 50 таких экспериментов на production-проектах.
Как интерпретировать результаты load-теста?
Тест закончился, цифры есть. После завершения мы анализируем три ключевых сценария.
p(50) нормальный, а p(99) — катастрофа. Это говорит о хвосте распределения — запросах, которые попадают на медленный path. Часто это cache miss, полный table scan при определённых параметрах, или GC pause в JVM.
Ошибки connection reset by peer под нагрузкой — исчерпан лимит соединений. Смотреть на ulimit -n на сервере, SOMAXCONN, keepalive настройки в Nginx/HAProxy, pool size в connection pool приложения.
Нагрузка линейно растёт, latency стабильна до 60% CPU, потом резко растёт — классический признак CPU-bound узкого места. Профилировщик (async-profiler для JVM, py-spy для Python) покажет, что именно занимает CPU. Такая диагностика позволяет снизить стоимость облачной инфраструктуры в среднем в 2 раза у наших клиентов.
Что входит в работу по нагрузочному тестированию?
- Анализ архитектуры и бизнес-сценариев (выявляем критичные экшенпоинты)
- Написание и отладка скриптов (k6/JMeter/Gatling)
- Smoke-тест, load-тест, spike-тест, soak-тест
- Проведение Chaos-экспериментов (если требуется)
- Детальный отчёт с графиками: latency по перцентилям, RPS, ошибки, bottleneck-компоненты
- Рекомендации по оптимизации (конфиги, инфраструктура, код)
- Интеграция тестов в ваш CI/CD (GitLab CI / GitHub Actions)
- Пост-релизная поддержка: выполнение замеров на проде
Сроки и формат работы
| Тип тестирования | Масштаб | Срок |
|---|---|---|
| Smoke + load тест | 1–3 сценария, один сервис | 1–2 дня |
| Полное нагрузочное тестирование | 10+ сценариев, микросервисы | 1–2 недели |
| Chaos Engineering | Kubernetes, 5–10 экспериментов | 1–3 недели |
| Continuous performance testing | Интеграция в CI/CD | 2–4 недели |
Стоимость рассчитывается индивидуально после анализа архитектуры и целей тестирования. Оценим ваш проект — свяжитесь с нами для консультации. Закажите нагрузочное тестирование под ключ и получите гарантированное выявление узких мест до того, как их найдёт ваш прод.







