
Hei, Habr! Olen Artem Karamyshev, järjestelmänhallintatiimin johtaja . Olemme julkaisseet useita uusia tuotteita kuluneen vuoden aikana. Halusimme varmistaa, että API-palvelut ovat helposti skaalautuvia, vikasietoisia ja valmiita nopeaan käyttäjäkuormituksen kasvuun. Alustamme on toteutettu OpenStackilla, ja haluan kertoa teille, mitä komponenttien vikasieto-ongelmia meidän piti ratkaista saadaksemme vikasietoisen järjestelmän. Uskon, että tämä on mielenkiintoinen niille, jotka myös kehittävät tuotteita OpenStackissa.
Alustan yleinen vikasietoisuus koostuu sen komponenttien kestävyydestä. Joten käymme vähitellen läpi kaikki tasot, joilla tunnistimme riskit ja suljemme ne.
Videoversio tästä jutusta, jonka ensisijainen lähde oli raportti Uptime day 4 -konferenssissa, jonka järjesti , sinä pystyt näkemään .
Fyysisen arkkitehtuurin joustavuus
MCS-pilven julkinen osa perustuu nyt kahteen Tier III -palvelinkeskukseen, joiden välissä on oma tumma kuitu, joka on varattu fyysisellä tasolla eri reittejä pitkin, nopeudella 200 Gbit/s. Taso III tarjoaa tarvittavan tason vikasietokyvyn fyysiselle infrastruktuurille.
Tumma kuitu on varattu sekä fyysisellä että loogisella tasolla. Kanavavarausprosessi oli iteratiivinen, ongelmia ilmeni ja parannamme jatkuvasti datakeskusten välistä viestintää.
Esimerkiksi vähän aikaa sitten työskennellessään kaivossa lähellä yhtä datakeskusta, kaivinkone rikkoi putken, ja tämän putken sisällä oli sekä pää- että varaoptinen kaapeli. Vikasietoinen viestintäkanavamme palvelinkeskuksen kanssa osoittautui haavoittuvaiseksi yhdessä kohdassa, kaivossa. Näin ollen olemme menettäneet osan infrastruktuurista. Teimme johtopäätökset ja teimme useita toimenpiteitä, mukaan lukien lisäoptiikan asentaminen viereiseen kaivoon.
Palvelinkeskuksissa on yhteyspisteitä viestintäpalveluntarjoajille, joille lähetämme etuliitteemme BGP:n kautta. Jokaiselle verkkosuunnalle valitaan paras mittari, jonka avulla eri asiakkaille voidaan tarjota paras yhteyslaatu. Jos viestintä yhden palveluntarjoajan kautta katkeaa, rakennamme reitityksen uudelleen käytettävissä olevien palveluntarjoajien kautta.
Jos palveluntarjoaja epäonnistuu, siirrymme automaattisesti seuraavaan. Jos jokin palvelinkeskuksista epäonnistuu, meillä on peilikopio palveluistamme toisessa palvelinkeskuksessa, joka ottaa koko kuorman.

Fyysisen infrastruktuurin kestävyys
Mitä käytämme sovellustason vikasietoisuuteen
Palvelumme perustuu useisiin avoimen lähdekoodin komponentteihin.
ExaBGP on palvelu, joka toteuttaa useita toimintoja käyttämällä BGP-pohjaista dynaamista reititysprotokollaa. Käytämme sitä aktiivisesti mainostaaksemme sallittuja IP-osoitteitamme, joiden kautta käyttäjät pääsevät API:aan.
HAProxy on korkean kuormituksen tasapainotin, jonka avulla voit määrittää erittäin joustavia liikenteen tasapainotussääntöjä OSI-mallin eri tasoilla. Käytämme sitä tasapainottamiseen kaikkien palveluiden edessä: tietokannat, viestivälittäjät, API-palvelut, verkkopalvelut, sisäiset projektimme - kaikki on HAProxyn takana.
API-sovellus — Pythonilla kirjoitettu verkkosovellus, jolla käyttäjä hallitsee infrastruktuuriaan ja palveluaan.
Työntekijän hakemus (jäljempänä yksinkertaisesti työntekijä) - OpenStack-palveluissa tämä on infrastruktuurin demoni, jonka avulla voit lähettää API-komentoja infrastruktuuriin. Esimerkiksi levyn luonti tapahtuu työntekijässä ja luontipyyntö sovelluksen API:ssa.
Vakio OpenStack-sovellusarkkitehtuuri
Suurin osa OpenStackille kehitetyistä palveluista yrittää noudattaa yhtä mallia. Palvelu koostuu yleensä kahdesta osasta: API ja työntekijät (backend executors). Pääsääntöisesti API on pythonissa WSGI-sovellus, joka käynnistetään joko itsenäisenä prosessina (daemonina) tai valmiilla Nginx- tai Apache-verkkopalvelimella. API käsittelee käyttäjän pyynnön ja välittää lisäohjeita työntekijäsovellukselle suorittamista varten. Siirto tapahtuu viestinvälittäjällä, yleensä RabbitMQ, muut ovat huonosti tuettuja. Kun viestit saapuvat välittäjälle, työntekijät käsittelevät ne ja tarvittaessa palauttavat vastauksen.
Tämä paradigma sisältää yksittäisiä yhteisiä vikakohtia: RabbitMQ ja tietokanta. Mutta RabbitMQ on eristetty yhden palvelun sisällä, ja teoriassa se voi olla yksilöllinen jokaiselle palvelulle. Joten MCS:ssä erottelemme nämä palvelut niin paljon kuin mahdollista jokaista yksittäistä projektia varten luomme erillisen tietokannan, erillisen RabbitMQ:n. Tämä lähestymistapa on hyvä, koska onnettomuuden sattuessa joissakin haavoittuvissa kohdissa ei koko palvelu katkea, vaan vain osa siitä.
Työntekijöiden sovellusten määrä on rajoittamaton, joten API voi helposti skaalata vaakasuunnassa tasapainottimien taakse parantaakseen suorituskykyä ja vikasietoisuutta.
Jotkut palvelut vaativat koordinointia palvelun sisällä, kun API:iden ja työntekijöiden välillä tapahtuu monimutkaisia peräkkäisiä toimintoja. Tässä tapauksessa käytetään yhtä koordinointikeskusta, klusterijärjestelmää, kuten Redis, Memcache jne., jonka avulla työntekijä voi kertoa toiselle, että tämä tehtävä on annettu hänelle ("älä ota sitä vastaan"). Käytämme etcd. Työntekijät kommunikoivat pääsääntöisesti aktiivisesti tietokannan kanssa kirjoittaen ja lukemalla sieltä tietoa. Käytämme tietokantana mariadb:tä, joka sijaitsee multimaster-klusterissa.
Tämä klassinen yksittäinen palvelu on järjestetty OpenStackille yleisesti hyväksytyllä tavalla. Sitä voidaan pitää suljettuna järjestelmänä, jonka skaalaus- ja vikasietomenetelmät ovat varsin ilmeisiä. Esimerkiksi API-vikasietokykyä varten riittää, että asetat tasapainottimen niiden eteen. Työntekijöiden skaalaus saavutetaan lisäämällä heidän määräänsä.
Koko järjestelmän heikko kohta ovat RabbitMQ ja MariaDB. Niiden arkkitehtuuri ansaitsee erillisen artikkelin. Tässä artikkelissa haluan keskittyä API-viansietokykyyn.

Openstack-sovellusarkkitehtuuri. Pilvialustan tasapainotus ja vikasietoisuus
HAProxy-tasapainottimen tekeminen vikasietoiseksi ExaBGP:n avulla
Tehdäksemme API-liittymistämme skaalautuvia, nopeita ja vikasietoisia, asetamme niiden eteen kuormituksen tasapainottimen. Valitsimme HAProxyn. Mielestäni siinä on kaikki tehtävämme edellyttämät ominaisuudet: tasapainotus useilla OSI-tasoilla, hallintarajapinta, joustavuus ja skaalautuvuus, suuri määrä tasapainotusmenetelmiä, tuki istuntotaulukoille.
Ensimmäinen ongelma, joka piti ratkaista, oli itse tasapainottimen vikasietoisuus. Pelkästään tasapainottimen asentaminen luo myös epäonnistumiskohdan: tasapainotin hajoaa ja palvelu kaatuu. Tämän estämiseksi käytimme HAProxya yhdessä ExaBGP:n kanssa.
ExaBGP antaa sinun toteuttaa mekanismin palvelun tilan tarkistamiseksi. Käytimme tätä mekanismia HAProxyn toimivuuden tarkistamiseen ja ongelmien sattuessa poistamme HAProxy-palvelun käytöstä BGP:ltä.
ExaBGP+HAProxy-malli
- Asennamme tarvittavat ohjelmistot, ExaBGP ja HAProxy, kolmelle palvelimelle.
- Luomme takaisinkytkentärajapinnan jokaiselle palvelimelle.
- Kaikilla kolmella palvelimella annamme tälle rajapinnalle saman valkoisen IP-osoitteen.
- Valkoinen IP-osoite mainostetaan Internetiin ExaBGP:n kautta.
Vikasietoisuus saavutetaan mainostamalla samaa IP-osoitetta kaikilta kolmelta palvelimelta. Verkon näkökulmasta sama osoite on käytettävissä kolmesta eri seuraavasta hyppystä. Reititin näkee kolme identtistä reittiä, valitsee niistä korkeimman prioriteetin oman mittansa perusteella (tämä on yleensä sama vaihtoehto), ja liikenne kulkee vain yhdelle palvelimista.
Mikäli HAProxyn toiminnassa ilmenee ongelmia tai palvelinvika, ExaBGP lopettaa reitin ilmoittamisen ja liikenne siirtyy sujuvasti toiselle palvelimelle.
Näin saavutimme tasapainottimen vikasietoisuuden.

HAProxy-tasapainottimien vikasietoisuus
Kaava osoittautui epätäydelliseksi: opimme varaamaan HAProxya, mutta emme oppineet jakamaan kuormitusta palvelujen sisällä. Siksi laajensimme tätä mallia hieman: siirryimme tasapainottamaan useiden valkoisten IP-osoitteiden välillä.
Tasapainotus perustuu DNS ja BGP
HAProxymme kuormituksen tasapainotus on edelleen ratkaisematta. Se voidaan kuitenkin ratkaista yksinkertaisesti, kuten teimme täällä.
Kolmen palvelimen tasapainottamiseksi tarvitset 3 valkoista IP-osoitetta ja vanhaa hyvää DNS-osoitetta. Jokainen näistä osoitteista määritetään kunkin HAProxyn silmukkaliittymässä ja mainostetaan Internetiin.
OpenStackissa resurssien hallintaan käytetään palveluhakemistoa, joka määrittää tietyn palvelun päätepisteen API:n. Tässä hakemistossa rekisteröimme verkkotunnuksen - public.infra.mail.ru, joka ratkaistaan DNS:n kautta kolmella eri IP-osoitteella. Tämän seurauksena saamme kuormituksen jakautumisen kolmen osoitteen välillä DNS:n kautta.
Mutta koska emme hallitse palvelimen valinnan prioriteetteja ilmoittaessamme valkoisia IP-osoitteita, tämä ei ole vielä tasapainoilua. Yleensä vain yksi palvelin valitaan IP-osoitteen vanhemmuuden perusteella, ja kaksi muuta ovat käyttämättömiä, koska BGP:ssä ei ole määritetty mittareita.
Aloimme lähettää reittejä ExaBGP:n kautta eri mittareilla. Jokainen tasapainotin mainostaa kaikkia kolmea valkoista IP-osoitetta, mutta yksi niistä, tämän tasapainottimen tärkein osoite, mainostetaan vähimmäismittauksella. Joten kun kaikki kolme tasapainotinta ovat toiminnassa, ensimmäiseen IP-osoitteeseen tulevat puhelut menevät ensimmäiseen tasapainottimeen, puhelut toiseen toiseen ja kolmanteen puhelut kolmanteen.
Mitä tapahtuu, kun yksi tasapainottimista putoaa? Jos jokin tasapainottimesta epäonnistuu, sen pääosoite ilmoitetaan edelleen kahdelta muulta ja liikenne jaetaan uudelleen niiden välillä. Siten annamme käyttäjälle useita IP-osoitteita kerralla DNS:n kautta. Tasapainottamalla DNS:n ja eri mittareilla saamme tasaisen kuormituksen jakautumisen kaikkien kolmen tasapainottimen välillä. Ja samalla emme menetä vikasietoisuutta.

Tasapainotus HAProxy perustuu DNS + BGP
ExaBGP:n ja HAProxyn välinen vuorovaikutus
Otimme siis käyttöön vikasietoisuuden siltä varalta, että palvelin lähtee, perustuen reittien ilmoittamisen pysäyttämiseen. Mutta HAProxy voi sammua muista syistä kuin palvelinvioista: hallintavirheistä, palvelun sisäisistä vioista. Haluamme poistaa rikkinäisen tasapainottimen kuorman alta myös näissä tapauksissa, ja tarvitsemme toisenlaisen mekanismin.
Siksi, laajentamalla edellistä järjestelmää, otimme käyttöön sydämenlyönnin ExaBGP:n ja HAProxyn välillä. Tämä on ExaBGP:n ja HAProxyn välisen vuorovaikutuksen ohjelmistototeutus, kun ExaBGP käyttää mukautettuja komentosarjoja sovellusten tilan tarkistamiseen.
Tätä varten sinun on määritettävä ExaBGP-konfiguraatiossa kuntotarkistus, joka voi tarkistaa HAProxyn tilan. Meidän tapauksessamme määritimme kunto-taustajärjestelmän HAProxyssa, ja ExaBGP-puolelta tarkistamme yksinkertaisella GET-pyynnöllä. Jos ilmoitus lakkaa toteutumasta, HAProxy ei todennäköisesti toimi, eikä sitä tarvitse mainostaa.

HAProxyn kuntotarkastus
HAProxy Peers: istunnon synkronointi
Seuraava asia oli synkronoida istunnot. Työskenneltäessä hajautettujen tasapainottajien kautta on vaikea järjestää asiakasistuntoja koskevien tietojen tallennusta. Mutta HAProxy on yksi harvoista tasapainottajista, jotka voivat tehdä tämän Peers-toiminnon ansiosta - kyvyn siirtää istuntotaulukoita eri HAProxy-prosessien välillä.
Tasapainotusmenetelmiä on erilaisia: yksinkertaisia, kuten , ja laajennettu, kun asiakkaan istunto muistetaan ja joka kerta, kun hän päätyy samalle palvelimelle kuin ennenkin. Halusimme toteuttaa toisen vaihtoehdon.
HAProxy käyttää stick-taulukoita tämän mekanismin asiakasistuntojen tallentamiseen. Ne tallentavat asiakkaan alkuperäisen IP-osoitteen, valitun kohdeosoitteen (backend) ja joitain palvelutietoja. Tyypillisesti tikkutaulukoita käytetään tallentamaan lähde-IP + kohde-IP-pari, mikä on erityisen hyödyllistä sovelluksille, jotka eivät voi siirtää käyttäjän istuntokontekstia vaihdettaessa toiseen tasapainottimeen, esimerkiksi RoundRobin-tasapainotustilassa.
Jos tikkupöytä opetetaan liikkumaan eri HAProxy-prosessien välillä (joiden välillä tasapainotus tapahtuu), tasapainottajamme voivat työskennellä yhden tikkupöytien kanssa. Tämä mahdollistaa saumattomasti asiakkaan verkon vaihtamisen, jos jokin balansoimista epäonnistuu, työ asiakasistuntojen kanssa jatkuu samoissa taustajärjestelmissä, jotka valittiin aiemmin.
Jotta se toimisi oikein, on ratkaistava sen balansoijan lähde-IP-osoite, josta istunto muodostettiin. Meidän tapauksessamme tämä on dynaaminen osoite loopback-rajapinnassa.
Oikea työkavereiden työ saavutetaan vain tietyissä olosuhteissa. Toisin sanoen TCP-aikakatkaisujen on oltava riittävän suuria tai vaihdon on oltava riittävän nopeaa, jotta TCP-istunto ei ehdi lopettaa. Se mahdollistaa kuitenkin saumattoman vaihdon.
IaaS:ssä meillä on palvelu, joka on rakennettu samalla tekniikalla. Tämä , joka on nimeltään Octavia. Se perustuu kahteen HAProxy-prosessiin ja sisältää aluksi tuen vertaisille. He ovat osoittautuneet erinomaisesti tässä palvelussa.
Kuvassa näkyy kaavamaisesti vertaistaulukoiden liikkuminen kolmen HAProxy-esiintymän välillä, ehdotetaan konfiguraatiota, kuinka tämä voidaan määrittää:

HAProxy Peers (istunnon synkronointi)
Jos käytät samaa järjestelmää, sen toiminta on testattava huolellisesti. Ei ole tosiasia, että se toimii samassa muodossa 100% ajasta. Mutta ainakaan et menetä tikkutaulukoita, kun sinun on muistettava asiakkaan lähde-IP.
Samanaikaisten pyyntöjen määrän rajoittaminen samalta asiakkaalta
Kaikki julkisesti saatavilla olevat palvelut, mukaan lukien sovellusliittymämme, voivat joutua pyyntöjen vyöryihin. Syyt niihin voivat olla täysin erilaisia, käyttäjän virheistä kohdennettuihin hyökkäyksiin. Olemme ajoittain DDoSed IP-osoitteiden perusteella. Asiakkaat tekevät usein virheitä komentosarjoissaan ja antavat meille mini-DDoS:ita.
Tavalla tai toisella lisäsuoja on tarjottava. Ilmeinen ratkaisu on rajoittaa API-pyyntöjen määrää eikä tuhlata CPU-aikaa haitallisten pyyntöjen käsittelyyn.
Tällaisten rajoitusten toteuttamiseksi käytämme HAProxyn pohjalta järjestettyjä nopeusrajoituksia samoilla tikkutaulukoilla. Rajojen asettaminen on melko yksinkertaista, ja sen avulla voit rajoittaa käyttäjää API-pyyntöjen määrällä. Algoritmi muistaa lähde-IP:n, josta pyynnöt tehdään, ja rajoittaa yhden käyttäjän samanaikaisten pyyntöjen määrää. Tietenkin laskemme kullekin palvelulle keskimääräisen API-kuormitusprofiilin ja asetimme rajaksi ≈ 10 kertaa tämä arvo. Seuraamme tilannetta edelleen tiiviisti ja pidämme sormi pulssissa.
Miltä tämä näyttää käytännössä? Meillä on asiakkaita, jotka käyttävät automaattisen skaalauksen sovellusliittymiämme jatkuvasti. He luovat noin kahdesta kolmeen sataa virtuaalikoneita aamulla ja poistavat ne illalla. OpenStackille virtuaalikoneen luominen, myös PaaS-palveluilla, vaatii vähintään 1000 API-pyyntöä, koska myös palveluiden välinen vuorovaikutus tapahtuu API:n kautta.
Tällainen tehtävien siirto aiheuttaa melko suuren kuormituksen. Arvioimme tämän kuorman, keräsimme päivittäisiä huippuja, kymmenkertaistimme ne, ja tästä tuli nopeusrajamme. Pidämme sormea pulssissa. Näemme usein botteja ja skannereita, jotka yrittävät katsoa meitä nähdäkseen, onko meillä CGA-skriptejä, joita voidaan ajaa. Leikkaamme niitä aktiivisesti.
Kuinka päivittää koodikantasi käyttäjien huomaamatta
Toteutamme myös vikasietoisuuden koodin käyttöönottoprosessien tasolla. Käyttöönoton aikana saattaa esiintyä häiriöitä, mutta niiden vaikutus palvelun saatavuuteen voidaan minimoida.
Päivitämme palveluitamme jatkuvasti ja meidän on varmistettava, että koodikanta päivitetään käyttäjiin vaikuttamatta. Onnistuimme ratkaisemaan tämän ongelman käyttämällä HAProxyn hallintaominaisuuksia ja Graceful Shutdownin käyttöönottoa palveluissamme.
Tämän ongelman ratkaisemiseksi oli tarpeen varmistaa tasapainottimen hallinta ja palvelujen "oikea" sammutus:
- HAProxyn tapauksessa ohjaus suoritetaan tilastotiedoston kautta, joka on pohjimmiltaan socket ja joka määritellään HAProxy-konfiguraatiossa. Voit lähettää sille komentoja stdion kautta. Mutta tärkein konfigurointityökalumme on mahdollinen, joten siinä on sisäänrakennettu moduuli HAProxyn hallintaan. jota käytämme aktiivisesti.
- Suurin osa API- ja Engine-palveluistamme tukevat sulavia sammutustekniikoita: sammutettaessa ne odottavat nykyisen tehtävän valmistumista, olipa kyseessä http-pyyntö tai jokin palvelutehtävä. Sama tapahtuu työntekijän kanssa. Se tietää kaikki tekemänsä tehtävät ja päättyy, kun se on onnistuneesti suorittanut kaiken.
Näiden kahden pisteen ansiosta käyttöönottomme turvallinen algoritmi näyttää tältä.
- Kehittäjä kokoaa uuden koodipaketin (meille tämä on RPM), testaa sen dev-ympäristössä, testaa sen vaiheessa ja jättää sen vaiheen arkistoon.
- Kehittäjä asettaa käyttöönoton tehtävän "artefaktien" yksityiskohtaisimmalla kuvauksella: uuden paketin versio, kuvaus uudesta toiminnallisuudesta ja muut käyttöönoton tiedot tarvittaessa.
- Järjestelmänvalvoja aloittaa päivityksen. Käynnistää Ansible playbookin, joka puolestaan tekee seuraavaa:
- Ottaa paketin vaiheen arkistosta ja käyttää sitä paketin version päivittämiseen tuotearkistossa.
- Kokoaa luettelon päivitetyn palvelun taustaohjelmista.
- Sulkee ensimmäisen HAProxyssa päivitettävän palvelun ja odottaa sen prosessien päättymistä. Suloisen sulkemisen ansiosta olemme varmoja, että kaikki käynnissä olevat asiakaspyynnöt suoritetaan onnistuneesti.
- Kun API ja työntekijät on pysäytetty kokonaan ja HAProxy on kytketty pois päältä, koodi päivitetään.
- Ansible hoitaa palveluita.
- Jokaista palvelua varten vedetään tietyt "kahvat", jotka suorittavat yksikkötestauksen useille ennalta määritetyille avaintesteille. Uuden koodin perustarkistus suoritetaan.
- Jos edellisessä vaiheessa ei löytynyt virheitä, taustaohjelma aktivoituu.
- Siirrytään seuraavaan taustaohjelmaan.
- Kun kaikki taustaohjelmat on päivitetty, toimintatestit käynnistetään. Jos ne puuttuvat, kehittäjä tarkastelee luomiaan uusia toimintoja.
Tämä päättää käyttöönoton.

Palvelun päivitysjakso
Tämä järjestelmä ei toimisi, jos meillä ei olisi yhtä sääntöä. Tuemme taistelussa sekä vanhoja että uusia versioita. Ohjelmistokehitysvaiheessa on etukäteen sovittu, että vaikka palvelutietokannassa tapahtuu muutoksia, ne eivät riko aiempaa koodia. Tämän seurauksena koodikanta päivitetään asteittain.
Johtopäätös
Jakaessani omia ajatuksiani vikasietoisesta WEB-arkkitehtuurista, haluaisin vielä kerran huomioida sen keskeiset kohdat:
- fyysinen vikasietoisuus;
- verkon vikasietoisuus (balancers, BGP);
- käytetyn ja kehitetyn ohjelmiston vikasietoisuus.
Vakaa käyttöaika kaikille!
Lähde: will.com
