
Ciao, Habr! Sò Artem Karamyshev, capu di a squadra di amministrazione di u sistema . Avemu avutu parechji lanciamenti di novi prudutti annantu à l'annu passatu. Vulemu assicurà chì i servizii API eranu facilmente scalabili, tolleranti à i difetti, è pronti per a crescita rapida di a carica di l'utilizatori. A nostra piattaforma hè implementata in OpenStack, è vogliu dì chì i prublemi di tolleranza à i difetti di cumpunenti avemu avutu da risolve per avè un sistema tolerante à i difetti. Pensu chì questu serà interessante per quelli chì sviluppanu ancu prudutti nantu à OpenStack.
A tolleranza generale à i difetti di una piattaforma hè custituita da a resistenza di i so cumpunenti. Allora andemu à pocu à pocu à traversu tutti i livelli induve avemu identificatu risichi è li chjusu.
A versione video di sta storia, a fonte primaria di quale era un rapportu à a cunferenza Uptime day 4, urganizata da , pudete vede .
Resilienza di l'architettura fisica
A parte publica di u nuvulu MCS hè issa basata in dui centri di dati Tier III, trà elli ci hè a so propria fibra scura, riservata à u livellu fisicu da diverse rotte, cù un throughput di 200 Gbit/s. U Tier III furnisce u livellu necessariu di tolleranza à i difetti per l'infrastruttura fisica.
A fibra scura hè riservata à u livellu fisicu è lògicu. U prucessu di riservazione di u canali era iterativu, i prublemi sò ghjunti, è simu constantemente migliurà a cumunicazione trà i centri di dati.
Per esempiu, pocu tempu fà, mentre travagliava in un pozzu vicinu à unu di i centri di dati, un escavatore rumpiu una pipa, è ind'è sta pipa ci era un cable otticu principale è un cable otticu di salvezza. U nostru canale di cumunicazione tolerante à i difetti cù u centru di dati hè vulnerabile à un puntu, in u pozzu. Per quessa, avemu persu una parte di l'infrastruttura. Avemu tiratu cunclusioni è pigliate una quantità di azzioni, cumprese l'installazione di ottiche supplementari in u pozzu adiacente.
In i centri di dati ci sò punti di prisenza di i fornituri di cumunicazione à quale trasmettemu i nostri prefissi via BGP. Per ogni direzzione di a rete, a megliu metrica hè selezziunata, chì permette à diversi clienti di esse furnitu cù a megliu qualità di cunnessione. Se a cumunicazione per mezu di un fornitore scende, ricustruemu u nostru routing attraversu i fornitori dispunibili.
Se un fornitore falla, cambiemu automaticamente à u prossimu. In casu di un fallimentu di unu di i centri di dati, avemu una copia specchiu di i nostri servizii in u sicondu centru di dati, chì piglianu tutta a carica.

Resilienza di l'infrastruttura fisica
Ciò chì usemu per a tolleranza di colpa à u livellu di l'applicazione
U nostru serviziu hè custruitu nantu à una quantità di cumpunenti opensource.
ExaBGP hè un serviziu chì implementa una quantità di funzioni chì utilizanu u protocolu di routing dinamicu basatu in BGP. L'utilicemu attivamente per publicità i nostri indirizzi IP in lista bianca attraversu quale l'utilizatori accede à l'API.
HAProxy hè un equilibratore di alta carica chì permette di cunfigurà regule di equilibriu di trafficu assai flexible à diversi livelli di u mudellu OSI. Avemu aduprà per equilibriu davanti à tutti i servizii: basa di dati, broker di messagi, servizii API, servizii web, i nostri prughjetti interni - tuttu hè daretu à HAProxy.
Applicazione API - una applicazione web scritta in python, cù quale l'utilizatore gestisce a so infrastruttura è u so serviziu.
Applicazione di u travagliu (da quì in seguitu solu u travagliu) - in i servizii OpenStack, questu hè un daemon di l'infrastruttura chì vi permette di trasmette cumandamenti API à l'infrastruttura. Per esempiu, a creazione di discu si trova in u travagliu, è a dumanda di creazione si trova in l'API di l'applicazione.
Architettura standard di l'applicazione OpenStack
A maiò parte di i servizii chì sò sviluppati per OpenStack pruvate à seguità un solu paradigma. Un serviziu hè generalmente custituitu di 2 parti: API è travagliadori (executors backend). Comu regula, una API hè una applicazione WSGI in python, chì hè lanciata o cum'è un prucessu indipendente (daemon), o cù un servitore web Nginx o Apache prontu. L'API processa a dumanda di l'utilizatori è passa più struzzioni à l'applicazione di u travagliu per l'esekzione. U trasferimentu si faci cù un broker di messagiu, di solitu RabbitMQ, l'altri sò pocu supportati. Quandu i missaghji ghjunghjenu à u broker, sò trattati da i travagliadori è, se ne necessariu, tornanu una risposta.
Stu paradigma implica punti cumuni isolati di fallimentu: RabbitMQ è a basa di dati. Ma RabbitMQ hè isolatu in un serviziu è, in teoria, pò esse individuale per ogni serviziu. Allora in MCS separemu questi servizii quant'è pussibule; per ogni prughjettu individuale creemu una basa di dati separata, una RabbitMQ separata. Stu approcciu hè bonu perchè in casu d'accidentu in certi punti vulnerabili, micca tuttu u serviziu si rompe, ma solu una parte.
U numaru d'applicazioni di u travagliu hè illimitatu, cusì l'API pò facilmente scala orizzontalmente daretu à i equilibratori per aumentà a prestazione è a tolleranza di difetti.
Certi servizii necessitanu a coordinazione in u serviziu quandu operazioni sequenziali cumplessi si verificanu trà API è travagliadori. In questu casu, hè utilizatu un unicu centru di coordinazione, un sistema di cluster cum'è Redis, Memcache, etcd, chì permette à un travagliadore di dì à un altru chì questu compitu hè attribuitu à ellu ("per piacè ùn piglià micca"). Avemu aduprà etcd. In regula, i travagliadori cumunicanu attivamente cù a basa di dati, scrivenu è leghje infurmazioni da quì. Avemu aduprà mariadb cum'è una basa di dati, chì si trova in un cluster multimaster.
Stu serviziu unicu classicu hè urganizatu in una manera generalmente accettata per OpenStack. Pò esse cunsideratu cum'è un sistema chjusu, per quale i metudi di scaling è a toleranza di difetti sò abbastanza evidenti. Per esempiu, per a tolleranza di difetti API, hè abbastanza per mette un equilibratore davanti à elli. A scala di i travagliadori hè ottenuta aumentendu u so numeru.
U puntu debule in tuttu u schema hè RabbitMQ è MariaDB. A so architettura meriteghja un articulu separatu In questu articulu vogliu fucalizza nantu à a tolleranza di difetti API.

Architettura di l'applicazione Openstack. Equilibratu è tolleranza à i difetti di a piattaforma cloud
Facendu u balancer HAProxy tollerante à i difetti cù ExaBGP
Per fà e nostre API scalabili, veloci è tolleranti à i difetti, mettemu un equilibratore di carica davanti à elli. Avemu sceltu HAProxy. In u mo parè, hà tutte e caratteristiche necessarie per u nostru compitu: equilibriu à parechji livelli OSI, una interfaccia di gestione, flessibilità è scalabilità, un gran numaru di metudi di equilibriu, supportu per e tavule di sessione.
U primu prublema chì deve esse risoltu era a tolleranza di difetti di u balancer stessu. Basta installà un balancer crea ancu un puntu di fallimentu: u balancer si rompe è u serviziu crash. Per impediscenu chì questu succede, avemu usatu HAProxy in cunjunzione cù ExaBGP.
ExaBGP permette di implementà un mecanismu per verificà u statu di un serviziu. Avemu usatu stu mecanismu per verificà a funziunalità di HAProxy è, in casu di prublemi, disattivà u serviziu HAProxy da BGP.
Schema ExaBGP + HAProxy
- Installemu u software necessariu, ExaBGP è HAProxy, in trè servitori.
- Creemu una interfaccia di loopback in ogni servitore.
- In tutti i trè servitori assignemu u listessu indirizzu IP biancu à sta interfaccia.
- Un indirizzu IP biancu hè annunziatu à Internet via ExaBGP.
A tolleranza di difetti hè ottenuta da publicità di u stessu indirizzu IP da tutti i trè servitori. Da u puntu di vista di a rete, u listessu indirizzu hè accessibile da trè diversi salti successivi. U router vede trè rotte identiche, selezziunate a più alta priorità di elli basatu nantu à a so propria metrica (questu hè di solitu a listessa opzione), è u trafficu và solu à unu di i servitori.
In casu di prublemi cù u funziunamentu di HAProxy o un fallimentu di u servitore, ExaBGP ferma l'annunziu di a strada, è u trafficu cambia in un altru servitore.
Cusì, avemu ottinutu a tolleranza di difetti di l'equilibriu.

Tolleranza à i difetti di i equilibratori HAProxy
U schema hè diventatu imperfettu: avemu amparatu à riservà HAProxy, ma ùn hà micca amparatu à distribuisce a carica in i servizii. Per quessa, avemu allargatu un pocu stu schema: avemu passatu à equilibriu trà parechji indirizzi IP bianchi.
Equilibratu basatu annantu à DNS più BGP
U prublema di bilanciamentu di carica per u nostru HAProxy resta senza risolve. Tuttavia, pò esse risolta abbastanza simplice, cum'è avemu fattu quì.
Per equilibrà trè servitori avete bisognu di 3 indirizzi IP bianchi è boni vechji DNS. Ognunu di sti indirizzi hè determinatu nantu à l'interfaccia di loopback di ogni HAProxy è annunziate in Internet.
In OpenStack, per gestisce e risorse, hè utilizatu un repertoriu di serviziu, chì specifica l'API endpoint di un serviziu particulari. In questu repertoriu avemu registratu un nome di duminiu - public.infra.mail.ru, chì hè risoltu via DNS da trè indirizzi IP differenti. In u risultatu, avemu a distribuzione di carica trà trè indirizzi via DNS.
Ma postu chì annunziendu l'indirizzi IP bianchi ùn cuntrullemu micca e priorità di selezzione di u servitore, questu ùn hè micca equilibratu ancu. Di genere, solu un servitore serà sceltu basatu annantu à l'anzianità di l'indirizzu IP, è l'altri dui seranu inattivi perchè nisuna metrica hè specificata in BGP.
Avemu cuminciatu à mandà rotte via ExaBGP cù diverse metriche. Ogni balancer publicità tutti i trè indirizzi IP bianchi, ma unu di elli, u principale per questu balancer, hè annunziatu cù a metrica minima. Allora mentre tutti i trè balancers sò in funziunamentu, chjama à u primu indirizzu IP vai à u primu balancer, chjama à u sicondu à u sicondu, è chjama à u terzu à u terzu.
Chì succede quandu unu di i equilibratori casca ? Se un equilibratore falla, u so indirizzu principale hè sempre annunziatu da l'altri dui, è u trafficu hè ridistribuitu trà elli. Cusì, demu à l'utilizatore parechji indirizzi IP à una volta via DNS. Equilibrati per DNS è metriche diverse, avemu una distribuzione uniforme di a carica in tutti i trè equilibratori. È à u listessu tempu ùn perdemu micca a tolleranza di difetti.

Equilibrà HAProxy basatu annantu à DNS + BGP
Interazione trà ExaBGP è HAProxy
Allora, avemu implementatu a tolleranza di difetti in casu chì u servitore parte, basatu annantu à l'annunziu di e rotte. Ma HAProxy pò chjude per altri motivi ch'è fallimentu di u servitore: errori di amministrazione, fallimenti in u serviziu. Vulemu caccià u balancer rottu da sottu à a carica in questi casi ancu, è avemu bisognu di un mecanismu diversu.
Dunque, espansione u schema precedente, avemu implementatu u battitu di u core trà ExaBGP è HAProxy. Questa hè una implementazione di software di l'interazzione trà ExaBGP è HAProxy, quandu ExaBGP usa script persunalizati per verificà l'estatus di l'applicazioni.
Per fà questu, avete bisognu di cunfigurà un verificatore di salute in a cunfigurazione ExaBGP, chì pò verificà l'statu di HAProxy. In u nostru casu, avemu cunfiguratu u backend di salute in HAProxy, è da u latu ExaBGP cuntrollemu cù una semplice dumanda GET. Se l'annunziu smette di accade, allora HAProxy ùn hè più prubabile di travaglià è ùn ci hè bisognu di publicità.

Verificazione di salute HAProxy
HAProxy Peers: sincronizazione di sessione
A prossima cosa da fà era di sincronizà e sessioni. Quandu u travagliu cù equilibrari distribuiti, hè difficiule d'urganizà u almacenamentu di l'infurmazioni nantu à e sessioni di u cliente. Ma HAProxy hè unu di i pochi balancers chì ponu fà questu per via di a funziunalità Peers - l'abilità di trasfiriri tabelle di sessione trà e diverse prucessi HAProxy.
Ci sò sfarenti metudi di equilibriu: simplicità cum'è , è allargata, quandu a sessione di u cliente hè ricurdata, è ogni volta chì finisci in u stessu servitore cum'è prima. Vulemu implementà a seconda opzione.
HAProxy usa stick-tables per salvà e sessioni di i clienti di stu mecanismu. Salvanu l'indirizzu IP originale di u cliente, l'indirizzu di destinazione sceltu (backend) è qualchì infurmazione di serviziu. Di genere, i tavulini di bastone sò usati per almacenà un paru fonte-IP + destinazione-IP, chì hè particularmente utile per l'applicazioni chì ùn ponu micca trasfiriri u cuntestu di sessione di l'utilizatori quandu si cambia à un altru balancer, per esempiu, in u modu di equilibriu RoundRobin.
Se un tavulinu di bastone hè insignatu à spustà trà e diverse prucessi HAProxy (trà quale si trova l'equilibriu), i nostri equilibratori puderanu travaglià cù una piscina di tavule di bastone. Questu permetterà di cambià a reta di u cliente in modu perfettu se unu di i equilibratori falla; u travagliu cù e sessioni di u cliente cuntinuerà nantu à i stessi backends chì sò stati scelti prima.
Per u funziunamentu propiu, u prublema di l'indirizzu IP fonte di u balancer da quale a sessione hè stata stabilita deve esse risolta. In u nostru casu, questu hè un indirizzu dinamicu nantu à l'interfaccia di loopback.
U travagliu currettu di i pari hè ottenutu solu in certi cundizioni. Vale à dì, i timeout TCP deve esse abbastanza grande o u cambiamentu deve esse abbastanza veloce per chì a sessione TCP ùn hà micca tempu per finisce. Tuttavia, permette un cambiamentu senza saldatura.
In IaaS avemu un serviziu custruitu cù a stessa tecnulugia. Questu , chì si chjama Octavia. Hè basatu annantu à dui prucessi HAProxy è inizialmente include supportu per i pari. Anu dimustratu eccellenti in stu serviziu.
A stampa mostra schematicamente u muvimentu di e tavule di peer trà trè istanze HAProxy, una cunfigurazione hè pruposta nantu à cumu si pò esse cunfigurata:

HAProxy Peers (sincronizzazione di sessione)
Se implementate u listessu schema, u so funziunamentu deve esse verificatu cù cura. Ùn hè micca un fattu chì hà da travaglià in u listessu modu 100% di u tempu. Ma almenu ùn perderà micca i tavulini di bastone quandu avete bisognu di ricurdà l'IP fonte di u cliente.
Limite u numeru di richieste simultanee da u stessu cliente
Tutti i servizii chì sò publicamente dispunibuli, cumprese i nostri API, ponu esse sottumessi à avalanche di richieste. I mutivi per elli ponu esse cumplettamente sfarente, da l'errori di l'utilizatori à l'attacchi mirati. Semu periodicamente DDoSed da indirizzi IP. I clienti spessu sbaglianu in i so script è ci dannu mini-DDoS.
In un modu o un altru, una prutezzione supplementaria deve esse furnita. A suluzione ovvia hè di limità u nùmeru di dumande API è ùn perdi micca u tempu di CPU processendu richieste maliziusi.
Per implementà tali restrizioni, usemu limiti di tariffu, urganizati nantu à a basa di HAProxy, utilizendu i stessi tavulini di bastone. Stabbilimentu di limiti hè abbastanza simplice è permette di limità l'utilizatore per u numeru di richieste à l'API. L'algoritmu ricorda l'IP fonte da quale e dumande sò fatte è limita u numeru di dumande simultanee da un utilizatore. Di sicuru, avemu calculatu u prufilu mediu di carica API per ogni serviziu è stabilitu un limitu di ≈ 10 volte stu valore. Cuntinuemu à seguità attentamente a situazione è mantene u nostru dettu nantu à u pulsu.
Chì hè questu in pratica? Avemu clienti chì utilizanu e nostre API autoscaling tuttu u tempu. Creanu circa dui à trè centu macchine virtuali in a mattina è sguassate à a sera. Per OpenStack, a creazione di una macchina virtuale, ancu cù servizii PaaS, richiede almenu 1000 richieste API, postu chì l'interazzione trà i servizii si trova ancu attraversu l'API.
Un tali trasferimentu di compiti causa una carica abbastanza grande. Avemu valutatu sta carica, cullate i picchi di ogni ghjornu, l'aumentanu deci volte, è questu hè diventatu u nostru limitu di tariffu. Tenemu u nostru dettu nantu à u polsu. Spessu vedemu bots è scanners chì cercanu di guardà à noi per vede s'ellu avemu qualchì script CGA chì pò esse eseguitu, avemu attivamente tagliatu.
Cumu aghjurnà a vostra basa di codice senza chì l'utilizatori s'avvisanu
Implementemu ancu a tolleranza di difetti à u livellu di i prucessi di implementazione di codice. Ci ponu esse glitches durante i rollouts, ma u so impattu nantu à a dispunibilità di u serviziu pò esse minimizatu.
Aggiornemu constantemente i nostri servizii è deve assicurà chì a basa di codice hè aghjurnata senza affettà l'utilizatori. Avemu riesciutu à risolve stu prublema utilizendu e capacità di gestione di HAProxy è l'implementazione di Graceful Shutdown in i nostri servizii.
Per risolve stu prublema, era necessariu di assicurà u cuntrollu di u balancer è u "correttu" chjusu di i servizii:
- In u casu di HAProxy, u cuntrollu hè realizatu attraversu un schedariu di stats, chì hè essenzialmente un socket è hè definitu in a cunfigurazione HAProxy. Pudete mandà cumandamenti via stdio. Ma u nostru strumentu di cuntrollu di cunfigurazione principale hè ansible, cusì hà un modulu integratu per a gestione HAProxy. Chì avemu aduprà attivamente.
- A maiò parte di i nostri servizii API è Engine supportanu tecnulugii di spegnimentu graziosu: quandu si chjude, aspettanu chì u compitu attuale sia cumpletu, sia una dumanda http o qualchì attività di serviziu. A listessa cosa succede cù u travagliadore. Sapi tutti i travaglii chì faci è finisce quandu hà finitu tuttu cù successu.
Grazie à questi dui punti, l'algoritmu sicuru per a nostra implementazione pare cusì.
- U sviluppatore riunisce un novu pacchettu di codice (per noi questu hè RPM), prova in l'ambiente di dev, prova in u palcuscenicu, è lascia in u repository stage.
- U sviluppatore stabilisce u compitu di implementazione cù a descrizzione più dettagliata di l'"artefatti": a versione di u novu pacchettu, una descrizzione di a nova funziunalità è altri dettagli nantu à a implementazione, se ne necessariu.
- L'amministratore di u sistema principia l'aghjurnamentu. Lancia u libru di ghjocu Ansible, chì à u turnu face u seguente:
- Piglia un pacchettu da u repositariu di u stadiu è l'utiliza per aghjurnà a versione di u pacchettu in u repositariu di u produttu.
- Cumpila una lista di backends di u serviziu aghjurnatu.
- Arresta u primu serviziu per esse aghjurnatu in HAProxy è aspetta chì i so prucessi finiscinu di correre. Grazie à una chjusa graziosa, simu cunfidenti chì tutte e richieste di i clienti attuali saranu cumplette cun successu.
- Dopu chì l'API è i travagliadori sò stati fermati cumplettamente, è HAProxy hè disattivatu, u codice hè aghjurnatu.
- Ansible gestisce i servizii.
- Per ogni serviziu, certi "manichi" sò tirati, chì facenu teste di unità nantu à una quantità di teste chjave predefinite. A verificazione basica di u novu codice hè fattu.
- Se ùn sò micca stati trovati errori in u passu precedente, u backend hè attivatu.
- Passemu à u prossimu backend.
- Dopu chì tutti i backend sò aghjurnati, i testi funziunali sò lanciati. Se mancanu, u sviluppatore guarda ogni nova funziunalità chì hà creatu.
Questu compie a implementazione.

Ciclu di aghjurnamentu di serviziu
Stu schema ùn funziona micca s'ellu ùn avemu micca una regula. Supportemu sia a versione vechja è nova in battaglia. In anticipu, in u stadiu di u sviluppu di u software, hè stabilitu chì ancu s'ellu ci sò cambiamenti in a basa di dati di serviziu, ùn anu micca rumpia u codice precedente. In u risultatu, a basa di codice hè aghjurnata gradualmente.
cunchiusioni
Spartendu i mo pensamenti nantu à una architettura WEB tolerante à i difetti, vogliu nutà una volta di più i so punti chjave:
- tolleranza à i difetti fisichi;
- tolleranza à i difetti di a rete (balancers, BGP);
- tolleranza à i difetti di u software utilizatu è sviluppatu.
Disponibilità stabile à tutti!
Source: www.habr.com
