
TL;DR: Popis klient-server architektúry nášho interného systému správy konfigurácie siete, QControl. Základom je dvojúrovňový transportný protokol, ktorý pracuje s komprimovanými gzip správami bez dekompresie medzi koncovými bodmi. Distribuované routery a koncové body prijímajú aktualizácie konfigurácie a samotný protokol umožňuje inštaláciu lokalizovaných medzi relay. Systém je postavený na princípe (“recent-stable”, vysvetlené nižšie) a využíva jazyk dopytov JMESpath spolu so šablonovačom Jinja na rendrovanie konfiguračných súborov.
Qrator Labs spravuje globálne distribuovanú sieť na neutralizáciu útokov. Naša sieť funguje na princípe anycast, pričom podsiete sú oznamované prostredníctvom BGP. Ako BGP anycast sieť, fyzicky umiestnená v niekoľkých regiónoch Zeme, môžeme spracovať a filtrovať nelegitímny trafic bližšie k jadru internetu — Tier-1 operátorom.
Na druhej strane, byť geograficky distribuovanou sieťou nie je jednoduché. Komunikácia medzi sieťovými prístupovými bodmi je kriticky dôležitá pre poskytovateľa bezpečnostných služieb, aby mala konzistentnú konfiguráciu všetkých uzlov siete, a pravidelne ich aktualizovala. Preto, aby sme poskytli maximálnu možnú úroveň základnej služby pre zákazníka, museli sme nájsť spôsob, ako spoľahlivo synchronizovať konfiguračné údaje medzi kontinentmi.
Na začiatku bolo Slovo. Rýchlo sa stalo komunikačným protokolom, ktorý potreboval aktualizáciu.
Základným kameňom existencie QControl a zároveň hlavnou príčinou významného množstva času a zdrojov vynaložených na vytvorenie takého druhu protokolu je potreba získať jediný autoritný zdroj konfigurácie a nakoniec synchronizovať naše body prítomnosti s ním. Samotné úložisko bolo len jedným z niekoľkých požiadaviek počas vývoja QControl. Okrem toho sme potrebovali aj integrácie s existujúcimi a plánovanými službami na bodoch prítomnosti (BP), inteligentné (a prispôsobiteľné) spôsoby validácie údajov, ako aj obmedzenie prístupu. Navyše sme chceli spravovať takýto systém pomocou príkazov, a nie úprav v súboroch. Pred QControl boli údaje posielané na body prítomnosti prakticky ručne. Ak bol jeden z bodov prítomnosti nedostupný a zabudli sme ho aktualizovať neskôr, konfigurácia bola desynchronizovaná — bolo potrebné venovať čas na jej opätovné uvedenie do prevádzky.
Nakoniec sme prišli na nasledujúcu schému:

Konfiguračný server je zodpovedný za validáciu údajov a úložisko, smerovač má niekoľko koncových bodov, ktoré prijímajú a vysielajú aktualizácie konfigurácie od klientov a tímu podpory na server, a z servera na body prítomnosti.
Kvalita internetového pripojenia sa stále výrazne líši v rôznych kútoch sveta — na ilustráciu tohto tvrdenia si pozrime jednoduché MTR z Prahy, Českej republiky do Singapuru a Hongkongu.
MTR z Prahy do Singapuru
To isté do Hongkongu
Vysoké latencie znamenajú nižšiu rýchlosť. Okrem toho dochádza k strate paketov. Šírka kanálov túto problém kompenzuje nezvyčajne a táto skutočnosť by sa vždy mala brať do úvahy pri budovaní decentralizovaných systémov.
Plná konfigurácia bodu prítomnosti predstavuje významný objem údajov, ktoré je potrebné preniesť mnohým prijímateľom cez nespolehlivé pripojenia. Našťastie, hoci sa konfigurácia neustále mení, deje sa to v malých porciách.
Dizajn recent-stable
Môžeme povedať, že vytvorenie distribuovanej siete na princípe inkrementálnych aktualizácií je dosť zrejmým riešením. Ale s diffmi sú spojené množstvo problémov. Musíme uchovávať všetky diffy medzi referenčnými bodmi a tiež ich dokázať doručiť, ak niekto zmeškal časť údajov. Každý cieľový bod by ich mal aplikovať v striktne zadanej sekvencii. Zvyčajne v prípade niekoľkých cieľových bodov môže takáto operácia trvať dlhý čas. Prijímač by mal byť schopný si vyžiadať chýbajúce časti a samozrejme, centrálny bod by mal na takúto žiadosť reagovať správne, posielajúc iba chýbajúce údaje.
Nakoniec sme dospeli k dosť zaujímavému riešeniu — máme iba jednu referenčnú vrstvu, pevnú, nazvime ju stable, a iba jeden diff pre ňu — recent. Každý recent je založený na posledne vytvorenom stable a je dostatočný na prestavbu konfiguračných údajov. Akonáhle čerstvý recent dorazí na cieľ, starý už nie je potrebný.
Zostáva len občas posielať čerstvú stable konfiguráciu, napríklad preto, že recent sa stal príliš veľkým. Rovnako dôležité je aj to, že všetky tieto aktualizácie rozosielame v režime broadcast/multicast, aniž by sme sa zaoberali jednotlivými prijímačmi a ich schopnosťou zozbierať kúsky dát dokopy. Akonáhle máme istotu, že všetci majú správny stable — posielame len nové recent. Stojí za to objasniť, že to funguje? Funguje. Stable je cachované na konfiguračnom serveri a prijímačoch, recent sa vytvára podľa potreby.
Architektúra dvojúrovňovej dopravy
Prečo sme postavili našu dopravu na dvoch úrovniach? Odpoveď je dosť jednoduchá — chceli sme oddeliť smerovanie od vysokúrovňovej logiky, inšpirujúc sa modelom OSI so svojou transportnou vrstvou a vrstvou aplikácií. Na úlohu transportného protokolu sme zvolili Thrift a pre vysokúrovňový formát riadiacich správ formát serializácie msgpack. Preto router (vykonávajúci multicast/broadcast/relay) nepozerá dovnútra msgpack, nerozbaľuje a nebalí obsah späť a vykonáva len preposielanie údajov.
Thrift (z angličtiny „úspornosť“, vyslovuje sa ako [θrift]) je jazyk na popis rozhraní, ktorý sa používa na definovanie a vytváranie služieb pre rôzne programovacie jazyky. Je to rámec pre vzdialené volanie procedúr (RPC). Spojuje softvérový pipeline s generátorom kódu na vývoj služieb, ktoré efektívne a jednoducho fungujú medzi jazykmi.
Vybrali sme si rámec Thrift kvôli RPC a podpore mnohých jazykov. Ako obvykle, jednoduchými časťami sú klient a server. Avšak, router sa ukázal ako tvrdý oriešok, z časti kvôli nedostatku hotových riešení počas našej vývoja.
Existujú aj iné možnosti, ako protobuf / gRPC, avšak, keď sme začínali náš projekt, gRPC bol dosť mladý a neodvážili sme sa ho zaviesť.
Samozrejme, mohli sme (a v skutočnosti, tak to aj bolo treba urobiť) vytvoriť si vlastné riešenie. Bolo by jednoduchšie vytvoriť protokol pre to, čo potrebujeme, pretože klient-server architektúra je relatívne priamočiarou na implementáciu v porovnaní s vybudovaním routera v Thrift. Tak či onak, k vlastným protokolom a implementáciám populárnych knižníc (nie bezdôvodne) existuje tradičný predsudok, okrem toho sa vždy pri diskusiách vynára otázka: „Ako to prenesieme na iné jazyky?“. Preto sme okamžite zamietli myšlienku o vlastnom riešení.
Msgpack je analóg JSON, ale rýchlejší a menší. Je to binárny formát serializácie dát, ktorý umožňuje výmenu dát medzi množstvom jazykov.
Na prvej úrovni máme Thrift s minimálne potrebnými informáciami pre router na preposielanie správ. Na druhej úrovni sú zabalené štruktúry msgpack.
Vybrali sme msgpack, pretože je rýchlejší a kompaktnejší v porovnaní s JSON. Ale to, čo je ešte dôležitejšie, podporuje vlastné typy dát, čo nám umožňuje využívať skvelé funkcie ako prenos surových binárov alebo špeciálnych objektov, ktoré označujú absenciu dát, čo bolo dôležité pre našu schému „recent-stable“.
JMESPath
JMESPath je jazyk dotazovania na JSON.
Toto je presne taký popis, aký získavame z oficiálnej dokumentácie JMESPath, ale v skutočnosti ponúka oveľa viac. JMESPath umožňuje vyhľadávať a filtrovať podstromy v ľubovoľnej stromovej štruktúre a taktiež aplikovať zmeny na dáta za behu. Navyše, umožňuje pridávať špeciálne filtre a procedúry na transformáciu dát. Aj keď si to samozrejme vyžaduje určitú duševnú námahu na pochopenie.
Jinja
Pre niektorých používateľov je potrebné premeniť konfiguráciu na súbor — preto používame šablónový engine a Jinja je očividnou voľbou. S ňou generujeme konfiguračný súbor zo šablóny a údajov získaných na mieste určenia.
Na vygenerovanie konfiguračného súboru potrebujeme požiadavku JMESPath, šablónu pre umiestnenie súboru v súborovom systéme a šablónu pre samotnú konfiguráciu. Taktiež v tomto štádiu nie je zlé spresniť prístupové práva k súboru. To všetko sa nám podarilo úspešne skombinovať do jedného súboru — pred začiatkom šablóny konfigurácie umiestňujeme hlavičku vo formáte YAML, ktorá popisuje zvyšok.
Napríklad:
---
výber: "[@][?@.fft._meta.verzia == `42`] | položky([0].fft_config || `{}`)"
cielovy_súbor: "fft/{{ match[0] }}.json"
režim_súboru: 0644
reload_daemons: [fft]
...
{{ dict(match[1]) | json(indent=2, sort_keys=True) }}
Aby sme vytvorili konfiguračný súbor pre novú službu, stačí pridať len nový súbor šablóny. Žiadne zmeny v zdrojovom kóde alebo softvéri na miestach prítomnosti nie sú potrebné.
Čo sa zmenilo po zavedení QControl do prevádzky? Prvé a najdôležitejšie — konzistentné a spoľahlivé doručenie aktualizácií konfigurácie po všetkých uzloch siete. Druhé — získanie mocného nástroja na kontrolu konfigurácie a jej zmeny naším podporným tímom, ako aj používateľmi služby.
Toto všetko sa nám podarilo dosiahnuť pomocou schémy aktualizácií recent-stable na zjednodušenie komunikácie medzi konfiguračným serverom a príjemcami konfigurácie. Používame dvojúrovňový protokol na podporu nezávislého od obsahu spôsobu smerovania dát. Úspešne sme integrovali engine na generovanie konfigurácie založený na Jinja do distribuovanej siete filtrácie. Tento systém podporuje široký súbor spôsobov konfigurácie pre našu distribuovanú a rôznorodú perifériu.
Ďakujeme za pomoc pri písaní materiálu. , , .
príspevku.
Zdroj: habr.com
