Система керування конфігурацією мережі фільтрації Qrator

Система керування конфігурацією мережі фільтрації Qrator

TL, д-р: Опис клієнт-серверної архітектури нашої внутрішньої системи управління конфігурацією мережі, QControl. В основі лежить дворівневий транспортний протокол, що працює з упакованими в gzip повідомлення без декомпресії між ендпойнтами. Розподілені роутери та ендпойнти одержують конфігураційні апдейти, а сам протокол дозволяє встановлення локалізованих проміжних релеїв. Система побудована за принципом диференціального бекапу (“recent-stable”, пояснюється нижче) і використовує мову запитів JMESpath разом із шаблонизатором Jinja для рендеру конфігураційних файлів.

Qrator Labs управляє глобально розподіленою мережею нейтралізації атак. Наша мережа працює за принципом anycast, а підмережі анонсуються за допомогою BGP. Будучи BGP anycast-мережею, фізично розташованою в кількох регіонах Землі, ми можемо обробити та відфільтрувати нелегітимний трафік ближче до ядра інтернету – Tier-1 операторам.

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

На початку було Слово. Воно швидко стало комунікаційним протоколом, що потребує апдейту.


Наріжним каменем існування QControl і одночасно основною причиною витрати значної кількості часу та ресурсів на побудову такого роду протоколу є необхідність отримати єдине авторитетне джерело конфігурації і, зрештою, синхронізувати з ним наші точки присутності. Саме сховище було лише однією з кількох вимог під час розробки QControl. Крім цього, нам також були необхідні інтеграції з існуючими та запланованими сервісами на точках присутності (ТП), розумні (та настроювані) способи валідації даних, а також розмежування доступу. Крім того, ми також хотіли керувати такою системою за допомогою команд, а не внесенням модифікацій у файли. До QControl дані відправлялися на точки присутності практично в ручному режимі. Якщо одна з точок присутності була недоступна, і ми забували оновити її пізніше, конфігурація виявлялася розсинхронізованою - доводилося витрачати час на повернення її в дію.

У результаті ми вигадали таку схему:
Система керування конфігурацією мережі фільтрації Qrator
Конфігураційний сервер відповідальний за валідацію даних і сховище, роутер має кілька ендпойнтів, які отримують і транслюють конфігураційні апдейти від клієнтів і команди підтримки на сервер, а з сервера на точки присутності.

Якість інтернет-з'єднання все ще значно різниться в різних куточках планети - для ілюстрації цієї тези, давайте подивимося на простий MTR з Праги, Чеської Республіки до Сінгапуру та Гонконгу.

Система керування конфігурацією мережі фільтрації Qrator
MTR з Праги до Сінгапуру

Система керування конфігурацією мережі фільтрації Qrator
Те саме до Гонконгу

Високі затримки означають меншу швидкість. Крім того, є втрата пакетів. Ширина каналів не компенсує цю проблему, яка має завжди братися до уваги при побудові децентралізованих систем.

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

Дизайн recent-stable

Можна сміливо сказати, що побудова розподіленої мережі за принципом інкрементальних оновлень є досить очевидним рішенням. Але з диффами пов'язана велика кількість проблем. Нам необхідно зберігати всі диффи між опорними точками, а також вміти надсилати їх у випадку, якщо хтось втратив частину даних. Кожна точка призначення повинна застосовувати їх у строго заданій послідовності. Зазвичай у разі кількох точок призначення подібна операція може тривати тривалий час. Одержувач також повинен бути в змозі запросити втрачені частини і, звичайно, центральна частина повинна відповісти на такий запит коректно, посилаючи тільки пропущені дані.

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

Залишається тільки час від часу відправляти свіжу стабільну конфігурацію, наприклад через те, що recent став занадто великим. Також важливим тут є і те, що ми розсилаємо всі ці оновлення в режимі бродкасту/мультикасту, не турбуючись про окремих одержувачів та їхню здатність зібрати шматочки даних разом. Як тільки ми переконалися в тому, що всі мають коректний stable — ми відправляємо тільки нові recent. Чи варто уточнювати, що це працює? Працює. Stable кешується на конфігураційному сервері та одержувачах, recent створюється за потребою.

Архітектура дворівневого транспорту

Чому ми збудували наш транспорт на двох рівнях? Відповідь досить проста - ми хотіли відокремити маршрутизацію від високорівневої логіки, черпаючи натхнення в моделі OSI з її транспортним рівнем та рівнем додатків. На роль транспортного протоколу ми взяли Thrift, а для високорівневого формату повідомлень, що управляють, — формат серіалізації msgpack. Саме тому роутер (виконує multicast/broadcast/relay) не дивиться всередину msgpack, не розпаковує та не упаковує вміст назад і виконує лише пересилання даних.

Thrift (з англ. - "ощадливість", вимовляється як [θrift]) - мова опису інтерфейсів, яка використовується для визначення та створення служб під різні мови програмування. Фреймворк до віддаленого виклику процедур (RPC). Поєднує в собі програмний конвеєр з двигуном генерації коду для розробки служб, які в тій чи іншій мірі ефективно і легко працюють між мовами.

Ми взяли фреймворк Thrift через RPC та підтримку багатьох мов. Як завжди, легкими частинами стали клієнт і сервер. Проте, роутер виявився міцним горішком, частково через відсутність готового рішення під час нашої розробки.

Система керування конфігурацією мережі фільтрації QratorІснують й інші опції, типу protobuf/gRPC, проте, коли ми розпочинали наш проект, gRPC був досить молодим і ми не зважилися взяти його на борт.

Звичайно, ми могли (і насправді так і варто було вчинити) створити власний велосипед. Було б простіше створити протокол для того, що нам потрібно, тому що клієнт-серверна архітектура є прямолінійною в реалізації порівняно з тим, щоб побудувати роутер на Thrift. Так чи інакше, до самописних протоколів і реалізації популярних бібліотек (не дарма) існує традиційне упереджене ставлення, крім того, під час обговорення завжди порушується питання: «А як ми це портуватимемо іншими мовами?». Тому ми одразу ж викинули ідеї про велосипед.

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

На першому рівні ми маємо Thrift з мінімум необхідної роутеру інформації для пересилання повідомлення. На другому рівні – упаковані структури msgpack.

Ми вибрали msgpack тому що воно швидше і компактніше порівняно з JSON. Але що ще важливіше, він підтримує кастомні типи даних, дозволяючи нам використовувати круті фічі типу передачі сирих бінарників або спеціальних об'єктів, що позначають відсутність даних, що було важливим для нашої схеми “recent-stable”.

JMESPath
JMESPath це мова запитів до JSON.
Саме так виглядає опис, який ми отримуємо з офіційної документації JMESPath, але насправді він дає набагато більше. JMESPath дозволяє шукати та фільтрувати піддерев'я у довільній деревоподібній структурі, а також застосовувати зміни до даних на льоту. А ще він дозволяє додавати спеціальні фільтри та процедури перетворення даних. Хоча він, звичайно, вимагає напруження головного мозку для розуміння.

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

Для генерації конфігураційного файлу нам потрібен запит JMESPath, шаблон для розташування файлу у ФС, шаблон для самого конфігу. Також цьому етапі непогано уточнити права доступу до файлу. Все це вдалося успішно скомбінувати в одному файлі - перед початком шаблону конфігурації ми ставимо заголовок у форматі YAML, що описує інше.

Наприклад:

---
selector: "[@][[email protected]._meta.version == `42`] | items([0].fft_config || `{}`)"
destination_filename: "fft/{{ match[0] }}.json"
file_mode: 0644
reload_daemons: [fft] ...
{{ dict(match[1]) | json(indent=2, sort_keys=True) }}

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

Що змінилося після введення QControl у операційну діяльність? Перше і найважливіше – узгоджена та надійна доставка оновлень конфігурації по всіх вузлах мережі. Друге — отримання потужного інструменту перевірки конфігурації та внесення змін до неї командою підтримки, а також споживачами послуги.

Все це нам вдалося зробити за допомогою схеми оновлень recent-stable для спрощення комунікації між конфігураційним сервером і одержувачами конфігурації. Використовуючи дворівневий протокол для підтримки незалежного від способу маршрутизації даних. Успішно інтегрувавши заснований на Jinja двигун конфігурації генерації в розподілену мережу фільтрації. Ця система підтримує широкий набір способів конфігурації для нашої розподіленої та різношерстної периферії.

За допомогу в написанні матеріалу дякую VolanDamrod, serenheit, Ні.

Англійська версія посту.

Джерело: habr.com

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