
TL;DR: Description de l'architecture client-serveur de notre systĂšme interne de gestion de configuration de rĂ©seau, QControl. Ă la base se trouve un protocole de transport Ă deux niveaux, fonctionnant avec des messages compressĂ©s en gzip sans dĂ©compression entre les points de terminaison. Des routeurs distribuĂ©s et des points de terminaison reçoivent des mises Ă jour de configuration, et le protocole permet l'installation de relais intermĂ©diaires localisĂ©s. Le systĂšme est construit selon le principe de ('recent-stable', expliquĂ© ci-dessous) et utilise le langage de requĂȘtes JMESpath avec le moteur de modĂšles Jinja pour le rendu des fichiers de configuration.
Qrator Labs gĂšre un rĂ©seau mondial distribuĂ© de neutralisation des attaques. Notre rĂ©seau fonctionne sur le principe de l'anycast, et les sous-rĂ©seaux sont annoncĂ©s via BGP. En tant que rĂ©seau BGP anycast, physiquement situĂ© dans plusieurs rĂ©gions du monde, nous pouvons traiter et filtrer le trafic illicite plus prĂšs du cĆur d'Internet - auprĂšs des opĂ©rateurs Tier-1.
D'un autre cĂŽtĂ©, ĂȘtre un rĂ©seau gĂ©ographiquement distribuĂ© n'est pas simple. La communication entre les points de prĂ©sence du rĂ©seau est cruciale pour le fournisseur de services de sĂ©curitĂ© afin de maintenir une configuration cohĂ©rente de tous les nĆuds du rĂ©seau, en les mettant Ă jour en temps utile. Par consĂ©quent, afin de fournir le niveau de service principal maximum possible pour le consommateur, nous devions trouver un moyen de synchroniser de maniĂšre fiable les donnĂ©es de configuration entre les continents.
Au commencement était le Verbe. Il est rapidement devenu un protocole de communication nécessitant une mise à jour.
La pierre angulaire de l'existence de QControl et la principale raison de la dĂ©pense considĂ©rable de temps et de ressources pour la mise en place d'un tel protocole est la nĂ©cessitĂ© d'obtenir une source de configuration unique et autorisĂ©e, et finalement de synchroniser nos points de prĂ©sence avec celle-ci. Le stockage lui-mĂȘme n'Ă©tait qu'une des nombreuses exigences lors du dĂ©veloppement de QControl. De plus, nous avions Ă©galement besoin d'intĂ©grations avec les services existants et prĂ©vus sur les points de prĂ©sence (PP), des mĂ©thodes intelligentes (et configurables) de validation des donnĂ©es, ainsi qu'une gestion des accĂšs. En outre, nous voulions Ă©galement gĂ©rer ce systĂšme par des commandes, plutĂŽt que par des modifications directes dans les fichiers. Avant QControl, les donnĂ©es Ă©taient envoyĂ©es aux points de prĂ©sence presque manuellement. Si l'un des points de prĂ©sence Ă©tait indisponible, et que nous oubliions de le mettre Ă jour plus tard, la configuration devenait dĂ©synchronisĂ©e â ce qui nous obligeait Ă investir du temps pour la remettre en Ă©tat.
En fin de compte, nous avons élaboré le schéma suivant :

Le serveur de configuration est responsable de la validation des données et du stockage, le routeur possÚde plusieurs points de terminaison qui reçoivent et retransmettent les mises à jour de configuration des clients et de l'équipe de support vers le serveur, et du serveur vers les points de présence.
La qualitĂ© de la connexion Internet varie encore considĂ©rablement dans diffĂ©rentes rĂ©gions du monde â pour illustrer cette thĂšse, examinons un simple MTR de Prague, RĂ©publique tchĂšque vers Singapour et Hong Kong.
MTR de Prague Ă Singapour
Le mĂȘme trajet vers Hong Kong
Des latences Ă©levĂ©es signifient une vitesse rĂ©duite. De plus, il y a perte de paquets. La bande passante ne compense pas ce problĂšme, qui doit toujours ĂȘtre pris en compte lors de la construction de systĂšmes dĂ©centralisĂ©s.
La configuration complÚte d'un point de présence représente un volume important de données à transmettre à de nombreux destinataires via des connexions peu fiables. Heureusement, bien que la configuration change constamment, cela se fait par petites portions.
Conception recent-stable
On peut dire que la construction d'un rĂ©seau distribuĂ© selon le principe des mises Ă jour incrĂ©mentielles est une solution assez Ă©vidente. Mais il y a de nombreux problĂšmes associĂ©s aux diffs. Nous devons conserver tous les diffs entre les points de rĂ©fĂ©rence, ainsi que pouvoir les envoyer Ă nouveau si quelqu'un a manquĂ© une partie des donnĂ©es. Chaque point de destination doit les appliquer dans un ordre strictement dĂ©fini. Habituellement, dans le cas de plusieurs points de destination, une telle opĂ©ration peut prendre beaucoup de temps. Le destinataire doit Ă©galement ĂȘtre en mesure de demander les parties manquantes et, bien sĂ»r, la partie centrale doit rĂ©pondre Ă cette demande de maniĂšre appropriĂ©e, en envoyant uniquement les donnĂ©es manquantes.
Au final, nous en sommes arrivĂ©s Ă une solution assez intĂ©ressante : nous avons un seul niveau de rĂ©fĂ©rence, fixe, que nous appellerons stable, et un seul diff pour cela â recent. Chaque recent est basĂ© sur le dernier stable formĂ© et est suffisant pour reconstruire les donnĂ©es de configuration. Une fois qu'un recent frais arrive Ă destination, l'ancien n'est plus nĂ©cessaire.
Il ne reste plus qu'Ă envoyer de temps en temps une nouvelle configuration stable, par exemple parce que le recent est devenu trop volumineux. Un autre point important ici est que nous diffusons toutes ces mises Ă jour en mode broadcast/multicast, sans nous soucier des destinataires individuels et de leur capacitĂ© Ă rassembler les morceaux de donnĂ©es. Une fois que nous sommes sĂ»rs que tout le monde a un stable correct, nous envoyons uniquement les nouveaux recent. Faut-il prĂ©ciser que cela fonctionne ? Ăa fonctionne. Le stable est mis en cache sur le serveur de configuration et sur les destinataires, le recent est créé si nĂ©cessaire.
Architecture de transport Ă deux niveaux
Pourquoi avons-nous construit notre transport sur deux niveaux ? La rĂ©ponse est assez simple : nous voulions sĂ©parer le routage de la logique de haut niveau, en nous inspirant du modĂšle OSI avec son niveau de transport et son niveau d'application. Pour le protocole de transport, nous avons choisi Thrift, et pour le format Ă©levĂ© des messages de contrĂŽle â le format de sĂ©rialisation msgpack. C'est pourquoi le routeur (effectuant multicast/broadcast/relay) ne regarde pas Ă l'intĂ©rieur de msgpack, ne dĂ©compresse pas et ne recompresse pas le contenu, et ne fait que le transfert des donnĂ©es.
Thrift (de l'anglais « thrift », prononcé comme [Ξrift]) est un langage de description des interfaces utilisé pour définir et créer des services dans différents langages de programmation. Il constitue un cadre pour les appels de procédures à distance (RPC). Il combine un pipeline de programmation avec un moteur de génération de code pour développer des services qui fonctionnent efficacement et facilement entre les langages.
Nous avons choisi le cadre Thrift en raison de la RPC et du support de nombreux langages. Comme d'habitude, les parties les plus simples Ă©taient le client et le serveur. Cependant, le routeur s'est rĂ©vĂ©lĂ© ĂȘtre un vĂ©ritable dĂ©fi, en partie Ă cause de l'absence de solution prĂȘte Ă l'emploi durant notre dĂ©veloppement.
Il existe d'autres options, comme protobuf / gRPC, cependant, lorsque nous avons commencé notre projet, gRPC était encore relativement jeune et nous n'avons pas osé l'intégrer.
Bien sĂ»r, nous aurions pu (et en fait, cela aurait Ă©tĂ© la bonne chose Ă faire) crĂ©er notre propre solution. Il aurait Ă©tĂ© plus simple de crĂ©er un protocole correspondant Ă nos besoins, car l'architecture client-serveur est relativement directe Ă mettre en Ćuvre par rapport Ă la construction d'un routeur sur Thrift. Quoi qu'il en soit, les protocoles auto-Ă©crits et les mises en Ćuvre de bibliothĂšques populaires font souvent l'objet d'un prĂ©jugĂ© traditionnel, de plus, la question revient toujours : « Comment allons-nous le porter sur d'autres langages ? ». Par consĂ©quent, nous avons rapidement Ă©cartĂ© l'idĂ©e du vĂ©lo.
Msgpack est analogue à JSON, mais plus rapide et plus léger. Il s'agit d'un format binaire de sérialisation de données qui permet d'échanger des données entre de nombreux langages.
Au premier niveau, nous avons Thrift avec le minimum d'informations nécessaires au routeur pour transmettre un message. Au deuxiÚme niveau, nous avons les structures empaquetées de msgpack.
Nous avons choisi msgpack car il est plus rapide et plus compact par rapport à JSON. Mais ce qui est encore plus important, c'est qu'il prend en charge les types de données personnalisés, nous permettant d'utiliser des fonctionnalités intéressantes telles que la transmission de binaires bruts ou d'objets spéciaux représentant l'absence de données, ce qui était crucial pour notre schéma « recent-stable ».
JMESPath
JMESPath est un langage de requĂȘtes pour JSON.
C'est ainsi que se présente la description que nous obtenons de la documentation officielle de JMESPath, mais en réalité, il offre bien plus. JMESPath permet de rechercher et de filtrer des sous-arbres dans une structure arborescente quelconque, ainsi que d'appliquer des modifications aux données à la volée. De plus, il permet d'ajouter des filtres spéciaux et des procédures de transformation des données. Bien qu'il nécessite sans aucun doute un certain effort de compréhension.
Jinja
Pour certains utilisateurs, nous devons transformer la configuration en fichier â c'est pourquoi nous utilisons un moteur de modĂšles et Jinja est un choix Ă©vident. GrĂące Ă cela, nous gĂ©nĂ©rons un fichier de configuration Ă partir d'un modĂšle et des donnĂ©es reçues Ă la destination.
Pour gĂ©nĂ©rer le fichier de configuration, nous avons besoin d'une requĂȘte JMESPath, d'un modĂšle pour l'emplacement du fichier dans le systĂšme de fichiers, et d'un modĂšle pour le fichier de configuration lui-mĂȘme. Ă ce stade, il est Ă©galement utile de prĂ©ciser les permissions d'accĂšs au fichier. Tout cela a rĂ©ussi Ă ĂȘtre combinĂ© avec succĂšs dans un seul fichier â avant le dĂ©but du modĂšle de configuration, nous plaçons un en-tĂȘte au format YAML, dĂ©crivant le reste.
Par exemple :
---
sélecteur : "[@][?@.fft._meta.version == `42`] | items([0].fft_config || `{}`)"
nom_fichier_destination : "fft/{{ match[0] }}.json"
mode_fichier : 0644
recharger_daemons : [fft]
...
{{ dict(match[1]) | json(indent=2, sort_keys=True) }}
Pour créer un fichier de configuration pour un nouveau service, il suffit d'ajouter un nouveau fichier modÚle. Aucune modification du code source ou du logiciel aux points de présence n'est nécessaire.
Qu'est-ce qui a changĂ© aprĂšs l'introduction de QControl dans les opĂ©rations ? La premiĂšre et la plus importante â une livraison cohĂ©rente et fiable des mises Ă jour de configuration sur tous les nĆuds du rĂ©seau. DeuxiĂšmement, obtenir un puissant outil de vĂ©rification de configuration et de modifications par notre Ă©quipe de support, ainsi que par les consommateurs du service.
Tout cela a été possible en utilisant le schéma de mises à jour recent-stable pour simplifier la communication entre le serveur de configuration et les destinataires de la configuration. En utilisant un protocole à deux niveaux pour maintenir une méthode de routage des données indépendante du contenu. Nous avons réussi à intégrer un moteur de génération de configuration basé sur Jinja dans un réseau distribué de filtrage. Ce systÚme prend en charge un large éventail de méthodes de configuration pour notre périphérie distribuée et hétérogÚne.
Merci pour votre aide dans la rédaction de ce matériel. , , .
post.
Source : habr.com
