
TL;DR: Opis arhitekture klijent-server naše interne upravljačke konfiguracijske mreže, QControl. Osnova se sastoji od dvostepeno transportnog protokola koji radi sa gzip paketiranim porukama bez dekompresije između krajnjih tačaka. Distribuirani usmjerivači i krajnje tačke primaju ažuriranja konfiguracije, a sam protokol omogućava postavljanje lokalizovanih međureleja. Sistem je izgrađen prema principu (“recent-stable”, objašnjeno u nastavku) i koristi JMESpath jezik upita zajedno sa Jinja predloškom za generisanje konfiguracijskih datoteka.
Qrator Labs upravlja globalno distribuiranom mrežom za neutralizaciju napada. Naša mreža radi prema principu anycast, a podmreže se najavljuju putem BGP-a. Kao BGP anycast mreža, fizički locirana u nekoliko regija Zemlje, možemo obraditi i filtrirati nelegitiman saobraćaj bliže jezgru interneta — Tier-1 operaterima.
S druge strane, biti geografijski distribuirana mreža nije lako. Komunikacija između mrežnih tačaka prisustva je kritično važna za pružatelje sigurnosnih usluga da bi imali dosljednu konfiguraciju svih čvorova mreže, ažurirajući ih u pravom trenutku. Stoga, s ciljem da pružimo maksimalni mogući nivo osnovne usluge potrošaču, morali smo pronaći način da pouzdano sinkronizujemo konfiguracione podatke između kontinenata.
Na početku bijaše Riječ. Ona je brzo postala komunikacijski protokol koji treba ažuriranje.
Temelj obstoja QControl in hkrati glavni razlog za mnoge izgubljene ure in vire pri razvoju takšnega protokola je potreba po enotnem avtoritativnem viru konfiguracije ter usklajevanju naših točk prisotnosti. Samo skladišče je bilo le eden od več zahtev med razvojem QControl. Poleg tega smo potrebovali tudi integracije z obstoječimi in načrtovanimi storitvami na točkah prisotnosti (TP), pametne (in prilagodljive) metode validacije podatkov ter omejevanje dostopa. Želeli smo tudi upravljati s sistemom preko ukazov, ne pa s spreminjanjem datotek. Pred QControl so se podatki na točke prisotnosti prenašali skoraj ročno. Če je bila ena od točk prisotnosti nedostopna in smo jo pozabili posodobiti kasneje, je bila konfiguracija nesinhronizirana — potrebovali smo čas, da jo vrnemo v delovanje.
Na koncu smo zasnovali naslednjo shemo:

Konfiguracijski strežnik je odgovoren za validacijo podatkov in skladiščenje, usmerjevalnik ima več končnih točk, ki prejemajo in prenašajo posodobitve konfiguracije od strank in podpornih ekip na strežnik ter s strežnika do točk prisotnosti.
Kakovost internetne povezave se še vedno močno razlikuje po različnih delih sveta — da to ilustriramo, si oglejmo preprost MTR iz Prage, Češke republike v Singapur in Hongkong.
MTR iz Prage v Singapur
Enako v Hongkong
Visoke zakasnitve pomenijo nižjo hitrost. Poleg tega so težave z izgubo paketov. Širina pasu ne kompenzira tega problema, ki ga je treba vedno upoštevati pri gradnji decentraliziranih sistemov.
Popolna konfiguracija točke prisotnosti je velik obseg podatkov, ki jih je treba prenesti mnogim prejemnikom po nezanesljivih povezavah. Na srečo, čeprav se konfiguracija nenehno spreminja, se to dogaja v majhnih delih.
Oblikovanje recent-stable
Lahko rečemo, da je gradnja porazdeljene mreže po načelu inkrementalnih posodobitev precej očitna rešitev. Vendar pa so z difi povezane številne težave. Ohraniti moramo vse dife med referenčnimi točkami ter zmožnost posredovanja le-teh, če je nekdo zamudil del podatkov. Vsaka ciljna točka mora te dife uporabiti v strogo določenem zaporedju. Običajno lahko pri več ciljnih točkah takšna operacija traja dolgo časa. Prejemnik mora imeti tudi možnost zahtevati zamujene dele in, seveda, osrednji del mora na takšen zahtevek ustrezno odgovoriti, tako da pošlje samo manjkajoče podatke.
Na koncu smo prišli do zelo zanimive rešitve — imamo samo en referenčni sloj, stalen, imenujmo ga stable, in samo en dif za njega — recent. Vsak recent je temelji na zadnjem oblikovanem stable in je dovolj za ponovno sestavljanje konfiguracijskih podatkov. Takoj ko svež recent prispe na cilj, stari več ni potreben.
Preostane le, da občasno pošljemo svežo stable konfiguracijo, na primer, ker je recent postal prevelik. Pomembno je tudi to, da vse te posodobitve pošiljamo v načinu broadcast/multicast, ne da bi skrbeli za posamezne prejemnike in njihovo sposobnost združevanja koščkov podatkov. Takoj ko smo se prepričali, da ima vsak pravilno stable — pošljemo samo nove recent. Ali je potrebno poudariti, da to deluje? Deluje. Stable se shranjuje na konfiguracijskem strežniku in pri prejemnikih, recent se ustvarja po potrebi.
Dvoslojna arhitektura prenosa
Zakaj smo zgradili naš transport na dveh nivojih? Odgovor je razmeroma preprost — želeli smo ločiti usmerjanje od visoko nivojske logike, navdih izhajajoč iz OSI modela s svojim transportnim in aplikacijskim nivojem. Za transportni protokol smo uporabili Thrift, za visoko nivojski format nadzornih sporočil pa format serializacije msgpack. Zato usmernik (ki izvaja multicast/broadcast/relay) ne pogleda v msgpack, ne razpakira in ponovno ne pakira vsebine ter izvaja samo posredovanje podatkov.
Thrift (iz angleščine »varčevanje«, izgovarja se [θrift]) je jezik za opis vmesnikov, ki se uporablja za definiranje in ustvarjanje storitev v različnih programskih jezikih. Je okvir za oddaljene klice postopkov (RPC). Povezuje programski pretok z motorjem za generiranje kode za razvoj storitev, ki delujejo učinkovito in enostavno med jeziki.
Izbrali smo okvir Thrift zaradi RPC in podpore mnogim jezikom. Kot običajno sta se enostavna dela izkazala za odjemalca in strežnik. Vendar pa se je usmerjevalnik izkazal za trd oreh, deloma zaradi pomanjkanja pripravljenih rešitev med našim razvojem.
Obstajajo tudi druge možnosti, kot sta protobuf / gRPC, vendar, ko smo začeli naš projekt, je bil gRPC še vedno precej mlad in nismo se odločili, da bi ga uvedli.
Seveda smo lahko (in v resnici bi bilo smiselno) ustvarili lastno rešitev. Bilo bi lažje razviti protokol za naše potrebe, saj je arhitektura odjemalec-strežnik relativno enostavna za implementacijo v primerjavi s postavitvijo usmerjevalnika na Thrift. Tako ali drugače pa je pri lastnih protokolih in implementacijah priljubljenih knjižnic (ni brez razloga) prisotna tradicionalna pristranskost, poleg tega se vedno postavlja vprašanje: »Kako bomo to portirali na druge jezike?«. Zato smo že na začetku zavrgli ideje o lastni rešitvi.
Msgpack je analog JSON, vendar hitrejši in manjši. To je binarni format za serializacijo podatkov, ki omogoča izmenjavo podatkov med več jeziki.
Na prvi ravni imamo Thrift z najmanj potrebnimi informacijami za usmerjevalnik za pošiljanje sporočil. Na drugi ravni so pakirane strukture msgpack.
Izbrali smo msgpack, ker je hitrejši in bolj kompakten v primerjavi z JSON. Toda kar je še pomembneje, podpira prilagojene tipe podatkov, kar nam omogoča uporabo zanimivih funkcij, kot je prenos surovih binarnih podatkov ali posebnih objektov, ki označujejo odsotnost podatkov, kar je bilo pomembno za našo shemo “recent-stable”.
JMESPath
JMESPath je jezik poizvedovanja za JSON.
Tako izgleda opis, ki ga dobimo iz uradne dokumentacije JMESPath, ampak v resnici ponuja veliko več. JMESPath omogoča iskanje in filtriranje poddreves v poljubni drevesni strukturi ter tudi uporabo sprememb podatkov v realnem času. Poleg tega omogoča dodajanje posebnih filtrov in postopkov za obdelavo podatkov. Čeprav je seveda potrebna malce duševne napetosti za razumevanje.
Jinja
Za nekatere uporabnike je potrebno konfiguracijo pretvoriti v datoteko — zato uporabljamo predlogovni motor, pri čemer je Jinja očitna izbira. Z njeno pomočjo generiramo konfiguracijsko datoteko iz predloge in podatkov, prejetih na končni točki.
Za generacijo datoteke konfiguracije potrebujemo JMESPath poizvedbo, predlogo za lokacijo datoteke v datotečnem sistemu in predlogo za samo konfiguracijo. Na tej točki je prav tako dobro natančno določiti pravice dostopa do datoteke. Vse to se je uspelo uspešno kombinirati v eni datoteki - pred začetkom predloge konfiguracije postavimo naslov v formatu YAML, ki opisuje ostalo.
Na primer:
---
selector: "[@][?@.fft._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) }}
Za ustvarjanje konfiguracijske datoteke za novo storitev dodajamo le novo datoteko predloge. Ni potrebno nikakršne spremembe izvorne kode ali programske opreme na prisotnih točkah.
Kaj se je spremenilo po uvedbi QControl v operativno dejavnost? Prvo in najpomembnejše — usklajena in zanesljiva dostava posodobitev konfiguracije po vseh vozliščih omrežja. Drugo — pridobitev močnega orodja za preverjanje konfiguracije in vnašanje sprememb s strani naše podporne ekipe ter uporabnikov storitve.
To smo lahko dosegli z uporabo sheme posodobitev recent-stable za poenostavitev komunikacije med konfiguracijskim strežnikom in prejemniki konfiguracije. Uporabljamo dvostopenjski protokol za podporo neodvisnemu načinu usmerjanja podatkov, ne glede na vsebino. Uspelo nam je uspešno integrirati Jinja-podprt generator konfiguracij v distribuirano omrežje filtriranja. Sistem podpira širok spekter načinov konfiguracije za našo distribuirano in raznoliko periferijo.
Hvala za pomoč pri pisanju gradiva , , .
post.
Izvor: habr.com
