
Pozdrav, Habr! Ja sam Artem Karamyshev, voditelj tima za administraciju sustava . Tijekom prošle godine lansirali smo mnogo novih proizvoda. Željeli smo osigurati da su API usluge lako skalabilne, tolerantne na greške i spremne za brz rast korisničkog opterećenja. Naša je platforma implementirana na OpenStacku i želim vam reći koje smo probleme s tolerancijom grešaka u komponentama morali riješiti kako bismo dobili sustav otporan na pogreške. Mislim da će ovo biti zanimljivo onima koji također razvijaju proizvode na OpenStacku.
Ukupna tolerancija grešaka platforme sastoji se od otpornosti njenih komponenti. Tako ćemo postupno proći sve razine na kojima smo identificirali rizike i zatvorili ih.
Video verzija ove priče, čiji je primarni izvor bilo izvješće na konferenciji Uptime day 4, koju je organizirao , možeš vidjeti .
Otpornost fizičke arhitekture
Javni dio MCS oblaka sada se nalazi u dva podatkovna centra Tier III, između njih postoji vlastiti dark fiber, rezerviran na fizičkoj razini različitim rutama, s propusnošću od 200 Gbit/s. Razina III pruža potrebnu razinu tolerancije grešaka za fizičku infrastrukturu.
Tamno vlakno je rezervirano i na fizičkoj i na logičkoj razini. Proces rezervacije kanala bio je iterativan, pojavili su se problemi, a komunikaciju između podatkovnih centara stalno poboljšavamo.
Primjerice, nedavno je tijekom rada u bušotini u blizini jednog od podatkovnih centara bager probio cijev, a unutar te cijevi nalazio se i glavni i rezervni optički kabel. Naš komunikacijski kanal otporan na pogreške s podatkovnim centrom pokazao se ranjivim u jednom trenutku, u bunaru. Sukladno tome, izgubili smo dio infrastrukture. Izveli smo zaključke i poduzeli niz radnji, uključujući ugradnju dodatne optike u susjedni bunar.
U podatkovnim centrima postoje točke prisutnosti pružatelja komunikacijskih usluga kojima emitiramo svoje prefikse putem BGP-a. Za svaki mrežni smjer odabire se najbolja metrika koja omogućuje da se različitim klijentima pruži najbolja kvaliteta veze. Ako se komunikacija preko jednog pružatelja prekine, ponovno gradimo naše usmjeravanje kroz dostupne pružatelje.
Ako provajder zakaže, automatski se prebacujemo na sljedećeg. U slučaju kvara jednog od podatkovnih centara, u drugom podatkovnom centru imamo zrcalnu kopiju naših usluga koje preuzimaju cjelokupno opterećenje.

Otpornost fizičke infrastrukture
Ono što koristimo za toleranciju pogreške na razini aplikacije
Naša usluga izgrađena je na brojnim komponentama otvorenog koda.
ExaBGP je usluga koja implementira niz funkcija korištenjem protokola dinamičkog usmjeravanja temeljenog na BGP-u. Aktivno ga koristimo za oglašavanje naših IP adresa s popisa dopuštenih putem kojih korisnici pristupaju API-ju.
HAProxy je balanser visokog opterećenja koji vam omogućuje konfiguriranje vrlo fleksibilnih pravila za balansiranje prometa na različitim razinama OSI modela. Koristimo ga za ravnotežu ispred svih usluga: baza podataka, brokera poruka, API usluga, web usluga, naših internih projekata - sve stoji iza HAProxyja.
API aplikacija — web aplikacija napisana u pythonu, pomoću koje korisnik upravlja svojom infrastrukturom i svojom uslugom.
Prijava radnika (u daljnjem tekstu samo radnik) - u OpenStack uslugama, ovo je infrastrukturni demon koji vam omogućuje emitiranje API naredbi infrastrukturi. Na primjer, stvaranje diska događa se u radnoj jedinici, a zahtjev za stvaranjem u API-ju aplikacije.
Standardna OpenStack arhitektura aplikacija
Većina usluga koje su razvijene za OpenStack pokušavaju slijediti jednu paradigmu. Usluga se obično sastoji od 2 dijela: API-ja i radnika (backend izvršitelja). U pravilu, API je WSGI aplikacija u pythonu, koja se pokreće ili kao neovisni proces (daemon), ili pomoću već gotovih Nginx ili Apache web poslužitelja. API obrađuje korisnički zahtjev i prosljeđuje daljnje upute radnoj aplikaciji na izvršenje. Prijenos se odvija pomoću brokera poruka, obično RabbitMQ, ostali su slabo podržani. Kada poruke dođu do brokera, radnici ih obrađuju i, ako je potrebno, vraćaju odgovor.
Ova paradigma uključuje izolirane zajedničke točke neuspjeha: RabbitMQ i bazu podataka. Ali RabbitMQ je izoliran unutar jedne usluge i, u teoriji, može biti individualan za svaku uslugu. Stoga u MCS-u odvajamo te usluge što je više moguće; za svaki pojedinačni projekt stvaramo zasebnu bazu podataka, zaseban RabbitMQ. Ovakav pristup je dobar jer u slučaju nezgode na nekim ranjivim točkama ne dolazi do kvara cijele usluge, već samo dijela.
Broj radnih aplikacija je neograničen, tako da se API može lako skalirati horizontalno iza balansera kako bi se povećala izvedba i tolerancija na pogreške.
Neke usluge zahtijevaju koordinaciju unutar usluge kada se dogode složene sekvencijalne operacije između API-ja i radnika. U ovom slučaju koristi se jedan koordinacijski centar, sustav klastera kao što je Redis, Memcache itd., koji omogućava jednom radniku da kaže drugom da mu je ovaj zadatak dodijeljen ("molim vas, nemojte ga preuzimati"). Koristimo itd. U pravilu, radnici aktivno komuniciraju s bazom podataka, pišu i čitaju informacije odatle. Koristimo mariadb kao bazu podataka koja se nalazi u multimaster klasteru.
Ova klasična jedinstvena usluga organizirana je na način koji je općenito prihvaćen za OpenStack. Može se smatrati zatvorenim sustavom, za koji su metode skaliranja i tolerancije na greške sasvim očite. Na primjer, za API fault tolerance dovoljno je ispred njih staviti balanser. Skaliranje radnika postiže se povećanjem njihovog broja.
Slaba točka u cijeloj shemi su RabbitMQ i MariaDB. Njihova arhitektura zaslužuje poseban članak.U ovom članku želim se usredotočiti na API toleranciju na pogreške.

Openstack aplikacijska arhitektura. Balansiranje i otpornost na greške platforme u oblaku
Izrada HAProxy balansera tolerantnog na pogreške pomoću ExaBGP-a
Kako bi naši API-ji bili skalabilni, brzi i tolerantni na greške, ispred njih smo postavili balanser opterećenja. Odabrali smo HAProxy. Po mom mišljenju, ima sve potrebne karakteristike za naš zadatak: balansiranje na nekoliko OSI razina, sučelje za upravljanje, fleksibilnost i skalabilnost, veliki broj metoda balansiranja, podršku za tablice sesija.
Prvi problem koji je trebalo riješiti bila je otpornost na pogreške samog balansera. Jednostavna ugradnja balansera također stvara točku kvara: balanser se pokvari i usluga pada. Kako bismo spriječili da se to dogodi, koristili smo HAProxy u kombinaciji s ExaBGP.
ExaBGP vam omogućuje implementaciju mehanizma za provjeru stanja usluge. Koristili smo ovaj mehanizam za provjeru funkcionalnosti HAProxyja i, u slučaju problema, onemogućili HAProxy uslugu iz BGP-a.
ExaBGP+HAProxy shema
- Instaliramo potreban softver, ExaBGP i HAProxy, na tri servera.
- Na svakom poslužitelju stvaramo povratno sučelje.
- Na sva tri poslužitelja ovom sučelju dodjeljujemo istu bijelu IP adresu.
- Bijela IP adresa oglašava se na Internetu putem ExaBGP-a.
Tolerancija grešaka postiže se oglašavanjem iste IP adrese sa sva tri poslužitelja. S mrežne točke gledišta, ista je adresa dostupna s tri različita sljedeća skoka. Usmjerivač vidi tri identične rute, odabire najveću prioritetnu od njih na temelju vlastite metrike (ovo je obično ista opcija), a promet ide samo na jedan od poslužitelja.
U slučaju problema s radom HAProxyja ili kvara poslužitelja, ExaBGP prestaje najavljivati rutu, a promet se glatko prebacuje na drugi poslužitelj.
Time smo postigli otpornost na pogreške balansera.

Tolerancija grešaka HAProxy balansera
Shema se pokazala nesavršenom: naučili smo kako rezervirati HAProxy, ali nismo naučili kako rasporediti opterećenje unutar usluga. Stoga smo malo proširili ovu shemu: prešli smo na balansiranje između nekoliko bijelih IP adresa.
Balansiranje na temelju DNS plus BGP
Pitanje uravnoteženja opterećenja za naš HAProxy ostaje neriješeno. Međutim, to se može riješiti prilično jednostavno, kao što smo mi ovdje učinili.
Za balansiranje tri servera trebat će vam 3 bijele IP adrese i dobri stari DNS. Svaka od ovih adresa određena je na povratnom sučelju svakog HAProxyja i reklamirana je na Internetu.
U OpenStacku, za upravljanje resursima, koristi se servisni direktorij, koji specificira API krajnje točke određene usluge. U ovom imeniku registriramo naziv domene - public.infra.mail.ru, koji se putem DNS-a rješava s tri različite IP adrese. Kao rezultat, dobivamo raspodjelu opterećenja između tri adrese putem DNS-a.
Ali budući da prilikom najave bijelih IP adresa ne kontroliramo prioritete odabira poslužitelja, to još nije balansiranje. Obično će samo jedan poslužitelj biti odabran na temelju starješine IP adrese, a druga dva će biti u stanju mirovanja jer u BGP-u nisu navedene metrike.
Počeli smo slati rute putem ExaBGP-a s različitim metrikama. Svaki balanser reklamira sve tri bijele IP adrese, ali jedna od njih, glavna za ovaj balanser, reklamira se s minimalnom metrikom. Dakle, dok su sva tri balansera u funkciji, pozivi na prvu IP adresu idu na prvi balanser, pozivi na drugi na drugi, a pozivi na treći na treći.
Što se događa kada jedan od balansera padne? Ako bilo koji balanser zakaže, njegova glavna adresa se i dalje oglašava s druga dva, a promet se redistribuira između njih. Dakle, korisniku putem DNS-a dajemo nekoliko IP adresa odjednom. Balansiranjem po DNS-u i različitim metrikama dobivamo ravnomjernu raspodjelu opterećenja na sva tri balansera. A u isto vrijeme ne gubimo toleranciju na greške.

Balansiranje HAProxy na temelju DNS + BGP
Interakcija između ExaBGP i HAProxy
Dakle, implementirali smo toleranciju grešaka u slučaju da poslužitelj ode, baziran na zaustavljanju najave ruta. Ali HAProxy se može isključiti iz drugih razloga osim kvara poslužitelja: pogreške administracije, kvarovi unutar usluge. I u tim slučajevima želimo ukloniti slomljeni balanser ispod tereta i potreban nam je drugačiji mehanizam.
Stoga smo, proširujući prethodnu shemu, implementirali otkucaje srca između ExaBGP i HAProxy. Ovo je softverska implementacija interakcije između ExaBGP i HAProxy, kada ExaBGP koristi prilagođene skripte za provjeru statusa aplikacija.
Da biste to učinili, trebate konfigurirati provjeru ispravnosti u ExaBGP konfiguraciji, koja može provjeriti status HAProxyja. U našem slučaju konfigurirali smo pozadinu zdravlja u HAProxyju, a sa strane ExaBGP-a provjeravamo jednostavnim GET zahtjevom. Ako se najava prestane događati, tada HAProxy najvjerojatnije ne radi i nema potrebe da ga reklamirate.

HAProxy provjera zdravlja
HAProxy Peers: sinkronizacija sesije
Sljedeća stvar koju je trebalo napraviti bila je sinkronizacija sesija. Kada radite preko distribuiranih balansera, teško je organizirati pohranu informacija o klijentskim sesijama. Ali HAProxy je jedan od rijetkih balansera koji to može učiniti zahvaljujući Peers funkcionalnosti - mogućnosti prijenosa tablica sesija između različitih HAProxy procesa.
Postoje različite metode balansiranja: jednostavne kao npr , i produženo, kada se sesija klijenta pamti, i svaki put kada završi na istom poslužitelju kao i prije. Željeli smo implementirati drugu opciju.
HAProxy koristi stick-tablice za spremanje klijentskih sesija ovog mehanizma. Oni spremaju izvornu IP adresu klijenta, odabranu ciljnu adresu (backend) i neke informacije o usluzi. Obično se stick tablice koriste za pohranjivanje para izvor-IP + odredište-IP, što je posebno korisno za aplikacije koje ne mogu prenijeti kontekst korisničke sesije kada se prebacuju na drugi balanser, na primjer, u RoundRobin načinu rada za balansiranje.
Ako se stick table nauči pomicati između različitih HAProxy procesa (između kojih dolazi do balansiranja), naši balanseri će moći raditi s jednim skupom stick tablica. To će omogućiti besprijekorno prebacivanje mreže klijenta ako jedan od balansera zakaže; rad sa sesijama klijenta nastavit će se na istim pozadinama koje su ranije odabrane.
Za ispravan rad potrebno je riješiti problem izvorne IP adrese balansera s kojeg je uspostavljena sesija. U našem slučaju, ovo je dinamička adresa na sučelju povratne petlje.
Korektan rad vršnjaka postiže se samo pod određenim uvjetima. Odnosno, TCP timeout mora biti dovoljno velik ili prebacivanje mora biti dovoljno brzo tako da TCP sesija nema vremena za prekid. Međutim, omogućuje besprijekorno prebacivanje.
U IaaS-u imamo uslugu izgrađenu korištenjem iste tehnologije. Ovaj , koja se zove Octavia. Temelji se na dva HAProxy procesa i inicijalno uključuje podršku za ravnopravne korisnike. U ovoj su se službi izvrsno pokazali.
Slika shematski prikazuje kretanje ravnopravnih tablica između tri HAProxy instance, predlaže se konfiguracija kako se to može konfigurirati:

HAProxy Peers (sinkronizacija sesije)
Ako provodite istu shemu, njezin rad mora biti pažljivo testiran. Nije činjenica da će raditi na isti način 100% vremena. Ali barem nećete izgubiti stick tablice kada trebate zapamtiti izvorni IP klijenta.
Ograničenje broja istodobnih zahtjeva od istog klijenta
Sve usluge koje su javno dostupne, uključujući naše API-je, mogu biti podložne lavini zahtjeva. Razlozi za njih mogu biti potpuno različiti, od korisničkih pogrešaka do ciljanih napada. Povremeno smo pod DDoS-om prema IP adresama. Klijenti često griješe u svojim skriptama i daju nam mini-DDoS-ove.
Na ovaj ili onaj način, mora se osigurati dodatna zaštita. Očito rješenje je ograničiti broj API zahtjeva i ne gubiti CPU vrijeme na obradu zlonamjernih zahtjeva.
Da bismo implementirali takva ograničenja, koristimo ograničenja stope, organizirana na temelju HAProxyja, koristeći iste tablice. Postavljanje ograničenja je vrlo jednostavno i omogućuje vam da ograničite korisnika prema broju zahtjeva za API. Algoritam pamti izvorni IP s kojeg se šalju zahtjevi i ograničava broj istodobnih zahtjeva jednog korisnika. Naravno, izračunali smo prosječni profil opterećenja API-ja za svaku uslugu i postavili ograničenje od ≈ 10 puta ove vrijednosti. I dalje pomno pratimo situaciju i držimo prst na pulsu.
Kako to izgleda u praksi? Imamo klijente koji cijelo vrijeme koriste naše API-je za automatsko skaliranje. Ujutro stvaraju otprilike dvjesto do tri stotine virtualnih strojeva i brišu ih navečer. Za OpenStack, stvaranje virtualnog stroja, također s PaaS uslugama, zahtijeva najmanje 1000 API zahtjeva, budući da se interakcija između usluga također odvija kroz API.
Takav prijenos zadataka uzrokuje prilično veliko opterećenje. Procijenili smo ovo opterećenje, prikupili dnevne vršne vrijednosti, udeseterostručili ih i to je postalo naše ograničenje brzine. Držimo prst na pulsu. Često vidimo robote i skenere koji nas pokušavaju pogledati da vide imamo li CGA skripte koje se mogu pokrenuti, mi ih aktivno režemo.
Kako ažurirati svoju bazu kodova, a da korisnici to ne primijete
Također implementiramo toleranciju grešaka na razini procesa implementacije koda. Može doći do kvarova tijekom uvođenja, ali njihov utjecaj na dostupnost usluge može se svesti na minimum.
Stalno ažuriramo naše usluge i moramo osigurati da se baza kodova ažurira bez utjecaja na korisnike. Uspjeli smo riješiti ovaj problem koristeći upravljačke mogućnosti HAProxyja i implementaciju Graceful Shutdowna u našim uslugama.
Za rješavanje ovog problema bilo je potrebno osigurati kontrolu balansera i "ispravno" gašenje usluga:
- U slučaju HAProxyja, kontrola se provodi putem datoteke statistike, koja je u biti utičnica i definirana je u konfiguraciji HAProxyja. Možete mu slati naredbe putem stdio. Ali naš glavni alat za kontrolu konfiguracije je ansible, tako da ima ugrađeni modul za upravljanje HAProxyjem. Koju aktivno koristimo.
- Većina naših API i Engine usluga podržava graciozne tehnologije isključivanja: kada se gase, čekaju da se završi trenutni zadatak, bilo da se radi o http zahtjevu ili nekom servisnom zadatku. Isto se događa i s radnikom. Zna sve zadatke koje radi i završava kada sve uspješno obavi.
Zahvaljujući ove dvije točke, siguran algoritam za našu implementaciju izgleda ovako.
- Programer sastavlja novi paket koda (za nas je to RPM), testira ga u razvojnom okruženju, testira ga u fazi i ostavlja u repozitoriju faze.
- Programer postavlja zadatak za implementaciju s najdetaljnijim opisom "artefakata": verzija novog paketa, opis nove funkcionalnosti i druge pojedinosti o implementaciji ako je potrebno.
- Administrator sustava započinje ažuriranje. Pokreće Ansible playbook, koji zauzvrat čini sljedeće:
- Uzima paket iz repozitorija pozornice i koristi ga za ažuriranje verzije paketa u repozitoriju proizvoda.
- Sastavlja popis pozadina ažurirane usluge.
- Isključuje prvu uslugu koja se ažurira u HAProxy i čeka da se njeni procesi završe. Zahvaljujući elegantnom gašenju, uvjereni smo da će se svi trenutni klijentski zahtjevi uspješno izvršiti.
- Nakon što se API i radnici potpuno zaustave, a HAProxy isključi, kod se ažurira.
- Ansible pokreće usluge.
- Za svaku uslugu povlače se određene "ručice" koje izvode jedinično testiranje na nizu unaprijed definiranih ključnih testova. Provodi se osnovna provjera novog koda.
- Ako u prethodnom koraku nisu pronađene greške, pozadina se aktivira.
- Prijeđimo na sljedeću pozadinu.
- Nakon ažuriranja svih pozadina, pokreću se funkcionalni testovi. Ako nedostaju, tada programer gleda svaku novu funkcionalnost koju je stvorio.
Time je implementacija dovršena.

Ciklus ažuriranja usluge
Ova shema ne bi funkcionirala da nemamo jedno pravilo. Podržavamo i staru i novu verziju u borbi. Unaprijed, u fazi razvoja softvera, utvrđeno je da čak i ako postoje promjene u bazi podataka usluge, one neće razbiti prethodni kod. Kao rezultat toga, baza koda se postupno ažurira.
Zaključak
Dijeleći vlastita razmišljanja o WEB arhitekturi otpornoj na pogreške, želio bih još jednom istaknuti njezine ključne točke:
- fizička tolerancija grešaka;
- tolerancija mrežnih grešaka (balanseri, BGP);
- otpornost na pogreške softvera koji se koristi i razvija.
Stabilno vrijeme rada za sve!
Izvor: www.habr.com
