Kako je web arhitektura otporna na greške implementirana na platformi Mail.ru Cloud Solutions

Kako je web arhitektura otporna na greške implementirana na platformi Mail.ru Cloud Solutions

Zdravo, Habr! Ja sam Artem Karamyshev, šef tima za sistemsku administraciju Mail.Ru Cloud Solutions (MCS). Imali smo mnogo lansiranja novih proizvoda u protekloj godini. Željeli smo osigurati da API usluge budu lako skalabilne, tolerantne na greške i spremne za brzi rast broja korisnika. Naša platforma je implementirana na OpenStack-u i želim da vam kažem koje probleme tolerancije grešaka komponenti smo morali da rešimo da bismo dobili sistem otporan na greške. Mislim da će ovo biti interesantno za one koji takođe razvijaju proizvode na OpenStack-u.

Ukupna otpornost na greške platforme sastoji se od otpornosti njenih komponenti. Tako ćemo postepeno prolaziti kroz sve nivoe na kojima smo identifikovali rizike i zatvorili ih.

Video verzija ove priče, čiji je primarni izvor bio izvještaj na konferenciji Uptime day 4, u organizaciji ITSumma, mozes da vidis na YouTube kanalu Uptime Community.

Otpornost fizičke arhitekture

Javni dio MCS oblaka sada je baziran u dva Tier III data centra, između njih postoji vlastito tamno vlakno, rezervisano na fizičkom nivou različitim rutama, sa propusnošću od 200 Gbit/s. Nivo III obezbeđuje neophodan nivo tolerancije na greške za fizičku infrastrukturu.

Tamna vlakna su rezervisana i na fizičkom i na logičkom nivou. Proces rezervacije kanala je bio iterativan, pojavili su se problemi i konstantno poboljšavamo komunikaciju između data centara.

Na primjer, ne tako davno, dok je radio u bušotini u blizini jednog od data centara, bager je slomio cijev, a unutar ove cijevi nalazio se i glavni i pomoćni optički kabel. Naš komunikacioni kanal otporan na greške sa data centrom pokazao se ranjivim u jednom trenutku, u bušotini. Shodno tome, izgubili smo dio infrastrukture. Izvukli smo zaključke i poduzeli niz radnji, uključujući ugradnju dodatne optike u susjedni bunar.

U data centrima postoje tačke prisutnosti komunikacijskih provajdera kojima emitujemo naše prefikse putem BGP-a. Za svaki mrežni pravac bira se najbolja metrika koja omogućava različitim klijentima da dobiju najbolji kvalitet veze. Ako komunikacija preko jednog provajdera nestane, mi ponovo gradimo naše rutiranje kroz dostupne provajdere.

Ako provajder ne uspije, automatski prelazimo na sljedećeg. U slučaju kvara jednog od podatkovnih centara, imamo zrcalnu kopiju naših usluga u drugom podatkovnom centru, koji preuzimaju cjelokupno opterećenje.

Kako je web arhitektura otporna na greške implementirana na platformi Mail.ru Cloud Solutions
Otpornost fizičke infrastrukture

Ono što koristimo za toleranciju grešaka na nivou aplikacije

Naša usluga je izgrađena na brojnim komponentama otvorenog koda.

ExaBGP je usluga koja implementira brojne funkcije koristeći BGP baziran dinamički protokol rutiranja. Aktivno ga koristimo za oglašavanje naših IP adresa na bijeloj listi preko kojih korisnici pristupaju API-ju.

HAProxy je balansir sa visokim opterećenjem koji vam omogućava da konfigurišete veoma fleksibilna pravila balansiranja saobraćaja na različitim nivoima OSI modela. Koristimo ga za balansiranje ispred svih servisa: baza podataka, brokera poruka, API servisa, web servisa, naših internih projekata - sve je iza HAProxy.

API aplikacija — web aplikacija napisana na pythonu, pomoću koje korisnik upravlja svojom infrastrukturom i svojim servisom.

Radnička aplikacija (u daljem tekstu jednostavno radnik) - u OpenStack uslugama, ovo je infrastrukturni demon koji vam omogućava da emitujete API komande infrastrukturi. Na primjer, kreiranje diska se događa u workeru, a zahtjev za kreiranje se javlja u API-ju aplikacije.

Standardna OpenStack arhitektura aplikacije

Većina usluga koje su razvijene za OpenStack pokušavaju pratiti jednu paradigmu. Servis se obično sastoji od 2 dijela: API-ja i radnika (backend executors). Po pravilu, API je WSGI aplikacija u pythonu, koja se pokreće ili kao nezavisan proces (daemon), ili koristeći gotov Nginx ili Apache web server. API obrađuje korisnički zahtjev i prosljeđuje daljnje upute radnoj aplikaciji za izvršenje. Prijenos se odvija pomoću posrednika poruka, obično RabbitMQ, ostali su slabo podržani. Kada poruke stignu 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 izolovan unutar jedne usluge i, u teoriji, može biti individualan za svaku uslugu. Tako u MCS-u razdvajamo ove usluge koliko god je to moguće; za svaki pojedinačni projekat kreiramo zasebnu bazu podataka, zaseban RabbitMQ. Ovaj pristup je dobar jer se u slučaju nezgode na nekim ranjivim tačkama ne kvari cijela usluga, već samo dio.

Broj radnih aplikacija je neograničen, tako da se API može lako horizontalno skalirati iza balansera kako bi se povećale performanse i tolerancija grešaka.

Neke usluge zahtijevaju koordinaciju unutar usluge kada se između API-ja i radnika javljaju složene sekvencijalne operacije. U ovom slučaju koristi se jedan koordinacioni centar, klaster sistem kao što su Redis, Memcache, itd, koji omogućava jednom radniku da kaže drugom da mu je ovaj zadatak dodijeljen („molim ne preuzimaj“). Koristimo etcd. Radnici po pravilu aktivno komuniciraju sa bazom podataka, pišu i čitaju informacije iz nje. Koristimo mariadb kao bazu podataka koja se nalazi u multimaster klasteru.

Ova klasična pojedinačna usluga je organizirana na način koji je općenito prihvaćen za OpenStack. Može se smatrati zatvorenim sistemom, za koji su metode skaliranja i tolerancije grešaka prilično očigledne. Na primjer, za toleranciju grešaka API-ja, dovoljno je staviti balanser ispred njih. Skaliranje radnika postiže se povećanjem njihovog broja.

Slaba tačka u celoj šemi su RabbitMQ i MariaDB. Njihova arhitektura zaslužuje poseban članak.U ovom članku želim se fokusirati na toleranciju grešaka API-ja.

Kako je web arhitektura otporna na greške implementirana na platformi Mail.ru Cloud Solutions
Openstack arhitektura aplikacije. Balansiranje i tolerancija kvarova platforme u oblaku

Pravljenje HAProxy balansera otpornim na greške koristeći ExaBGP

Da bi naši API-ji bili skalabilni, brzi i tolerantni na greške, stavljamo balansator opterećenja ispred njih. Izabrali smo HAProxy. Po mom mišljenju, ima sve potrebne karakteristike za naš zadatak: balansiranje na nekoliko OSI nivoa, interfejs za upravljanje, fleksibilnost i skalabilnost, veliki broj metoda balansiranja, podršku za tabele sesija.

Prvi problem koji je trebalo riješiti bila je tolerancija na greške samog balansera. Jednostavno instaliranje balansera također stvara tačku kvara: balanser se pokvari i usluga se ruši. Da se to ne dogodi, koristili smo HAProxy zajedno sa ExaBGP.

ExaBGP vam omogućava da implementirate mehanizam za provjeru stanja usluge. Koristili smo ovaj mehanizam da provjerimo funkcionalnost HAProxy i, u slučaju problema, onemogućimo HAProxy uslugu iz BGP-a.

ExaBGP+HAProxy šema

  1. Instaliramo potreban softver, ExaBGP i HAProxy, na tri servera.
  2. Na svakom serveru kreiramo loopback interfejs.
  3. Na sva tri servera ovom interfejsu dodjeljujemo istu bijelu IP adresu.
  4. Bijela IP adresa se oglašava na Internetu preko ExaBGP-a.

Tolerancija grešaka se postiže oglašavanjem iste IP adrese sa sva tri servera. Sa mrežne tačke gledišta, istoj adresi se može pristupiti sa tri različita sljedeća skoka. Ruter vidi tri identične rute, bira najveći prioritet od njih na osnovu sopstvene metrike (ovo je obično ista opcija), a saobraćaj ide samo na jedan od servera.

U slučaju problema sa radom HAProxy-a ili kvara servera, ExaBGP prestaje da najavljuje rutu, a saobraćaj se glatko prebacuje na drugi server.

Time smo postigli otpornost na greške balansera.

Kako je web arhitektura otporna na greške implementirana na platformi Mail.ru Cloud Solutions
Tolerancija grešaka HAProxy balansera

Pokazalo se da je shema nesavršena: naučili smo kako rezervirati HAProxy, ali nismo naučili kako rasporediti opterećenje unutar usluga. Stoga smo malo proširili ovu šemu: prešli smo na balansiranje između nekoliko bijelih IP adresa.

Balansiranje bazirano na DNS plus BGP

Pitanje balansiranja opterećenja za naš HAProxy ostaje neriješeno. Međutim, to se može riješiti prilično jednostavno, kao što smo to učinili ovdje.

Za balansiranje tri servera trebat će vam 3 bijele IP adrese i stari dobri DNS. Svaka od ovih adresa je određena na interfejsu povratne petlje svakog HAProxy servera i reklamira se na Internetu.

U OpenStack-u, za upravljanje resursima, koristi se servisni direktorij, koji specificira API krajnje točke određene usluge. U ovom direktorijumu registrujemo naziv domene - public.infra.mail.ru, koji se rešava preko DNS-a sa tri različite IP adrese. Kao rezultat, dobijamo distribuciju opterećenja između tri adrese preko DNS-a.

Ali pošto prilikom objavljivanja bijelih IP adresa ne kontroliramo prioritete odabira servera, ovo još nije balansiranje. Obično će samo jedan server biti odabran na osnovu senioriteta IP adrese, a druga dva će biti neaktivna jer u BGP-u nisu specificirane 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, se oglašava 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ću na treću.

Šta se dešava kada jedan od balansera padne? Ako bilo koji balanser pokvari, njegova glavna adresa se i dalje oglašava sa druga dva, a promet se redistribuira između njih. Tako dajemo korisniku nekoliko IP adresa odjednom preko DNS-a. Balansiranjem pomoću DNS-a i različitih metrika, dobijamo ravnomjernu raspodjelu opterećenja na sva tri balansera. I u isto vrijeme ne gubimo toleranciju na greške.

Kako je web arhitektura otporna na greške implementirana na platformi Mail.ru Cloud Solutions
Balansiranje HAProxy-a na osnovu DNS + BGP

Interakcija između ExaBGP i HAProxy

Dakle, implementirali smo toleranciju grešaka u slučaju da server napusti, na osnovu zaustavljanja najave ruta. Ali HAProxy se može ugasiti iz drugih razloga osim kvara servera: greške administracije, kvarovi unutar usluge. I u ovim slučajevima želimo ukloniti pokvareni balans ispod opterećenja, a potreban nam je drugačiji mehanizam.

Stoga smo, proširivši prethodnu šemu, 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, morate konfigurirati provjeru zdravlja u ExaBGP konfiguraciji, koja može provjeriti status HAProxy. U našem slučaju, konfigurisali smo pozadinu zdravlja u HAProxy, a sa strane ExaBGP provjeravamo jednostavnim GET zahtjevom. Ako se najava prestane događati, onda HAProxy najvjerovatnije ne radi i nema potrebe da ga oglašavate.

Kako je web arhitektura otporna na greške implementirana na platformi Mail.ru Cloud Solutions
HAProxy provjera zdravlja

HAProxy Peers: sinhronizacija sesije

Sljedeće što je trebalo učiniti je sinhronizacija sesija. Kada radite preko distribuiranih balansera, teško je organizovati skladištenje informacija o klijentskim sesijama. Ali HAProxy je jedan od rijetkih balansera koji to mogu učiniti zahvaljujući Peers funkcionalnosti - mogućnosti prijenosa tabela sesija između različitih HAProxy procesa.

Postoje različite metode balansiranja: jednostavne kao npr kolo-kolo, i produžen, kada se klijentova sesija zapamti, i svaki put kada završi na istom serveru kao i prije. Željeli smo implementirati drugu opciju.

HAProxy koristi stick-tabele za spremanje klijentskih sesija ovog mehanizma. Oni čuvaju originalnu IP adresu klijenta, odabranu ciljnu adresu (backend) i neke informacije o usluzi. Tipično, stick tablice se 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 prebace na drugi balanser, na primjer, u RoundRobin načinu balansiranja.

Ako se štap tabela uči da se kreće između različitih HAProxy procesa (između kojih se dešava balansiranje), naši balanseri će moći da rade sa jednim skupom štap tablica. Ovo će omogućiti neprimjetno prebacivanje klijentove mreže ako jedan od balansera pokvari; rad sa klijentskim sesijama nastavit će se na istim pozadinskim mrežama koje su ranije odabrane.

Za pravilan rad, problem izvorne IP adrese balansera sa kojeg je uspostavljena sesija mora biti riješen. 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 uslovima. To jest, vremenska ograničenja TCP-a moraju biti dovoljno velika ili prebacivanje mora biti dovoljno brzo tako da TCP sesija nema vremena da se završi. Međutim, omogućava neometano prebacivanje.

U IaaS-u imamo uslugu izgrađenu korištenjem iste tehnologije. Ovo Load Balancer kao servis za OpenStack, koja se zove Octavia. Zasnovan je na dva HAProxy procesa i u početku uključuje podršku za ravnopravne korisnike. Odlično su se dokazali u ovoj usluzi.

Slika šematski prikazuje kretanje ravnopravnih tablica između tri HAProxy instance, predložena je konfiguracija kako se to može konfigurirati:

Kako je web arhitektura otporna na greške implementirana na platformi Mail.ru Cloud Solutions
HAProxy Peers (sinhronizacija sesije)

Ako implementirate istu shemu, njen rad mora biti pažljivo testiran. Nije činjenica da će raditi na isti način 100% vremena. Ali barem nećete izgubiti stick tabele kada trebate zapamtiti klijentov izvorni IP.

Ograničavanje broja istovremenih zahtjeva od istog klijenta

Sve usluge koje su javno dostupne, uključujući naše API-je, mogu biti predmet lavina zahtjeva. Razlozi za njih mogu biti potpuno različiti, od grešaka korisnika do ciljanih napada. Povremeno smo DDoSed IP adresama. Klijenti često prave greške u svojim skriptama i daju nam mini-DDoS-ove.

Na ovaj ili onaj način, mora se obezbijediti dodatna zaštita. Očigledno rješenje je ograničiti broj API zahtjeva i ne gubiti CPU vrijeme na obradu zlonamjernih zahtjeva.

Za implementaciju takvih ograničenja koristimo ograničenja stope, organizirana na osnovu HAProxy-a, koristeći iste stick tablice. Postavljanje ograničenja je prilično jednostavno i omogućava vam da ograničite korisnika brojem zahtjeva prema API-ju. Algoritam pamti izvornu IP adresu s koje se upućuju zahtjevi i ograničava broj istovremenih zahtjeva od jednog korisnika. Naravno, izračunali smo prosječan 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 stalno koriste naše API-je za automatsko skaliranje. Ujutro kreiraju otprilike dvije do tri stotine virtuelnih mašina, a uveče ih brišu. Za OpenStack, kreiranje virtuelne mašine, takođe sa PaaS uslugama, zahteva najmanje 1000 API zahteva, pošto se interakcija između usluga takođe dešava preko API-ja.

Takav prijenos zadataka uzrokuje prilično veliko opterećenje. Procijenili smo ovo opterećenje, prikupili dnevne pikove, povećali ih deset puta i to je postalo naša granica stope. Držimo prst na pulsu. Često viđamo botove i skenere koji pokušavaju da nas pogledaju da vide da li imamo neku CGA skriptu koja se može pokrenuti, mi ih aktivno režemo.

Kako ažurirati svoju bazu kodova, a da korisnici to ne primjete

Takođe implementiramo toleranciju grešaka na nivou procesa implementacije koda. Može doći do kvarova tokom uvođenja, ali njihov uticaj 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. Ovaj problem smo uspjeli riješiti korištenjem mogućnosti upravljanja HAProxy-a i implementacije Graceful Shutdown-a u našim servisima.

Da bi se riješio ovaj problem, bilo je potrebno osigurati kontrolu balansera i "ispravno" gašenje usluga:

  • U slučaju HAProxyja, kontrola se vrši preko datoteke statistike, koja je u suštini socket i definirana je u HAProxy konfiguraciji. Možete mu slati komande putem stdio-a. Ali naš glavni alat za kontrolu konfiguracije je ansible, tako da ima ugrađeni modul za upravljanje HAProxy. Koje aktivno koristimo.
  • Većina naših usluga API-ja i Engine-a podržavaju graciozne tehnologije isključivanja: kada se gase, oni čekaju da se završi trenutni zadatak, bilo da se radi o http zahtjevu ili nekom servisnom zadatku. Ista stvar se dešava i sa radnikom. Zna sve zadatke koje obavlja i završava kada sve uspješno završi.

Zahvaljujući ove dvije tačke, siguran algoritam za našu implementaciju izgleda ovako.

  1. Programer sastavlja novi paket koda (za nas je to RPM), testira ga u dev okruženju, testira ga u fazi i ostavlja u spremištu pozornice.
  2. Programer postavlja zadatak za implementaciju sa najdetaljnijim opisom „artefakata“: verzijom novog paketa, opisom nove funkcionalnosti i ostalim detaljima o implementaciji ako je potrebno.
  3. Administrator sistema započinje ažuriranje. Pokreće Ansible playbook, koji zauzvrat radi sljedeće:
    • Uzima paket iz repozitorija faze i koristi ga za ažuriranje verzije paketa u spremištu proizvoda.
    • Sastavlja listu pozadina ažurirane usluge.
    • Isključuje prvu uslugu koja se ažurira u HAProxy i čeka da se njeni procesi završe s radom. Zahvaljujući gracioznom zatvaranju, uvjereni smo da će svi trenutni zahtjevi klijenata biti uspješno završeni.
    • Nakon što su API i radnici potpuno zaustavljeni, a HAProxy isključen, kod se ažurira.
    • Ansible pokreće usluge.
    • Za svaku uslugu povlače se određene „ručke“ koje izvode testiranje jedinica na nizu unaprijed definiranih ključnih testova. Osnovna provjera novog koda se odvija.
    • Ako u prethodnom koraku nisu pronađene greške, aktivira se backend.
    • Pređimo na sljedeći backend.
  4. Nakon što su svi backendovi ažurirani, pokreću se funkcionalni testovi. Ako nedostaju, programer pregledava svaku novu funkcionalnost koju je stvorio.

Time je implementacija završena.

Kako je web arhitektura otporna na greške implementirana na platformi Mail.ru Cloud Solutions
Ciklus ažuriranja servisa

Ova šema ne bi funkcionirala da nemamo jedno pravilo. Podržavamo i stare i nove verzije u borbi. Unaprijed, u fazi razvoja softvera, propisano je da čak i ako dođe do promjena u bazi podataka usluge, one neće razbiti prethodni kod. Kao rezultat, baza kodova se postepeno ažurira.

zaključak

Dijeleći svoja razmišljanja o WEB arhitekturi otpornoj na greške, želio bih još jednom napomenuti njene ključne tačke:

  • fizička tolerancija grešaka;
  • tolerancija na greške mreže (balanseri, BGP);
  • tolerancija grešaka u korištenom i razvijenom softveru.

Svima stabilno vrijeme rada!

izvor: www.habr.com

Dodajte komentar