Verkon kuormituksen tasapainottimen arkkitehtuuri Yandex.Cloudissa

Verkon kuormituksen tasapainottimen arkkitehtuuri Yandex.Cloudissa
Hei, olen Sergei Elantsev, kehitän verkon kuormituksen tasapainotin Yandex.Cloudissa. Aiemmin johdin L7-tasapainottimen kehitystä Yandex-portaalille - kollegat vitsailevat, että riippumatta siitä, mitä teen, se osoittautuu tasapainottajaksi. Kerron Habrin lukijoille kuinka hallita kuormitusta pilvialustassa, mikä on mielestämme ihanteellinen työkalu tämän tavoitteen saavuttamiseen ja kuinka olemme siirtymässä tämän työkalun rakentamiseen.

Ensin esitellään joitain termejä:

  • VIP (Virtual IP) - tasapainottimen IP-osoite
  • Palvelin, taustajärjestelmä, ilmentymä – virtuaalikone, joka käyttää sovellusta
  • RIP (Real IP) - palvelimen IP-osoite
  • Healthcheck - palvelimen valmiuden tarkistaminen
  • Availability Zone, AZ - eristetty infrastruktuuri datakeskuksessa
  • Alue - eri AZ:iden liitto

Kuormantasaajat ratkaisevat kolme päätehtävää: ne suorittavat tasapainotuksen itse, parantavat palvelun vikasietoisuutta ja yksinkertaistavat sen skaalausta. Vikasietoisuus varmistetaan automaattisella liikenteenohjauksella: tasapainotin tarkkailee sovelluksen tilaa ja sulkee tasapainotuksen pois tilanteet, jotka eivät läpäise elävyystarkistusta. Skaalaus varmistetaan jakamalla kuorma tasaisesti instanssien kesken sekä päivittämällä esiintymien luettelo lennossa. Jos tasapainotus ei ole riittävän tasainen, osa instansseista saa kapasiteettirajan ylittävän kuormituksen ja palvelu heikkenee.

Kuormantasaaja luokitellaan usein sen OSI-mallin protokollakerroksen mukaan, jossa se toimii. Cloud Balancer toimii TCP-tasolla, joka vastaa neljättä kerrosta, L4.

Siirrytään Cloud Balancer -arkkitehtuurin yleiskatsaukseen. Lisäämme asteittain yksityiskohtaisuutta. Jaamme tasapainotuskomponentit kolmeen luokkaan. Konfigurointitason luokka vastaa käyttäjän vuorovaikutuksesta ja tallentaa järjestelmän kohdetilan. Ohjaustaso tallentaa järjestelmän nykyisen tilan ja hallitsee datatasoluokan järjestelmiä, jotka ovat suoraan vastuussa liikenteen toimittamisesta asiakkailta instansseihisi.

Datataso

Liikenne päätyy kalliille laitteille, joita kutsutaan rajareitittimiksi. Vikasietokyvyn lisäämiseksi useita tällaisia ​​laitteita toimii samanaikaisesti yhdessä datakeskuksessa. Seuraavaksi liikenne siirtyy balansoijille, jotka ilmoittavat kaikille AZ:ille anycast-IP-osoitteet BGP:n kautta asiakkaille. 

Verkon kuormituksen tasapainottimen arkkitehtuuri Yandex.Cloudissa

Liikenne välitetään ECMP:n kautta - tämä on reititysstrategia, jonka mukaan kohteeseen voi olla useita yhtä hyviä reittejä (tapauksessamme kohteena on kohde IP-osoite) ja paketteja voidaan lähettää mitä tahansa niistä pitkin. Tuemme myös työskentelyä useilla käytettävyysvyöhykkeillä seuraavan kaavan mukaan: mainostamme jokaisella vyöhykkeellä osoitteen, liikenne menee lähimpään eikä ylitä sen rajoja. Myöhemmin postauksessa tarkastellaan tarkemmin, mitä liikenteelle tapahtuu.

Konfigurointitaso

 
Konfig-tason avainkomponentti on API, jonka kautta suoritetaan perustoiminnot tasapainottajilla: luodaan, poistetaan, instanssien kokoonpanoa muutetaan, saadaan kuntotarkastuksen tulokset jne. Toisaalta tämä on REST API, ja toisaalta Muuten, me pilvessä käytämme hyvin usein kehyskehystä gRPC:tä, joten "käännämme" REST:n gRPC:ksi ja käytämme sitten vain gRPC:tä. Kaikki pyynnöt johtavat sarjan asynkronisten idempotenttien luomiseen, jotka suoritetaan Yandex.Cloud-työntekijöiden yhteisellä poolilla. Tehtävät on kirjoitettu siten, että ne voidaan keskeyttää milloin tahansa ja käynnistää uudelleen. Tämä varmistaa toimintojen skaalautuvuuden, toistettavuuden ja kirjaamisen.

Verkon kuormituksen tasapainottimen arkkitehtuuri Yandex.Cloudissa

Tämän seurauksena tehtävä API:sta tekee pyynnön Balancer-palvelun ohjaimelle, joka on kirjoitettu Go-kielellä. Se voi lisätä ja poistaa tasapainottajia, muuttaa taustaohjelmien koostumusta ja asetuksia. 

Verkon kuormituksen tasapainottimen arkkitehtuuri Yandex.Cloudissa

Palvelu tallentaa tilansa Yandex-tietokantaan, hajautettuun hallittuun tietokantaan, jota voit pian käyttää. Yandex.Cloudissa, kuten jo kertoi, koiranruokakonsepti pätee: jos käytämme itse palveluitamme, niin myös asiakkaamme käyttävät niitä mielellään. Yandex-tietokanta on esimerkki tällaisen konseptin toteutuksesta. Tallennamme kaikki tietomme YDB:hen, eikä meidän tarvitse miettiä tietokannan ylläpitoa ja skaalausta: nämä ongelmat ratkaistaan ​​puolestamme, käytämme tietokantaa palveluna.

Palataan tasapainottimen ohjaimeen. Sen tehtävänä on tallentaa tiedot tasapainottimesta ja lähettää tehtävä virtuaalikoneen valmiuden tarkistamiseksi Healthcheck-ohjaimelle.

Healthcheck-ohjain

Se vastaanottaa tarkistussääntöjen muutospyynnöt, tallentaa ne YDB:hen, jakaa tehtävät terveystarkistussolmujen kesken ja aggregoi tulokset, jotka sitten tallennetaan tietokantaan ja lähetetään loadbalancerin ohjaimelle. Se puolestaan ​​lähettää pyynnön muuttaa datatason klusterin koostumusta loadbalancer-solmulle, jota käsittelen alla.

Verkon kuormituksen tasapainottimen arkkitehtuuri Yandex.Cloudissa

Puhutaanpa lisää terveystarkastuksista. Ne voidaan jakaa useisiin luokkiin. Auditoinneilla on erilaiset onnistumiskriteerit. TCP-tarkistusten on muodostettava yhteys onnistuneesti tietyn ajan kuluessa. HTTP-tarkistukset vaativat sekä onnistuneen yhteyden että vastauksen tilakoodilla 200.

Myös sekit eroavat toimintaluokista - ne ovat aktiivisia ja passiivisia. Passiiviset tarkistukset vain valvovat, mitä liikenteessä tapahtuu ilman erityistoimenpiteitä. Tämä ei toimi kovin hyvin L4:llä, koska se riippuu ylemmän tason protokollien logiikasta: L4:llä ei ole tietoa kuinka kauan toiminto kesti tai oliko yhteyden valmistuminen hyvä vai huono. Aktiiviset tarkistukset edellyttävät, että tasapainottaja lähettää pyynnöt jokaiselle palvelininstanssille.

Useimmat kuormantasaajat tekevät elävyyden tarkistukset itse. Cloudilla päätimme erottaa nämä järjestelmän osat skaalautuvuuden lisäämiseksi. Tämän lähestymistavan avulla voimme lisätä tasapainottajien määrää ja säilyttää samalla palveluun tulevien terveystarkastuspyyntöjen määrän. Tarkistukset suorittavat erilliset kuntotarkistussolmut, joiden kautta tarkistuskohteet sirpaloituvat ja replikoidaan. Et voi suorittaa tarkistuksia yhdestä isännästä, koska se voi epäonnistua. Silloin emme saa selville hänen tarkistamiensa tapausten tilaa. Suoritamme tarkastuksia mille tahansa ilmentymälle vähintään kolmesta kuntotarkistussolmusta. Jaamme solmujen välisten tarkistusten tarkoituksen johdonmukaisilla hajautusalgoritmeilla.

Verkon kuormituksen tasapainottimen arkkitehtuuri Yandex.Cloudissa

Tasapainotuksen ja terveystarkastuksen erottaminen toisistaan ​​voi johtaa ongelmiin. Jos kuntotarkistussolmu tekee pyyntöjä ilmentymälle ohittaen tasapainottimen (joka ei tällä hetkellä palvele liikennettä), syntyy outo tilanne: resurssi näyttää olevan elossa, mutta liikenne ei saavuta sitä. Ratkaisemme tämän ongelman näin: käynnistämme taatusti terveystarkastuksen liikenteen tasapainottimien kautta. Toisin sanoen asiakkaiden ja terveystarkastusten liikenteen pakettien siirtäminen eroaa minimaalisesti: molemmissa tapauksissa paketit saapuvat tasapainottajille, jotka toimittavat ne kohderesursseihin.

Erona on, että asiakkaat tekevät pyyntöjä VIPille, kun taas terveystarkastukset tekevät pyynnöt jokaiselle yksittäiselle RIP:lle. Tässä syntyy mielenkiintoinen ongelma: annamme käyttäjillemme mahdollisuuden luoda resursseja harmaissa IP-verkoissa. Kuvitellaan, että on olemassa kaksi erilaista pilvenomistajaa, jotka ovat piilottaneet palvelunsa tasapainottajien taakse. Jokaisella niistä on resurssit 10.0.0.1/24-aliverkossa samoilla osoitteilla. Sinun on pystyttävä jotenkin erottamaan ne, ja tässä sinun on sukeltaa Yandex.Cloud-virtuaaliverkon rakenteeseen. Parempi ottaa selvää tarkemmin osoitteesta video about:cloud-tapahtumasta, meille on nyt tärkeää, että verkko on monikerroksinen ja siinä on tunneleita, jotka voidaan erottaa aliverkon tunnuksella.

Healthcheck-solmut ottavat yhteyttä tasapainottajiin käyttämällä niin kutsuttuja kvasi-IPv6-osoitteita. Kvasiosoite on IPv6-osoite, jonka sisään on upotettu IPv4-osoite ja käyttäjän aliverkon tunnus. Liikenne saavuttaa tasapainottimen, joka poimii siitä IPv4-resurssiosoitteen, korvaa IPv6:n IPv4:llä ja lähettää paketin käyttäjän verkkoon.

Käänteinen liikenne kulkee samalla tavalla: tasapainotin näkee, että kohteena on harmaa verkko terveystarkistajilta, ja muuntaa IPv4:n IPv6:ksi.

VPP - datatason sydän

Tasapainotin on toteutettu Vector Packet Processing (VPP) -tekniikalla, Ciscon kehyksellä verkkoliikenteen eräkäsittelyyn. Meidän tapauksessamme kehys toimii käyttäjätilan verkon laitehallintakirjaston - Data Plane Development Kitin (DPDK) - päällä. Tämä varmistaa korkean paketinkäsittelyn suorituskyvyn: ytimessä esiintyy paljon vähemmän keskeytyksiä, eikä kontekstin vaihtoa kerneltilan ja käyttäjätilan välillä ole. 

VPP menee vielä pidemmälle ja puristaa entistä enemmän suorituskykyä järjestelmästä yhdistämällä paketteja eriin. Suorituskyvyn lisäys johtuu välimuistien aggressiivisesta käytöstä nykyaikaisissa prosessoreissa. Käytössä on sekä datavälimuisti (paketit käsitellään ”vektoreissa”, tiedot ovat lähellä toisiaan) että käskyvälimuisti: VPP:ssä pakettien käsittely seuraa graafia, jonka solmut sisältävät toimintoja, jotka suorittavat samaa tehtävää.

Esimerkiksi IP-pakettien käsittely VPP:ssä tapahtuu seuraavassa järjestyksessä: ensin jäsennyssolmussa jäsennetään pakettien otsikot ja sitten ne lähetetään solmulle, joka välittää paketit edelleen reititystaulukoiden mukaisesti.

Hieman hardcorea. VPP:n kirjoittajat eivät siedä kompromisseja prosessorien välimuistien käytössä, joten tyypillinen koodi pakettivektorin käsittelyyn sisältää manuaalisen vektoroinnin: on prosessointisilmukka, jossa käsitellään tilanne, kuten "meillä on neljä pakettia jonossa", sitten sama kahdelle, sitten - yhdelle. Esihakuohjeita käytetään usein tietojen lataamiseen välimuistiin nopeuttamaan niiden käyttöä myöhemmissä iteraatioissa.

n_left_from = frame->n_vectors;
while (n_left_from > 0)
{
    vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
    // ...
    while (n_left_from >= 4 && n_left_to_next >= 2)
    {
        // processing multiple packets at once
        u32 next0 = SAMPLE_NEXT_INTERFACE_OUTPUT;
        u32 next1 = SAMPLE_NEXT_INTERFACE_OUTPUT;
        // ...
        /* Prefetch next iteration. */
        {
            vlib_buffer_t *p2, *p3;

            p2 = vlib_get_buffer (vm, from[2]);
            p3 = vlib_get_buffer (vm, from[3]);

            vlib_prefetch_buffer_header (p2, LOAD);
            vlib_prefetch_buffer_header (p3, LOAD);

            CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, STORE);
            CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, STORE);
        }
        // actually process data
        /* verify speculative enqueues, maybe switch current next frame */
        vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
                to_next, n_left_to_next,
                bi0, bi1, next0, next1);
    }

    while (n_left_from > 0 && n_left_to_next > 0)
    {
        // processing packets by one
    }

    // processed batch
    vlib_put_next_frame (vm, node, next_index, n_left_to_next);
}

Joten Healthchecks puhuu IPv6:n kautta VPP:lle, mikä muuttaa ne IPv4:ksi. Tämän tekee graafin solmu, jota kutsumme algoritmiseksi NAT:ksi. Käänteiselle liikenteelle (ja IPv6:sta IPv4:ksi muuntamiseen) on sama algoritminen NAT-solmu.

Verkon kuormituksen tasapainottimen arkkitehtuuri Yandex.Cloudissa

Suora liikenne tasapainotusasiakkailta kulkee graafin solmujen kautta, jotka suorittavat tasapainotuksen itse. 

Verkon kuormituksen tasapainottimen arkkitehtuuri Yandex.Cloudissa

Ensimmäinen solmu on tarttuvia istuntoja. Se tallentaa hashin 5-tupla vakiintuneisiin istuntoihin. 5-tuple sisältää sen asiakkaan osoitteen ja portin, josta tiedot lähetetään, liikenteen vastaanottamiseen käytettävissä olevien resurssien osoitteen ja portit sekä verkkoprotokollan. 

5-tuple hash auttaa meitä suorittamaan vähemmän laskentaa seuraavassa johdonmukaisessa hajautussolmussa sekä käsittelemään paremmin resurssiluettelon muutoksia tasapainottimen takana. Kun paketti, jolle ei ole istuntoa, saapuu tasapainottajaan, se lähetetään johdonmukaiseen hajautussolmuun. Tässä tasapainotus tapahtuu johdonmukaisen hajautusjärjestelmän avulla: valitsemme resurssin käytettävissä olevien "elävien" resurssien luettelosta. Seuraavaksi paketit lähetetään NAT-solmuun, joka itse asiassa korvaa kohdeosoitteen ja laskee tarkistussummat uudelleen. Kuten näette, noudatamme VPP - like to like -sääntöjä, ryhmittelemme samankaltaisia ​​laskelmia prosessorin välimuistien tehokkuuden lisäämiseksi.

Johdonmukainen hajautus

Miksi valitsimme sen ja mitä se edes on? Tarkastellaan ensin edellistä tehtävää - resurssin valintaa luettelosta. 

Verkon kuormituksen tasapainottimen arkkitehtuuri Yandex.Cloudissa

Epäjohdonmukaisessa hajautusarvossa lasketaan saapuvan paketin tiiviste ja resurssi valitaan luettelosta jakamalla tämä tiiviste resurssien lukumäärällä. Niin kauan kuin luettelo pysyy ennallaan, tämä malli toimii hyvin: lähetämme aina paketit, joissa on sama 5-tuple samaan esiintymään. Jos esimerkiksi jokin resurssi lakkasi vastaamasta terveystarkastuksiin, valinta muuttuu merkittävässä osassa tiivisteistä. Asiakkaan TCP-yhteydet katkeavat: aiemmin ilmentymän A saavuttanut paketti saattaa alkaa saavuttaa ilmentymän B, joka ei tunne tämän paketin istuntoa.

Johdonmukainen hajautus ratkaisee kuvatun ongelman. Helpoin tapa selittää tämä käsite on tämä: kuvittele, että sinulla on rengas, johon jaat resursseja hajautusmuodossa (esimerkiksi IP:portin mukaan). Resurssin valinta on pyörän kääntämistä kulmassa, joka määräytyy paketin hajautusarvon mukaan.

Verkon kuormituksen tasapainottimen arkkitehtuuri Yandex.Cloudissa

Tämä minimoi liikenteen uudelleenjaon, kun resurssien koostumus muuttuu. Resurssin poistaminen vaikuttaa vain siihen johdonmukaisen hajautusrenkaan osaan, jossa resurssi sijaitsi. Resurssin lisääminen muuttaa myös jakelua, mutta meillä on tahmea istuntosolmu, jonka avulla emme voi vaihtaa jo muodostettuja istuntoja uusiin resursseihin.

Tarkastelimme, mitä tapahtuu ohjaamaan liikennettä tasapainottimen ja resurssien välillä. Katsotaanpa nyt paluuliikennettä. Se noudattaa samaa kaavaa kuin tarkistusliikenne - algoritmisen NAT:n kautta, toisin sanoen käänteisen NAT 44:n kautta asiakasliikenteelle ja NAT 46:n kautta terveystarkistusliikenteelle. Noudatamme omaa suunnitelmaamme: yhdistämme terveystarkastusten liikenteen ja todellisen käyttäjäliikenteen.

Loadbalancer-solmu ja kootut komponentit

Tasapainottimien ja resurssien koostumuksen VPP:ssä raportoi paikallinen palvelu - loadbalancer-solmu. Se tilaa tapahtumavirran loadbalancer-ohjaimelta ja pystyy piirtämään eron nykyisen VPP-tilan ja ohjaimelta vastaanotetun kohdetilan välillä. Saamme suljetun järjestelmän: tapahtumat API:sta tulevat balancer-ohjaimelle, joka jakaa tehtäviä Healthcheck-ohjaimelle tarkistaakseen resurssien "elävyyden". Se puolestaan ​​osoittaa tehtäviä Healthcheck-solmulle ja aggregoi tulokset, minkä jälkeen se lähettää ne takaisin tasapainotusohjaimelle. Loadbalancer-solmu tilaa ohjaimen tapahtumia ja muuttaa VPP:n tilaa. Tällaisessa järjestelmässä jokainen palvelu tietää vain sen, mikä on tarpeellista naapuripalveluista. Liitäntöjen määrä on rajoitettu ja meillä on kyky operoida ja skaalata eri segmenttejä itsenäisesti.

Verkon kuormituksen tasapainottimen arkkitehtuuri Yandex.Cloudissa

Miltä ongelmalta vältyttiin?

Kaikki palvelumme ohjaustasolla on kirjoitettu Go-kielellä ja niillä on hyvät skaalaus- ja luotettavuusominaisuudet. Golla on monia avoimen lähdekoodin kirjastoja hajautettujen järjestelmien rakentamiseen. Käytämme aktiivisesti GRPC:tä, kaikki komponentit sisältävät avoimen lähdekoodin palveluiden löytämisen toteutuksen - palvelumme seuraavat toistensa suorituskykyä, voivat muuttaa koostumustaan ​​dynaamisesti, ja liitimme tämän GRPC-tasapainotukseen. Mittarissa käytämme myös avoimen lähdekoodin ratkaisua. Datatasossa saimme kunnollisen suorituskyvyn ja suuren resurssireservin: osoittautui erittäin vaikeaksi koota jalusta, johon voisi luottaa VPP:n suorituskykyyn rautaisen verkkokortin sijaan.

Ongelmia ja ratkaisuja

Mikä ei toiminut niin hyvin? Gossa on automaattinen muistinhallinta, mutta muistivuotoja tapahtuu silti. Helpoin tapa käsitellä niitä on ajaa gorutiineja ja muistaa lopettaa ne. Takeaway: Katso Go-ohjelmien muistin kulutusta. Usein hyvä indikaattori on gorutiinien määrä. Tässä tarinassa on plussa: Gossa on helppo saada ajonaikaiset tiedot - muistin kulutus, käynnissä olevien gorutiinien määrä ja monet muut parametrit.

Go ei myöskään välttämättä ole paras valinta toiminnallisiin testeihin. Ne ovat varsin monisanaisia, eikä tavallinen lähestymistapa "ajoa kaikki CI:ssä erässä" ei sovellu heille. Tosiasia on, että toiminnalliset testit ovat resursseja vaativampia ja aiheuttavat todellisia aikakatkaisuja. Tämän vuoksi testit voivat epäonnistua, koska CPU on varattu yksikkötestien kanssa. Johtopäätös: Jos mahdollista, suorita "raskaat" testit erillään yksikkötesteistä. 

Mikropalvelutapahtumaarkkitehtuuri on monimutkaisempi kuin monoliitti: lokien kerääminen kymmenillä eri koneilla ei ole kovin kätevää. Johtopäätös: jos teet mikropalveluita, mieti heti jäljitystä.

Meidän suunnitelmat

Lanseeraamme sisäisen tasapainottimen, IPv6-tasapainottimen, lisäämme tuen Kubernetes-skripteille, jatkamme palveluidemme sirpalointia (tällä hetkellä vain healthcheck-solmu ja healthcheck-ctrl ovat sirpaloituja), lisäämme uusia terveystarkistuksia ja otamme myös käyttöön älykkään tarkistusten yhdistämisen. Harkitsemme mahdollisuutta tehdä palveluistamme entistä itsenäisempiä - jotta ne eivät kommunikoi suoraan keskenään, vaan viestijonon avulla. SQS-yhteensopiva palvelu on hiljattain ilmestynyt pilveen Yandexin viestijono.

Äskettäin Yandex Load Balancer julkaistiin. Tutkia dokumentointi palveluun, hallitse tasapainoimia sinulle sopivalla tavalla ja lisää projektiesi vikasietoisuutta!

Lähde: will.com

Lisää kommentti