Bioyino - agrégateur de métriques distribué et évolutif

Vous collectez donc des métriques. Comme nous sommes. Nous collectons également des métriques. Bien sûr, nécessaire pour les affaires. Aujourd'hui, nous allons parler du tout premier maillon de notre système de surveillance - un serveur d'agrégation compatible statsd bioyino, pourquoi nous l'avons écrit et pourquoi nous avons abandonné Brubeck.

Bioyino - agrégateur de métriques distribué et évolutif

De nos articles précédents (1, 2) vous pouvez découvrir que jusqu'à un certain temps, nous collections des notes en utilisant Brubeck. Il est écrit en C. D'un point de vue code, c'est aussi simple qu'un plug (c'est important quand on veut contribuer) et, surtout, il gère nos volumes de 2 millions de métriques par seconde (MPS) au maximum sans aucun problème. La documentation indique la prise en charge de 4 millions de MPS avec un astérisque. Cela signifie que vous obtiendrez le chiffre indiqué si vous configurez correctement le réseau sous Linux. (Nous ne savons pas combien de MPS vous pouvez obtenir si vous laissez le réseau tel quel). Malgré ces avantages, nous avons eu plusieurs plaintes sérieuses concernant Brubeck.

Réclamation 1. Github, le développeur du projet, a cessé de le prendre en charge : en publiant des correctifs et des correctifs, en acceptant les nôtres et (pas seulement les nôtres) les relations publiques. Au cours des derniers mois (entre février et mars 2018), l'activité a repris, mais avant cela, il y a eu presque 2 ans de calme complet. De plus, le projet est en cours de développement pour les besoins internes de Gihub, ce qui peut devenir un obstacle sérieux à l'introduction de nouvelles fonctionnalités.

Réclamation 2. Précision des calculs. Brubeck collecte un total de 65536 30 valeurs pour l'agrégation. Dans notre cas, pour certaines métriques, pendant la période d'agrégation (1 secondes), beaucoup plus de valeurs peuvent arriver (527 392 XNUMX au pic). Suite à cet échantillonnage, les valeurs maximales et minimales apparaissent inutiles. Par exemple, comme ceci :

Bioyino - agrégateur de métriques distribué et évolutif
Comme c'était

Bioyino - agrégateur de métriques distribué et évolutif
Comment ça aurait dû être

Pour la même raison, les montants sont généralement mal calculés. Ajoutez ici un bug avec un débordement de float 32 bits, qui envoie généralement le serveur en erreur de segmentation lors de la réception d'une métrique apparemment innocente, et tout devient génial. Soit dit en passant, le bug n’a pas été corrigé.

Et, enfin, Réclamation X. Au moment de la rédaction de cet article, nous sommes prêts à le présenter aux 14 implémentations de statsd plus ou moins fonctionnelles que nous avons pu trouver. Imaginons qu'une infrastructure se soit tellement développée qu'accepter 4 millions de MPS ne suffit plus. Ou même s'il n'a pas encore augmenté, mais les indicateurs sont déjà si importants pour vous que même de courtes baisses de 2 à 3 minutes dans les graphiques peuvent déjà devenir critiques et provoquer des crises de dépression insurmontables parmi les managers. Le traitement de la dépression étant une tâche ingrate, des solutions techniques sont nécessaires.

Premièrement, la tolérance aux pannes, afin qu'un problème soudain sur le serveur ne provoque pas une apocalypse zombie psychiatrique au bureau. Deuxièmement, évoluer pour pouvoir accepter plus de 4 millions de MPS, sans creuser profondément dans la pile réseau Linux et sans augmenter calmement « en largeur » jusqu'à la taille requise.

Comme nous avions de la marge pour évoluer, nous avons décidé de commencer par la tolérance aux pannes. "À PROPOS DE! Tolérance aux pannes ! C'est simple, nous pouvons le faire », avons-nous pensé et lancé 2 serveurs, en élevant une copie de brubeck sur chacun. Pour ce faire, nous avons dû copier le trafic avec les métriques sur les deux serveurs et même écrire pour cela petit utilitaire. Nous avons résolu le problème de tolérance aux pannes avec cela, mais... pas très bien. Au début, tout semblait bien : chaque brubeck collecte sa propre version d'agrégation, écrit les données dans Graphite toutes les 30 secondes, en écrasant l'ancien intervalle (cela se fait du côté de Graphite). Si un serveur tombe soudainement en panne, nous en avons toujours un deuxième avec sa propre copie des données agrégées. Mais voici le problème : si le serveur tombe en panne, une « scie » apparaît sur les graphiques. Cela est dû au fait que les intervalles de 30 secondes de Brubeck ne sont pas synchronisés et qu'au moment d'un crash, l'un d'entre eux n'est pas écrasé. Lorsque le deuxième serveur démarre, la même chose se produit. Tout à fait tolérable, mais je veux mieux ! Le problème de l’évolutivité n’a pas non plus disparu. Toutes les métriques « volent » toujours vers un seul serveur, et nous sommes donc limités aux mêmes 2 à 4 millions de MPS, selon le niveau du réseau.

Si vous réfléchissez un peu au problème et en même temps déterrez la neige avec une pelle, alors l'idée évidente suivante peut vous venir à l'esprit : vous avez besoin d'un statsd capable de fonctionner en mode distribué. C'est-à-dire celui qui implémente la synchronisation entre les nœuds dans le temps et les métriques. "Bien sûr, une telle solution existe probablement déjà", avons-nous dit en nous tournant vers Google…. Et ils n'ont rien trouvé. Après avoir parcouru la documentation des différentes statistiques (https://github.com/etsy/statsd/wiki#server-implementations au 11.12.2017 décembre XNUMX), nous n'avons absolument rien trouvé. Apparemment, ni les développeurs ni les utilisateurs de ces solutions n'ont encore rencontré autant de métriques, sinon ils trouveraient certainement quelque chose.

Et puis nous nous sommes souvenus du "jouet" statsd - bioyino, qui a été écrit lors du hackathon Just for Fun (le nom du projet a été généré par le script avant le début du hackathon) et avons réalisé que nous avions un besoin urgent de nos propres statsd. Pour quoi?

  • parce qu'il y a trop peu de clones statsd dans le monde,
  • car il est possible de fournir la tolérance aux pannes et l'évolutivité souhaitées ou proches de celles souhaitées (y compris la synchronisation des métriques agrégées entre les serveurs et la résolution du problème des conflits d'envoi),
  • parce qu'il est possible de calculer des métriques avec plus de précision que Brubeck,
  • parce que vous pouvez collecter vous-même des statistiques plus détaillées, que Brubeck ne nous a pratiquement pas fournies,
  • parce que j'ai eu la chance de programmer ma propre application de laboratoire à échelle distribuée hyperperformance, qui ne répétera pas complètement l'architecture d'une autre hyperfor similaire... eh bien, c'est tout.

Sur quoi écrire ? Bien sûr, à Rust. Pourquoi?

  • car il existait déjà une solution prototype,
  • parce que l'auteur de l'article connaissait déjà Rust à cette époque et était impatient d'y écrire quelque chose pour la production avec la possibilité de le mettre en open source,
  • car les langages avec GC ne nous conviennent pas en raison de la nature du trafic reçu (presque en temps réel) et les pauses GC sont pratiquement inacceptables,
  • parce que vous avez besoin de performances maximales comparables à C
  • parce que Rust nous offre une concurrence intrépide, et si nous avions commencé à l'écrire en C/C++, nous aurions récolté encore plus de vulnérabilités, de débordements de tampon, de conditions de concurrence et d'autres mots effrayants que Brubeck.

Il y a également eu une dispute contre Rust. L'entreprise n'avait aucune expérience dans la création de projets dans Rust et nous ne prévoyons désormais pas non plus de l'utiliser dans le projet principal. On craignait donc sérieusement que rien ne marche, mais nous avons décidé de tenter notre chance et d’essayer.

Le temps passait...

Finalement, après plusieurs tentatives infructueuses, la première version fonctionnelle était prête. Ce qui s'est passé? C'est ce qui s'est passé.

Bioyino - agrégateur de métriques distribué et évolutif

Chaque nœud reçoit son propre ensemble de métriques et les accumule, et ne regroupe pas les métriques pour les types pour lesquels leur ensemble complet est requis pour l'agrégation finale. Les nœuds sont connectés les uns aux autres par une sorte de protocole de verrouillage distribué, qui vous permet de sélectionner parmi eux le seul (ici nous avons pleuré) qui est digne d'envoyer des métriques au Grand. Ce problème est actuellement résolu par Consul, mais à l’avenir les ambitions de l’auteur s’étendent à posséder mise en oeuvre Raft, où le plus méritant sera, bien sûr, le nœud leader du consensus. En plus du consensus, les nœuds envoient assez souvent (une fois par seconde par défaut) à leurs voisins les parties de métriques pré-agrégées qu'ils ont réussi à collecter au cours de cette seconde. Il s'avère que la mise à l'échelle et la tolérance aux pannes sont préservées : chaque nœud contient toujours un ensemble complet de métriques, mais les métriques sont envoyées déjà agrégées, via TCP et codées dans un protocole binaire, de sorte que les coûts de duplication sont considérablement réduits par rapport à UDP. Malgré le nombre assez important de métriques entrantes, l’accumulation nécessite très peu de mémoire et encore moins de CPU. Pour nos mertics hautement compressibles, cela ne représente que quelques dizaines de mégaoctets de données. En prime, nous n'obtenons aucune réécriture de données inutile dans Graphite, comme ce fut le cas avec Burbeck.

Les paquets UDP avec métriques sont déséquilibrés entre les nœuds des équipements réseau via un simple Round Robin. Bien entendu, le matériel réseau n’analyse pas le contenu des paquets et peut donc extraire bien plus de 4 millions de paquets par seconde, sans parler des métriques dont il ne sait rien du tout. Si nous tenons compte du fait que les métriques n'arrivent pas une par une dans chaque paquet, nous ne prévoyons aucun problème de performances à cet endroit. Si un serveur tombe en panne, le périphérique réseau détecte rapidement (en 1 à 2 secondes) ce fait et supprime le serveur en panne de la rotation. En conséquence, les nœuds passifs (c'est-à-dire non leaders) peuvent être activés et désactivés pratiquement sans remarquer de baisses sur les graphiques. Le maximum que nous perdons fait partie des métriques arrivées à la dernière seconde. Une perte/arrêt/commutation soudaine d'un leader créera toujours une anomalie mineure (l'intervalle de 30 secondes est toujours désynchronisé), mais s'il y a une communication entre les nœuds, ces problèmes peuvent être minimisés, par exemple, en envoyant des paquets de synchronisation. .

Un peu sur la structure interne. L'application est bien sûr multithread, mais l'architecture des threads est différente de celle utilisée à Brubeck. Les fils de discussion à Brubeck sont les mêmes - chacun d'eux est responsable à la fois de la collecte et de l'agrégation des informations. Dans bioyino, les travailleurs sont répartis en deux groupes : ceux responsables du réseau et ceux responsables de l'agrégation. Cette division vous permet de gérer l'application de manière plus flexible en fonction du type de métriques : là où une agrégation intensive est requise, vous pouvez ajouter des agrégateurs, là où il y a beaucoup de trafic réseau, vous pouvez ajouter le nombre de flux réseau. Actuellement, sur nos serveurs, nous travaillons en 8 flux réseau et 4 flux d'agrégation.

La partie comptage (responsable de l'agrégation) est assez ennuyeuse. Les tampons remplis par les flux réseau sont répartis entre les flux de comptage, où ils sont ensuite analysés et agrégés. Sur demande, des métriques sont fournies pour être envoyées à d'autres nœuds. Tout cela, y compris l'envoi de données entre les nœuds et le travail avec Consul, est effectué de manière asynchrone, en s'exécutant sur le framework. Tokyo.

Beaucoup plus de problèmes lors du développement ont été causés par la partie réseau chargée de recevoir les métriques. L'objectif principal de la séparation des flux de réseau en entités distinctes était la volonté de réduire le temps passé par un flux aucun pour lire les données du socket. Les options utilisant UDP asynchrone et recvmsg régulier ont rapidement disparu : la première consomme trop d'espace CPU pour le traitement des événements, la seconde nécessite trop de changements de contexte. C'est pourquoi il est maintenant utilisé recevoir un message avec de gros tampons (et les tampons, messieurs les officiers, ce n'est rien pour vous !). La prise en charge d'UDP standard est réservée aux cas légers où recvmmsg n'est pas nécessaire. En mode multimessage, il est possible d'atteindre l'essentiel : la grande majorité du temps, le thread réseau ratisse la file d'attente du système d'exploitation - lit les données du socket et les transfère vers le tampon de l'espace utilisateur, ne passant qu'occasionnellement à donner le tampon rempli à agrégateurs. La file d'attente dans le socket ne s'accumule pratiquement pas, le nombre de paquets abandonnés n'augmente pratiquement pas.

Noter

Dans les paramètres par défaut, la taille du tampon est assez grande. Si vous décidez soudainement d'essayer le serveur vous-même, vous risquez de constater qu'après l'envoi d'un petit nombre de métriques, elles n'arriveront pas dans Graphite et resteront dans le tampon du flux réseau. Pour travailler avec un petit nombre de métriques, vous devez définir bufsize et task-queue-size sur des valeurs plus petites dans la configuration.

Enfin, quelques graphiques pour les amateurs de graphiques.

Statistiques sur le nombre de métriques entrantes pour chaque serveur : plus de 2 millions de MPS.

Bioyino - agrégateur de métriques distribué et évolutif

Désactivation de l'un des nœuds et redistribution des métriques entrantes.

Bioyino - agrégateur de métriques distribué et évolutif

Statistiques sur les métriques sortantes : un seul nœud envoie toujours - le patron du raid.

Bioyino - agrégateur de métriques distribué et évolutif

Statistiques de fonctionnement de chaque nœud, prenant en compte les erreurs dans différents modules du système.

Bioyino - agrégateur de métriques distribué et évolutif

Détails des métriques entrantes (les noms des métriques sont masqués).

Bioyino - agrégateur de métriques distribué et évolutif

Que prévoyons-nous de faire de tout cela ensuite ? Bien sûr, écrivez du code, putain... ! Le projet était initialement prévu pour être open-source et le restera tout au long de sa vie. Nos plans immédiats incluent le passage à notre propre version de Raft, le remplacement du protocole homologue par un protocole plus portable, l'introduction de statistiques internes supplémentaires, de nouveaux types de métriques, des corrections de bugs et d'autres améliorations.

Bien entendu, tout le monde est invité à contribuer au développement du projet : créer des relations publiques, des problèmes, si possible nous y répondrons, nous améliorerons, etc.

Cela étant dit, c’est tout, achetez nos éléphants !



Source: habr.com

Ajouter un commentaire