
Hallo, Habr! Ik bin Artem Karamyshev, haad fan it systeembehearteam . Wy hawwe it ôfrûne jier in protte nije produktlansearrings hân. Wy woenen derfoar soargje dat API-tsjinsten maklik skalberber, fouttolerant en ree wiene foar rappe groei yn brûkerslading. Us platfoarm wurdt ymplementearre op OpenStack, en ik wol jo fertelle hokker komponint fouttolerânsjeproblemen wy moasten oplosse om in fouttolerant systeem te krijen. Ik tink dat dit ynteressant sil wêze foar dyjingen dy't ek produkten ûntwikkelje op OpenStack.
De algemiene fouttolerânsje fan in platfoarm bestiet út de fearkrêft fan syn komponinten. Dat wy sille stadichoan troch alle nivo's gean wêr't wy risiko's identifisearre en se slute.
Fideoferzje fan dit ferhaal, wêrfan de primêre boarne in rapport wie op 'e Uptime day 4-konferinsje, organisearre troch , Do kinst sjen .
Resiliency fan 'e fysike arsjitektuer
It iepenbiere diel fan 'e MCS-wolk is no basearre yn twa Tier III-datasintra, tusken har is d'r in eigen donkere glêstried, reservearre op it fysike nivo troch ferskate rûtes, mei in trochfier fan 200 Gbit / s. Tier III leveret it nedige nivo fan fouttolerânsje foar de fysike ynfrastruktuer.
Donkere glêstried is reservearre op sawol it fysike as logysk nivo. De kanaal reservearring proses wie iteratyf, problemen ûntstienen, en wy binne hieltyd ferbetterjen kommunikaasje tusken data sintra.
Bygelyks, net lang lyn, doe't wurke yn in put tichtby ien fan de gegevens sintra, graafmachine bruts in piip, en binnen dizze piip wie sawol in haad en in reservekopy optyske kabel. Us fouttolerante kommunikaasjekanaal mei it datasintrum die bliken op ien punt kwetsber te wêzen, yn 'e put. Dêrtroch hawwe wy in diel fan 'e ynfrastruktuer ferlern. Wy lutsen konklúzjes en namen in oantal aksjes, wêrûnder it ynstallearjen fan ekstra optyk yn 'e neistlizzende put.
Yn datasintra binne d'r punten fan oanwêzigens fan kommunikaasjeproviders oan wa't wy ús foarheaksels fia BGP útstjoere. Foar elke netwurkrjochting wurdt de bêste metrik selektearre, wêrtroch ferskate kliïnten de bêste ferbiningskwaliteit kinne wurde foarsjoen. As kommunikaasje fia ien provider ôffalt, bouwe wy ús routing opnij op fia de beskikbere providers.
As in provider mislearret, skeakelje wy automatysk oer nei de folgjende. Yn it gefal fan in mislearring fan ien fan 'e datasintra hawwe wy in spegelkopy fan ús tsjinsten yn it twadde datasintrum, dy't de folsleine lading oernimme.

Resiliency fan fysike ynfrastruktuer
Wat wy brûke foar flatertolerânsje op applikaasjenivo
Us tsjinst is boud op in oantal opensource-komponinten.
ExaBGP is in tsjinst dy't in oantal funksjes ymplemintearret mei it BGP-basearre dynamyske routingprotokol. Wy brûke it aktyf om ús IP-adressen op wite list te advertearjen wêrtroch brûkers tagong krije ta de API.
HAProxy is in balancer mei hege lading wêrmei jo heul fleksibele regels foar ferkearsbalansearring kinne konfigurearje op ferskate nivo's fan it OSI-model. Wy brûke it om te balânsjen foar alle tsjinsten: databases, berjochtmakelaars, API-tsjinsten, webtsjinsten, ús ynterne projekten - alles is efter HAProxy.
API applikaasje - in webapplikaasje skreaun yn python, wêrmei't de brûker syn ynfrastruktuer en syn tsjinst beheart.
Worker applikaasje (hjirnei gewoan arbeider) - yn OpenStack-tsjinsten is dit in ynfrastruktuer-daemon wêrmei jo API-kommando's kinne útstjoere nei de ynfrastruktuer. Bygelyks, skiif oanmeitsjen komt foar yn 'e arbeider, en it oanmeitsjen fersyk komt foar yn de applikaasje API.
Standert OpenStack Application Architecture
De measte tsjinsten dy't ûntwikkele binne foar OpenStack besykje in inkeld paradigma te folgjen. In tsjinst bestiet meastentiids út 2 dielen: API en arbeiders (backend executors). As regel is in API in WSGI-applikaasje yn python, dy't wurdt lansearre as in ûnôfhinklik proses (daemon), of mei in ready-made Nginx- of Apache-webserver. De API ferwurket it brûkersfersyk en jout fierdere ynstruksjes troch oan de wurkapplikaasje foar útfiering. De oerdracht bart mei in berjochtmakelaar, meastal RabbitMQ, de oaren wurde min stipe. As berjochten de makelder berikke, wurde se ferwurke troch arbeiders en, as nedich, in antwurd werombringe.
Dit paradigma omfettet isolearre mienskiplike punten fan mislearring: RabbitMQ en de databank. Mar RabbitMQ is isolearre binnen ien tsjinst en kin yn teory yndividueel wêze foar elke tsjinst. Dat by MCS skiede wy dizze tsjinsten safolle mooglik; foar elk yndividueel projekt meitsje wy in aparte database, in aparte RabbitMQ. Dy oanpak is goed, want by in ûngelok op guon kwetsbere punten brekt net de hiele tsjinst, mar mar in part derfan.
It oantal arbeidersapplikaasjes is ûnbeheind, sadat de API maklik horizontaal kin skaalje efter balancers om prestaasjes en fouttolerânsje te ferheegjen.
Guon tsjinsten fereaskje koördinaasje binnen de tsjinst as komplekse sekwinsjele operaasjes foarkomme tusken API's en arbeiders. Yn dit gefal wurdt in inkeld koördinaasjesintrum brûkt, in klustersysteem lykas Redis, Memcache, ensfh, wêrtroch ien arbeider in oar kin fertelle dat dizze taak oan him is tawiisd ("net nimme it asjebleaft"). Wy brûke ensfh. As regel kommunisearje arbeiders aktyf mei de databank, skriuwe en lêze ynformaasje dêrwei. Wy brûke mariadb as in databank, dy't leit yn in multimaster kluster.
Dizze klassike ienige tsjinst wurdt organisearre op in manier dy't algemien akseptearre is foar OpenStack. It kin beskôge wurde as in sletten systeem, dêr't de metoaden fan skaalfergrutting en fout tolerânsje binne frij dúdlik. Bygelyks, foar API-fouttolerânsje is it genôch om in balancer foar har te setten. Skaalfergrutting fan arbeiders wurdt berikt troch har oantal te ferheegjen.
It swakke punt yn it hiele skema is RabbitMQ en MariaDB. Harren arsjitektuer fertsjinnet in apart artikel. Yn dit artikel wol ik rjochtsje op API-fouttolerânsje.

Openstack Application Architecture. Balansearjen en fouttolerânsje fan it wolkplatfoarm
It meitsjen fan de HAProxy balancer fouttolerant mei ExaBGP
Om ús API's skalberber, rap en fouttolerant te meitsjen, sette wy in loadbalancer foar har. Wy hawwe keazen foar HAProxy. Yn myn miening hat it alle nedige skaaimerken foar ús taak: balânsjen op ferskate OSI-nivo's, in behearynterface, fleksibiliteit en skalberens, in grut oantal balânsmetoaden, stipe foar sesjetabellen.
It earste probleem dat moast wurde oplost wie de fout tolerânsje fan de balancer sels. It gewoan ynstallearjen fan in balancer makket ek in punt fan mislearring: de balancer brekt en de tsjinst crasht. Om foar te kommen dat dit bart, hawwe wy HAProxy brûkt yn kombinaasje mei ExaBGP.
ExaBGP lit jo in meganisme ymplementearje foar it kontrolearjen fan de steat fan in tsjinst. Wy brûkten dit meganisme om de funksjonaliteit fan HAProxy te kontrolearjen en, yn gefal fan problemen, de HAProxy-tsjinst fan BGP út te skeakeljen.
ExaBGP+HAProxy-skema
- Wy ynstallearje de nedige software, ExaBGP en HAProxy, op trije servers.
- Wy meitsje in loopback-ynterface op elke tsjinner.
- Op alle trije servers jouwe wy itselde wite IP-adres ta oan dizze ynterface.
- In wyt IP-adres wurdt advertearre op it ynternet fia ExaBGP.
Fault tolerance wurdt berikt troch advertearje itselde IP-adres fan alle trije servers. Ut in netwurk eachpunt, itselde adres is tagonklik út trije ferskillende folgjende hops. De router sjocht trije identike rûtes, selekteart de heechste prioriteit fan har op basis fan har eigen metrik (dit is normaal deselde opsje), en it ferkear giet allinich nei ien fan 'e servers.
Yn gefal fan problemen mei de wurking fan HAProxy of in tsjinner flater, stopet ExaBGP mei it oankundigjen fan de rûte, en it ferkear skeakelt soepel nei in oare tsjinner.
Sa hawwe wy berikt fout tolerânsje fan de balancer.

Fault tolerance fan HAProxy balancers
It skema blykte ûnfolslein te wêzen: wy learden hoe't jo HAProxy reservearje, mar learden net hoe't wy de lading binnen de tsjinsten ferdielde. Dêrom hawwe wy dit skema in bytsje útwreide: wy binne oergien nei it balansearjen tusken ferskate wite IP-adressen.
Balansearjen basearre op DNS plus BGP
It probleem fan load balancing foar ús HAProxy bliuwt net oplost. It kin lykwols frij ienfâldich wurde oplost, lykas wy hjir diene.
Om trije servers te balansearjen sille jo 3 wite IP-adressen en goede âlde DNS nedich hawwe. Elk fan dizze adressen wurdt bepaald op 'e loopback-ynterface fan elke HAProxy en advertearre op it ynternet.
Yn OpenStack, om boarnen te behearjen, wurdt in tsjinstmap brûkt, dy't de einpunt API fan in bepaalde tsjinst spesifisearret. Yn dizze map registrearje wy in domeinnamme - public.infra.mail.ru, dy't fia DNS wurdt oplost troch trije ferskillende IP-adressen. As gefolch krije wy loadferdieling tusken trije adressen fia DNS.
Mar om't wy by it oankundigjen fan wite IP-adressen de prioriteiten foar seleksje fan tsjinners net kontrolearje, is dit noch net yn balans. Typysk wurdt mar ien tsjinner selektearre op basis fan IP-adres senioriteit, en de oare twa sille idle wêze, om't gjin metriken binne oantsjutte yn BGP.
Wy begûnen rûtes te ferstjoeren fia ExaBGP mei ferskate metriken. Elke balancer advertearret alle trije wite IP-adressen, mar ien fan harren, de wichtichste foar dizze balancer, wurdt advertearre mei de minimale metryske. Dus wylst alle trije balancers yn wurking binne, ropt nei it earste IP-adres nei de earste balancer, ropt nei de twadde nei de twadde, en ropt nei de tredde nei de tredde.
Wat bart der as ien fan de balancers falt? As in balancer mislearret, wurdt it haadadres noch altyd advertearre fan 'e oare twa, en it ferkear wurdt ferdield tusken har. Sa jouwe wy de brûker ferskate IP-adressen tagelyk fia DNS. Troch balansearjen troch DNS en ferskate metriken, krije wy in evenredige ferdieling fan 'e lading oer alle trije balancers. En tagelyk ferlieze wy de skuldtolerânsje net.

Balancing HAProxy basearre op DNS + BGP
Ynteraksje tusken ExaBGP en HAProxy
Dat, wy ymplementearre fouttolerânsje yn gefal de tsjinner fuortgiet, basearre op it stopjen fan de oankundiging fan rûtes. Mar HAProxy kin ôfslute foar oare redenen dan tsjinner flater: administraasje flaters, mislearrings binnen de tsjinst. Wy wolle ek yn dizze gefallen de brutsen balancer fan ûnder de lading fuortsmite, en wy hawwe in oar meganisme nedich.
Dêrom, it útwreidzjen fan it foarige skema, hawwe wy hertslach ymplementearre tusken ExaBGP en HAProxy. Dit is in software ymplemintaasje fan de ynteraksje tusken ExaBGP en HAProxy, doe't ExaBGP brûkt oanpaste skripts te kontrolearjen de status fan applikaasjes.
Om dit te dwaan, moatte jo in sûnenskontrôler ynstelle yn 'e ExaBGP-konfiguraasje, dy't de status fan HAProxy kontrolearje kin. Yn ús gefal hawwe wy de sûnensbackend yn HAProxy konfigureare, en fan 'e ExaBGP-kant kontrolearje wy mei in ienfâldich GET-fersyk. As de oankundiging ophâldt te barren, dan wurket HAProxy nei alle gedachten net en is d'r net nedich om it te advertearjen.

HAProxy Health Check
HAProxy Peers: sesje syngronisaasje
It folgjende ding om te dwaan wie de sesjes te syngronisearje. By it wurkjen troch ferdielde balancers is it lestich om de opslach fan ynformaasje oer kliïntsesjes te organisearjen. Mar HAProxy is ien fan 'e pear balancers dy't dit kin dwaan fanwegen de Peers-funksjonaliteit - de mooglikheid om sesjetabellen oer te dragen tusken ferskate HAProxy-prosessen.
D'r binne ferskate balânsmethoden: ienfâldige lykas , en útwreide, doe't de kliïnt syn sesje wurdt ûnthâlden, en eltse kear hy einiget op deselde tsjinner as foarhinne. Wy woenen de twadde opsje útfiere.
HAProxy brûkt stick-tabellen om kliïntsesjes fan dit meganisme te bewarjen. Se bewarje it orizjinele IP-adres fan 'e kliïnt, it selektearre doeladres (efterkant) en wat tsjinstynformaasje. Typysk wurde stoktabellen brûkt om in boarne-IP + bestimming-IP-paar op te slaan, dat is benammen nuttich foar applikaasjes dy't net kinne oerdrage brûker sesje kontekst by it wikseljen nei in oare balancer, Bygelyks, yn RoundRobin balancing modus.
As in stoktafel wurdt leard om te bewegen tusken ferskate HAProxy-prosessen (tusken hokker balâns optreedt), sille ús balancers kinne wurkje mei ien pool stoktafels. Dit sil it mooglik meitsje om it netwurk fan 'e kliïnt naadloos te wikseljen as ien fan 'e balancers mislearret; wurkje mei kliïntsesjes sil trochgean op deselde backends dy't earder selekteare binne.
Foar goede wurking moat it probleem fan it boarne IP-adres fan 'e balancer wêrfan't de sesje waard oprjochte, wurde oplost. Yn ús gefal is dit in dynamysk adres op 'e loopback-ynterface.
Korrekte wurk fan peers wurdt berikt allinnich ûnder bepaalde betingsten. Dat is, TCP-timeouts moatte grut genôch wêze of wikseljen moat fluch genôch wêze, sadat de TCP-sesje gjin tiid hat om te beëinigjen. It soarget lykwols foar naadleaze wikseljen.
Yn IaaS hawwe wy in tsjinst boud mei deselde technology. Dit , dat hjit Octavia. It is basearre op twa HAProxy-prosessen en omfettet yn earste ynstânsje stipe foar peers. Se hawwe bewiisd harsels poerbêst yn dizze tsjinst.
De ôfbylding toant skematysk de beweging fan peer-tabellen tusken trije HAProxy-eksimplaren, in konfiguraasje wurdt foarsteld oer hoe't dit kin wurde konfigureare:

HAProxy Peers (sesjesyngronisaasje)
As jo itselde skema útfiere, moat de wurking dêrfan soarchfâldich hifke wurde. It is net in feit dat it sil wurkje op deselde wize 100% fan 'e tiid. Mar jo sille teminsten stoktabellen net ferlieze as jo de boarne IP fan 'e kliïnt moatte ûnthâlde.
Beheining fan it oantal simultane oanfragen fan deselde klant
Alle tsjinsten dy't iepenbier beskikber binne, ynklusyf ús API's, kinne ûnderwurpen wêze oan lawines fan oanfragen. De redenen foar har kinne folslein oars wêze, fan brûkersflaters oant doelgerichte oanfallen. Wy wurde periodyk DDoSed troch IP-adressen. Klanten meitsje faak flaters yn har skripts en jouwe ús mini-DDoS's.
Op ien of oare manier moat ekstra beskerming soarge wurde. De foar de hân lizzende oplossing is om it oantal API-oanfragen te beheinen en CPU-tiid net te fergrieme mei it ferwurkjen fan kweade oanfragen.
Foar it útfieren fan sokke beheiningen brûke wy tarivengrinzen, organisearre op basis fan HAProxy, mei deselde stoktafels. It ynstellen fan grinzen is frij ienfâldich en lit jo de brûker beheine troch it oantal oanfragen nei de API. It algoritme ûnthâldt de boarne IP wêrfan oanfragen makke wurde en beheint it oantal simultane oanfragen fan ien brûker. Fansels hawwe wy it gemiddelde API-loadprofyl foar elke tsjinst berekkene en in limyt ynsteld fan ≈ 10 kear dizze wearde. Wy bliuwe de situaasje goed yn de gaten hâlde en hâlde de finger oan de pols.
Hoe sjocht dit der yn de praktyk út? Wy hawwe klanten dy't ús autoskalearjende API's de hiele tiid brûke. Se meitsje moarns sawat twa oant trijehûndert firtuele masines en wiskje se jûns. Foar OpenStack fereasket it meitsjen fan in firtuele masine, ek mei PaaS-tsjinsten, op syn minst 1000 API-oanfragen, om't ynteraksje tusken tsjinsten ek foarkomt fia de API.
Sa'n oerdracht fan taken soarget foar in frij grutte lading. Wy beoardielje dizze lading, sammele deistige peaks, fergrutte se tsienfold, en dit waard ús taryflimyt. Wy hâlde de finger oan de pols. Wy sjogge faak bots en scanners dy't besykje ús te sjen om te sjen oft wy CGA-skripts hawwe dy't kinne wurde útfierd, wy snijje se aktyf.
Hoe kinne jo jo koadebase bywurkje sûnder dat brûkers it merken
Wy implementearje ek fouttolerânsje op it nivo fan koade-ynsetprosessen. D'r kinne glitches wêze tidens rollouts, mar har ynfloed op tsjinstbeskikberens kin minimalisearre wurde.
Wy aktualisearje ús tsjinsten konstant en moatte derfoar soargje dat de koadebase wurdt bywurke sûnder brûkers te beynfloedzjen. Wy binne der yn slagge dit probleem op te lossen mei de behearmooglikheden fan HAProxy en de ymplemintaasje fan Graceful Shutdown yn ús tsjinsten.
Om dit probleem op te lossen, wie it nedich om de kontrôle fan 'e balancer te garandearjen en de "korrekte" shutdown fan tsjinsten:
- Yn it gefal fan HAProxy wurdt kontrôle útfierd fia in statsbestân, dat yn essinsje in socket is en wurdt definieare yn 'e HAProxy-konfiguraasje. Jo kinne stjoere kommando nei it fia stdio. Mar ús wichtichste konfiguraasjekontrôleark is mooglik, dus it hat in ynboude module foar it behearen fan HAProxy. Wat wy aktyf brûke.
- De measte fan ús API- en Engine-tsjinsten stypje sierlike shutdown-technologyen: as se ôfslute, wachtsje se op 'e aktuele taak om te foltôgjen, of it no in http-fersyk is as in tsjinsttaak. Itselde bart mei de arbeider. It wit alle taken dy't it docht en einiget as it alles mei súkses hat foltôge.
Mei tank oan dizze twa punten sjocht it feilige algoritme foar ús ynset der sa út.
- De ûntwikkelder sammelet in nij pakket fan koade (foar ús is dit RPM), testet it yn 'e dev-omjouwing, testet it yn' e poadium, en lit it yn 'e poadiumrepository.
- De ûntwikkelder stelt de taak foar ynset mei de meast detaillearre beskriuwing fan 'e "artefakten": de ferzje fan it nije pakket, in beskriuwing fan 'e nije funksjonaliteit en oare details oer de ynset as nedich.
- De systeembehearder begjint de fernijing. Lansearret it Ansible playbook, dat op syn beurt it folgjende docht:
- Nimt in pakket fan 'e poadiumbewarring en brûkt it om de ferzje fan it pakket yn' e produktrepository te aktualisearjen.
- Kompileart in list mei backends fan 'e bywurke tsjinst.
- Slút de earste tsjinst dy't wurdt bywurke yn HAProxy ôf en wachtet op har prosessen om te rinnen. Mei tank oan sierlike ôfsluting binne wy der wis fan dat alle aktuele klantoanfragen mei sukses sille foltôgje.
- Neidat de API en arbeiders binne folslein stoppe, en HAProxy is útskeakele, de koade wurdt bywurke.
- Ansible rint tsjinsten.
- Foar elke tsjinst wurde bepaalde "handgrepen" lutsen, dy't ienheidstesten útfiere op in oantal foarôf definieare toetstests. In basiskontrôle fan 'e nije koade fynt plak.
- As der gjin flaters waarden fûn yn 'e foarige stap, wurdt de backend aktivearre.
- Litte wy trochgean nei de folgjende backend.
- Nei't alle backends binne bywurke, wurde funksjonele tests lansearre. As se ûntbrekke, dan sjocht de ûntwikkelder nei elke nije funksjonaliteit dy't hy makke.
Dit foltôget de ynset.

Service update syklus
Dizze regeling soe net wurkje as wy net ien regel hiene. Wy stypje sawol de âlde as nije ferzjes yn 'e striid. Foarôf, yn it stadium fan softwareûntwikkeling, wurdt bepaald dat sels as der feroaringen binne yn 'e tsjinstdatabase, se de foarige koade net brekke. As resultaat wurdt de koadebasis stadichoan bywurke.
konklúzje
Troch myn eigen gedachten oer in fouttolerante WEB-arsjitektuer te dielen, wol ik de wichtichste punten nochris opmerke:
- fysike fout tolerânsje;
- netwurk flater tolerânsje (balancers, BGP);
- fouttolerânsje fan 'e brûkte en ûntwikkele software.
Stabiele uptime elkenien!
Boarne: www.habr.com
