Com s'implementa l'arquitectura web tolerant a errors a la plataforma Mail.ru Cloud Solutions

Com s'implementa l'arquitectura web tolerant a errors a la plataforma Mail.ru Cloud Solutions

Hola, Habr! Sóc Artem Karamyshev, cap de l'equip d'administració del sistema Mail.Ru Cloud Solutions (MCS). Durant l'últim any hem presentat molts productes nous. Volíem assegurar-nos que els serveis d'API fossin fàcilment escalables, tolerants a errors i preparats per a un ràpid creixement de la càrrega dels usuaris. La nostra plataforma està implementada a OpenStack i vull dir-vos quins problemes de tolerància a errors de components hem hagut de resoldre per obtenir un sistema tolerant a errors. Crec que això serà interessant per a aquells que també desenvolupen productes a OpenStack.

La tolerància general a fallades d'una plataforma consisteix en la resistència dels seus components. Així, a poc a poc anirem passant per tots els nivells on hem identificat riscos i els hem tancat.

Versió en vídeo d'aquesta història, la font principal de la qual va ser un informe a la conferència del dia 4 de Uptime, organitzada per ITSumma, pots veure al canal de YouTube de la comunitat Uptime.

Resiliència de l'arquitectura física

La part pública del núvol MCS es basa ara en dos centres de dades Tier III, entre ells hi ha la seva pròpia fibra fosca, reservada a nivell físic per diferents vies, amb un rendiment de 200 Gbit/s. El nivell III proporciona el nivell necessari de tolerància a fallades per a la infraestructura física.

La fibra fosca es reserva tant a nivell físic com lògic. El procés de reserva de canals va ser iteratiu, van sorgir problemes i estem millorant constantment la comunicació entre centres de dades.

Per exemple, no fa gaire, mentre treballava en un pou prop d'un dels centres de dades, una excavadora va trencar una canonada, i dins d'aquesta canonada hi havia un cable òptic principal i un de seguretat. El nostre canal de comunicació tolerant a errors amb el centre de dades va resultar ser vulnerable en un moment donat, al pou. En conseqüència, hem perdut part de la infraestructura. Vam treure conclusions i vam fer diverses accions, inclosa la instal·lació d'òptiques addicionals al pou adjacent.

Als centres de dades hi ha punts de presència de proveïdors de comunicació als quals transmetem els nostres prefixos via BGP. Per a cada direcció de xarxa, es selecciona la millor mètrica, que permet proporcionar a diferents clients la millor qualitat de connexió. Si la comunicació a través d'un proveïdor falla, reconstruirem el nostre encaminament a través dels proveïdors disponibles.

Si un proveïdor falla, canviem automàticament al següent. En cas de fallada d'un dels centres de dades, disposem d'una còpia mirall dels nostres serveis al segon centre de dades, que assumeixen tota la càrrega.

Com s'implementa l'arquitectura web tolerant a errors a la plataforma Mail.ru Cloud Solutions
Resiliència de la infraestructura física

Què fem servir per a la tolerància a errors a nivell d'aplicació

El nostre servei es basa en una sèrie de components de codi obert.

ExaBGP és un servei que implementa una sèrie de funcions mitjançant el protocol d'encaminament dinàmic basat en BGP. L'utilitzem activament per anunciar les nostres adreces IP a la llista blanca a través de les quals els usuaris accedeixen a l'API.

HAProxy és un equilibrador d'alta càrrega que permet configurar regles d'equilibri de trànsit molt flexibles a diferents nivells del model OSI. L'utilitzem per equilibrar-nos davant de tots els serveis: bases de dades, corredors de missatges, serveis API, serveis web, els nostres projectes interns: tot hi ha darrere d'HAProxy.

Aplicació API — una aplicació web escrita en python, amb la qual l'usuari gestiona la seva infraestructura i el seu servei.

Aplicació del treballador (d'ara endavant, simplement treballador): als serveis d'OpenStack, aquest és un dimoni d'infraestructura que us permet transmetre ordres de l'API a la infraestructura. Per exemple, la creació de disc es produeix al treballador i la sol·licitud de creació es produeix a l'API de l'aplicació.

Arquitectura d'aplicacions d'OpenStack estàndard

La majoria dels serveis que es desenvolupen per a OpenStack intenten seguir un únic paradigma. Un servei normalment consta de 2 parts: API i treballadors (executors de backend). Com a regla general, una API és una aplicació WSGI en Python, que es llança com a procés independent (dimoni) o utilitzant un servidor web Nginx o Apache preparat. L'API processa la sol·licitud de l'usuari i passa instruccions addicionals a l'aplicació del treballador perquè les executi. La transferència es fa mitjançant un intermediari de missatges, generalment RabbitMQ, els altres són poc compatibles. Quan els missatges arriben al corredor, són processats pels treballadors i, si cal, retornen una resposta.

Aquest paradigma implica punts de fallada comuns aïllats: RabbitMQ i la base de dades. Però RabbitMQ està aïllat dins d'un servei i, en teoria, pot ser individual per a cada servei. Així que a MCS separem aquests serveis tant com sigui possible; per a cada projecte individual creem una base de dades separada, un RabbitMQ independent. Aquest plantejament és bo perquè en cas d'accident en alguns punts vulnerables, no es trenca tot el servei, sinó només una part.

El nombre d'aplicacions de treball és il·limitat, de manera que l'API es pot escalar fàcilment horitzontalment darrere dels equilibradors per augmentar el rendiment i la tolerància a errors.

Alguns serveis requereixen coordinació dins del servei quan es produeixen operacions seqüencials complexes entre les API i els treballadors. En aquest cas, s'utilitza un únic centre de coordinació, un sistema de clúster com Redis, Memcache, etcd, que permet a un treballador dir-li a un altre que aquesta tasca li està assignada (“si us plau, no l'agafes”). Utilitzem etcd. Per regla general, els treballadors es comuniquen activament amb la base de dades, escriuen i llegeixen informació des d'allà. Utilitzem mariadb com a base de dades, que es troba en un clúster multimaster.

Aquest servei únic clàssic s'organitza d'una manera generalment acceptada per a OpenStack. Es pot considerar com un sistema tancat, per al qual els mètodes d'escala i la tolerància a fallades són força evidents. Per exemple, per a la tolerància a errors de l'API, n'hi ha prou amb posar un equilibrador al davant. L'escalada dels treballadors s'aconsegueix augmentant-ne el nombre.

El punt feble de tot l'esquema és RabbitMQ i MariaDB. La seva arquitectura mereix un article a part. En aquest article vull centrar-me en la tolerància a errors de l'API.

Com s'implementa l'arquitectura web tolerant a errors a la plataforma Mail.ru Cloud Solutions
Arquitectura d'aplicacions Openstack. Equilibri i tolerància a errors de la plataforma núvol

Fer que l'equilibrador HAProxy sigui tolerant a errors mitjançant ExaBGP

Per fer que les nostres API siguin escalables, ràpides i tolerants a errors, els hi posem un equilibrador de càrrega. Hem escollit HAProxy. Al meu entendre, té totes les característiques necessàries per a la nostra tasca: equilibri a diversos nivells OSI, una interfície de gestió, flexibilitat i escalabilitat, un gran nombre de mètodes d'equilibri, suport per a taules de sessió.

El primer problema que calia resoldre va ser la tolerància a fallades del propi equilibrador. Simplement instal·lar un equilibrador també crea un punt de fallada: l'equilibrador es trenca i el servei falla. Per evitar que això passi, hem utilitzat HAProxy juntament amb ExaBGP.

ExaBGP permet implementar un mecanisme per comprovar l'estat d'un servei. Hem utilitzat aquest mecanisme per comprovar la funcionalitat d'HAProxy i, en cas de problemes, desactivar el servei HAProxy de BGP.

Esquema ExaBGP+HAProxy

  1. Instal·lem el programari necessari, ExaBGP i HAProxy, en tres servidors.
  2. Creem una interfície de loopback a cada servidor.
  3. Als tres servidors assignem la mateixa adreça IP blanca a aquesta interfície.
  4. Una adreça IP blanca s'anuncia a Internet mitjançant ExaBGP.

La tolerància a errors s'aconsegueix anunciant la mateixa adreça IP des dels tres servidors. Des del punt de vista de la xarxa, la mateixa adreça és accessible des de tres salts següents diferents. L'encaminador veu tres rutes idèntiques, en selecciona la prioritat més alta en funció de la seva pròpia mètrica (acostuma a ser la mateixa opció) i el trànsit només va a un dels servidors.

En cas de problemes amb el funcionament d'HAProxy o una fallada del servidor, ExaBGP deixa d'anunciar la ruta i el trànsit canvia sense problemes a un altre servidor.

Així, vam aconseguir la tolerància a fallades de l'equilibrador.

Com s'implementa l'arquitectura web tolerant a errors a la plataforma Mail.ru Cloud Solutions
Tolerància a errors dels equilibradors HAProxy

L'esquema va resultar imperfecte: vam aprendre a reservar HAProxy, però no vam aprendre a distribuir la càrrega dins dels serveis. Per tant, vam ampliar una mica aquest esquema: vam passar a l'equilibri entre diverses adreces IP blanques.

Equilibri basat en DNS més BGP

El problema de l'equilibri de càrrega per al nostre HAProxy continua sense resoldre. Tanmateix, es pot resoldre de manera molt senzilla, com hem fet aquí.

Per equilibrar tres servidors necessitareu 3 adreces IP blanques i un bon DNS antic. Cadascuna d'aquestes adreces es determina a la interfície de loopback de cada HAProxy i s'anuncia a Internet.

A OpenStack, per gestionar recursos, s'utilitza un directori de serveis, que especifica l'API del punt final d'un servei concret. En aquest directori registrem un nom de domini: public.infra.mail.ru, que es resol mitjançant DNS amb tres adreces IP diferents. Com a resultat, obtenim una distribució de càrrega entre tres adreces mitjançant DNS.

Però com que quan anunciem adreces IP blanques no controlem les prioritats de selecció del servidor, això encara no s'equilibra. Normalment, només es seleccionarà un servidor en funció de l'antiguitat de l'adreça IP i els altres dos estaran inactius perquè no s'especifica cap mètrica a BGP.

Vam començar a enviar rutes mitjançant ExaBGP amb diferents mètriques. Cada equilibrador anuncia les tres adreces IP blanques, però una d'elles, la principal d'aquest equilibrador, s'anuncia amb la mètrica mínima. Així, mentre els tres equilibradors estan en funcionament, les trucades a la primera adreça IP van al primer equilibrador, les trucades a la segona a la segona i les trucades a la tercera a la tercera.

Què passa quan cau un dels equilibradors? Si algun equilibrador falla, la seva adreça principal encara s'anuncia des dels altres dos i el trànsit es redistribueix entre ells. Així, donem a l'usuari diverses adreces IP alhora mitjançant DNS. Mitjançant l'equilibri per DNS i diferents mètriques, obtenim una distribució uniforme de la càrrega entre els tres equilibradors. I al mateix temps no perdem la tolerància a falles.

Com s'implementa l'arquitectura web tolerant a errors a la plataforma Mail.ru Cloud Solutions
Equilibrant HAProxy basat en DNS + BGP

Interacció entre ExaBGP i HAProxy

Així doncs, hem implementat la tolerància a errors en cas que el servidor marxi, basant-nos en aturar l'anunci de rutes. Però HAProxy es pot tancar per altres motius que no siguin errors del servidor: errors d'administració, errors dins del servei. També volem eliminar l'equilibrador trencat de sota la càrrega en aquests casos, i necessitem un mecanisme diferent.

Per tant, ampliant l'esquema anterior, vam implementar el batec del cor entre ExaBGP i HAProxy. Aquesta és una implementació de programari de la interacció entre ExaBGP i HAProxy, quan ExaBGP utilitza scripts personalitzats per comprovar l'estat de les aplicacions.

Per fer-ho, heu de configurar un verificador de salut a la configuració d'ExaBGP, que pot comprovar l'estat d'HAProxy. En el nostre cas, vam configurar el backend de salut a HAProxy, i des del costat ExaBGP comprovem amb una simple sol·licitud GET. Si l'anunci deixa de produir-se, és probable que HAProxy no funcioni i no cal anunciar-lo.

Com s'implementa l'arquitectura web tolerant a errors a la plataforma Mail.ru Cloud Solutions
Comprovació de salut HAProxy

HAProxy Peers: sincronització de sessions

El següent a fer era sincronitzar les sessions. Quan es treballa amb equilibradors distribuïts, és difícil organitzar l'emmagatzematge d'informació sobre les sessions del client. Però HAProxy és un dels pocs equilibradors que pot fer-ho a causa de la funcionalitat Peers: la capacitat de transferir taules de sessió entre diferents processos HAProxy.

Hi ha diferents mètodes d'equilibri: simples com ara round robin, i ampliat, quan es recorda la sessió del client, i cada vegada que acaba al mateix servidor que abans. Volíem implementar la segona opció.

HAProxy utilitza stick-tables per desar les sessions dels clients d'aquest mecanisme. Desen l'adreça IP original del client, l'adreça de destinació seleccionada (backend) i alguna informació del servei. Normalment, les taules stick s'utilitzen per emmagatzemar un parell font-IP + destinació-IP, que és especialment útil per a aplicacions que no poden transferir el context de sessió d'usuari quan es canvia a un altre equilibrador, per exemple, en el mode d'equilibri RoundRobin.

Si s'ensenya a una taula stick a moure's entre diferents processos HAProxy (entre els quals es produeix l'equilibri), els nostres equilibradors podran treballar amb un conjunt de taules stick. Això permetrà canviar de manera perfecta la xarxa del client si un dels equilibradors falla; el treball amb les sessions del client continuarà als mateixos backends que es van seleccionar anteriorment.

Per al bon funcionament, s'ha de resoldre el problema de l'adreça IP d'origen de l'equilibrador des del qual s'ha establert la sessió. En el nostre cas, aquesta és una adreça dinàmica a la interfície de loopback.

El treball correcte dels companys només s'aconsegueix sota determinades condicions. És a dir, els temps d'espera TCP han de ser prou grans o la commutació ha de ser prou ràpida perquè la sessió TCP no tingui temps d'acabar. Tanmateix, permet una commutació perfecta.

A IaaS tenim un servei construït amb la mateixa tecnologia. Això Load Balancer com a servei per a OpenStack, que es diu Octavia. Es basa en dos processos HAProxy i inicialment inclou suport per als companys. Han demostrat ser excel·lents en aquest servei.

La imatge mostra esquemàticament el moviment de les taules d'iguals entre tres instàncies HAProxy, es proposa una configuració sobre com es pot configurar:

Com s'implementa l'arquitectura web tolerant a errors a la plataforma Mail.ru Cloud Solutions
HAProxy Peers (sincronització de sessions)

Si implementeu el mateix esquema, el seu funcionament s'ha de provar acuradament. No és un fet que funcioni de la mateixa manera el 100% del temps. Però almenys no perdràs les taules de fixació quan necessites recordar la IP d'origen del client.

Limitació del nombre de peticions simultànies d'un mateix client

Qualsevol servei que estigui disponible públicament, incloses les nostres API, pot estar subjecte a allaus de sol·licituds. Els motius poden ser completament diferents, des d'errors d'usuari fins a atacs dirigits. Periòdicament estem DDoSed per adreces IP. Els clients sovint cometen errors en els seus scripts i ens donen mini-DDoS.

D'una manera o altra, s'ha de proporcionar una protecció addicional. La solució òbvia és limitar el nombre de sol·licituds d'API i no perdre el temps de la CPU processant sol·licituds malicioses.

Per implementar aquestes restriccions, utilitzem límits de tarifes, organitzats sobre la base d'HAProxy, utilitzant les mateixes taules de pals. Configurar límits és bastant senzill i permet limitar l'usuari pel nombre de sol·licituds a l'API. L'algoritme recorda la IP d'origen des de la qual es fan les sol·licituds i limita el nombre de peticions simultànies d'un usuari. Per descomptat, hem calculat el perfil de càrrega mitjà de l'API per a cada servei i hem establert un límit de ≈ 10 vegades aquest valor. Seguim vigilant de prop la situació i mantenint el dit al pols.

Com es veu això a la pràctica? Tenim clients que utilitzen les nostres API d'escala automàtica tot el temps. Creen aproximadament entre dues-centes i tres-centes màquines virtuals al matí i les suprimeixen al vespre. Per a OpenStack, la creació d'una màquina virtual, també amb serveis PaaS, requereix almenys 1000 sol·licituds d'API, ja que la interacció entre serveis també es produeix a través de l'API.

Aquesta transferència de tasques provoca una càrrega bastant gran. Vam avaluar aquesta càrrega, vam recollir pics diaris, els vam multiplicar per deu i aquest es va convertir en el nostre límit de tarifa. Mantenim el dit al pols. Sovint veiem robots i escàners que intenten mirar-nos per veure si tenim scripts CGA que es puguin executar, els estem tallant activament.

Com actualitzar la vostra base de codi sense que els usuaris s'adonin

També implementem la tolerància a errors a nivell dels processos de desplegament de codi. Pot haver-hi errors durant el llançament, però el seu impacte en la disponibilitat del servei es pot minimitzar.

Actualitzem constantment els nostres serveis i hem de garantir que la base de codis s'actualitzi sense afectar els usuaris. Hem aconseguit resoldre aquest problema utilitzant les capacitats de gestió d'HAProxy i la implementació de Graceful Shutdown als nostres serveis.

Per solucionar aquest problema, calia garantir el control de l'equilibrador i l'aturada "correcta" dels serveis:

  • En el cas d'HAProxy, el control es realitza mitjançant un fitxer d'estadístiques, que és essencialment un sòcol i es defineix a la configuració d'HAProxy. Podeu enviar-li ordres mitjançant stdio. Però la nostra principal eina de control de configuració és ansible, de manera que té un mòdul integrat per gestionar HAProxy. Que fem servir activament.
  • La majoria dels nostres serveis d'API i de motor admeten tecnologies d'apagada elegants: quan s'apaga, esperen que es completi la tasca actual, ja sigui una sol·licitud http o alguna tasca de servei. Amb el treballador passa el mateix. Coneix totes les tasques que està fent i acaba quan ho ha completat amb èxit.

Gràcies a aquests dos punts, l'algoritme segur per al nostre desplegament té aquest aspecte.

  1. El desenvolupador munta un nou paquet de codi (per a nosaltres això és RPM), el prova a l'entorn de desenvolupament, el prova a l'etapa i el deixa al repositori de l'etapa.
  2. El desenvolupador estableix la tasca per al desplegament amb la descripció més detallada dels "artefactes": la versió del nou paquet, una descripció de la nova funcionalitat i altres detalls sobre el desplegament si cal.
  3. L'administrador del sistema comença l'actualització. Llança el llibre de jugades Ansible, que al seu torn fa el següent:
    • Agafa un paquet del dipòsit de fase i l'utilitza per actualitzar la versió del paquet al dipòsit del producte.
    • Compila una llista de backends del servei actualitzat.
    • Tanca el primer servei que s'actualitza a HAProxy i espera que els seus processos s'acabin d'executar. Gràcies a un tancament elegant, estem segurs que totes les sol·licituds actuals dels clients es completaran amb èxit.
    • Després que l'API i els treballadors estiguin completament aturats i HAProxy s'apaga, el codi s'actualitza.
    • Ansible executa serveis.
    • Per a cada servei, s'extreuen determinades "manilles", que realitzen proves d'unitat en una sèrie de proves clau predefinides. Es realitza una comprovació bàsica del nou codi.
    • Si no s'han trobat errors al pas anterior, el backend s'activa.
    • Passem al següent backend.
  4. Després d'actualitzar tots els backends, s'inicien les proves funcionals. Si falten, el desenvolupador mira qualsevol funcionalitat nova que hagi creat.

Això completa el desplegament.

Com s'implementa l'arquitectura web tolerant a errors a la plataforma Mail.ru Cloud Solutions
Cicle d'actualització del servei

Aquest esquema no funcionaria si no tinguéssim una regla. Donem suport tant a la versió antiga com a la nova a la batalla. Per endavant, en l'etapa de desenvolupament del programari, s'estableix que, fins i tot si hi ha canvis a la base de dades del servei, no trencaran el codi anterior. Com a resultat, la base de codi s'actualitza gradualment.

Conclusió

Compartint els meus propis pensaments sobre una arquitectura WEB tolerant a errors, m'agradaria tornar a assenyalar els seus punts clau:

  • tolerància a fallades físiques;
  • tolerància a errors de xarxa (equilibradors, BGP);
  • tolerància a errors del programari utilitzat i desenvolupat.

Temps de funcionament estable per a tothom!

Font: www.habr.com

Afegeix comentari