Чому типовий load-тест не знаходить проблем?
Реліз пройшов гладко. Через три дні — промо-акція, трафік зростає в 10 разів. Через чотири години після старту акції база даних стає на lock wait timeout, API повертає 504, і підтримка тоне в тікетах. При цьому staging пройшов навантажувальний тест «успішно» — тому що тестували 50 користувачів, а реальний трафік дав 5000. Така ситуація повторюється в кожному другому проєкті, який до нас приходить. Ми бачимо одні й ті ж помилки: тести пишуться на один сценарій, не враховуються повільні запити при cache miss, і повністю ігнорується поведінка системи при відмові залежних сервісів. Навантажувальне тестування — це не галочка перед релізом. Це процес знаходження точки, де система починає деградувати, і розуміння, чому саме там. Наші інженери з 10-річним досвідом у performance testing, а команда — з 5 років на ринку DevOps, гарантують, що після роботи з нами ви точно знатимете межу своєї системи.
k6: від простого скрипта до production-grade тесту
k6 — найзручніший інструмент для написання навантажувальних тестів у вигляді коду. JavaScript-синтаксис, вбудовані метрики, нативна інтеграція з Grafana через k6 cloud або xk6-output-prometheus-remote. Він споживає приблизно в 5 разів менше пам’яті, ніж JMeter, що робить його ідеальним для CI/CD.
Базовий скрипт вирішує просту задачу. Реальні проблеми починаються, коли потрібно симулювати складні користувацькі сценарії.
Приклад: тест з авторизацією та залежними запитами:
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 необхідний після load-тесту?
Chaos Engineering — це наступний рівень після навантажувального тестування. Навантаження перевіряє, що система тримає трафік. Chaos перевіряє, що система правильно деградує при відмові компонентів.
«Chaos Engineering — це дисципліна експериментування над розподіленою системою, щоб виявити слабкі місця до того, як вони спричинять збій» (Принципи Chaos Engineering).
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-проєктах. Середній час відновлення після інциденту скорочується на 30% завдяки цим тестам.
Як інтерпретувати результати 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)
- Пост-релізна підтримка: виконання замірів на проді
- Навчання команди роботі з результатами та сценаріями
Додатково ми надаємо документацію з описом сценаріїв, інструкції для self-service тестування та доступ до дашбордів моніторингу на час контракту.
Терміни та формат роботи
| Тип тестування | Масштаб | Термін |
|---|---|---|
| Smoke + load тест | 1–3 сценарії, один сервіс | 1–2 дні |
| Повне навантажувальне тестування | 10+ сценаріїв, мікросервіси | 1–2 тижні |
| Chaos Engineering | Kubernetes, 5–10 експериментів | 1–3 тижні |
| Continuous performance testing | Інтеграція в CI/CD | 2–4 тижні |
Вартість розраховується індивідуально після аналізу архітектури та цілей тестування. Оцінимо ваш проєкт — зв’яжіться з нами для консультації. Замовте навантажувальне тестування під ключ і отримайте гарантоване виявлення вузьких місць до того, як їх знайде ваш прод. Пишіть нам — ми підготуємо пропозицію протягом 24 годин.







