Slow Cooker: навантажувальне тестування мережевих сервісів


Linkerd, наша сервісна сітка (service mesh) для хмарних додатків, з обов'язку служби зобов'язана протягом тривалого часу справлятися з великими обсягами мережевого трафіку. Перед випуском чергового релізу відповідність цій вимозі необхідно ретельно перевіряти. У цій статті ми опишемо стратегії навантажувального тестування та використані нами інструменти, а також розглянемо кілька виявлених проблем. В результаті буде представлений slow_cooker — написаний на Go інструмент навантажувального тестування з відкритим вихідним кодом, який був створений для виконання тривалих навантажувальних тестів та виявлення проблем життєвого циклу (life cycle issue identification).
linkerd діє як прозорий проксі. Він додає до призначених для певних сервісів запитам використання пулів сполук, відпрацювання відмов, повторні спроби, балансування навантаження з урахуванням затримок і багато іншого. Щоб бути життєздатною і придатною для промислової експлуатації системою, linkerd повинен уміти справлятися з дуже великою кількістю запитів на протязі тривалих періодів часу в умовах мінливої обстановки. На щастя, linkerd побудований на основі netty і Finagle. Серед всіх мережевих програм їх код є одним з найбільш широко протестованих і перевірених у процесі промислової експлуатації. Але код — це одне, а реальна продуктивність — зовсім інше.
Щоб оцінити поведінку системи в умовах промислової експлуатації, linkerd повинен бути підданий самому ретельному і всебічному навантажувальному тестування. Більше того, оскільки linkerd є частиною базової інфраструктури, його примірники рідко зупиняються або відновлений, і кожен з них може пропустити через себе мільярди запитів в умовах мінливого поведінки сервісів і їх клієнтів. Це означає, що ми також повинні тестувати проблеми життєвого циклу (lifecycle issues). Для мережевих серверів з високою пропускною здатністю, яким є і linkerd, проблеми життєвого циклу включають витоку пам'яті і сокетів, погані GC-паузи, а також перевантаження мережі і дискової підсистеми. Такі речі відбуваються нечасто, але якщо не навчитися відпрацьовувати їх належним чином, наслідки можуть бути катастрофічними.

Хто тестує програми для тестування?

На початковому етапі розробки linkerd ми використовували популярні інструменти навантажувального тестування ApacheBench і hey. (Звичайно, вони працюють тільки з HTTP, а linkerd проксирует різні протоколи, включаючи Thrift, gRPC і Mux—but. Але нам треба було з чогось починати.)
На жаль, ми швидко усвідомили, що, незважаючи на безсумнівну корисність цих інструментів для швидкого отримання даних про продуктивність, вони не дуже гарні у визначенні проблем життєвого циклу, які ми хотіли навчитися виявляти. Ці інструменти видають загальний підсумок за фактом завершення тесту, а при такому підході проблеми можна не помітити. Більше того, вони покладаються на середні значення і стандартні відхилення, що є не найкращим, на нашу думку, способом оцінки продуктивності системи.
Щоб виявити проблеми життєвого циклу, нам були потрібні більш якісні показники та можливість бачити, як linkerd поводиться під час тривалих тестів, що виконуються години і дні, а не хвилини.

Для отримання ніжного коду готуємо не кваплячись

Оскільки ми не змогли знайти підходящий інструмент, довелося зробити свій: slow_cooker. slow_cooker — програма для навантажувального тестування, розроблена спеціально для виконання тривалих навантажувальних тестів та виявлення проблем життєвого циклу. Ми широко використовуємо slow_cooker для пошуку проблем з продуктивністю і тестування змін в наших продуктах. slow_cooker є покрокові звіти (incremental reports) про хід процесу тестування, виявлення змін (change detection) та всі необхідні метрики.
Щоб інші люди могли користуватися slow_cooker і брати участь в розробці, ми сьогодні відкриваємо його вихідний код. См. slow_cooker source на GitHub і нещодавно випущений реліз 1.0.
Давайте поговоримо про можливості, які надає slow_cooker.
(Для спрощення ми протестуємо його безпосередньо на веб-сервісах. На практиці ми, звичайно, використовуємо slow_cooker в першу чергу для пошуку проблем з linkerd, а не з сервісами, які він обслуговує.)

Покроковий звіт про затримки мережі

Так як slow_cooker в першу чергу націлений на виявлення проблем життєвого циклу, що виникають на великих часових проміжках, в нього закладена ідея покрокових звітів. Занадто багато можна пропустити, якщо аналізувати усереднені значення за дуже великої кількості вихідних даних, особливо в тому випадку, коли справа стосується таких вресних явищ, як робота збирача сміття або насичення мережі. З допомогою покрокових звітів ми можемо бачити зміни пропускної спроможності і затримки прямо на працюючій системі.
У прикладі наведено висновок slow_cooker, отриманий при навантажувальному тестуванні linkerd. У нашому тестовому сценарії linkerd балансує навантаження між трьома серверами nginx, кожен з яких роздає статичний контент. Затримки дані в мілісекундах, і ми виводимо min, p50, p95, p99, p999 і max-затримки, зафіксовані на десятисекундных інтервалах.
$ ./slow_cooker_linux_amd64 -url http://target:4140 -qps 50 -concurrency 10 http://perf-target-2:8080
# sending 500 req/s with concurrency=10 to http://perf-target-2:8080 ...
# good/b/f t good% min [p50 p95 p99 p999] max change
2016-10-12T20:34:20Z 4990/0/0 5000 99% 10s 0 [ 1 3 4 9 ] 9
2016-10-12T20:34:30Z 5020/0/0 5000 100% 10s 0 [ 1 3 6 11 ] 11
2016-10-12T20:34:40Z 5020/0/0 5000 100% 10s 0 [ 1 3 7 10 ] 10
2016-10-12T20:34:50Z 5020/0/0 5000 100% 10s 0 [ 1 3 5 8 ] 8
2016-10-12T20:35:00Z 5020/0/0 5000 100% 10s 0 [ 1 3 5 9 ] 9
2016-10-12T20:35:11Z 5020/0/0 5000 100% 10s 0 [ 1 3 5 11 ] 11
2016-10-12T20:35:21Z 5020/0/0 5000 100% 10s 0 [ 1 3 5 9 ] 9
2016-10-12T20:36:11Z 5020/0/0 5000 100% 10s 0 [ 1 3 5 9 ] 9
2016-10-12T20:36:21Z 5020/0/0 5000 100% 10s 0 [ 1 3 6 9 ] 9
2016-10-12T20:35:31Z 5019/0/0 5000 100% 10s 0 [ 1 3 5 9 ] 9
2016-10-12T20:35:41Z 5020/0/0 5000 100% 10s 0 [ 1 3 6 10 ] 10
2016-10-12T20:35:51Z 5020/0/0 5000 100% 10s 0 [ 1 3 5 9 ] 9
2016-10-12T20:36:01Z 5020/0/0 5000 100% 10s 0 [ 1 3 5 10 ] 10

У цьому звіті в колонці
good%
показана пропускна здатність: як близько ми підійшли до потрібного кількості запитів в секунду (RPS, requests per second).
Цей звіт виглядає непогано: система працює швидко і час відгуку стабільний. При цьому ми повинні мати можливість чітко бачити, де і коли почалися неприємності. Висновок slow_cooker був налаштований таким чином, щоб полегшити візуальний пошук проблем і викидів за допомогою вертикального вирівнювання, а також індикатора події зміни. Давайте подивимося приклад, де у нас з'явився сильно гальмує сервер:
$ ./slow_cooker_linux_amd64 -totalRequests 100000 -qps 5 -concurrency 100 http://perf-target-1:8080
# sending 500 req/s with concurrency=10 to http://perf-target-2:8080 ...
# good/b/f t good% min [p50 p95 p99 p999] max change
2016-11-14T20:58:13Z 4900/0/0 5000 98% 10s 0 [ 1 2 6 8 ] 8 +
2016-11-14T20:58:23Z 5026/0/0 5000 100% 10s 0 [ 1 2 3 4 ]4
2016-11-14T20:58:33Z 5017/0/0 5000 100% 10s 0 [ 1 2 3 4 ] 4
2016-11-14T20:58:43Z 1709/0/0 5000 34% 10s 0 [ 1 6987 6987 6987 ] 6985 +++
2016-11-14T20:58:53Z 5020/0/0 5000 100% 10s 0 [ 1 2 2 3 ] 3 --
2016-11-14T20:59:03Z 5018/0/0 5000 100% 10s 0 [ 1 2 2 3 ] 3 --
2016-11-14T20:59:13Z 5010/0/0 5000 100% 10s 0 [ 1 2 2 3 ] 3 --
2016-11-14T20:59:23Z 4985/0/0 5000 99% 10s 0 [ 1 2 2 3 ] 3 --
2016-11-14T20:59:33Z 5015/0/0 5000 100% 10s 0 [ 1 2 3 4 ] 4 --
2016-11-14T20:59:43Z 5000/0/0 5000 100% 10s 0 [ 1 2 3 5 ] 5
2016-11-14T20:59:53Z 5000/0/0 5000 100% 10s 0 [ 1 2 2 3 ] 3
FROM TO #REQUESTS
0 2 49159
2 8 4433
8 32 8
32 64 0
64 128 0
128 256 0
256 512 0
512 1024 0
1024 4096 0
4096 16384 100

Як бачите, система працює швидко і чуйно, за винятком однієї заминки в 2016-11-14T20:58:43Z, під час якої пропускна спроможність впала до 34%, а потім повернулася до норми. Будучи власником цього сервісу, ви напевно захочете подивитися логи або показники продуктивності, щоб з'ясувати причину інциденту.

Приклад проблеми життєвого циклу: GC-пауза

Щоб продемонструвати переваги покрокових звітів у порівнянні зі звичайними, що виводять тільки підсумкові дані, давайте змоделюємо ситуацію, в якій на сервері запускається збирач сміття. У цьому прикладі ми будемо безпосередньо тестувати єдиний процес nginx, що роздає статичний контент. Для симуляції затримок, викликаних складальником сміття, ми будемо припиняти і відновлювати роботу nginx у циклі з пятисекундными інтервалами (використовуючи
kill -STOP $PID
та
kill -CONT $pid
).
Для порівняння давайте почнемо з звіту від ApacheBench:
$ ab -n 100000 -c 10 http://perf-target-1:8080/
This is ApacheBench, Version 2.3 <$Revision: 1604373 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking perf-target-1 (be patient)
Completed 10000 requests
Completed 20000 requests
Completed 30000 requests
Completed 40000 requests
Completed 50000 requests
Completed 60000 requests
Completed 70000 requests
Completed 80000 requests
Completed 90000 requests
Completed 100000 requests
Finished 100000 requests

Server Software: nginx/1.9.12
Server Hostname: перфорація-target-1
Server Port: 8080

Document Path: /
Document Length: 612 bytes

Concurrency Level: 10
Time taken for tests: 15.776 seconds
Complete requests: 100000
Failed requests: 0
Total transferred: 84500000 bytes
HTML transferred: 61200000 bytes
Requests per second: 6338.89 [#/sec] (mean)
Time per request: 1.578 [ms] (mean)
Time per request: 0.158 [ms] (mean, across all concurrent requests)
Transfer rate: 5230.83 [Kbytes/sec] received

Connection Times (ms)
min mean[±sd] median max
Connect: 0 0 0.2 0 3
Processing: 0 1 64.3 0 5003
Waiting: 0 1 64.3 0 5003
Total: 0 2 64.3 1 5003

Percentage of the requests served within a certain time (ms)
50% 1
66% 1
75% 1
80% 1
90% 1
95% 1
98% 1
99% 2
100% 5003 (longest request)

Тут ми бачимо затримку в 1,5 мс, при цьому є декілька викидів з бпрольшими затримками. Такий звіт досить легко помилково вважати нормальним навіть незважаючи на те, що досліджуваний сервіс не відповідав на запити рівно половину витраченого на перевірку часу. Якщо цільове значення SLA дорівнює 1 секунді, то сервіс перевищував його протягом більш ніж половини прогону, але з цього звіту можна і не помітити!
З покроковими звітами slow_cooker ми бачимо, що постійно виявляється проблема з пропускною здатністю. Також тут набагато більш очевидно, що P99,9 має стабільно високі значення протягом усього тесту:
$ ./slow_cooker_linux_amd64 -totalRequests 20000 -qps 50 -concurrency 10 http://perf-target-2:8080
# sending 500 req/s with concurrency=10 to http://perf-target-2:8080 ...
# good/b/f t good% min [p50 p95 p99 p999] max change
2016-12-07T19:05:37Z 2510/0/0 5000 50% 10s 0 [ 0 0 2 4995 ] 4994 +
2016-12-07T19:05:47Z 2520/0/0 5000 50% 10s 0 [ 0 0 1 4999 ] 4997 +
2016-12-07T19:05:57Z 2519/0/0 5000 50% 10s 0 [ 0 0 1 5003 ] 5000 +
2016-12-07T19:06:07Z 2521/0/0 5000 50% 10s 0 [ 0 0 1 4983 ] 4983 +
2016-12-07T19:06:17Z 2520/0/0 5000 50% 10s 0 [ 0 0 1 4987 ] 4986
2016-12-07T19:06:27Z 2520/0/0 5000 50% 10s 0 [ 0 0 1 4991 ] 4988
2016-12-07T19:06:37Z 2520/0/0 5000 50% 10s 0 [ 0 0 1 4995 ] 4992
2016-12-07T19:06:47Z 2520/0/0 5000 50% 10s 0 [ 0 0 2 4995 ] 4994
FROM TO #REQUESTS
0 2 19996
2 8 74
8 32 0
32 64 0
64 128 0
128 256 0
256 512 0
512 1024 0
1024 4096 0
4096 16384 80

Звіти про затримки на основі перцентилей

Як видно з прикладу з ApacheBench, деякі інструменти навантажувального тестування виводять лише середнє значення та стандартне відхилення. Однак ці метрики зазвичай недоречні при оцінці затримок, які не підкоряються закону нормального розподілу і часто мають дуже довгі хвости.
slow_cooker ми не використовуємо середнє значення і стандартне відхилення, а замість цього відображаємо мінімум, максимум і кілька перцентилей високого порядку (P50, P95, P99 і P99,9). Цей підхід все частіше застосовується в сучасному програмному забезпеченні, де один запит може породити десятки або навіть сотні звернень до інших систем і сервісів. У таких ситуаціях метрики, подібні 95-го і 99-му перцентилями, дозволяють отримати переважне значення затримки.

Висновок

Хоча в наш час написання інструменту по навантаженню тестування не є надто складним завданням (особливо при використанні сучасних мов програмування, що мають вбудовану підтримку паралелізму і орієнтованих на роботу з мережею, таких як, наприклад, Go), реалізація системи вимірювань і структура звітів можуть істотно впливати на корисність такої програми.
зараз ми широко використовуємо slow_cooker для тестування linkerd та інших сервісів (наприклад, nginx). linkerd тестується в режимі 24x7 в умовах взаємодії з різними сервісами, і slow_cooker допоміг нам не тільки запобігти розгортання коду із серйозними помилками, але і знайти проблеми з продуктивністю вже працюють релізах. Використання slow_cooker Buoyant стало настільки розповсюдженим, що ми почали називати навантажувальне тестування програм «слоукукингом».
з slow_cooker можна почати з відвідин сторінки з релізами на Github. Завантажте інструмент і запустіть тестування свого улюбленого сервера, щоб перевірити, чи немає у нього проблем з продуктивністю. При тестуванні linkerd slow_cooker нам дуже допоміг, і ми сподіваємося, що ви знайдете його не менш корисним.

Матеріали для подальшого читання (англійською)

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.