Grafana k6: Performance, stress та smoke тестування. Теорія і практика про те, як покрити все однією кодовою базою

Усі статті, обговорення, новини про тестування — в одному місці. Підписуйтеся на QA DOU!

Привіт! Мене звати Вадим Поспєлов, я виконую роль Engineering Manager у компанії Uklon. Протягом усього мого професійного досвіду на різних посадах, як технічних, так і менеджерських, досить часто в командах, з якими я працював, підіймалася тема навантажувального (performance, stress, load etc) тестування. Воно було потрібно для різних цілей і на різних проєктах: як за масштабом, доменом так і етапом розробки. Знаєте скільки разів реально дійшло до реалізації таких тестів під час роботи на різних проєктах? Аж 2!

Перший з них був моїм волонтерським (читай, де все має бути безкоштовно і швидко) проєктом. Мені потрібно було отримати пруф, що сервіс тримає хоч якесь прийнятне навантаження. JMeter і пів години часу на те, щоб у ньому розібратися — ось, що знадобилося для отримання пруфу (до речі, позитивного). Звісно, я розумію, що вибірка на кілька десятків проєктів не є репрезентативною. Але погодьтесь, наявність такого, здавалося б must have виду тестування, як unit test у проєкті, вже є святом для розробників, і можна сказати, що компанія (проєкт/ клієнт) з більшою відповідальністю та розумінням поставилася до побудови робочого процесу.

Які лоад-тести коли «бюджети, дедлайни, стейкхолдери»? Як часто закладаємо час на такі тести? Не кажучи вже про необхідність наявності в компанії (чи команді) спеціалістів, які можуть дійсно якісно і швидко підійти до постановки цілей, реалізації, та отримання якісного та інформативного результату, на який можна хоча б мінімально покластися перед виходом проєкту в лайв.

Другого разу мені довелося мати справу з навантажувальними тестами в Uklon. До речі, окрім manual тестування, у нас є компонентні (інтеграційні), unit, performance і зараз працюємо над реалізацією e2e тестів на базі Cypress. Можливо, це стане темою однієї з наступних статей.

А в цьому блозі мова піде саме про навантажувальне тестування. Виділю теоретичну та практичну частину, але акцент буде саме на практиці. Прикладної теорії також вистачить ;)

Трохи історії та контексту

Кому цей блок нецікавий — пропускайте і переходьте одразу до практики. Але деякі речі там можуть бути незрозумілими без контексту та цілей з цієї частини.

Рішення про написання навантажувальних тестів було вимогою архітектора ще на етапі технічного дизайну в ініціативі по переписуванню досить великої, бізнес-значущої та навантаженої частини сервісу Uklon. Сервіс писався протягом року і, звісно, були й дедлайни, і бюджети, і пуш зі сторони стейкхолдерів. Це природно, ми маємо розуміти, що так працює бізнес.

Але разом з тим було і розуміння ціни, яку ми можемо заплатити в момент міграції реальних юзерів на новий сервіс у випадку, якщо він не витягне навантаження. Мушу зазначити, тести були лише частиною стратегії виходу в лайв. Архітектором компанії було прийнято рішення на користь інструменту k6 від Grafana, на той момент мені зовсім невідомого. Але великим плюсом було використання там синтаксису JS, а за бажанням й TS.

Ціль було поставлено трохи ширше, ніж просто тестування окремо взятого сервісу. А саме — розробити в середині компанії фреймворк, поставити процес наповнення тестів та автоматизувати випуск та запуск по розладу. Наразі не всі цілі ми підкорили, але процес все ще триває. У цьому матеріалі нас буде цікавити саме перша частина.

Вимоги до тестового фреймворку

Фреймворк мав полегшити поріг входу написання тестів. Наприклад, ми закладали написання тестів в тому числі й колегами з QA (k6 навіть допомагає з навчанням). З огляду на те, що топологія команд в Uklon має виражений поділ на домени та сервіси, і кожна команда має певну автономію, наш фрейм повинен бути гнучким у плані написання і запуску ізольованих тестів з різним рівнем покриття, для різних проєктів і команд. Тобто кожна команда повинна мати змогу писати й ранити тести тоді, коли для неї це зручно та доцільно.

Крім цього, мала бути зрозуміла візуалізація. На щастя, це те, що k6 підтримує з коробки (як виявилось, майже з коробки), бо це ж врешті-решт рішення від Grafana.

Остання з основних вимог, але не по важливості — простота запуску та користування.

Що таке k6, його переваги та недоліки, та в чому відмінності від JMeter

❗️Увага: цей розділ я згенерував у ChatGPT та залишив в оригіналі. Хайп є хайп :)

k6 is an open-source load testing tool. Together, Grafana and k6 can be used to visualize and analyze the performance of web applications under different loads and stress conditions. This allows developers to identify and fix performance bottlenecks, as well as ensure that their application can handle the expected level of traffic and usage.

k6 pros and cons:

k6 is an open-source load testing tool that is designed to be simple and easy to use, with a focus on performance testing of web applications. It has the following pros and cons:

Pros:

  • It is easy to use and has a simple and streamlined command line interface.
  • It is lightweight and has a small footprint, which makes it suitable for running large-scale tests.
  • It has built-in support for the collection and export of performance metrics, which makes it easy to analyze and interpret test results.
  • It is written in JavaScript, which allows for easy integration with other JavaScript tools and frameworks.
  • It can be integrated with Grafana for visualization and monitoring of test results.

Cons:

  • It only supports HTTP and HTTPS protocols, which may limit its use cases.
  • It does not have a GUI, which may make it difficult for some users to create and execute tests.
  • It may not have as many built-in test elements and plugins as some other load testing tools, such as Apache JMeter.
  • It requires some level of knowledge of JavaScript to use effectively.

Overall, k6 is a great tool for load testing and performance testing of web applications, especially if you are looking for a simple and lightweight tool that is easy to use, and can be integrated with other JavaScript tools and frameworks.

K6 vs JMeter

Apache JMeter and k6 are both open-source load testing tools, but they have some differences in terms of features, performance and usage.

  • JMeter is a Java-based tool that is primarily used for functional and performance testing of web applications. It has a wide range of built-in test elements and plugins, and it supports various protocols such as HTTP, HTTPS, FTP, JDBC, and JMS. JMeter also has a GUI which allows for easy test creation, execution, and analysis.
  • k6, on the other hand, is a JavaScript-based tool that is designed to be simple and easy to use, with a focus on performance testing of web applications. It has a simple and streamlined command line interface, and it supports only HTTP and HTTPS protocols. k6 also has built-in support for the collection and export of performance metrics, and it can be integrated with Grafana for visualization.

JMeter is more feature-rich and versatile, but k6 is more focused and easier to use. It also has a smaller footprint and lower resource usage, making it more suitable for running large-scale tests. Depending on your needs, either one of these tools can be a good option for performance testing.

Визначення меж тестування

Кожен тест повинен мати ціль, так само як і кожна компанія має мати таргети по доступності своїх сервісів (uptime) на основі бізнес-цілей та прийнятих SLO. В Uklon це: Availability — в межах 99.95 — 99.99% в залежності від типу API. Latency: 100ms для 95perc, 200ms — 99perc, але також залежить від API.

У нашому випадку плюс був у тому, що ми переписували уже працюючий сервіс і мали можливість зібрати дані для визначення горизонту performance тестування. Але і стрес-тестування ми також проводили (звісно, не на проді 😀). В k6 стосовно цього є влучне застереження:

If your team is at the maturity level of running chaos experiments in production, then fine, run load test in production as well. If not, stick to pre-production.

Взагалі, кількість сутностей, які генерують досить великий обʼєм даних та трафіку: ~200к. Пікове RPM топ навантажених ендпоінтів: ~5-6k.

Пошук пікового навантаження сервісу для визначення меж тестування

Вже після розробки і презентації фрейму, окрім тестування нових сервісів перед виходом в прод, ми виявили ще декілька кейсів, де тести можуть полегшити життя розробникам.

Перше — це дебаг існуючих сервісів. Інколи трапляються особливо «цікаві» баги, які відтворюються тільки під навантаженням. І якщо єдине місце, де баг відтворюється, це прод, і ви не маєте тестів (особливо, коли логи вашої системи не є досконалими), тоді, панове, welcome to hell :)

Ще один випадок — це виявлення hot path для подальшої оптимізації. Крім того, замір деградації перфомансу сервіса після якогось обʼємного релізу.

І особливо цікавим для мене інсайтом стало використання одного і того ж коду для таких видів тестування, як performance, stress, smoke, integration tests. При чому в документації k6 є інформація про те, що k6 може покривати всі ці (і навіть більше) види тестування, але я не до кінця розумів, що всі їх можна ранити на одній і тій же кодовій базі, змінюючи налаштування тестів. Але про це піде мова трохи далі.

Практична частина

Репозиторій можна знайти тут.

Дисклеймер: автор не претендує на зразковість коду, архітектури, ідеального використання TS. Також не маю великого досвіду в автоматичному тестуванні і впевнений, що є промахи у самому підході автотестів. Даний репо представлений лише як кейс практичного використання в окремо взятій компанії, і може не відповідати цілям чи вимогам (див. розділ «Вимоги до тестового фреймворку») у вашій компанії. Усі конструктивні зауваження та пропозиції вітаються в коментарях. Проєкт був розроблений на OSX, при запуску на інших платформах можуть бути незначні нюанси.

Загалом документація в k6 дуже якісна, тому не буду зупинятися на синтаксисі чи особливостях технології. Єдине, що порекомендую — звернути увагу на топіки на моменті:

k6 is not NodeJS, nor is it a browser. Packages that rely on APIs provided by NodeJS, for instance the os and fs modules, will not work in k6. The same goes for browser-specific APIs like the window object.

Стек

Окрім очевидних k6, js:

  • WebPack потрібен для збірки ts;
  • Docker, Docker Compose — для локального запуску, яке не потребує окремого встановлення всіх залежностей та інструментів;
  • Prometheus — сторедж, куди надсилаються результати тестування для подальшої візуалізації в Grafana;
  • Grafana — сама візуалізація результатів тестів.

Хочу окремо сказати про збереження результатів тестів. Спочатку вибрав InfluxDB і локальне налаштування зайняло мінімум часу. Я був задоволений результатом рівно до того часу, поки не дізнався, що компанія переїхала з InfluxDb на Prometheus. А Prometheus (remote write) — це експериментальна фіча в k6, і потребує додаткових маніпуляцій з докером для запуску. Тому це слід брати до уваги при виборі real-time data output recourse.

Структура проєкту

Усі стандартні для JS репи, файли й каталоги буду упускати.

Grafana-config — каталог, де зберігаються налаштування датасорсу та дашборду Grafana.

.env_stage.example — енв-файл, на основі якого потрібно створити файл та назвати відповідно до стеджа з цим енвайроментом, залежними та сенсетів даними, які будуть використовуватися. Наприклад, .env_dev, .env_stage. Ці файли потім будуть використовуватися при білді проєкту.

Налаштувати неймінг можна в конфігі webpack. Зараз тестові credentials вкомітані в .env_stage.example, але ніколи не робіть так з реальними даними.

Docker — клонимо образ та закидаємо збілджені файли в імедж.

docker-compose.yml — описані та сконфігуровані сервіси, які підіймаються локально:

  • prometheus — доступний після запуску за адресою http://localhost:9090;
  • grafana — доступна після запуску за адресою http://localhost:300;
  • k6 — сервіс, зібраний на основі кастомного імеджу, в якому вже є вірно налаштований Prometheus;
  • K6-local — сервіс, призначений для легкого локального запуску тестів без необхідності встановлювати k6 локально. Але якщо потрібно, то в доку описані способи на різні платформи.

Архітектура проєкту

Відповідно до поставлених цілей написання фрейму, було сформовано архітектуру проєкту. Уявіть, що ми маємо компанію, яка складається з 2-х незалежних команд (fangs-team та tails-team). У цих командах є набір сервісів, за які вони відповідають. Тут треба сказати, що в даному проєкті прийнято ряд умовностей для полегшення написання прикладу та полегшення розуміння кодової бази. Саме тому кожна з команд мають по два сервіси з однаковою назвою: alligator та caiman сервіси. Як сорс для тестування було використано test-api.k6.io.

Повернемося до структури. Окрім каталогів з каталогами команд ще є каталог shared-services, який містить логіку спільних для всіх команд сервісів. Наприклад, таким може бути сервіс identity або auth.

В models зберігаються TS моделі й типи. Utils — загальні для всіх команд та сервісів допоміжні утиліти та хелпери.

Структура каталогів команд

config містять налаштування та дані, які використовуються для тестів. Наприклад, специфічні для конкретного сервісу credentials чи дані для формування ендпоінтів.

setup використовується для налаштування тестів і прокидки контексту тестування для подальшого використання протягом усього test-lifecycle.

options безпосередньо налаштування етапів, трешхолдів, віртуальних юзерів (VUs).

Памʼятаєте назву статті? Саме тут можна обрати вид тестування: потрібен smoke, ставимо `vus: 1` і `duration: ’1m’ — ось вам і smoke чи навіть інтеграційні тести. Треба стрес-тест, то ставимо побільше (в мене вистачило 50 на стейджі) і бінарним пошуком (або методом наукового тику) виявляємо ту кількість VUs, на якій система починає адекватно працювати. Ну і performance налаштовуємо згідно з цілями тестування.

http.ts — звичайний http клієнт, куди винесені безпосередні запити до сервісу, що тестується.

Index.ts по суті — інтерфейс експорту логіки тестування в поточний сервіс, або в файл тестування всієї команди.

Файли типу [team|team_service|]-test.ts — це вхідні точки тестування. Наприклад, якщо нам треба прогнати тести по всім сервісам команди fangs-team, то запускати маємо саме цей файл (команда npm run test-fangs для виводу без графічних метрик). А якщо потрібно прогнати лише один сервіс, то є змога ізольовано заранити тести тільки для цього сервісу (наприклад, команда npm run test-fangs-alligator для виводу без графічних метрик).

Якщо подивитися уважно, то можна помітити, що структура в середині каталогів команди й сервісу подібні. Цим ми накриваємо дві цілі: по-перше, ми забезпечуємо індивідуальний гнучкий запуск окремих сервісів, а по-друге, навіть при запуску всіх сервісів команди, налаштування розширюють тести з верхнього рівня (тести сервісу розширюють командні, наприклад config чи options).

Таким чином можна гнучко підійти до тестування сервісів з урахуванням їх технічних чи бізнесових особливостей. До прикладу тут ми змінюємо логін та пароль, використовуючи, наприклад, іншу роль для входу в систему.

Збірка тестів та структура dist

Збірка тестів відбувається стандартною командою npm run build:[stage] з вказанням відповідного енву. На назву стейджу завʼязано використання того чи іншого .env_[stage] файлу. Збірка генерує набір тестів і розділення по каталогах відповідно командам.

Важливо зауважити, що файлам тестів (*-test.ts) обов’язково давати унікальні імена, бо, як видно з картинки, webpack формує флет-структуру в рамках однієї команди, і у випадку не унікальності, тести будуть перезатерті.

Трохи більше про запуск тестів та команди в package.json

Беручи до уваги одну з цілей фрейму, а саме пункту, що стосується простоти використання і запуску, були використані сервіси, загорнені в Docker. Локально запускати тести можна у 2х режимах: зі стримінгом результатів тестів в Grafana через Prometheus:

Дашборд з результатами роботи тестів

Запуск виконується за допомогою команд з постфіксом :monitoring, наприклад:

npm run test-tails-alligator:monitoring

При чому, окремо, стартувати сервіси Grafana та Prometheus не обовʼязково через явне вказування залежностей в docker-compose.yml файлі. Але є і команда: npm run monitoring:up окремого запуску і відкриття сайту в браузері: npm run monitoring:open.

Та звичайний запуск k6 тестів і відображення результатів тільки в CLI. Як можна зрозуміти, робиться це без префіксу, наприклад: npm run ​​test-tails-alligator.

Вивід в консоль результатів тестування

Перевірка результатів тестів та логування в коді

Перевірка роботи тестів винесена в окремий файл check-response.ts, загальний для всіх тестів. Тут використовується assertion library chai.js, адаптований саме до k6. Він затягується в проєкт на етапі postinstal (автоматичний npm хук). Тут нічого складного, проста перевірка на статус 200.

У випадку smoke цю логіку можна розширити перевіркою якихось полів респонсу. Крім респонсу, метод checkResponse приймає describeText для коректного відображення ходу тестування.

Також, для полегшення дебагу тестів не зайвим буде додавати логування у випадку виклику припинення тестів. Саме припинення тестів може бути викликане різними причинами. У прикладі це стається через невідповідність очікуваних даних.

Запуск в клауді

Тут залишу діаграму, як ми запускаємо тести вже в інфраструктурі Uklon. Зараз вони запускаються мануально через ArgoCD, але на майбутнє думаємо про автоматичний запуск по тригеру, або з якоюсь періодичністю.

На першій ітерації ми ранали поди безпосередньо в пайплайнах GitLab, але після консультації з DevOps team, було прийнято рішення перенесення запуску в тулзу, відповідальну CD процесу.

Скейлінг подіків при запуску тестів

Висновки

Насправді крім поставленого питання у назві статті, намагався допомогти командам і інженерам (BE, QA, TL, Architects) отримати розуміння того, що performance тестування можна і треба робити, і на це не мають йти тижні чи місяці входу в технологію k6.

Переваги його використання перевищують ціну помилки при виході в лайв стартапів, MVP рішень чи сервісів в рамках продуктів. Але і варто розуміти, що будь-яка тула далеко не панацея. Не варто завищувати очікування перед їх використанням та планувати стратегію виходу сервісів в прод із застосуванням бакетної міграції користувачів, бета-тестування, AB тестування і так далі. Ну і звісно, буду радий, якщо згенерований bolilerplate проєкту стане комусь у пригоді та пришвидшить написання тестів.

👍ПодобаєтьсяСподобалось20
До обраногоВ обраному11
LinkedIn
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

Цікава стаття, на проекті копаємо в сторону перформенса і якраз обраний k6

Дякую, за відгук, Богдане. Подивіться на репо — може стане в пригоді.
github.com/vadomen/load-testing

Вместо InfluxDB для записи результатов тестов можно использовать VictoriaMetrics — она поддерживает запись данных по InfluxDB протоколу — см. docs.victoriametrics.com/...​e-agents-such-as-telegraf

Ми принли як стандарт в компанії для цілей всіх видів моніторингу використовувати як сторедж саме Prometheus — тому треба було використовувати саме його. Але дякую за підсказку!

Вадим, ви могли заощадити свої зусилля та згенерувати всю статтю в ChatGPT

Буду відвертим — була така спокуса. Але в моєму олдсукльному баченні речей — це був би чітінг :)

Юзав цей інструмент. Реально крута штука і готові дашборди топ, можна писати свої розширення на Go.
Із мінусів помітив — не усі штуки оптимальні і у деяких кейсах починає жерти пам`ять що капець. Вирішується написанням кастомних методів на go.
Ще на платних дашбордах є можливість дивитись таймінги вкладено по групі, потім розгортати і дивитись окремо по кожному запиту у цій групі. На безкоштовному для графани такого немає, треба городити своє.

Go методи — це вже некст левел :). Не юзав платні фічі, але дякую за інформацію.

Підписатись на коментарі