
Sveiks, Habr! Es esmu Artjoms Karamiševs, sistēmas administrēšanas komandas vadītājs . Pēdējā gada laikā esam laiduši klajā daudz jaunu produktu. Mēs vēlējāmies nodrošināt, lai API pakalpojumi būtu viegli mērogojami, izturīgi pret kļūmēm un gatavi straujai lietotāju slodzes pieaugumam. Mūsu platforma ir ieviesta vietnē OpenStack, un es vēlos jums pastāstīt, kādas komponentu kļūdu tolerances problēmas mums bija jāatrisina, lai iegūtu kļūdu izturīgu sistēmu. Es domāju, ka tas būs interesanti tiem, kas arī izstrādā produktus OpenStack.
Platformas kopējo kļūdu toleranci veido tās sastāvdaļu noturība. Tātad mēs pakāpeniski iziesim cauri visiem līmeņiem, kuros identificējām riskus un tos novērsām.
Šī stāsta video versija, kuras primārais avots bija ziņojums Uptime 4. dienas konferencē, ko organizēja , tu vari redzēt .
Fiziskās arhitektūras noturība
MCS mākoņa publiskā daļa tagad atrodas divos III līmeņa datu centros, starp tiem ir sava tumšā šķiedra, kas rezervēta fiziskajā līmenī pa dažādiem maršrutiem, ar caurlaidspēju 200 Gbit/s. Trešais līmenis nodrošina nepieciešamo kļūdu tolerances līmeni fiziskajai infrastruktūrai.
Tumšā šķiedra ir rezervēta gan fiziskajā, gan loģiskajā līmenī. Kanālu rezervēšanas process bija iteratīvs, radās problēmas, un mēs pastāvīgi uzlabojam saziņu starp datu centriem.
Piemēram, pirms neilga laika, strādājot akā netālu no viena no datu centriem, ekskavators salauza cauruli, un šīs caurules iekšpusē atradās gan galvenais, gan rezerves optiskais kabelis. Mūsu pret defektiem izturīgais saziņas kanāls ar datu centru vienā brīdī, akā, izrādījās neaizsargāts. Attiecīgi esam zaudējuši daļu infrastruktūras. Izdarījām secinājumus un veicām virkni darbību, tai skaitā blakus esošajā akā uzstādījām papildu optiku.
Datu centros ir sakaru nodrošinātāju klātbūtnes punkti, kuriem mēs pārraidām savus prefiksus, izmantojot BGP. Katram tīkla virzienam tiek izvēlēta labākā metrika, kas ļauj dažādiem klientiem nodrošināt vislabāko savienojuma kvalitāti. Ja pārtrūkst sakari, izmantojot vienu pakalpojumu sniedzēju, mēs atjaunojam maršrutu, izmantojot pieejamos pakalpojumu sniedzējus.
Ja pakalpojumu sniedzējs neizdodas, mēs automātiski pārslēdzamies uz nākamo. Ja kāds no datu centriem sabojājas, otrajā datu centrā mums ir mūsu pakalpojumu spoguļkopija, kas uzņemas visu slodzi.

Fiziskās infrastruktūras noturība
Ko mēs izmantojam lietojumprogrammas līmeņa kļūdu tolerancei
Mūsu pakalpojums ir balstīts uz vairākiem atvērtā pirmkoda komponentiem.
ExaBGP ir pakalpojums, kas ievieš vairākas funkcijas, izmantojot uz BGP balstītu dinamisko maršrutēšanas protokolu. Mēs to aktīvi izmantojam, lai reklamētu mūsu baltajā sarakstā iekļautās IP adreses, caur kurām lietotāji piekļūst API.
HAProxy ir augstas slodzes balansētājs, kas ļauj konfigurēt ļoti elastīgus satiksmes līdzsvarošanas noteikumus dažādos OSI modeļa līmeņos. Mēs to izmantojam, lai līdzsvarotu visus pakalpojumus: datu bāzes, ziņojumu brokeri, API pakalpojumi, tīmekļa pakalpojumi, mūsu iekšējie projekti - viss ir aiz HAProxy.
API lietojumprogramma — python valodā rakstīta tīmekļa lietojumprogramma, ar kuru lietotājs pārvalda savu infrastruktūru un pakalpojumu.
Strādnieka pieteikums (turpmāk vienkārši darbinieks) - OpenStack pakalpojumos tas ir infrastruktūras dēmons, kas ļauj pārraidīt API komandas infrastruktūrai. Piemēram, diska izveide notiek darbiniekā, un izveides pieprasījums notiek lietojumprogrammas API.
Standarta OpenStack lietojumprogrammu arhitektūra
Lielākā daļa pakalpojumu, kas ir izstrādāti OpenStack, cenšas ievērot vienu paradigmu. Pakalpojums parasti sastāv no 2 daļām: API un darbiniekiem (backend izpildītājiem). Parasti API ir WSGI lietojumprogramma python, kas tiek palaista vai nu kā neatkarīgs process (dēmons), vai izmantojot gatavu Nginx vai Apache tīmekļa serveri. API apstrādā lietotāja pieprasījumu un nodod papildu norādījumus darbinieka lietojumprogrammai izpildei. Pārsūtīšana notiek, izmantojot ziņojumu brokeri, parasti RabbitMQ, pārējie tiek vāji atbalstīti. Kad ziņojumi nonāk pie brokera, darbinieki tos apstrādā un, ja nepieciešams, nosūta atbildi.
Šī paradigma ietver izolētus kopīgus neveiksmes punktus: RabbitMQ un datu bāzi. Taču RabbitMQ ir izolēts viena pakalpojuma ietvaros un teorētiski var būt individuāls katram pakalpojumam. Tāpēc MCS mēs šos pakalpojumus nodalām pēc iespējas vairāk; katram atsevišķam projektam mēs izveidojam atsevišķu datu bāzi, atsevišķu RabbitMQ. Šī pieeja ir laba, jo negadījuma gadījumā dažos neaizsargātos punktos sabojājas nevis viss pakalpojums, bet tikai daļa no tā.
Darbinieku lietojumprogrammu skaits ir neierobežots, tāpēc API var viegli mērogot horizontāli aiz balansieriem, lai palielinātu veiktspēju un kļūdu toleranci.
Dažiem pakalpojumiem ir nepieciešama koordinācija pakalpojuma ietvaros, ja starp API un darbiniekiem notiek sarežģītas secīgas darbības. Šajā gadījumā tiek izmantots viens koordinācijas centrs, klasteru sistēma, piemēram, Redis, Memcache utt., kas ļauj vienam darbiniekam pateikt otram, ka šis uzdevums viņam ir uzticēts (“lūdzu, neņemiet to”). Mēs izmantojam etcd. Parasti darbinieki aktīvi sazinās ar datu bāzi, raksta un lasa informāciju no turienes. Mēs izmantojam mariadb kā datu bāzi, kas atrodas multimaster klasterī.
Šis klasiskais vienotais pakalpojums ir organizēts OpenStack vispārpieņemtā veidā. To var uzskatīt par slēgtu sistēmu, kurai mērogošanas un kļūdu tolerances metodes ir diezgan acīmredzamas. Piemēram, lai nodrošinātu API kļūdu toleranci, pietiek ar to, ka tiem priekšā tiek novietots balansētājs. Darbinieku mērogošana tiek panākta, palielinot viņu skaitu.
Vājā vieta visā shēmā ir RabbitMQ un MariaDB. To arhitektūra ir pelnījusi atsevišķu rakstu.Šajā rakstā es vēlos koncentrēties uz API kļūdu toleranci.

Openstack lietojumprogrammu arhitektūra. Mākoņu platformas līdzsvarošana un kļūdu tolerance
HAProxy balansētāja kļūmju izturīguma izveide, izmantojot ExaBGP
Lai padarītu mūsu API mērogojamus, ātrus un kļūmju izturīgas, tām priekšā ir slodzes balansētājs. Mēs izvēlējāmies HAProxy. Manuprāt, tam ir visas mūsu uzdevumam nepieciešamās īpašības: balansēšana vairākos OSI līmeņos, pārvaldības saskarne, elastība un mērogojamība, liels skaits balansēšanas metožu, atbalsts sesiju tabulām.
Pirmā problēma, kas bija jāatrisina, bija paša balansiera kļūdu tolerance. Vienkārša balansiera uzstādīšana rada arī kļūmes punktu: balansētājs sabojājas un serviss avarē. Lai tas nenotiktu, mēs izmantojām HAProxy kopā ar ExaBGP.
ExaBGP ļauj ieviest pakalpojuma stāvokļa pārbaudes mehānismu. Mēs izmantojām šo mehānismu, lai pārbaudītu HAProxy funkcionalitāti un problēmu gadījumā atspējotu HAProxy pakalpojumu no BGP.
ExaBGP+HAProxy shēma
- Mēs uzstādām nepieciešamo programmatūru ExaBGP un HAProxy uz trīs serveriem.
- Mēs izveidojam cilpas interfeisu katrā serverī.
- Visos trīs serveros šai saskarnei piešķiram vienu un to pašu balto IP adresi.
- Izmantojot ExaBGP, internetā tiek reklamēta balta IP adrese.
Kļūdu tolerance tiek panākta, reklamējot vienu un to pašu IP adresi no visiem trim serveriem. No tīkla viedokļa viena un tā pati adrese ir pieejama no trim dažādiem nākamajiem apiņiem. Maršrutētājs redz trīs identiskus maršrutus, izvēlas no tiem augstāko prioritāti, pamatojoties uz savu metriku (parasti tā ir viena un tā pati opcija), un trafika notiek tikai uz vienu no serveriem.
Ja rodas problēmas ar HAProxy darbību vai servera kļūme, ExaBGP pārtrauc maršruta izziņošanu, un satiksme vienmērīgi pārslēdzas uz citu serveri.
Tādējādi mēs panācām balansētāja kļūdu toleranci.

HAProxy balansētāju kļūdu tolerance
Shēma izrādījās nepilnīga: mēs uzzinājām, kā rezervēt HAProxy, bet neiemācījāmies sadalīt slodzi pakalpojumu ietvaros. Tāpēc mēs šo shēmu nedaudz paplašinājām: pārgājām uz balansēšanu starp vairākām baltajām IP adresēm.
Balansēšana, pamatojoties uz DNS un BGP
Mūsu HAProxy slodzes līdzsvarošanas problēma joprojām nav atrisināta. Tomēr to var atrisināt pavisam vienkārši, kā mēs to darījām šeit.
Lai līdzsvarotu trīs serverus, jums būs nepieciešamas 3 baltas IP adreses un vecais labais DNS. Katra no šīm adresēm tiek noteikta katra HAProxy cilpas interfeisā un tiek reklamēta internetā.
OpenStack resursu pārvaldībai tiek izmantots pakalpojumu direktorijs, kas norāda konkrēta pakalpojuma galapunkta API. Šajā direktorijā mēs reģistrējam domēna nosaukumu - public.infra.mail.ru, kuru caur DNS atrisina trīs dažādas IP adreses. Rezultātā mēs iegūstam slodzes sadalījumu starp trim adresēm, izmantojot DNS.
Bet, tā kā, paziņojot baltās IP adreses, mēs nekontrolējam servera atlases prioritātes, tas vēl nelīdzsvaro. Parasti tiek atlasīts tikai viens serveris, pamatojoties uz IP adreses vecumu, un pārējie divi būs dīkstāvē, jo BGP nav norādīta metrika.
Mēs sākām sūtīt maršrutus caur ExaBGP ar dažādiem rādītājiem. Katrs balansētājs reklamē visas trīs baltās IP adreses, bet viena no tām, galvenā šim balansētājam, tiek reklamēta ar minimālo metriku. Tātad, kamēr darbojas visi trīs balansieri, zvani uz pirmo IP adresi tiek novirzīti uz pirmo balansētāju, zvani uz otro — uz otro un zvani uz trešo — uz trešo.
Kas notiek, ja kāds no līdzsvarotājiem nokrīt? Ja kāds līdzsvarotājs neizdodas, tā galvenā adrese joprojām tiek reklamēta no pārējām divām, un trafika tiek pārdalīta starp tām. Tādējādi mēs lietotājam dodam vairākas IP adreses vienlaikus, izmantojot DNS. Balansējot pēc DNS un dažādām metrikām, mēs iegūstam vienmērīgu slodzes sadalījumu starp visiem trim balansētājiem. Un tajā pašā laikā mēs nezaudējam kļūdu toleranci.

HAProxy līdzsvarošana, pamatojoties uz DNS + BGP
Mijiedarbība starp ExaBGP un HAProxy
Tātad, mēs ieviesām kļūdu toleranci gadījumam, ja serveris aiziet, pamatojoties uz maršrutu paziņošanas apturēšanu. Taču HAProxy var izslēgties citu iemeslu dēļ, nevis servera kļūmes dēļ: administrēšanas kļūdu, pakalpojuma kļūmju dēļ. Arī šajos gadījumos mēs vēlamies izņemt no slodzes salūzušo balansieri, un mums ir nepieciešams cits mehānisms.
Tāpēc, paplašinot iepriekšējo shēmu, mēs ieviesām sirdsdarbību starp ExaBGP un HAProxy. Šī ir programmatūras ieviešana ExaBGP un HAProxy mijiedarbībai, kad ExaBGP izmanto pielāgotus skriptus, lai pārbaudītu lietojumprogrammu statusu.
Lai to izdarītu, ExaBGP konfigurācijā ir jākonfigurē veselības pārbaudītājs, kas var pārbaudīt HAProxy statusu. Mūsu gadījumā mēs konfigurējām veselības aizmugursistēmu HAProxy, un no ExaBGP puses mēs pārbaudām, izmantojot vienkāršu GET pieprasījumu. Ja paziņojums pārstāj notikt, HAProxy, visticamāk, nedarbojas un nav nepieciešams to reklamēt.

HAProxy veselības pārbaude
HAProxy Peers: sesijas sinhronizācija
Nākamā lieta, kas jādara, bija sesiju sinhronizācija. Strādājot ar sadalītajiem balansētājiem, ir grūti organizēt informācijas par klientu sesijām uzglabāšanu. Bet HAProxy ir viens no nedaudzajiem balansētājiem, kas to var izdarīt, pateicoties Peers funkcionalitātei - iespējai pārsūtīt sesiju tabulas starp dažādiem HAProxy procesiem.
Ir dažādas balansēšanas metodes: vienkāršas, piemēram, , un pagarināts, kad tiek atcerēta klienta sesija un katru reizi, kad viņš nonāk tajā pašā serverī, kur iepriekš. Mēs gribējām realizēt otro variantu.
HAProxy izmanto stick-tabulas, lai saglabātu šī mehānisma klientu sesijas. Tie saglabā klienta sākotnējo IP adresi, atlasīto mērķa adresi (backend) un daļu pakalpojuma informācijas. Parasti stick tabulas tiek izmantotas, lai saglabātu avota-IP + galamērķa-IP pāri, kas ir īpaši noderīgi lietojumprogrammām, kuras nevar pārsūtīt lietotāja sesijas kontekstu, pārslēdzoties uz citu balansētāju, piemēram, RoundRobin balansēšanas režīmā.
Ja nūju galds ir iemācīts pārvietoties starp dažādiem HAProxy procesiem (starp kuriem notiek balansēšana), mūsu balansieri varēs strādāt ar vienu nūju galdu baseinu. Tas ļaus nemanāmi pārslēgt klienta tīklu, ja kāds no balansētājiem neizdodas; darbs ar klienta sesijām turpināsies tajās pašās aizmugursistēmās, kuras tika atlasītas iepriekš.
Lai darbotos pareizi, ir jāatrisina balansētāja avota IP adreses problēma, no kuras tika izveidota sesija. Mūsu gadījumā šī ir dinamiska adrese cilpas saskarnē.
Pareizs vienaudžu darbs tiek panākts tikai noteiktos apstākļos. Tas nozīmē, ka TCP noildzei ir jābūt pietiekami lielai vai pārslēgšanai jābūt pietiekami ātrai, lai TCP sesijai nebūtu laika pārtraukt. Tomēr tas nodrošina netraucētu pārslēgšanu.
IaaS mums ir pakalpojums, kas izveidots, izmantojot to pašu tehnoloģiju. Šis , ko sauc par Octavia. Tas ir balstīts uz diviem HAProxy procesiem un sākotnēji ietver atbalstu vienaudžiem. Viņi ir sevi lieliski pierādījuši šajā pakalpojumā.
Attēlā shematiski parādīta vienādranga tabulu kustība starp trim HAProxy gadījumiem, tiek piedāvāta konfigurācija, kā to var konfigurēt:

HAProxy Peers (sesijas sinhronizācija)
Ja ieviešat to pašu shēmu, tās darbība ir rūpīgi jāpārbauda. Tas nav fakts, ka tas darbosies vienādi 100% laika. Bet vismaz jūs nezaudēsit stick tabulas, kad jums būs jāatceras klienta avota IP.
Vienlaicīgu pieprasījumu skaita ierobežošana no viena un tā paša klienta
Jebkuri publiski pieejami pakalpojumi, tostarp mūsu API, var tikt pakļauti pieprasījumu lavīnām. To iemesli var būt pilnīgi dažādi, sākot no lietotāja kļūdām līdz mērķtiecīgiem uzbrukumiem. Mēs periodiski tiekam DDoSed pēc IP adresēm. Klienti bieži pieļauj kļūdas savos skriptos un izsniedz mums mini-DDoS.
Tā vai citādi ir jānodrošina papildu aizsardzība. Acīmredzams risinājums ir ierobežot API pieprasījumu skaitu un netērēt CPU laiku, apstrādājot ļaunprātīgus pieprasījumus.
Lai ieviestu šādus ierobežojumus, mēs izmantojam ātruma ierobežojumus, kas organizēti, pamatojoties uz HAProxy, izmantojot tās pašas stick tabulas. Ierobežojumu iestatīšana ir diezgan vienkārša un ļauj ierobežot lietotāju pēc API pieprasījumu skaita. Algoritms atceras avota IP, no kura tiek veikti pieprasījumi, un ierobežo vienlaicīgu pieprasījumu skaitu no viena lietotāja. Protams, mēs aprēķinājām vidējo API slodzes profilu katram pakalpojumam un iestatījām ierobežojumu, kas ir ≈ 10 reizes lielāka par šo vērtību. Turpinām rūpīgi sekot līdzi situācijai un turam roku uz pulsa.
Kā tas izskatās praksē? Mums ir klienti, kuri visu laiku izmanto mūsu automātiskās mērogošanas API. Viņi izveido aptuveni divus līdz trīs simtus virtuālo mašīnu no rīta un izdzēš tās vakarā. OpenStack virtuālās mašīnas izveidei, arī ar PaaS pakalpojumiem, ir nepieciešami vismaz 1000 API pieprasījumi, jo mijiedarbība starp pakalpojumiem notiek arī caur API.
Šāda uzdevumu nodošana rada diezgan lielu slodzi. Mēs novērtējām šo slodzi, savācām ikdienas maksimumus, palielinājām tos desmitkārtīgi, un tas kļuva par mūsu likmes ierobežojumu. Turam pirkstu uz pulsa. Mēs bieži redzam robotprogrammatūras un skenerus, kuri mēģina uz mums skatīties, vai mums ir kādi CGA skripti, kurus var palaist, mēs tos aktīvi apgriežam.
Kā atjaunināt savu kodu bāzi lietotājiem nepamanot
Mēs arī ieviešam kļūdu toleranci koda izvietošanas procesu līmenī. Izlaišanas laikā var rasties kļūmes, taču to ietekmi uz pakalpojuma pieejamību var samazināt.
Mēs pastāvīgi atjauninām savus pakalpojumus, un mums ir jānodrošina, ka kodu bāze tiek atjaunināta, neietekmējot lietotājus. Mums izdevās atrisināt šo problēmu, izmantojot HAProxy pārvaldības iespējas un Graceful Shutdown ieviešanu mūsu pakalpojumos.
Lai atrisinātu šo problēmu, bija jānodrošina balansētāja kontrole un pakalpojumu “pareiza” izslēgšana:
- HAProxy gadījumā vadība tiek veikta, izmantojot statistikas failu, kas būtībā ir ligzda un ir definēts HAProxy konfigurācijā. Varat tai nosūtīt komandas, izmantojot stdio. Taču mūsu galvenais konfigurācijas kontroles rīks ir iespējams, tāpēc tajā ir iebūvēts modulis HAProxy pārvaldībai. Ko mēs aktīvi lietojam.
- Lielākā daļa mūsu API un Engine pakalpojumu atbalsta graciozas izslēgšanas tehnoloģijas: izslēdzot, tie gaida, līdz tiks pabeigts pašreizējais uzdevums, neatkarīgi no tā, vai tas ir http pieprasījums vai kāds pakalpojuma uzdevums. Tas pats notiek ar strādnieku. Tas zina visus uzdevumus, ko tas veic, un beidzas, kad tas ir veiksmīgi pabeidzis visu.
Pateicoties šiem diviem punktiem, mūsu izvietošanas drošais algoritms izskatās šādi.
- Izstrādātājs saliek jaunu koda paketi (mums tas ir RPM), pārbauda to dev vidē, pārbauda stadijā un atstāj stadijas repozitorijā.
- Izstrādātājs uzstāda izvietošanas uzdevumu ar visdetalizētāko “artefaktu” aprakstu: jaunās pakotnes versiju, jaunās funkcionalitātes aprakstu un citu informāciju par izvietošanu, ja nepieciešams.
- Sistēmas administrators sāk atjaunināšanu. Palaiž Ansible rokasgrāmatu, kas savukārt veic šādas darbības:
- Paņem pakotni no posma krātuves un izmanto to, lai atjauninātu pakotnes versiju produktu repozitorijā.
- Sastāda atjauninātā pakalpojuma aizmugursistēmu sarakstu.
- Izslēdz pirmo HAProxy atjaunināto pakalpojumu un gaida, līdz tā procesi beigsies. Pateicoties graciozai izslēgšanai, mēs esam pārliecināti, ka visi pašreizējie klientu pieprasījumi tiks veiksmīgi izpildīti.
- Kad API un darbinieki ir pilnībā apturēti un HAProxy ir izslēgts, kods tiek atjaunināts.
- Ansible nodrošina pakalpojumus.
- Katram pakalpojumam tiek izvilkti noteikti “rokturi”, kas veic vienības testēšanu vairākiem iepriekš definētiem atslēgu testiem. Notiek jaunā koda pamata pārbaude.
- Ja iepriekšējā darbībā kļūdas netika atrastas, tiek aktivizēta aizmugursistēma.
- Pāriesim pie nākamās aizmugursistēmas.
- Kad visas aizmugursistēmas ir atjauninātas, tiek palaisti funkcionālie testi. Ja to trūkst, izstrādātājs izskata jebkuru jaunu funkcionalitāti, ko viņš ir izveidojis.
Tas pabeidz izvietošanu.

Pakalpojuma atjaunināšanas cikls
Šī shēma nedarbotos, ja mums nebūtu viena noteikuma. Mēs kaujā atbalstām gan veco, gan jauno versiju. Iepriekš programmatūras izstrādes stadijā ir noteikts, ka pat tad, ja pakalpojumu datu bāzē būs izmaiņas, tās nepārkāps iepriekšējo kodu. Tā rezultātā koda bāze tiek pakāpeniski atjaunināta.
Secinājums
Daloties savās domās par kļūmēm izturīgu WEB arhitektūru, vēlos vēlreiz atzīmēt tās galvenos punktus:
- fiziska defektu tolerance;
- tīkla defektu tolerance (balansieri, BGP);
- izmantotās un izstrādātās programmatūras kļūdu tolerance.
Stabils darbības laiks visiem!
Avots: www.habr.com
