Почему вашему приложению нужно тестирование производительности (и почему k6 — ваш новый лучший друг)
Каждый разработчик боится этого момента: ваше приложение запускается, пользователи заходят на сайт, и вдруг всё начинает работать медленно, как ленивец в воскресенье. Запросы к базе данных, которые казались молниеносными в локальной среде, начинают выполняться с задержкой. Ответы API, которые выполнялись за миллисекунды, вдруг занимают секунды. Ваш идеально написанный код превращается в кошмар производительности в продакшене.
Это не обязательно должна быть ваша история.
Тестирование производительности — это не роскошь, это необходимость. И благодаря k6, инструменту для тестирования нагрузки с открытым исходным кодом, созданному для разработчиков и инженеров по обеспечению качества, обнаружение проблем с производительностью до их попадания в продакшен стало как никогда доступным. Самое приятное то, что вам не нужно быть экспертом по тестированию производительности, чтобы начать. k6 предоставляет тестирование производительности каждому разработчику, позволяя писать тесты так же естественно, как вы пишете код приложения.
В этом руководстве мы рассмотрим, как автоматизировать тестирование производительности с помощью k6, начиная с написания первого теста и заканчивая его беспроблемной интеграцией в ваш конвейер CI/CD. Мы рассмотрим практические сценарии, реальные примеры кода и стратегии, которые команды фактически используют в продакшене.
Понимание ландшафта тестирования производительности
Прежде чем углубляться в k6, давайте разберёмся, что мы на самом деле тестируем. Тестирование производительности бывает нескольких видов, и понимание каждого из них поможет вам написать более эффективные тесты:
- Тестирование дыма — быстрая проверка. Вы не пытаетесь нагружать систему; вы хотите убедиться, что базовая функциональность работает при минимальной нагрузке. Это как убедиться, что ваш тестовый скрипт сам по себе не сломан, прежде чем запускать настоящие тесты.
- Тестирование нагрузки — реалистичный сценарий. Вы моделируете ожидаемые паттерны пользовательского трафика, чтобы увидеть, как ваше приложение ведёт себя в нормальных условиях. Если вы ожидаете, что 100 пользователей будут просматривать сайт одновременно в рабочее время, это ваш тест.
- Тестирование на устойчивость — точка разрыва. Вы постепенно увеличиваете нагрузку, пока что-то не сломается, выявляя пределы системы и потенциальные узкие места. Это как найти, где ваше приложение говорит «хватит».
- Тестирование всплесков — внезапный наплыв. Вы моделируете неожиданные всплески трафика — например, вирусный пост в социальных сетях, направляющий тысячи пользователей на ваш сайт, — чтобы увидеть, как ваша система адаптируется.
- Тестирование на выдержку — длительный тест на выносливость. Вы поддерживаете умеренную нагрузку в течение длительного периода (часы или дни), чтобы обнаружить утечки памяти, исчерпание ресурсов и другие проблемы, которые проявляются только после длительной работы.
Каждый тип теста отвечает на разные вопросы. И вот где k6 проявляет себя: он позволяет вам определять и выполнять все эти типы тестов с элегантным, удобным для разработчиков кодом.
Начало работы с k6
Установка k6 удивительно проста. Независимо от того, используете ли вы Windows, macOS или Linux, k6 предоставляет установщики и менеджеры пакетов для всех платформ. Посетите [k6.io] и следуйте инструкциям по установке — у вас всё будет готово за минуты, а не за часы.
После установки вы можете проверить, что всё работает:
k6 version
Вы должны увидеть номер версии, выведенный на экран. Отлично. Теперь мы готовы к работе.
Ваш первый тест производительности: пошаговое руководство
Давайте напишем реалистичный пример. Представьте, что мы тестируем простой API-конец, который создаёт элементы todo. Этот конец критически важен для вашего приложения, и вы хотите убедиться, что он грамотно обрабатывает трафик.
Создайте файл под названием todo-api-test.js:
import http from 'k6/http';
import { check, sleep } from 'k6';
export const options = {
stages: [
{ duration: '30s', target: 10 }, // Плавный переход к 10 пользователям
{ duration: '1m30s', target: 10 }, // Поддержание 10 пользователей
{ duration: '30s', target: 0 }, // Плавный переход к 0 пользователям
],
thresholds: {
http_req_duration: ['p(95)<500'], // 95% запросов менее 500 мс
http_req_failed: ['rate<0.1'], // Уровень ошибок менее 10%
},
};
export default function () {
const url = 'https://jsonplaceholder.typicode.com/todos';
const payload = JSON.stringify({
title: 'Тест производительности todo',
completed: false,
userId: 1,
});
const params = {
headers: {
'Content-Type': 'application/json',
},
};
const response = http.post(url, payload, params);
check(response, {
'статус равен 200': (r) => r.status === 200,
'время ответа < 300 мс': (r) => r.timings.duration < 300,
'идентификатор в ответе': (r) => r.json().id !== undefined,
});
sleep(1); // Пауза в 1 секунду между итерациями
}
Теперь запустите его:
k6 run todo-api-test.js
Когда тест завершится, k6 выведет сводку, показывая метрики, такие как длительность запроса, пропускная способность, уровень ошибок и успешное прохождение порогов. Это ваша базовая линия — правда о производительности вашего API.
Понимание этапов и порогов: сердце тестирования k6
Здесь k6 становится действительно умным. Давайте разберём, что происходит в нашем тесте:
Этапы определяют, как изменяется нагрузка со временем. В нашем примере у нас есть три этапа:
- Плавный переход: постепенное добавление 10 пользователей в течение 30 секунд.
- Поддержание: сохранение активности 10 пользователей в течение 90 секунд.
- Плавный переход: постепенное освобождение всех пользователей в течение 30 секунд.
Это лучше имитирует реальные паттерны трафика, чем мгновенное подключение 10 пользователей к вашему серверу. Вы можете визуализировать этапы как временную шкалу:
Пороговые значения — это ваши контракты на производительность. Они определяют приемлемые уровни производительности. Если ваш тест не соответствует этим порогам, весь тест считается неудачным — идеально для конвейеров CI/CD, где вы хотите, чтобы сборки проваливались при ухудшении производительности.
thresholds: {
http_req_duration: ['p(95)<500'], // 95-й процентиль запросов должен быть менее 500 мс
http_req_failed: ['rate<0.1'], // Менее 10% запросов могут завершиться ошибкой
}
Это говорит: «Я ожидаю, что 95% запросов завершатся в течение 500 миллисекунд, и я могу допустить уровень ошибок менее 10%». Если реальность не соответствует ожиданиям, k6 помечает тест как неудачный.
Создание многосценарного набора тестов
Реальные приложения не являются одноточечными системами. Давайте создадим более реалистичный тест, охватывающий различные сценарии:
import http from 'k6/http';
import { check, group, sleep } from 'k6';
export const options = {
scenarios: {
smoke_test: {
executor: 'constant-vus',
vus: 2,
duration: '30s',
gracefulStop: '10s',
},
average_load: {
executor: 'ramping-vus',
startVUs: 0,
stages: [
{ duration: '2m', target: 50 },
{ duration: '3m', target: 50 },
{ duration: '2m', target: 0 },
],
},
stress_test: {
executor: 'ramping-vus',
startVUs: 0,
stages: [
{ duration: '5m', target: 100 },
{ duration: '5m', target: 200 },
{ duration: '3m', target: 0 },
],
},
},
thresholds: {
http_req_duration: ['p(95)<300', 'p(99)<500'],
http_req_failed: ['rate<0.05'],
},
};
const BASE_URL = 'https://jsonplaceholder.typicode.com';
export default function () {
