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

Первый smoke-тест и минимальный набор опций

15 минут

Установка в двух вариантах

  • Brew (macOS): brew install k6
  • Docker (универсально): docker run --rm -i grafana/k6 run - <test.js

Проверка: k6 version (ожидаем v0.4x.x).

Первый smoke: жив ли ShopStack?

// smoke.js
import http from "k6/http";
import { check, sleep } from "k6";
 
export const options = {
  vus: 3, // shortcut для простых тестов
  duration: "2m",
  thresholds: {
    http_req_failed: ["rate<0.02"],
    http_req_duration: ["p(95)<800"],
  },
};
 
export default function () {
  const res = http.get(`${__ENV.BASE_URL}/api/catalog/popular`);
 
  check(res, {
    "status is 200": (r) => r.status === 200,
    "has products": (r) => (r.json("items") || []).length > 0,
  });
 
  sleep(1); // think time
}

Запуск:

BASE_URL=https://stage.shopstack.io k6 run smoke.js

Разбор вывода k6: что означают метрики

После запуска k6 выводит два типа данных: прогресс в реальном времени и финальный summary.

Метрики в реальном времени (progress bar)

running (0m15.0s), 3/3 VUs, 45 complete and 0 interrupted iterations
default ✓ [======================================] 3 VUs  2m0s
  • 3/3 VUs — активных виртуальных пользователей (все запущены)
  • 45 complete — завершенные итерации (вызовы export default function)
  • 0 interrupted — прерванных итераций (должно быть 0)

Финальный summary: ключевые метрики

checks.........................: 98.50% ✓ 197    ✗ 3
data_received..................: 2.4 MB 20 kB/s
data_sent......................: 28 kB  230 B/s
http_req_blocked...............: avg=1.2ms   min=0s    med=0s     max=234ms  p(90)=0s     p(95)=1ms
http_req_connecting............: avg=580µs   min=0s    med=0s     max=115ms  p(90)=0s     p(95)=0s
http_req_duration..............: avg=142ms   min=98ms  med=135ms  max=456ms  p(90)=189ms  p(95)=234ms
  { expected_response:true }...: avg=142ms   min=98ms  med=135ms  max=456ms  p(90)=189ms  p(95)=234ms
http_req_failed................: 0.50%  ✓ 1      ✗ 199
http_req_receiving.............: avg=1.2ms   min=200µs med=800µs  max=12ms   p(90)=2.4ms  p(95)=3.1ms
http_req_sending...............: avg=89µs    min=45µs  med=78µs   max=456µs  p(90)=145µs  p(95)=189µs
http_req_tls_handshaking.......: avg=0s      min=0s    med=0s     max=0s     p(90)=0s     p(95)=0s
http_req_waiting...............: avg=140ms   min=97ms  med=134ms  max=454ms  p(90)=187ms  p(95)=232ms
http_reqs......................: 200    1.666667/s
iteration_duration.............: avg=1.14s   min=1.09s med=1.13s  max=1.46s  p(90)=1.19s  p(95)=1.23s
iterations.....................: 200    1.666667/s
vus............................: 3      min=3     max=3
vus_max........................: 3      min=3     max=3

Что означает каждая метрика

Checks (проверки корректности):

  • checks: 98.50% ✓ 197 ✗ 3 — 98.5% проверок прошли успешно (197 из 200)
  • ❌ Если checks < 97% — скорее всего API возвращает некорректные данные
  • ✅ Цель: checks > 97% для smoke-тестов

HTTP Request Timing (breakdown времени запроса):

  • http_req_durationГЛАВНАЯ МЕТРИКА: полное время запроса (от отправки до получения)
    • p(95)=234ms — 95% запросов быстрее 234ms (сравниваем с SLO!)
    • p(99) — 99-й перцентиль (для критичных endpoint'ов)
  • http_req_waiting — время ожидания ответа сервера (без сети)
    • Рост waiting при стабильном connecting → проблема на сервере/БД
  • http_req_blocked — время ожидания свободного сокета из пула
    • Большое значение → увеличьте batch или connection pool
  • http_req_connecting — время установки TCP-соединения
    • Рост → проблемы с сетью или DNS
  • http_req_tls_handshaking — время TLS handshake (для HTTPS)
  • http_req_sending — время отправки данных (обычно микросекунды)
  • http_req_receiving — время получения ответа (зависит от размера payload)

Ошибки и throughput:

  • http_req_failed: 0.50% ✓ 1 ✗ 199 — 0.5% запросов завершились ошибкой
    • ✅ Норма: < 0.5% для smoke, < 0.1% для production load
    • ❌ > 1% → расследуем (5xx? timeout? сеть?)
  • http_reqs: 200 (1.67/s) — всего запросов и RPS (requests per second)
  • data_received/sent — объем переданных данных

VU и итерации:

  • iterations: 200 (1.67/s) — сколько раз выполнилась export default function
  • iteration_duration — время полной итерации (включая sleep)
  • vus: 3 — количество активных виртуальных пользователей

Как читать результат smoke-теста:

  1. Проверьте checks ≥ 97% (API возвращает корректные данные)
  2. Проверьте http_req_failed < 1% (нет массовых ошибок)
  3. Сравните http_req_duration p(95) с вашим SLO (например, < 800ms)
  4. Если все зеленое — API жив, можно двигаться к load-тестам

Как интерпретировать проблемы

СимптомВозможная причинаЧто проверить
checks < 95%API возвращает некорректные данныеПроверьте response body, статус коды
http_req_failed > 1%Сервис отдает 5xx или timeoutЛоги сервиса, мониторинг CPU/RAM
http_req_waiting растетМедленная обработка на сервереБД queries, внешние API, CPU сервиса
http_req_connecting растетПроблемы с сетью/DNSПроверьте DNS, сетевую латентность
http_req_blocked > 10msМало сокетов в пулеУвеличьте connection pool или используйте http.batch()

Пример интерпретации

✓ checks: 100%           → Все проверки прошли
✓ http_req_failed: 0%    → Нет ошибок
✓ p(95): 234ms < 800ms   → Укладываемся в SLO
✅ Smoke test PASSED: API здоров, готов к нагрузке
✗ checks: 87%            → 13% некорректных ответов
✗ http_req_failed: 3%    → 3% запросов с ошибками
✗ p(95): 1.2s > 800ms    → Превышаем SLO
❌ Smoke test FAILED: расследуем проблемы перед load-тестом

Переопределение опций из CLI

# Больше VU и короче тест
k6 run smoke.js --vus 5 --duration 1m
 
# Тише вывод
k6 run smoke.js --quiet
 
# Экспорт summary в JSON для CI
k6 run smoke.js --summary-export summary.json

Внутри теста одиночные опции (vus, duration, stages) — shortcuts. Для кастомных executors и миксов переходим на options.scenarios (следующий урок), но для smoke они удобны и валидны.

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

1.Установите k6 и напишите свой первый smoke-тест

  • Установите k6 (brew install k6 или через Docker)
  • Создайте файл smoke.js с простым GET-запросом к вашему API (или к https://httpbin.org/get)
  • Добавьте check для проверки status === 200
  • Запустите тест: k6 run smoke.js
  • Найдите в выводе метрики: checks, http_req_duration (p95), http_req_failed

2.Проанализируйте результаты

  • Проверьте checks >= 97% (если меньше — разберитесь, почему API возвращает ошибки)
  • Сравните p95 http_req_duration с вашим SLO (если нет SLO — запишите текущее значение как baseline)
  • Проверьте http_req_failed < 1%
  • Если все зелёное — API здоров, переходите к следующему уроку

3.Экспериментируйте с опциями

  • Запустите с --vus 10 --duration 30s и сравните результаты
  • Добавьте threshold в options: http_req_duration: ['p(95)<1000']
  • Запустите тест — k6 должен вернуть exit code 0 если threshold прошел
  • Экспортируйте summary: k6 run smoke.js --summary-export summary.json

📊 Реальный кейс: SaaS-платформа и "быстрый smoke перед деплоем"

Контекст: SaaS для управления проектами (15K активных компаний), деплоят 3-5 раз в день через CI/CD.

Проблема: В пятницу вечером деплой "небольшого рефакторинга" сломал авторизацию для 40% пользователей. Инцидент длился 23 минуты, пока не откатили.

Что пошло не так:

  1. Рефакторинг JWT-валидации:

    • Разработчик переписал функцию validateToken() для "упрощения кода"
    • Unit-тесты прошли ✅ (тестировали только happy path)
    • E2E тесты прошли ✅ (использовали свежие токены, TTL > 24h)
  2. Production incident:

    • Пользователи с токенами старше 12 часов получали 401 Unauthorized
    • Причина: новый код не поддерживал старый формат iss claim (issuer)
    • 40% активных сессий использовали токены со старым форматом
  3. Потери:

    • 23 минуты downtime для 6K пользователей
    • 847 support tickets за 2 часа
    • Репутационный ущерб: trending в Twitter #ProjectManagerDown
    • SLA breach: monthly uptime 99.95% → 99.89% (потеряли error budget на 3 недели)

Root cause: Smoke-тест в CI проверял только /health, не проверял реальные user journeys.

Что сделали после инцидента:

  1. Добавили comprehensive smoke-тест:

    // smoke-auth-flows.js
    export const options = {
      vus: 5,
      duration: "3m",
      thresholds: {
        checks: [{ threshold: "rate>0.99", abortOnFail: true }],
        http_req_failed: ["rate<0.01"],
        http_req_duration: ["p(95)<500"],
      },
    };
     
    export default function () {
      // Scenario 1: Fresh token (created < 1h ago)
      const freshToken = getTokenFromPool("fresh"); // TTL 30min
      checkAuth(freshToken, "Fresh token auth");
     
      // Scenario 2: Old token (created 12h ago, refreshed)
      const oldToken = getTokenFromPool("legacy"); // Old format, still valid
      checkAuth(oldToken, "Legacy token auth");
     
      // Scenario 3: Expired token (should fail gracefully)
      const expiredToken = getTokenFromPool("expired");
      checkAuthExpected401(expiredToken, "Expired token rejection");
     
      sleep(randomIntBetween(1, 3));
    }
     
    function checkAuth(token, label) {
      const res = http.get(`${BASE_URL}/api/projects`, {
        headers: { Authorization: `Bearer ${token}` },
      });
      check(res, {
        [`${label}: status 200`]: (r) => r.status === 200,
        [`${label}: has projects`]: (r) => r.json("projects").length > 0,
      });
    }
  2. Интегрировали в CI/CD pipeline:

    • Smoke запускается после деплоя на staging, перед production
    • Использует realistic test data: токены из pool (fresh + legacy + expired)
    • Exit code != 0 → автоматический rollback
  3. Добавили проверку на breaking changes:

    • Static analysis: CodeQL проверяет изменения в auth-модулях
    • Mandatory review: любой PR, касающийся JWT/auth → 2 approvals от senior

Результат через 6 месяцев:

  • 42 деплоя заблокированы smoke-тестом до production (regression caught early)
  • Zero auth-related инцидентов с момента внедрения
  • Время от коммита до production: 12 минут (было 8 минут, +4 минуты на smoke — acceptable)
  • Monthly uptime: стабильно 99.98%

ROI:

  • Стоимость внедрения: 16 часов (smoke-тест + CI интеграция + test data pool) = $4K
  • Избежали: 42 потенциальных инцидента × $15K средний ущерб = $630K
  • ROI: 157x

War story: После этого инцидента команда ввела термин "Friday Night Deploy Syndrome". Теперь все критичные изменения (auth, payments, billing) проходят extended smoke с legacy compatibility checks. Один разработчик сказал: "Раньше я боялся деплоить в пятницу. Теперь я боюсь деплоить без smoke-теста".

Урок: Smoke-тест — это не про "проверить, что API жив", а про проверить, что критичные user journeys работают для всех типов пользователей/токенов/данных. Включайте в smoke: - Legacy compatibility (старые форматы данных) - Edge cases (expired tokens, empty responses) - Realistic data (не только свежесозданные объекты)

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

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

Установка и базовые команды:

  • k6 установлен и работает (k6 version выдает версию)
  • Умеете запускать тест: k6 run smoke.js
  • Умеете передавать параметры через env: BASE_URL=... k6 run smoke.js
  • Умеете переопределять опции из CLI: --vus, --duration, --quiet

Написание smoke-теста:

  • Создали файл smoke.js с базовым HTTP GET запросом
  • Добавили check() для проверки status code и response body
  • Настроили options с vus, duration, thresholds
  • Добавили sleep() для think time

Чтение результатов:

  • Понимаете, что такое checks и почему должно быть ≥ 97%
  • Знаете, где найти p95/p99 latency в выводе k6
  • Умеете интерпретировать http_req_failed (должно быть < 1%)
  • Понимаете breakdown времени запроса: blocked/connecting/sending/waiting/receiving
  • Умеете отличить проблему на сервере от проблемы в сети

Troubleshooting:

  • Знаете, когда смотреть на http_req_waiting (проблемы сервера)
  • Знаете, когда смотреть на http_req_connecting (проблемы сети/DNS)
  • Умеете использовать --summary-export для сохранения результатов

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

  • Запустили smoke-тест на вашем API (или httpbin.org)
  • Проанализировали результаты по чек-листу выше
  • Зафиксировали baseline: записали p95, error rate, checks
  • Тест прошел thresholds (exit code 0)

Если чек-лист пройден — переходите к уроку 06: научимся управлять нагрузкой через scenarios и executors.

Первый smoke-тест и минимальный набор опций — k6: нагрузочное тестирование как система — Potapov.me