Bioyino: agregador de mètriques distribuïts i escalables

Així que recopileu mètriques. Com som nosaltres. També recollim mètriques. Per descomptat, necessari per als negocis. Avui parlarem del primer enllaç del nostre sistema de monitorització: un servidor d'agregació compatible amb statsd bioyino, per què el vam escriure i per què vam abandonar Brubeck.

Bioyino: agregador de mètriques distribuïts i escalables

Dels nostres articles anteriors (1, 2) podeu esbrinar que fins un moment vam recollir marques utilitzant Brubeck. Està escrit en C. Des del punt de vista del codi, és tan senzill com un endoll (això és important quan es vol contribuir) i, el més important, gestiona els nostres volums de 2 milions de mètriques per segon (MPS) al màxim. sense cap problema. La documentació indica el suport per a 4 milions de MPS amb un asterisc. Això vol dir que obtindreu la xifra indicada si configureu correctament la xarxa a Linux. (No sabem quants MPS podeu obtenir si deixeu la xarxa tal com està). Malgrat aquests avantatges, vam tenir diverses queixes serioses sobre el brubeck.

Reclamació 1. Github, el desenvolupador del projecte, va deixar de donar-li suport: publicant pedaços i correccions, acceptant el nostre i (no només el nostre) PR. En els darrers mesos (entre febrer-març del 2018), l'activitat s'ha reprès, però abans hi havia gairebé 2 anys de calma total. A més, s'està desenvolupant el projecte per a les necessitats internes de Gihub, que pot esdevenir un obstacle seriós per a la introducció de noves funcions.

Reclamació 2. Exactitud dels càlculs. Brubeck recull un total de 65536 valors per a l'agregació. En el nostre cas, per a algunes mètriques, durant el període d'agregació (30 segons), poden arribar molts més valors (1 al pic). Com a resultat d'aquest mostreig, els valors màxim i mínim semblen inútils. Per exemple, així:

Bioyino: agregador de mètriques distribuïts i escalables
Tal com va ser

Bioyino: agregador de mètriques distribuïts i escalables
Com hauria d'haver estat

Per la mateixa raó, les quantitats generalment es calculen incorrectament. Afegiu aquí un error amb un desbordament flotant de 32 bits, que generalment envia el servidor a un error de seg quan rep una mètrica aparentment innocent, i tot es farà genial. L'error, per cert, no s'ha corregit.

I, finalment, Reclamació X. En el moment d'escriure aquest article, estem preparats per presentar-lo a les 14 implementacions d'estadístiques més o menys funcionals que hem pogut trobar. Imaginem que una sola infraestructura ha crescut tant que acceptar 4 milions de MPS ja no n'hi ha prou. O fins i tot si encara no ha crescut, però les mètriques ja són tan importants per a tu que fins i tot caigudes curtes de 2-3 minuts als gràfics ja poden arribar a ser crítiques i provocar atacs de depressió insuperable entre els directius. Com que tractar la depressió és una tasca ingrata, calen solucions tècniques.

En primer lloc, la tolerància a errors, perquè un problema sobtat al servidor no provoqui un apocalipsi psiquiàtric zombi a l'oficina. En segon lloc, escalar per poder acceptar més de 4 milions de MPS, sense aprofundir en la pila de xarxa de Linux i créixer amb calma "en amplitud" fins a la mida requerida.

Com que teníem espai per escalar, vam decidir començar amb la tolerància a fallades. "SOBRE! Falta de tolerància! És senzill, ho podem fer”, vam pensar i vam llançar 2 servidors, aixecant una còpia de brubeck a cadascun. Per fer-ho, vam haver de copiar el trànsit amb mètriques als dos servidors i fins i tot escriure per a això petita utilitat. Hem resolt el problema de la tolerància a errors amb això, però... no gaire bé. Al principi, tot semblava genial: cada brubeck recull la seva pròpia versió d'agregació, escriu dades a Graphite una vegada cada 30 segons, sobreesscrivint l'interval antic (això es fa al costat de Graphite). Si un servidor falla de sobte, sempre en tenim un segon amb la seva pròpia còpia de les dades agregades. Però aquí hi ha el problema: si el servidor falla, apareix una "serra" als gràfics. Això es deu al fet que els intervals de 30 segons de Brubeck no estan sincronitzats i, en el moment d'un accident, un d'ells no se sobreescriu. Quan s'inicia el segon servidor, passa el mateix. Bastant tolerable, però vull millor! El problema de l'escalabilitat tampoc no ha desaparegut. Totes les mètriques encara "volen" a un sol servidor i, per tant, estem limitats als mateixos 2-4 milions de MPS, depenent del nivell de xarxa.

Si penseu una mica en el problema i al mateix temps desenterrau neu amb una pala, us pot venir al cap la següent idea òbvia: necessiteu una estadística que pugui funcionar en mode distribuït. És a dir, un que implementa la sincronització entre nodes en temps i mètriques. "Per descomptat, aquesta solució probablement ja existeix", vam dir i vam anar a Google... I no van trobar res. Després de revisar la documentació de diferents estadístiques (https://github.com/etsy/statsd/wiki#server-implementations a l'11.12.2017 de desembre de XNUMX), no vam trobar absolutament res. Pel que sembla, ni els desenvolupadors ni els usuaris d'aquestes solucions encara s'han trobat amb TANTS mètriques, en cas contrari, sens dubte trobarien alguna cosa.

I aleshores vam recordar les estadístiques de "joguina" - bioyino, que es van escriure al hackathon Just for Fun (el nom del projecte va ser generat pel guió abans de l'inici del hackathon) i ens vam adonar que necessitàvem urgentment les nostres pròpies estadístiques. Per a què?

  • perquè hi ha massa pocs clons d'estadístiques al món,
  • perquè és possible proporcionar la tolerància a errors i escalabilitat desitjades o properes a les desitjades (inclosa la sincronització de mètriques agregades entre servidors i la resolució del problema de l'enviament de conflictes),
  • perquè és possible calcular mètriques amb més precisió que Brubeck,
  • perquè podeu recopilar estadístiques més detallades, que Brubeck pràcticament no ens va proporcionar,
  • perquè vaig tenir l'oportunitat de programar la meva pròpia aplicació de laboratori d'escala distribuïda d'hiperrendiment, que no repetirà completament l'arquitectura d'un altre hiperperformance semblant... bé, això és tot.

En què escriure? Això sí, a Rust. Per què?

  • perquè ja hi havia una solució prototip,
  • perquè l'autor de l'article ja coneixia Rust en aquell moment i tenia ganes d'escriure alguna cosa per a la producció amb l'oportunitat de posar-lo en codi obert,
  • perquè els idiomes amb GC no ens són adequats a causa de la naturalesa del trànsit rebut (gairebé en temps real) i les pauses de GC són pràcticament inacceptables,
  • perquè necessiteu un rendiment màxim comparable al C
  • perquè Rust ens proporciona una concurrència sense por, i si haguéssim començat a escriure-ho en C/C++, hauríem acumulat encara més vulnerabilitats, desbordaments de memòria intermèdia, condicions de carrera i altres paraules espantoses que brubeck.

També hi va haver un argument contra Rust. L'empresa no tenia experiència creant projectes a Rust, i ara tampoc tenim previst utilitzar-lo en el projecte principal. Per tant, hi havia greus temors que res no funcionés, però vam decidir arriscar i ho vam intentar.

El temps va passar...

Finalment, després de diversos intents fallits, la primera versió de treball estava a punt. Què va passar? Això és el que va passar.

Bioyino: agregador de mètriques distribuïts i escalables

Cada node rep el seu propi conjunt de mètriques i les acumula, i no agrega mètriques per a aquells tipus on es requereix el seu conjunt complet per a l'agregació final. Els nodes estan connectats entre si mitjançant algun tipus de protocol de bloqueig distribuït, que permet seleccionar entre ells l'únic (aquí vam plorar) que mereix enviar mètriques al Gran. Aquest problema està sent resolt actualment per Cònsol, però en el futur les ambicions de l'autor s'estenen a propi implementació Raft, on el més digne serà, per descomptat, el node líder del consens. A més del consens, els nodes sovint (un cop per segon de manera predeterminada) envien als seus veïns aquelles parts de mètriques pre-agregades que van aconseguir recollir en aquell segon. Resulta que es conserven l'escala i la tolerància a errors: cada node encara té un conjunt complet de mètriques, però les mètriques s'envien ja agregades, mitjançant TCP i codificades en un protocol binari, de manera que els costos de duplicació es redueixen significativament en comparació amb UDP. Malgrat el nombre bastant gran de mètriques entrants, l'acumulació requereix molt poca memòria i encara menys CPU. Per als nostres mèrits altament compressibles, això són només unes desenes de megabytes de dades. Com a avantatge addicional, no obtenim cap reescriptura de dades innecessàries a Graphite, com va ser el cas de Burbeck.

Els paquets UDP amb mètriques es desequilibren entre els nodes dels equips de xarxa mitjançant un simple Round Robin. Per descomptat, el maquinari de xarxa no analitza el contingut dels paquets i, per tant, pot extreure molt més de 4 milions de paquets per segon, per no parlar de mètriques de les quals no sap res. Si tenim en compte que les mètriques no surten d'una en una a cada paquet, no preveiem cap problema de rendiment en aquest lloc. Si un servidor falla, el dispositiu de xarxa ràpidament (en 1-2 segons) detecta aquest fet i elimina el servidor bloquejat de la rotació. Com a resultat d'això, els nodes passius (és a dir, no líders) es poden activar i desactivar pràcticament sense notar inconvenients als gràfics. El màxim que perdem és part de les mètriques que han arribat a l'últim segon. Una pèrdua/apagada/canvi sobtada d'un líder encara crearà una petita anomalia (l'interval de 30 segons encara no està sincronitzat), però si hi ha comunicació entre nodes, aquests problemes es poden minimitzar, per exemple, enviant paquets de sincronització. .

Una mica sobre l'estructura interna. L'aplicació és, per descomptat, multiprocés, però l'arquitectura de fils és diferent de la que s'utilitza a Brubeck. Els fils de brubeck són els mateixos: cadascun d'ells és responsable tant de la recollida d'informació com de l'agregació. A bioyino, els treballadors es divideixen en dos grups: els responsables de la xarxa i els responsables de l'agregació. Aquesta divisió permet gestionar l'aplicació de manera més flexible en funció del tipus de mètriques: on es requereix una agregació intensiva, es poden afegir agregadors, on hi ha molt trànsit de xarxa, es pot afegir el nombre de fluxos de xarxa. Actualment, als nostres servidors treballem en 8 fluxos de xarxa i 4 d'agregació.

La part del recompte (responsable de l'agregació) és força avorrida. Els buffers omplerts pels fluxos de xarxa es distribueixen entre els fluxos de recompte, on posteriorment s'analitzen i s'agreguen. A petició, es donen mètriques per enviar-les a altres nodes. Tot això, inclòs l'enviament de dades entre nodes i el treball amb Consul, es realitza de manera asíncrona, s'executa al framework tokio.

Molts més problemes durant el desenvolupament van ser causats per la part de xarxa responsable de rebre mètriques. L'objectiu principal de separar els fluxos de xarxa en entitats separades era el desig de reduir el temps que passa un flux no per llegir dades del sòcol. Les opcions que utilitzaven UDP asíncron i recvmsg regular van desaparèixer ràpidament: el primer consumeix massa CPU d'espai d'usuari per al processament d'esdeveniments, el segon requereix massa canvis de context. Per tant, ara s'utilitza recvmmsg amb amortidors grans (i els amortidors, senyors oficials, no són res per a vosaltres!). El suport per a UDP normal es reserva per a casos lleugers on no cal recvmmsg. En el mode multimissatge, és possible aconseguir el més important: la gran majoria de les vegades, el fil de xarxa rastreja la cua del sistema operatiu: llegeix les dades del sòcol i les transfereix a la memòria intermèdia de l'espai d'usuari, canviant només ocasionalment a donar la memòria intermèdia plena a agregadors. La cua al sòcol pràcticament no s'acumula, el nombre de paquets deixats pràcticament no augmenta.

Nota

A la configuració predeterminada, la mida de la memòria intermèdia està configurada per ser bastant gran. Si de sobte decideixes provar el servidor tu mateix, és possible que et trobis amb el fet que després d'enviar un nombre reduït de mètriques, no arribaran a Graphite, romanent a la memòria intermèdia de la xarxa. Per treballar amb un nombre reduït de mètriques, heu d'establir la mida de bus i la mida de la cua de tasques en valors més petits a la configuració.

Finalment, algunes cartes per als amants de les cartes.

Estadístiques sobre el nombre de mètriques entrants per a cada servidor: més de 2 milions de MPS.

Bioyino: agregador de mètriques distribuïts i escalables

Desactivar un dels nodes i redistribuir les mètriques entrants.

Bioyino: agregador de mètriques distribuïts i escalables

Estadístiques sobre mètriques de sortida: només envia un node: el cap de la incursió.

Bioyino: agregador de mètriques distribuïts i escalables

Estadístiques del funcionament de cada node, tenint en compte els errors en diversos mòduls del sistema.

Bioyino: agregador de mètriques distribuïts i escalables

Detall de les mètriques entrants (els noms de les mètriques estan ocults).

Bioyino: agregador de mètriques distribuïts i escalables

Què tenim previst fer amb tot això a continuació? Això sí, escriu codi, carai...! El projecte es va plantejar inicialment per ser de codi obert i ho mantindrà durant tota la seva vida. Els nostres plans immediats inclouen canviar a la nostra pròpia versió de Raft, canviar el protocol d'iguals a un de més portàtil, introduir estadístiques internes addicionals, nous tipus de mètriques, correccions d'errors i altres millores.

Per descomptat, tothom és benvingut a ajudar en el desenvolupament del projecte: crear PR, Issues, si és possible respondrem, millorarem, etc.

Dit això, això és tot gent, compra els nostres elefants!



Font: www.habr.com

Afegeix comentari