Bioyino - розподілений, масштабований агрегатор метрик

Отже, ви збираєте метрики. Як і ми. Ми також збираємо метрики. Звісно ж, потрібні для бізнесу. Сьогодні ми розповімо про першу ланку системи нашого моніторингу — statsd-сумісний сервер агрегації. bioyino, навіщо ми його написали і чомусь відмовилися від brubeck.

Bioyino - розподілений, масштабований агрегатор метрик

З попередніх наших статей (1, 2) можна дізнатися, що до деякого часу мітки ми збирали за допомогою Brubeck. Він написаний на C. З точки зору коду - простий як пробка (це важливо, коли ви хочете контриб'ютити) і, найголовніше, без особливих проблем справляється з нашими обсягами 2 млн. метрик на секунду (MPS) у піку. Документація заявляє підтримку 4 млн. MPS із зірочкою. Це означає, що заявлену цифру ви отримаєте, якщо правильно налаштуєте мережу на Linux. (Скільки MPS можна отримати, якщо залишити мережу як є, нам невідомо). Незважаючи на ці переваги, до brubeck ми мали кілька серйозних претензій.

Претензія 1. Github – розробник проекту – перестав його підтримувати: публікувати патчі та фікси, приймати наші та (не тільки наші) PR. В останні кілька місяців (десь із лютого-березня 2018 року) активність відновилася, але перед цим було майже 2 роки повного затишшя. Крім того, проект розробляється для внутрішніх потреб Gihubщо може стати серйозною перешкодою для впровадження нових можливостей.

Претензія 2. Точність обчислень. Brubeck збирає для агрегації всього 65536 30 значень. У нашому випадку для деяких метрик у період агрегації (1 сек) може приходити набагато більше значень (527 в піку). В результаті такого семплювання, значення максимумів та мінімумів виглядають марними. Наприклад, ось так:

Bioyino - розподілений, масштабований агрегатор метрик
Як було

Bioyino - розподілений, масштабований агрегатор метрик
Як мало бути

З тієї ж причини суми взагалі вважаються некоректними. Додайте сюди баг з переповненням 32-бітного float, яке взагалі відправляє сервер у segfault при отриманні на вигляд безневинної метрики, і стає взагалі відмінно. Баг, до речі, так і не виправлено.

І, нарешті, Претензія X. На момент написання статті ми готові пред'явити її всім 14 більш-менш працюючим реалізаціям statsd, які нам вдалося знайти. Давайте уявімо, що окрема взята інфраструктура зросла настільки, що приймати 4 млн MPS вже недостатньо. Або хай ще не виросла, але метрики для вас вже важливі настільки, що навіть короткі, 2-3 хвилинні провали на графіках вже можуть стати критичними та викликати напади непереборної депресії у менеджерів. Оскільки лікування депресії — справа невдячна, потрібні технічні рішення.

По-перше, стійкість до відмови, щоб раптова проблема на сервері не влаштувала в офісі психіатричний зомбі апокаліпсис. По-друге, масштабування, щоб отримати можливість приймати більше 4 млн MPS, при цьому не копати вглиб мережевого стека Linux і спокійно зростати вшир до потрібних розмірів.

Оскільки за масштабуванням запас у нас був, ми вирішили почати з відмовостійкості. «О! Відмовостійкість! Це просто, це ми вміємо», — подумали ми і запустили два сервери, піднявши на кожному копію brubeck. Для цього нам довелося копіювати трафік з метриками на обидва сервери і навіть написати для цього невелику утилітку. Проблему стійкості до відмови ми цим вирішили, але... не дуже добре. Спочатку все було начебто чудово: кожен brubeck збирає свій варіант агрегації, пише дані в Graphite раз на 30 секунд, перезаписуючи старий інтервал (це робиться на боці Graphite). Якщо один сервер відмовить, у нас завжди є другий з власною копією агрегованих даних. Але проблема: якщо сервер відмовляє, на графіках виникає «пила». Пов'язано це з тим, що 30-секундні інтервали в Brubeck не синхронізовані, і в момент падіння один з них не перезаписується. У момент запуску другого сервера відбувається те саме. Цілком терпимо, але хочеться краще! Проблема масштабованості теж нікуди не поділася. Всі метрики, як і раніше, «летять» на одиночний сервер, і тому ми обмежені тими самими 2-4 млн MPS залежно від прокачування мережі.

Якщо трохи подумати про проблему і одночасно покопати сніг, то на думку може спасти така очевидна ідея: потрібен statsd, який вміє працювати в розподіленому режимі. Тобто такий, у якому реалізовано синхронізацію між нодами за часом та метриками. "Звичайно ж, таке рішення напевно вже є", - сказали ми і пішли гуглити. І нічого не знайшли. Прошерстивши документацію з різних statsd (https://github.com/etsy/statsd/wiki#server-implementations на момент 11.12.2017), ми не знайшли зовсім нічого. Мабуть, ні розробники, ні користувачі цих рішень з такою кількістю метрик поки що не стикалися, інакше вони обов'язково б що-небудь придумали.

І тут ми згадали про "іграшковий" statsd - bioyino, який писали на хакатоні just for fun (назва проекту згенерував скрипт перед початком хакатону) і зрозуміли, що нам терміново потрібен власний statsd. Навіщо?

  • тому що в світі занадто мало клонів statsd,
  • тому що можна забезпечити бажану або близьку до бажаної відмовостійкість та масштабованість (у тому числі синхронізувати агреговані метрики між серверами та вирішити проблему конфліктів під час відправлення),
  • тому що можна вважати метрики точніше, ніж це робить brubeck,
  • тому що можна самим збирати більш детальну статистику, яку brubeck нам практично не надавав,
  • тому що надався шанс запрограмувати свій власний хайперформансдистриб'ютедскейлаблаплікейшен, який не повністю повторюватиме архітектуру іншого такого ж хайперфору... нувипонелі.

На чому писати? Звісно ж, на Rust. Чому?

  • тому що вже був прототип рішення,
  • тому що автор статті на той момент уже знав Rust і рвався написати на ньому щось для продакшена з можливістю викласти це в open-source,
  • тому що мови з GC нам не підходять в силу природи трафіку (практично realtime) і GC-паузи практично неприпустимі,
  • тому що потрібна максимальна продуктивність, порівнянна з C
  • тому що Rust надає нам fearless concurrency, а почавши писати це на C/C++, ми погребли б ще більше, ніж у brubeck, вразливостей, переповнень буфера, race conditions та інших страшних слів.

Був і аргумент проти Rust. Компанія не мала досвіду створення проектів на Rust, і зараз ми теж не плануємо використовувати його в основному проекті. Тому були серйозні побоювання, що нічого не вийде, але ми вирішили ризикнути та спробували.

Ішов час…

Нарешті, після кількох невдалих спроб, перша версія була готова. Що вийшло? Вийшло таке.

Bioyino - розподілений, масштабований агрегатор метрик

Кожна нода отримує свій власний набір метрик і накопичує їх у себе, причому не агрегує метрики для тих типів, де для фінальної агрегації знадобиться повний набір. Ноди з'єднані між собою деяким протоколом розподіленого блокування (distributed lock), який дозволяє вибрати серед них те єдине (тут ми плакали), яке гідне відправляти метрики Великому. На даний момент ця проблема вирішується коштами Консул, але в майбутньому амбіції автора простягаються до власною реалізації Raft, де тою гідною буде, звичайно ж, нода-лідер консенсусу. Крім консенсусу, ноди досить часто (за умовчанням один раз на секунду) надсилають своїм сусідам ті частини передагрегованих метрик, які вдалося за цю секунду набрати. Виходить, що масштабування і стійкість до відмов зберігаються — кожна з нод як і раніше тримає в себе повний набір метрик, але метрики при цьому відправляються вже агрегованими, по TCP і з кодуванням в бінарний протокол, тому витрати на дублювання в порівнянні з UDP значно знижуються. Незважаючи на досить велику кількість вхідних метрик, накопичення вимагає зовсім небагато пам'яті та ще менше CPU. Для наших добре стисканих мертик це лише кілька десятків мегабайт даних. Додатковим бонусом отримуємо відсутність зайвих перезаписів даних у Graphite, як це було у випадку з burbeck.

UDP-пакети з метриками розбалансовані між нодами на обладнанні мережею через простий Round Robin. Само собою, мережева залізяка не розбирає вміст пакетів і тому може потягнути набагато більше, ніж 4M пакетів за секунду, не кажучи вже про метрики, про які вона взагалі нічого не знає. Якщо врахувати, що метрики приходять не по одній у кожному пакеті, то проблем із продуктивністю у цьому місці ми не передбачаємо. У разі падіння сервера мережевий пристрій швидко (протягом 1-2 секунд) виявляє цей факт і прибирає сервер, що впав з ротації. В результаті цього пасивні (тобто не є лідером) ноди можна включати і вимикати практично не помічаючи просідання на графіках. Максимум, що ми втрачаємо - це частина метрик, що прийшли за останню мить. Раптова втрата/вимкнення/перемикання лідера, як і раніше, намалює незначну аномалію (30-секундний інтервал, як і раніше, розсинхронізований), але за наявності зв'язку між нодами можна звести до мінімуму і ці проблеми, наприклад, шляхом розсилки пакетів, що синхронізують.

Трохи про внутрішній пристрій. Програма, звичайно ж, багатопотокова, але архітектура потоків відрізняється від тієї, що використана в brubeck. Потоки в brubeck – однакові – кожен із них відповідає одночасно і за збір інформації, і за агрегацію. У bioyino робочі потоки (workers) розділені на дві групи: відповідальні за мережу та відповідальні за агрегацію. Такий поділ дозволяє гнучкіше керувати додатком залежно від типу метрик: там, де потрібна інтенсивна агрегація, можна додати агрегаторів, там де багато мережного трафіку — додати кількість мережевих потоків. В даний момент на наших серверах ми працюємо в 8 мережевих та 4 агрегуючих потоках.

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

Набагато більше проблем при створенні викликала мережева частина, відповідальна за прийом метрик. Основним завданням виділення мережевих потоків в окремі сутності було прагнення зменшити час, який потік витрачає НЕ на читання даних із сокету. Варіанти з використанням асинхронного UDP та звичайного recvmsg швидко відпали: перший їсть надто багато user-space CPU на обробку подій, другий – надто багато перемикань контексту. Тому зараз використовується recvmmsg з великими буферами (а буфера, панове офіцери, це вам не аби що!). Підтримка звичайного UDP залишена для ненавантажених випадків, коли recvmmsg немає необхідності. У режимі multimessage вдається досягти головного: переважна більшість часу мережевий потік розгрібає чергу ОС - вичитує дані з сокету і перекладає їх у userspace-буфер, лише зрідка перемикаючись на те, щоб віддати заповнений буфер агрегаторам. Черга у сокеті практично не накопичується, кількість відкинутих пакетів практично не зростає.

Примітка

У стандартних налаштуваннях розмір буфера виставлений досить великим. Якщо ви раптом вирішите випробувати сервер самостійно, то, можливо, зіткнетеся з тим, що після відправки невеликої кількості метрик, вони не прилетять до Graphite, залишившись у буфері потоку. Для роботи з невеликою кількістю метрик потрібно виставити в конфізі bufsize та task-queue-size менші значення.

Насамкінець — трохи графіків для любителів графіків.

Статистика кількості вхідних метрик по кожному серверу: понад 2 млн. MPS.

Bioyino - розподілений, масштабований агрегатор метрик

Відключення однієї з нід та перерозподіл вхідних метрик.

Bioyino - розподілений, масштабований агрегатор метрик

Статистика за вихідними метриками: відправляє завжди лише одна нода – рейдбос.

Bioyino - розподілений, масштабований агрегатор метрик

Статистика роботи кожної ноди з урахуванням помилок у різних модулях системи.

Bioyino - розподілений, масштабований агрегатор метрик

Деталізація вхідних метрик (імена метрик приховані).

Bioyino - розподілений, масштабований агрегатор метрик

Що ми плануємо із цим усім робити далі? Звичайно ж писати код, бл ...! Проект спочатку планувався як open-source і залишиться таким його життя. У найближчих планах - перехід на власну версію Raft, зміна peer-протоколу на більш переносимий, внесення додаткової внутрішньої статистики, нових типів метрик, виправлення помилок та інші покращення.

Звичайно ж, вітаються всі охочі допомагати у розвитку проекту: створюйте PR, Issues, по можливості відповідатимемо, допрацьовуватимемо і т.д.

На цьому, як кажуть, that's all folks, купуйте наших слонів!



Джерело: habr.com

Додати коментар або відгук