
Sveiki, Habr! Esu Artemas Karamiševas, sistemos administravimo komandos vadovas . Per pastaruosius metus pristatėme daug naujų produktų. Norėjome užtikrinti, kad API paslaugos būtų lengvai keičiamos, atsparios gedimams ir būtų paruoštos sparčiai augti vartotojų apkrovai. Mūsų platforma įdiegta „OpenStack“, todėl noriu pasakyti, kokias komponentų atsparumo gedimams problemas turėjome išspręsti, kad gautume gedimams atsparią sistemą. Manau, kad tai bus įdomu tiems, kurie taip pat kuria produktus „OpenStack“.
Bendrą platformos atsparumą gedimams sudaro jos komponentų atsparumas. Taigi palaipsniui pereisime visus lygius, kuriuose nustatėme rizikas ir jas uždarėme.
Šios istorijos vaizdo versija, kurios pirminis šaltinis buvo pranešimas Uptime day 4 konferencijoje, kurią organizavo , Jūs galite pamatyti .
Fizinės architektūros atsparumas
Viešoji MCS debesies dalis dabar yra įsikūrusi dviejuose III pakopos duomenų centruose, tarp jų yra atskiras tamsusis pluoštas, rezervuotas fiziniu lygmeniu skirtingais maršrutais, kurio pralaidumas siekia 200 Gbit/s. III pakopa užtikrina reikiamą fizinės infrastruktūros gedimų tolerancijos lygį.
Tamsus pluoštas yra rezervuotas tiek fiziniu, tiek loginiu lygmenimis. Kanalo rezervavimo procesas buvo kartotinis, iškilo problemų, nuolat tobuliname ryšį tarp duomenų centrų.
Pavyzdžiui, visai neseniai, dirbant šulinyje prie vieno duomenų centrų, ekskavatorius nulaužė vamzdį, o šio vamzdžio viduje buvo ir pagrindinis, ir atsarginis optinis kabelis. Mūsų gedimams atsparus ryšio kanalas su duomenų centru vienu metu, šulinyje, pasirodė esąs pažeidžiamas. Atitinkamai praradome dalį infrastruktūros. Padarėme išvadas ir ėmėmės daugybės veiksmų, įskaitant ir papildomos optikos įrengimą gretimame šulinyje.
Duomenų centruose yra ryšių tiekėjų, kuriems mes transliuojame savo prefiksus per BGP, buvimo vietos. Kiekvienai tinklo krypčiai parenkama geriausia metrika, kuri leidžia skirtingiems klientams užtikrinti geriausią ryšio kokybę. Jei ryšys per vieną teikėją nutrūksta, mes iš naujo sukuriame maršrutą per galimus teikėjus.
Jei paslaugų teikėjas sugenda, automatiškai pereiname prie kito. Sugedus vienam iš duomenų centrų, antrajame duomenų centre turime veidrodinę savo paslaugų kopiją, kuri prisiima visą apkrovą.

Fizinės infrastruktūros atsparumas
Tai, ką naudojame taikomojo lygio gedimams nustatyti
Mūsų paslauga yra pagrįsta daugybe atvirojo kodo komponentų.
ExaBGP yra paslauga, kuri įgyvendina daugybę funkcijų naudojant BGP pagrįstą dinaminio maršruto parinkimo protokolą. Aktyviai naudojame jį reklamuodami į baltąjį sąrašą įtrauktus IP adresus, per kuriuos vartotojai pasiekia API.
HAProxy yra didelės apkrovos balansavimo priemonė, leidžianti sukonfigūruoti labai lanksčias srauto balansavimo taisykles skirtinguose OSI modelio lygiuose. Mes naudojame jį norėdami balansuoti prieš visas paslaugas: duomenų bazes, pranešimų brokerius, API paslaugas, žiniatinklio paslaugas, mūsų vidinius projektus - viskas yra už HAProxy.
API programa — python parašyta žiniatinklio programa, su kuria vartotojas valdo savo infrastruktūrą ir paslaugą.
Darbuotojo paraiška (toliau tiesiog darbuotojas) – OpenStack paslaugose tai infrastruktūros demonas, leidžiantis transliuoti API komandas į infrastruktūrą. Pavyzdžiui, diskas sukuriamas darbuotojui, o kūrimo užklausa – programos API.
Standartinė OpenStack programų architektūra
Dauguma paslaugų, sukurtų OpenStack, bando vadovautis viena paradigma. Paslauga paprastai susideda iš 2 dalių: API ir darbuotojų (backend vykdytojų). Paprastai API yra WSGI programa python, kuri paleidžiama kaip nepriklausomas procesas (demonas) arba naudojant paruoštą Nginx arba Apache žiniatinklio serverį. API apdoroja vartotojo užklausą ir perduoda tolesnius nurodymus darbuotojo programai vykdyti. Perdavimas vyksta naudojant pranešimų brokerį, dažniausiai RabbitMQ, kiti yra prastai palaikomi. Kai pranešimai pasiekia brokerį, darbuotojai juos apdoroja ir, jei reikia, grąžina atsakymą.
Ši paradigma apima atskirus bendrus gedimo taškus: RabbitMQ ir duomenų bazę. Tačiau RabbitMQ yra izoliuotas vienoje paslaugoje ir teoriškai gali būti individualus kiekvienai paslaugai. Taigi MCS šias paslaugas kiek įmanoma atskiriame, kiekvienam atskiram projektui sukuriame atskirą duomenų bazę, atskirą RabbitMQ. Toks požiūris yra geras, nes įvykus avarijai kai kuriose pažeidžiamose vietose sugenda ne visa paslauga, o tik dalis jos.
Darbuotojų taikomųjų programų skaičius neribojamas, todėl API gali lengvai keisti mastelį horizontaliai už balansierių, kad padidintų našumą ir atsparumą gedimams.
Kai kurios paslaugos reikalauja koordinavimo paslaugos viduje, kai tarp API ir darbuotojų atliekamos sudėtingos nuoseklios operacijos. Šiuo atveju naudojamas vienas koordinavimo centras, klasterių sistema, tokia kaip Redis, Memcache ir kt., kuri leidžia vienam darbuotojui pasakyti kitam, kad ši užduotis jam priskirta („prašau neatimk“). Mes naudojame etcd. Paprastai darbuotojai aktyviai bendrauja su duomenų baze, rašo ir skaito informaciją iš ten. Naudojame mariadb kaip duomenų bazę, kuri yra multimaster klasteryje.
Ši klasikinė atskira paslauga yra organizuota taip, kaip visuotinai priimta OpenStack. Tai gali būti laikoma uždara sistema, kuriai mastelio keitimo ir gedimų tolerancijos metodai yra gana akivaizdūs. Pvz., API gedimų tolerancijai pakanka priešais juos pastatyti balansavimo priemonę. Darbuotojų mastelis pasiekiamas didinant jų skaičių.
Silpnoji visos schemos vieta yra RabbitMQ ir MariaDB. Jų architektūra nusipelno atskiro straipsnio.Šiame straipsnyje noriu sutelkti dėmesį į API atsparumą gedimams.

„Openstack“ programų architektūra. Debesų platformos balansavimas ir atsparumas gedimams
Padaryti HAProxy balansyro atsparų gedimams naudojant ExaBGP
Kad mūsų API būtų keičiamas, greitas ir atsparus gedimams, prieš jas įdėjome apkrovos balansavimo priemonę. Mes pasirinkome HAProxy. Mano nuomone, jis turi visas mūsų užduotims reikalingas charakteristikas: balansavimą keliuose OSI lygiuose, valdymo sąsają, lankstumą ir mastelį, daugybę balansavimo metodų, seansų lentelių palaikymą.
Pirmoji problema, kurią reikėjo išspręsti, buvo paties balansyro atsparumas gedimams. Tiesiog sumontavus balansyrą taip pat atsiranda gedimo taškas: sugenda balansyras ir sugenda servisas. Kad taip neatsitiktų, mes naudojome HAProxy kartu su ExaBGP.
ExaBGP leidžia įdiegti paslaugos būsenos tikrinimo mechanizmą. Naudojome šį mechanizmą norėdami patikrinti HAProxy funkcionalumą ir, iškilus problemoms, išjungti HAProxy paslaugą iš BGP.
ExaBGP+HAProxy schema
- Trijuose serveriuose įdiegiame reikiamą programinę įrangą ExaBGP ir HAProxy.
- Kiekviename serveryje sukuriame atgalinę sąsają.
- Visuose trijuose serveriuose šiai sąsajai priskiriame tą patį baltą IP adresą.
- Baltas IP adresas reklamuojamas internetu per ExaBGP.
Gedimų tolerancija pasiekiama reklamuojant tą patį IP adresą iš visų trijų serverių. Tinklo požiūriu tas pats adresas pasiekiamas iš trijų skirtingų sekančių apynių. Maršrutizatorius mato tris vienodus maršrutus, pagal savo metriką parenka aukščiausią jų prioritetą (dažniausiai tai yra ta pati parinktis), o srautas eina tik į vieną iš serverių.
Iškilus problemų dėl HAProxy veikimo arba sugedus serveriui, ExaBGP nustoja skelbti maršrutą, o srautas sklandžiai persijungia į kitą serverį.
Taigi, mes pasiekėme balansyro gedimų toleranciją.

HAProxy balansierių atsparumas gedimams
Schema pasirodė netobula: išmokome rezervuoti HAProxy, bet neišmokome paskirstyti apkrovos paslaugų viduje. Todėl šią schemą šiek tiek išplėtėme: perėjome prie balansavimo tarp kelių baltų IP adresų.
Balansavimas pagal DNS ir BGP
Mūsų HAProxy apkrovos balansavimo problema lieka neišspręsta. Tačiau tai galima išspręsti gana paprastai, kaip tai padarėme čia.
Norėdami subalansuoti tris serverius, jums reikės 3 baltų IP adresų ir seno gero DNS. Kiekvienas iš šių adresų nustatomas kiekvieno HAProxy atgalinio ryšio sąsajoje ir skelbiamas internete.
„OpenStack“ ištekliams valdyti naudojamas paslaugų katalogas, kuriame nurodoma konkrečios paslaugos galutinio taško API. Šiame kataloge registruojame domeno vardą – public.infra.mail.ru, kurį per DNS išsprendžia trys skirtingi IP adresai. Dėl to mes gauname apkrovos paskirstymą tarp trijų adresų per DNS.
Bet kadangi skelbdami baltus IP adresus mes nekontroliuojame serverio pasirinkimo prioritetų, tai dar nebalansuoja. Paprastai tik vienas serveris bus pasirinktas pagal IP adreso senumą, o kiti du bus neaktyvūs, nes BGP nenurodyta jokia metrika.
Pradėjome siųsti maršrutus per ExaBGP su skirtinga metrika. Kiekvienas balansuotojas reklamuoja visus tris baltus IP adresus, tačiau vienas iš jų, pagrindinis šiam balansuotojui, reklamuojamas su minimalia metrika. Taigi, kol veikia visi trys balansuotojai, skambučiai į pirmąjį IP adresą patenka į pirmąjį balansavimo įrenginį, į antrąjį į antrąjį, o į trečiąjį – į trečiąjį.
Kas atsitiks, kai vienas iš balansuotojų nukrenta? Jei kuris nors balansuotojas sugenda, pagrindinis jo adresas vis tiek skelbiamas iš kitų dviejų, o srautas perskirstomas tarp jų. Taigi mes suteikiame vartotojui kelis IP adresus vienu metu per DNS. Balansuodami pagal DNS ir skirtingas metrikas, gauname tolygų apkrovos paskirstymą visuose trijuose balansuotojuose. Ir tuo pačiu neprarandame tolerancijos gedimams.

Subalansuotas HAProxy, pagrįstas DNS + BGP
ExaBGP ir HAProxy sąveika
Taigi, mes įdiegėme toleranciją gedimams serverio pasitraukimo atveju, remdamiesi maršrutų paskelbimo sustabdymu. Tačiau HAProxy gali išsijungti dėl kitų priežasčių nei serverio gedimas: administravimo klaidų, gedimų paslaugos viduje. Šiais atvejais taip pat norime pašalinti sugedusį balansavimo įtaisą iš apkrovos, todėl mums reikia kitokio mechanizmo.
Todėl, išplėtę ankstesnę schemą, įdiegėme širdies plakimą tarp ExaBGP ir HAProxy. Tai yra programinė įranga, skirta sąveikai tarp ExaBGP ir HAProxy, kai ExaBGP naudoja pasirinktinius scenarijus programų būsenai patikrinti.
Norėdami tai padaryti, „ExaBGP“ konfigūracijoje turite sukonfigūruoti sveikatos tikrintuvą, kuris gali patikrinti HAProxy būseną. Mūsų atveju sveikatos užklausą sukonfigūravome HAProxy, o iš ExaBGP pusės tikriname naudodami paprastą GET užklausą. Jei pranešimas nustoja įvykti, HAProxy greičiausiai neveikia ir nereikia jo reklamuoti.

HAProxy sveikatos patikrinimas
HAProxy Peers: seanso sinchronizavimas
Kitas dalykas, kurį reikėjo padaryti, buvo seansų sinchronizavimas. Dirbant per paskirstytus balansuotojus, sunku organizuoti informacijos apie kliento seansus saugojimą. Tačiau HAProxy yra vienas iš nedaugelio balansierių, galinčių tai padaryti dėl Peers funkcionalumo – galimybės perkelti seansų lenteles tarp skirtingų HAProxy procesų.
Yra įvairių balansavimo būdų: paprastų, pvz , ir pratęsiamas, kai prisimenama kliento sesija ir kiekvieną kartą, kai jis atsiduria tame pačiame serveryje kaip ir anksčiau. Norėjome įgyvendinti antrąjį variantą.
HAProxy naudoja klijavimo lenteles, kad išsaugotų šio mechanizmo klientų seansus. Jie išsaugo pradinį kliento IP adresą, pasirinktą tikslinį adresą (backend) ir dalį paslaugų informacijos. Paprastai lazdelės naudojamos šaltinio-IP + paskirties-IP porai saugoti, o tai ypač naudinga programoms, kurios negali perkelti vartotojo seanso konteksto perjungiant į kitą balansavimo priemonę, pavyzdžiui, RoundRobin balansavimo režimu.
Jei lazdos stalas bus išmokytas judėti tarp skirtingų HAProxy procesų (tarp kurių vyksta balansavimas), mūsų balansuotojai galės dirbti su vienu lazdelių stalu. Tai leis sklandžiai perjungti kliento tinklą, jei vienas iš balansierių sugenda; darbas su kliento seansais bus tęsiamas tose pačiose anksčiau pasirinktose sistemose.
Norint tinkamai veikti, turi būti išspręsta balansyro, iš kurio buvo sukurta sesija, šaltinio IP adreso problema. Mūsų atveju tai yra dinaminis adresas atgalinio ryšio sąsajoje.
Teisingas bendraamžių darbas pasiekiamas tik tam tikromis sąlygomis. Tai reiškia, kad TCP skirtasis laikas turi būti pakankamai didelis arba perjungimas turi būti pakankamai greitas, kad TCP seansas nespėtų nutraukti. Tačiau tai leidžia sklandžiai perjungti.
„IaaS“ turime paslaugą, sukurtą naudojant tą pačią technologiją. Tai , kuri vadinasi Octavia. Jis pagrįstas dviem HAProxy procesais ir iš pradžių apima palaikymą kolegoms. Jie puikiai pasitvirtino šioje tarnyboje.
Paveikslėlyje schematiškai parodytas lygiaverčių lentelių judėjimas tarp trijų HAProxy egzempliorių, siūloma konfigūracija, kaip tai galima konfigūruoti:

HAProxy Peers (sesijos sinchronizavimas)
Jei įgyvendinsite tą pačią schemą, jos veikimas turi būti kruopščiai išbandytas. Tai nėra faktas, kad jis veiks taip pat 100% laiko. Bet bent jau neprarasite lipdukų lentelių, kai reikės atsiminti kliento šaltinio IP.
Vienu metu gaunamų užklausų iš to paties kliento skaičiaus ribojimas
Visoms viešai prieinamoms paslaugoms, įskaitant mūsų API, gali būti užklausų lavinos. Jų priežastys gali būti visiškai skirtingos – nuo vartotojo klaidų iki tikslinių atakų. Mus periodiškai DDoSuoja IP adresai. Klientai dažnai daro klaidų savo scenarijuose ir pateikia mums mini DDoS.
Vienaip ar kitaip, reikia pasirūpinti papildoma apsauga. Akivaizdus sprendimas yra apriboti API užklausų skaičių ir nešvaistyti procesoriaus laiko kenkėjiškoms užklausoms apdoroti.
Siekdami įgyvendinti tokius apribojimus, naudojame HAProxy pagrindu organizuotus tarifų limitus, naudojant tas pačias lazdeles. Ribų nustatymas yra gana paprastas ir leidžia apriboti vartotoją API užklausų skaičiumi. Algoritmas įsimena šaltinio IP, iš kurio pateikiamos užklausos, ir riboja vieno vartotojo užklausų skaičių vienu metu. Žinoma, mes apskaičiavome vidutinį API apkrovos profilį kiekvienai paslaugai ir nustatėme ≈ 10 kartų šią vertę didesnę ribą. Mes ir toliau atidžiai stebime situaciją ir laikome pirštu ant pulso.
Kaip tai atrodo praktikoje? Turime klientų, kurie nuolat naudojasi mūsų automatinio mastelio keitimo API. Ryte jie sukuria maždaug nuo dviejų iki trijų šimtų virtualių mašinų, o vakare jas ištrina. Norint sukurti „OpenStack“ virtualią mašiną, taip pat naudojant „PaaS“ paslaugas, reikia mažiausiai 1000 API užklausų, nes paslaugų sąveika taip pat vyksta per API.
Toks užduočių perkėlimas sukelia gana didelį krūvį. Įvertinome šį krūvį, surinkome dienos pikus, padidinome dešimt kartų ir tai tapo mūsų normos riba. Laikome pirštą ant pulso. Dažnai matome robotus ir skaitytuvus, kurie bando į mus pažiūrėti, ar turime kokių nors CGA scenarijų, kuriuos galima paleisti, mes aktyviai juos pjaustome.
Kaip atnaujinti kodų bazę vartotojams to nepastebėjus
Taip pat įdiegiame gedimų toleranciją kodo diegimo procesų lygiu. Diegimo metu gali kilti nesklandumų, tačiau jų poveikį paslaugų prieinamumui galima sumažinti iki minimumo.
Mes nuolat atnaujiname savo paslaugas ir privalome užtikrinti, kad kodų bazė būtų atnaujinta nepažeidžiant vartotojų. Šią problemą mums pavyko išspręsti pasinaudojant HAProxy valdymo galimybėmis ir mūsų paslaugose įdiegus Graceful Shutdown.
Norint išspręsti šią problemą, reikėjo užtikrinti balanso valdymą ir „teisingą“ paslaugų išjungimą:
- HAProxy atveju valdymas atliekamas per statistikos failą, kuris iš esmės yra lizdas ir yra apibrėžtas HAProxy konfigūracijoje. Galite siųsti komandas į jį per stdio. Tačiau mūsų pagrindinis konfigūracijos valdymo įrankis yra įmanomas, todėl jame yra integruotas HAProxy valdymo modulis. Kuriuos mes aktyviai naudojame.
- Dauguma mūsų API ir variklio paslaugų palaiko grakščias išjungimo technologijas: išjungdamos jos laukia, kol bus atlikta dabartinė užduotis, nesvarbu, ar tai būtų http užklausa, ar kokia nors paslaugos užduotis. Tas pats atsitinka ir su darbuotoju. Jis žino visas atliekamas užduotis ir baigiasi, kai viską sėkmingai įvykdo.
Dėl šių dviejų punktų saugus mūsų diegimo algoritmas atrodo taip.
- Kūrėjas surenka naują kodo paketą (mums tai yra RPM), išbando jį dev aplinkoje, išbando etape ir palieka scenos saugykloje.
- Kūrėjas nustato diegimo užduotį, pateikdamas išsamiausią „artefaktų“ aprašymą: naujojo paketo versiją, naujos funkcijos aprašymą ir kitą išsamią diegimo informaciją, jei reikia.
- Sistemos administratorius pradeda naujinimą. Paleidžia „Ansible“ žaidimų knygą, kuri savo ruožtu atlieka šiuos veiksmus:
- Paima paketą iš etapo saugyklos ir naudoja jį paketo versijai atnaujinti produkto saugykloje.
- Sudaro atnaujintos paslaugos užpakalinių programų sąrašą.
- Išjungia pirmąją paslaugą, kuri turi būti atnaujinta HAProxy, ir laukia, kol baigsis jos procesai. Dėl grakštaus išjungimo esame įsitikinę, kad visos esamos klientų užklausos bus sėkmingai įvykdytos.
- Kai API ir darbuotojai visiškai sustabdomi, o HAProxy išjungiamas, kodas atnaujinamas.
- Ansible teikia paslaugas.
- Kiekvienai paslaugai ištraukiamos tam tikros „rankenos“, kurios atlieka vienetų testavimą pagal daugybę iš anksto nustatytų pagrindinių testų. Atliekamas pagrindinis naujojo kodo patikrinimas.
- Jei ankstesniame žingsnyje klaidų nerasta, suaktyvinama užpakalinė programa.
- Pereikime prie kitos užpakalinės programos.
- Atnaujinus visas pagrindines programas, paleidžiami funkciniai testai. Jei jų trūksta, kūrėjas peržiūri visas naujas sukurtas funkcijas.
Tai užbaigia diegimą.

Paslaugos atnaujinimo ciklas
Ši schema neveiks, jei neturėtume vienos taisyklės. Mūšyje palaikome tiek seną, tiek naują versiją. Iš anksto programinės įrangos kūrimo etape yra nustatyta, kad net jei paslaugų duomenų bazėje yra pakeitimų, jie nepažeis ankstesnio kodo. Dėl to kodo bazė palaipsniui atnaujinama.
išvada
Pasidalydamas savo mintimis apie gedimams atsparią WEB architektūrą, norėčiau dar kartą atkreipti dėmesį į pagrindinius jos dalykus:
- fizinių gedimų tolerancija;
- tinklo gedimų tolerancija (balansai, BGP);
- naudojamos ir kuriamos programinės įrangos atsparumas gedimams.
Stabilus veikimo laikas visiems!
Šaltinis: www.habr.com
