
Saluton, Habr! Mi estas Artem Karamyshev, estro de la sistemo-administra teamo . Ni havis multajn novajn produktolanĉojn dum la pasinta jaro. Ni volis certigi, ke API-servoj estas facile skaleblaj, mistoleremaj kaj pretaj por rapida kresko de uzantŝarĝo. Nia platformo estas efektivigita sur OpenStack, kaj mi volas diri al vi, kiajn komponentajn mistoleremajn problemojn ni devis solvi por akiri mistoleran sistemon. Mi pensas, ke ĉi tio estos interesa por tiuj, kiuj ankaŭ disvolvas produktojn sur OpenStack.
La totala faŭltoleremo de platformo konsistas el la rezisteco de ĝiaj komponentoj. Do ni iom post iom trairos ĉiujn nivelojn, kie ni identigis riskojn kaj fermis ilin.
Videoversio de ĉi tiu rakonto, kies ĉeffonto estis raporto ĉe la Uptime-tago 4-konferenco, organizita de , vi povas vidi .
Eltenemo de la fizika arkitekturo
La publika parto de la MCS-nubo nun baziĝas en du datumcentroj de Tier III, inter ili estas sia propra malhela fibro, rezervita ĉe fizika nivelo per malsamaj vojoj, kun trafluo de 200 Gbit/s. Tier III disponigas la necesan nivelon de faŭltoleremo por la fizika infrastrukturo.
Malhela fibro estas rezervita ĉe la fizika kaj logika niveloj. La kanala rezerva procezo estis ripeta, problemoj ekestis, kaj ni konstante plibonigas komunikadon inter datumcentroj.
Ekzemple, antaŭ nelonge, laborante en puto proksime de unu el la datumcentroj, elkavatoro rompis tubon, kaj ene de ĉi tiu tubo estis kaj ĉefa kaj rezerva optika kablo. Nia mistolerema komunika kanalo kun la datumcentro montriĝis vundebla ĉe unu punkto, en la puto. Sekve, ni perdis parton de la infrastrukturo. Ni eltiris konkludojn kaj faris kelkajn agojn, inkluzive instali pliajn optikojn en la apuda puto.
En datumcentroj estas punktoj de ĉeesto de komunikadprovizantoj al kiuj ni dissendas niajn prefiksojn per BGP. Por ĉiu retodirekto, la plej bona metriko estas elektita, kio permesas al malsamaj klientoj esti provizitaj per la plej bona konektkvalito. Se komunikado tra unu provizanto malsukcesas, ni rekonstruas nian vojigon per la disponeblaj provizantoj.
Se provizanto malsukcesas, ni aŭtomate ŝanĝas al la sekva. En la okazo de malsukceso de unu el la datumcentroj, ni havas spegulan kopion de niaj servoj en la dua datumcentro, kiu prenas la tutan ŝarĝon.

Fortikeco de fizika infrastrukturo
Kion ni uzas por aplikaĵ-nivela misfunkciado
Nia servo estas konstruita sur kelkaj malfermfontaj komponantoj.
ExaBGP estas servo kiu efektivigas kelkajn funkciojn uzante la BGP-bazitan dinamikan vojigprotokolon. Ni aktive uzas ĝin por reklami niajn enlistigitajn IP-adresojn per kiuj uzantoj aliras la API.
HAProxy estas alt-ŝarĝa balancilo, kiu ebligas al vi agordi tre flekseblajn trafikajn ekvilibrajn regulojn ĉe malsamaj niveloj de la OSI-modelo. Ni uzas ĝin por ekvilibrigi antaŭ ĉiuj servoj: datumbazoj, mesaĝaj makleristoj, API-servoj, retservoj, niaj internaj projektoj - ĉio estas malantaŭ HAProxy.
API-apliko — TTT-apliko skribita en python, per kiu la uzanto administras sian infrastrukturon kaj sian servon.
Apliko de laboristo (ĉi-poste simple laboristo) - en OpenStack-servoj, ĉi tio estas infrastruktura demono, kiu permesas vin dissendi API-komandojn al la infrastrukturo. Ekzemple, kreado de disko okazas en la laboristo, kaj la peto de kreado okazas en la aplikaĵo API.
Norma OpenStack Aplika Arkitekturo
Plej multaj servoj, kiuj estas evoluigitaj por OpenStack, provas sekvi ununuran paradigmon. Servo kutime konsistas el 2 partoj: API kaj laboristoj (backend-ekzekutistoj). Kiel regulo, API estas WSGI-aplikaĵo en python, kiu estas lanĉita aŭ kiel sendependa procezo (demono), aŭ uzante pretan Nginx aŭ Apache retservilon. La API prilaboras la uzantpeton kaj pasas pliajn instrukciojn al la laborista aplikaĵo por ekzekuto. La translokigo okazas uzante mesaĝan broker, kutime RabbitMQ, la aliaj estas malbone subtenataj. Kiam mesaĝoj atingas la makleriston, ili estas procesitaj de laboristoj kaj, se necese, resendas respondon.
Tiu paradigmo implikas izolitajn komunajn punktojn de fiasko: RabbitMQ kaj la datumbazo. Sed RabbitMQ estas izolita ene de unu servo kaj, en teorio, povas esti individua por ĉiu servo. Do ĉe MCS ni disigas ĉi tiujn servojn kiel eble plej multe; por ĉiu individua projekto ni kreas apartan datumbazon, apartan RabbitMQ. Ĉi tiu aliro estas bona ĉar en okazo de akcidento ĉe iuj vundeblaj punktoj, ne la tuta servo paneiĝas, sed nur parto de ĝi.
La nombro da laboristaj aplikoj estas senlima, do la API povas facile grimpi horizontale malantaŭ balanciloj por pliigi rendimenton kaj toleremon al misfunkciadoj.
Kelkaj servoj postulas kunordigon ene de la servo kiam kompleksaj sinsekvaj operacioj okazas inter APIoj kaj laboristoj. En ĉi tiu kazo, unuopa kunordiga centro estas uzata, cluster-sistemo kiel Redis, Memcache, ktpd, kiu permesas al unu laboristo diri al alia, ke ĉi tiu tasko estas atribuita al li ("bonvolu ne preni ĝin"). Ni uzas etcd. Kiel regulo, laboristoj aktive komunikas kun la datumbazo, skribas kaj legas informojn de tie. Ni uzas mariadb kiel datumbazon, kiu situas en plurmastra areto.
Ĉi tiu klasika ununura servo estas organizita en maniero ĝenerale akceptita por OpenStack. Ĝi povas esti konsiderata kiel fermita sistemo, por kiu la metodoj de skalo kaj faŭltoleremo estas sufiĉe evidentaj. Ekzemple, por API-faŭltoleremo, sufiĉas meti ekvilibron antaŭ ili. Skalado de laboristoj estas atingita per pliigo de ilia nombro.
La malforta punkto en la tuta skemo estas RabbitMQ kaj MariaDB. Ilia arkitekturo meritas apartan artikolon.En ĉi tiu artikolo mi volas koncentriĝi pri API-faŭltoleremo.

Openstack Aplika Arkitekturo. Ekvilibro kaj faŭltoleremo de la nuba platformo
Farante la HAProxy-ekvilibran toleran misfunkciadon uzante ExaBGP
Por fari niajn API-ojn skaleblaj, rapidaj kaj toleremaj al misfunkciadoj, ni metas ŝarĝan ekvilibrilon antaŭ ili. Ni elektis HAProxy. Miaopinie, ĝi havas ĉiujn necesajn karakterizaĵojn por nia tasko: ekvilibro je pluraj OSI-niveloj, administrada interfaco, fleksebleco kaj skalebleco, granda nombro da ekvilibraj metodoj, subteno por sesiotabloj.
La unua problemo, kiu devis esti solvita, estis la faŭltoleremo de la ekvilibristo mem. Simple instali ekvilibron ankaŭ kreas punkton de malsukceso: la balancilo rompas kaj la servo kraŝas. Por malhelpi ĉi tion okazi, ni uzis HAProxy kune kun ExaBGP.
ExaBGP permesas efektivigi mekanismon por kontroli la staton de servo. Ni uzis ĉi tiun mekanismon por kontroli la funkciecon de HAProxy kaj, en kazo de problemoj, malŝalti la servon HAProxy de BGP.
ExaBGP+HAProxy-skemo
- Ni instalas la necesajn programojn, ExaBGP kaj HAProxy, sur tri serviloj.
- Ni kreas loopback interfacon sur ĉiu servilo.
- Sur ĉiuj tri serviloj ni asignas la saman blankan IP-adreson al ĉi tiu interfaco.
- Blanka IP-adreso estas reklamita al la Interreto per ExaBGP.
Faŭltoleremo estas atingita per reklamado de la sama IP-adreso de ĉiuj tri serviloj. El reta vidpunkto, la sama adreso estas alirebla de tri malsamaj sekvaj saltoj. La enkursigilo vidas tri identajn itinerojn, elektas la plej altan prioritaton de ili surbaze de sia propra metriko (ĉi tio estas kutime la sama opcio), kaj la trafiko iras nur al unu el la serviloj.
En kazo de problemoj kun la funkciado de HAProxy aŭ fiasko de servilo, ExaBGP ĉesas anonci la itineron, kaj la trafiko glate ŝanĝas al alia servilo.
Tiel, ni atingis misfunkciadon de la ekvilibristo.

Toleremo al misfunkciadoj de HAProxy-balanciloj
La skemo montriĝis neperfekta: ni lernis kiel rezervi HAProxy, sed ne lernis kiel distribui la ŝarĝon ene de la servoj. Tial ni iomete pligrandigis ĉi tiun skemon: ni pluiris al ekvilibro inter pluraj blankaj IP-adresoj.
Ekvilibro bazita sur DNS plus BGP
La problemo pri ŝarĝoekvilibro por nia HAProxy restas nesolvita. Tamen, ĝi povas esti solvita tute simple, kiel ni faris ĉi tie.
Por ekvilibrigi tri servilojn vi bezonos 3 blankajn IP-adresojn kaj bonan malnovan DNS. Ĉiu el ĉi tiuj adresoj estas determinita sur la loopback interfaco de ĉiu HAProxy kaj reklamita al la Interreto.
En OpenStack, por administri rimedojn, servodosierujo estas uzata, kiu specifas la finpunkton API de aparta servo. En ĉi tiu dosierujo ni registras domajnan nomon - public.infra.mail.ru, kiu estas solvita per DNS per tri malsamaj IP-adresoj. Kiel rezulto, ni ricevas ŝarĝan distribuon inter tri adresoj per DNS.
Sed ĉar kiam ni anoncas blankajn IP-adresojn ni ne kontrolas la prioritatojn pri elektado de serviloj, ĉi tio ankoraŭ ne ekvilibriĝas. Tipe, nur unu servilo estos elektita surbaze de IP-adresa antikva tempo, kaj la aliaj du estos neaktivaj ĉar neniuj metrikoj estas specifitaj en BGP.
Ni komencis sendi itinerojn per ExaBGP kun malsamaj metrikoj. Ĉiu ekvilibristo reklamas ĉiujn tri blankajn IP-adresojn, sed unu el ili, la ĉefa por ĉi tiu ekvilibristo, estas reklamita kun la minimuma metriko. Do dum ĉiuj tri balanciloj funkcias, vokoj al la unua IP-adreso iras al la unua ekvilibristo, vokoj al la dua al la dua, kaj vokoj al la tria al la tria.
Kio okazas kiam unu el la ekvilibruloj falas? Se iu ekvilibristo malsukcesas, ĝia ĉefa adreso daŭre estas reklamita de la aliaj du, kaj trafiko estas redistribuita inter ili. Tiel, ni donas al la uzanto plurajn IP-adresojn samtempe per DNS. Per ekvilibro per DNS kaj malsamaj metrikoj, ni ricevas egalan distribuon de la ŝarĝo tra ĉiuj tri balanciloj. Kaj samtempe ni ne perdas kulp-toleremon.

Ekvilibro HAProxy bazita sur DNS + BGP
Interago inter ExaBGP kaj HAProxy
Do, ni efektivigis misfunkciadon en la okazo ke la servilo foriras, surbaze de ĉesigo de la anonco de itineroj. Sed HAProxy povas malŝalti pro aliaj kialoj ol servila fiasko: administraj eraroj, misfunkciadoj ene de la servo. Ni volas forigi la rompitan ekvilibron de sub la ŝarĝo ankaŭ en ĉi tiuj kazoj, kaj ni bezonas malsaman mekanismon.
Tial, vastigante la antaŭan skemon, ni efektivigis korbaton inter ExaBGP kaj HAProxy. Ĉi tio estas programara efektivigo de la interago inter ExaBGP kaj HAProxy, kiam ExaBGP uzas kutimajn skriptojn por kontroli la staton de aplikoj.
Por fari tion, vi devas agordi sankontrolilon en la agordo ExaBGP, kiu povas kontroli la staton de HAProxy. En nia kazo, ni agordis la sanan backend en HAProxy, kaj de la ExaBGP-flanko ni kontrolas per simpla GET-peto. Se la anonco ĉesas okazi, tiam HAProxy plej verŝajne ne funkcias kaj ne necesas reklami ĝin.

HAProxy Sankontrolo
HAProxy Peers: sinkronigado de sesio
La sekva afero estis sinkronigi la sesiojn. Kiam vi laboras per distribuitaj balanciloj, estas malfacile organizi la stokadon de informoj pri klientsesioj. Sed HAProxy estas unu el la malmultaj ekvilibristoj, kiuj povas fari tion pro la Peers-funkcio - la kapablo transdoni sesiajn tabelojn inter malsamaj procezoj de HAProxy.
Estas malsamaj ekvilibraj metodoj: simplaj kiel ekz , kaj plilongigita, kiam la sesio de la kliento estas memorita, kaj ĉiufoje li finiĝas sur la sama servilo kiel antaŭe. Ni volis efektivigi la duan opcion.
HAProxy uzas stick-tablojn por konservi klientajn sesiojn de ĉi tiu mekanismo. Ili konservas la originan IP-adreson de la kliento, la elektitan cel-adreson (backend) kaj iujn servajn informojn. Tipe, bastontabloj estas uzataj por stoki paron fonto-IP + celloko-IP, kiu estas precipe utila por aplikoj kiuj ne povas transdoni uzantan sean kuntekston kiam ŝanĝas al alia balancilo, ekzemple, en RoundRobin-balanca reĝimo.
Se bastontabelo estas instruata moviĝi inter malsamaj HAProxy-procezoj (inter kiuj okazas ekvilibro), niaj ekvilibristoj povos labori kun unu grupo de bastontabloj. Ĉi tio ebligos perfekte ŝanĝi la reton de la kliento se unu el la ekvilibristoj malsukcesas; laboro kun klientsesioj daŭros sur la samaj backends kiuj estis elektitaj antaŭe.
Por ĝusta funkciado, la problemo de la fonta IP-adreso de la ekvilibristo de kiu la sesio estis establita devas esti solvita. En nia kazo, ĉi tio estas dinamika adreso sur la loopback interfaco.
Ĝusta laboro de kunuloj estas atingita nur sub certaj kondiĉoj. Tio estas, TCP-tempotempoj devas esti sufiĉe grandaj aŭ ŝanĝado devas esti sufiĉe rapida por ke la TCP-sesio ne havu tempon por fini. Tamen ĝi ebligas senjuntan ŝanĝadon.
En IaaS ni havas servon konstruitan uzante la saman teknologion. Ĉi tio , kiu nomiĝas Octavia. Ĝi baziĝas sur du HAProxy-procezoj kaj komence inkluzivas subtenon por kunuloj. Ili pruvis sin bonegaj en ĉi tiu servo.
La bildo skeme montras la movadon de samrangaj tabloj inter tri HAProxy-okazoj, agordo estas proponita pri kiel ĉi tio povas esti agordita:

HAProxy Peers (sinkronigado de sesio)
Se vi efektivigas la saman skemon, ĝia funkciado devas esti zorge provita. Ne estas fakto, ke ĝi funkcios sammaniere 100% de la tempo. Sed almenaŭ vi ne perdos bastonajn tabelojn kiam vi bezonos memori la fontan IP de la kliento.
Limigante la nombron da samtempaj petoj de la sama kliento
Ĉiuj servoj publike haveblaj, inkluzive de niaj API-oj, povas esti submetitaj al lavangoj de petoj. La kialoj de ili povas esti tute malsamaj, de uzantaj eraroj ĝis celitaj atakoj. Ni periode estas DDoSed per IP-adresoj. Klientoj ofte faras erarojn en siaj skriptoj kaj donas al ni mini-DDoS-ojn.
Unu maniero aŭ alia, aldona protekto devas esti provizita. La evidenta solvo estas limigi la nombron da API-petoj kaj ne malŝpari CPU-tempon pritraktante malicajn petojn.
Por efektivigi tiajn limigojn, ni uzas tariflimojn, organizitajn surbaze de HAProxy, uzante la samajn bastontablojn. Agordi limojn estas sufiĉe simpla kaj permesas vin limigi la uzanton per la nombro da petoj al la API. La algoritmo memoras la fontan IP de kiu petoj estas faritaj kaj limigas la nombron da samtempaj petoj de unu uzanto. Kompreneble, ni kalkulis la mezan API-ŝarĝan profilon por ĉiu servo kaj starigis limon de ≈ 10-oble ĉi tiu valoro. Ni daŭre atente monitoras la situacion kaj tenas nian fingron sur la pulso.
Kiel ĉi tio aspektas en la praktiko? Ni havas klientojn, kiuj uzas niajn aŭtomatajn APIojn la tutan tempon. Ili kreas proksimume du ĝis tricent virtualajn maŝinojn matene kaj forigas ilin vespere. Por OpenStack, krei virtualan maŝinon, ankaŭ kun PaaS-servoj, postulas almenaŭ 1000 API-petojn, ĉar interagado inter servoj ankaŭ okazas per la API.
Tia translokigo de taskoj kaŭzas sufiĉe grandan ŝarĝon. Ni taksis ĉi tiun ŝarĝon, kolektis ĉiutagajn pintojn, pliigis ilin dekoble, kaj ĉi tio fariĝis nia tariflimo. Ni tenas nian fingron sur la pulso. Ni ofte vidas bots kaj skaniloj kiuj provas rigardi nin por vidi ĉu ni havas iujn CGA-skriptojn, kiuj povas esti rulitaj, ni aktive tranĉas ilin.
Kiel ĝisdatigi vian kodbazon sen ke uzantoj rimarku
Ni ankaŭ efektivigas erartoleremon je la nivelo de kodaj deplojprocezoj. Povas esti misfunkciadoj dum landoj, sed ilia efiko al servo havebleco povas esti minimumigita.
Ni konstante ĝisdatigas niajn servojn kaj devas certigi, ke la kodbazo estas ĝisdatigita sen tuŝi uzantojn. Ni sukcesis solvi ĉi tiun problemon uzante la administradkapablojn de HAProxy kaj la efektivigon de Graceful Shutdown en niaj servoj.
Por solvi ĉi tiun problemon, estis necese certigi kontrolon de la ekvilibristo kaj la "ĝustan" ĉesigon de servoj:
- En la kazo de HAProxy, kontrolo estas farita per statistika dosiero, kiu estas esence ingo kaj estas difinita en la agordo de HAProxy. Vi povas sendi komandojn al ĝi per stdio. Sed nia ĉefa agorda kontrolilo estas ansible, do ĝi havas enkonstruitan modulon por administri HAProxy. Kiun ni aktive uzas.
- Plej multaj el niaj API kaj Engine-servoj subtenas graciajn malŝaltajn teknologiojn: kiam ili malŝaltas, ili atendas ke la nuna tasko finiĝos, ĉu ĝi estas http-peto aŭ iu serva tasko. La sama afero okazas kun la laboristo. Ĝi konas ĉiujn taskojn kiujn ĝi faras kaj finiĝas kiam ĝi sukcese plenumis ĉion.
Danke al ĉi tiuj du punktoj, la sekura algoritmo por nia deplojo aspektas tiel.
- La programisto kunvenas novan pakaĵon de kodo (por ni ĉi tio estas RPM), testas ĝin en la dev-medio, testas ĝin en la scenejo kaj lasas ĝin en la sceneja deponejo.
- La programisto fiksas la taskon por deplojo kun la plej detala priskribo de la "artefaktoj": la versio de la nova pako, priskribo de la nova funkcieco kaj aliaj detaloj pri la deplojo se necese.
- La sistemadministranto komencas la ĝisdatigon. Lanĉas la ludlibron Ansible, kiu siavice faras la jenon:
- Prenas pakaĵon el la sceneja deponejo kaj uzas ĝin por ĝisdatigi la version de la pakaĵo en la produkta deponejo.
- Kompilas liston de backends de la ĝisdatigita servo.
- Malŝaltas la unuan servon ĝisdatigitan en HAProxy kaj atendas ke ĝiaj procezoj finiĝos. Danke al gracia haltigo, ni certas, ke ĉiuj nunaj klientpetoj sukcese plenumiĝos.
- Post kiam la API kaj laboristoj estas tute haltigitaj, kaj HAProxy estas malŝaltita, la kodo estas ĝisdatigita.
- Ansible kuras servojn.
- Por ĉiu servo, certaj "teniloj" estas tiritaj, kiuj elfaras unutestadon sur kelkaj antaŭdifinitaj ŝlosilaj testoj. Baza kontrolo de la nova kodo okazas.
- Se neniuj eraroj estis trovitaj en la antaŭa paŝo, la backend estas aktivigita.
- Ni transiru al la sekva backend.
- Post kiam ĉiuj backends estas ĝisdatigitaj, funkciaj testoj estas lanĉitaj. Se ili mankas, tiam la programisto rigardas ajnan novan funkcion, kiun li kreis.
Ĉi tio kompletigas la deplojon.

Serva ĝisdatiga ciklo
Ĉi tiu skemo ne funkcius se ni ne havus unu regulon. Ni subtenas kaj la malnovajn kaj novajn versiojn en batalo. Antaŭe, en la etapo de evoluado de programaro, estas dirite, ke eĉ se estas ŝanĝoj en la serva datumbazo, ili ne rompos la antaŭan kodon. Kiel rezulto, la koda bazo estas iom post iom ĝisdatigita.
konkludo
Kundividante miajn proprajn pensojn pri mistolerema RETA arkitekturo, mi ŝatus denove noti ĝiajn ĉefajn punktojn:
- fizika faŭltoleremo;
- toleremo de retaj eraroj (balanciloj, BGP);
- mistoleremo de la programaro uzata kaj evoluinta.
Stabila funkciada tempo ĉiuj!
fonto: www.habr.com
