Yandex.Cloudi võrgu koormuse tasakaalustaja arhitektuur

Yandex.Cloudi võrgu koormuse tasakaalustaja arhitektuur
Tere, mina olen Sergei Jlantsev, arendan võrgubalansseerijat Yandex Cloudis. Varem juhtisin ma Yandexi portaali L7-balansseerija arendust — kolleegid naljatavad, et mida iganes ma ei teeks, tuleb välja balansseerija. Räägin Habra lugejatele, kuidas tuleb hallata koormust pilveplatvormil, milline on meie ideaalne tööriist selle eesmärgi saavutamiseks ja kuidas me liikume selle tööriista loomise suunas.

Alustame mõnede terminite tutvustamisega:

  • VIP (Virtuaalne IP) — balansseerija IP-aadress
  • Server, backend, instants — virtuaalne masin, kus töötab rakendus
  • RIP (Reaalne IP) — serveri IP-aadress
  • Healthcheck — serveri töövalmiduse kontroll
  • Kättesaadavuse tsoon, Availability Zone, AZ — isoleeritud infrastruktuur andmekeskuses
  • Regioon — erinevate AZ-de kombinatsioon

Koormabalansseerijad lahendavad kolm põhiteemat: nad teevad ise balansseerimist, parandavad teenuse töökindlust ja lihtsustavad selle skaleerimist. Töökindlust tagab automaatne liikluse haldamine: balansseerija jälgib rakenduse seisundit ja välistab balansseerimisest instantsid, mis ei ole läbinud elujõudluse testimist. Skaleerimist tagab koormuse ühtlane jaotamine instantside vahel ning instantside nimekirja reaalajas uuendamine. Kui balansseerimine ei ole piisavalt ühtlane, võivad mõned instantsid saada koormust, mis ületab nende töövõimepiirid, vähendades teenuse usaldusväärsust.

Koormabalansseerijat klassifitseeritakse sageli OSI mudeli protokolli taseme järgi, millel see töötab. Cloud Balancer töötab TCP tasemel, mis vastab neljandale tasemele, L4.

Liigume edasi Cloud Balanceri arhitektuuri ülevaatesse. Tõstame detailide taset järk-järgult. Jagame balansseerija komponendid kolme klassi. Config plane vastutab kasutajaga suhtlemise eest ja säilitab süsteemi soovitud olekut. Control plane säilitab süsteemi praegust olekut ja haldab andmeplaadi klassi süsteeme, mis vastutavad liikluse edastamise eest klientidelt teie instantsideni.

Data plane

Liiklus jõuab kallitele seadmetele, mida nimetatakse piirrouteriteks. Töökindluse parandamiseks töötab ühes andmekeskuses samal ajal mitu sellist seadet. Järgmiseks jõuab liiklus balansseerijatele, mis kliendile kuulutavad anycast IP-aadressi kõigis AZ-des BGP kaudu. 

Yandex.Cloudi võrgu koormuse tasakaalustaja arhitektuur

Liiklus edastatakse ECMP kaudu — see on marsruutimisstrateegia, mille kohaselt võib sihtkohta (meie puhul siht IP-aadress) olla mitu võrdselt head marsruuti ja pakette saab saata läbi mõne neist. Samuti toetame tööd mitmes kättesaadavuse tsoonis järgmisel skeemil: kuulutame aadressi igas tsoonis, liiklus jõuab kõige lähemale ja enam sellest piiri ei ületa. Edasi arutame postituses, mis juhtub liiklusega.

Config plane

 
Config plane'i peamine komponent on API, mille kaudu teostatakse põhitoimingud balansseerijatega: loomine, kustutamine, instantside koosseisu muutmine, healthcheck tulemuste saamine jne. Ühest küljest on see REST API, teisest küljest kasutame Cloudis sageli gRPC raamistikku, seega „tõlgime“ RESTi gRPC-ks ja kasutame edaspidi ainult gRPC-d. Iga päring käivitab seeria asünkroonseid idempotente ülesandeid, mis täidetakse Yandex Cloudi ühisel töötajate basseinis. Ülesandeid kirjutatakse selliselt, et neid saab igal ajal peatada ja hiljem uuesti käivitada. See tagab operatsioonide skaleeritavuse, korduvuse ja logitavuse.

Yandex.Cloudi võrgu koormuse tasakaalustaja arhitektuur

Lõpuks teostab API ülesanne päringu balansseerijate teenusekontrollerile, mis on kirjutatud Go-programmeerimiskeeles. See võib lisada ja eemaldada balansseerijaid, muuta backendide koosseisu ja seadeid. 

Yandex.Cloudi võrgu koormuse tasakaalustaja arhitektuur

Teenusel on oma olek Yandex Database'is — jaotatud hallatav andmebaas, mida varsti saate kasutada ka teie. Yandex Cloudis, nagu me juba olen rääkinud, kehtib dog food kontseptsioon: kui me ise kasutame oma teenuseid, siis naudivad neid ka meie kliendid. Yandex Database on näide sellise kontseptsiooni elluviimisest. Me hoiame kõik oma andmed YDB-s ja meil ei ole vaja mõelda andmebaasi hooldusele ja skaleerimisele: need probleemid on lahendatud meie eest, me kasutame andmebaasi teenusena.

Naaseme balansseerija kontrolleri juurde. Tema ülesanne on säilitada teavet balansseerija kohta, saata ülesanne virtuaalse masina töövalmiduse kontrollimiseks healthcheck kontrollerile.

Healthcheck controller

See teeb taotlusi reeglite muutmiseks, salvestab need YDB-s, jagab ülesanded healthcheck nodede vahel ning koondab tulemused, mis seejärel salvestatakse andmebaasi ja saadetakse loadbalancer kontrollerisse. See omakorda saadab taotluse klastrikoosseisu muutmiseks data plane'i loadbalancer-node'ile, millest räägin allpool.

Yandex.Cloudi võrgu koormuse tasakaalustaja arhitektuur

Räägime lähemalt healthcheck'idest. Need võib jagada mitmesse klassi. Kontrollidel on erinevad eduka tulemuse kriteeriumid. TCP kontrollid peavad edukalt looma ühenduse kindlaksmääratud ajavahemikus. HTTP kontrollid nõuavad nii eduka ühenduse loomist kui ka vastuse saamist staatuse koodiga 200.

Samuti erinevad kontrollid tegevuse klassi poolest — need on aktiivsed ja passiivsed. Passiivsed kontrollid jälgivad lihtsalt, mis toimub liikluses, ette võtmata mingeid erilisi meetmeid. See ei tööta eriti hästi L4 peal, kuna see sõltub kõrgema taseme protokollide loogikast: L4-l puudub teave selle kohta, kui kaua operatsioon kestis, ja kas ühenduse lõpetamine oli hea või halb. Aktiivsed kontrollid nõuavad, et tasakaalustaja saadaks päringud iga serveri instantsi juurde.

Enamik koormuse tasakaalustajaid viib üksi ellu «elujõud» kontrollid. Meie pilves otsustasime eraldada need süsteemi osad suurema skaleeritavuse tagamiseks. Selline lähenemine võimaldab meil suurendada tasakaalustajate arvu, säilitades samas healthcheck päringute arvu teenusele. Kontrollid viiakse läbi eraldi healthcheck nodede poolt, kuhu on sharditud ja replitseeritud kontrollitavad sihid. Kontrollid ei saa tulla ühest hostist, kuna see võib ebaõnnestuda. Sel juhul ei saa me kontrollitud instantside olekut. Me viime läbi kontrollid iga instantsi kohta vähemalt kolmest healthcheck nodest. Kontrollitavad sihid jagame nodede vahel konsistentse häälestamise algoritmide abil.

Yandex.Cloudi võrgu koormuse tasakaalustaja arhitektuur

Tasakaalu ja healthcheck'i eristamine võib põhjustada probleeme. Kui healthcheck node saadab päringuid instantsile, mööda minnes tasakaalustajast (mis sel hetkel ei tegele liiklusega), tekib kummaline olukord: ressurss näib olevat elujõuline, kuid liiklus ei jõua sinna. Me lahendame selle probleemi nii: garanteerime healthcheck liikluse suunamise läbi tasakaalustajate. Teisisõnu, pakettide edastamise skeem klientide liiklusest ja healthcheck'idest erineb minimaalselt: mõlemal juhul jõuavad paketid tasakaalustajatesse, mis toimetavad need sihtressurssidele.

Erinevus seisneb selles, et kliendid teevad päringud VIP-ile, samas kui healthcheck'id pöörduvad iga eraldi RIP-i poole. Siit kerkib huvitav probleem: meie kasutajatele anname võimaluse luua ressursse hallides IP-võrkudes. Kujutage ette, et on kaks erinevat pilve omanikku, kes on varjanud oma teenuseid tasakaalustajate taha. Igalühel neist on ressursid alamvõrgus 10.0.0.1/24, ja neil on samad aadressid. Tuleb osata neid kuidagi eristada, ja siin tuleb süveneda Yandex. Cloud virtuaalse võrgu ülesehitusse. Üksikasjad on parem vaadata videos, mis on saadaval üritusel about:cloud. , praegu on oluline, et võrk on mitmekihiline ja sisaldab tunnelite süsteemi, mida saab eristada alamvõrgu id-de järgi.Healthcheck nodede pöördumine tasakaalustajate poole toimub nn quasi-IPv6 aadresside abil. Quasi-aadress on IPv6-aadress, mille sees on kodeeritud IPv4-aadress ja kasutaja alamvõrgu id. Liiklus jõuab tasakaalustajasse, kes eemaldab sellest IPv4-aadressi ressursist, asendab IPv6 IPv4-ga ja saadab paketi kasutaja võrku.

Tagasi liiklus kulgeb samuti: tasakaalustaja näeb, et sihtkoht on hall võrk healthcheck'ide poolt, ja muudab IPv4 tagasi IPv6-ks.

VPP — data plane'i süda.

Tasakaalustaja on реализован использованным технологией Vector Packet Processing (VPP) — Cisco raamistik paketise töötluseks. Meie puhul töötab raamistik user-space võrgu seadmete halduse raamatukogu — Data Plane Development Kit (DPDK) peal. See tagab kõrge pakettide töötlemise jõudluse: tuumas toimub palju vähem katkestusi, ei toimu konteksti vahetusi kernel space'i ja user space'i vahel.

VPP viib selle veel kaugemale ja pigistab süsteemist veelgi rohkem jõudlust pakettide rühmasside ühendamise kaudu. Jõudluse suurenemine toimub kaasaegsete protsessorite agressiivse mälupangastamise kasutamise tõttu. Kasutatakse nii andmete vahemälusid (pakettide töödeldakse „vektorite” järgi, andmed on koos lähedal) kui ka käsu vahemälusid: VPP-s järgneb pakettide töötlemine graafikule, mille sõlmedes asuvad ühtset ülesannet täitvad funktsioonid. 

VPP läheb veel kaugemale ja kasutab süsteemist veelgi rohkem efektiivsust, kombineerides pakette partiidesse. Efektiivsuse suurenemine toimub agressiivse tänapäevaste protsessorite vahemälu kasutamise kaudu. Kasutatakse nii andmevahemällu (pakette töödeldakse ‘vektorite’ kaudu, andmed asuvad üksteise lähedal) kui ka käskude vahemälu: VPP-le järgneb pakettide töötlemine graafikule, mille sõlmedes asuvad funktsioonid, mis täidavad ühte ülesannet.

Näiteks IP-pakettide töötlemine VPP-s toimub järgmiselt: esmalt pakettide pealkirjade analüüs toimub analüüsipunktis ja seejärel saadetakse need edasi sõlme, mis edastab paketid vastavalt marsruudistustabelitele.

Natuke väärituks. VPP autorid ei tee järeleandmisi protsessori vahemähitsemiste kasutamises, mistõttu tüüpiline paketivektorite töötlemiskoodeks sisaldab käsitsi vektorimise: on töötlemise tsükkel, kus käideldakse olukorda 'meil on järjekorras neli paketti', seejärel sama kahe jaoks ja lõpuks ühe jaoks. Sageli kasutatakse prefetch-käske, mis laadivad andmeid vahemähitsemistesse, et kiirendada sellele järgnevate iteratsioonide juurdepääsu.

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)
    {
        // mitu paketti töötlus korraga
        u32 next0 = SAMPLE_NEXT_INTERFACE_OUTPUT;
        u32 next1 = SAMPLE_NEXT_INTERFACE_OUTPUT;
        // ...
        /* Prefetch järgmine iteratsioon. */
        {
            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);
        }
        // töötleme andmeid
        /* kontrolli spekulatiivseid puhverdamisi, võib-olla vaheta praegune järgmine raami */
        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)
    {
        // töötleme pakette üksikult
    }

    // töödeldud partiid
    vlib_put_next_frame (vm, node, next_index, n_left_to_next);
}

Nii Healthchecks pöördub IPv6 kaudu VPP poole, mis muundab need IPv4-ks. Sellega tegeleb graafisõlm, mida me nimetame algoritmiliseks NAT-iks. Tagasikutsumise liikluses (ja IPv6-lt IPv4-le konverteerimisel) on sama algoritmiline NAT-i sõlm.

Yandex.Cloudi võrgu koormuse tasakaalustaja arhitektuur

Otse klientide vahel balansseeriast tulev liiklus läbib graafisõlme, mis teostab tõeliselt tasakaalustamist. 

Yandex.Cloudi võrgu koormuse tasakaalustaja arhitektuur

Esimene sõlm — sticky sessions. Siin hoitakse hash'i 5-tuple aktiveeritud sessioonide jaoks. 5-tuple sisaldab kliendi aadressi ja porti, mille kaudu antakse teavet, aadressi ja porti ressurssidest, mis on kergesti kättesaadavad liikluse vastuvõtmiseks, ning ka võrgu protokolli. 

5-tuple hash aitab meil vähendada edasisi arvutuskatkestusi järgmises konsistentset häshtamis sõlmes ja paremini hallata ressursside loendi muutumist tasakaalustaja taga. Kui tasakaalustajale jõuab pakett, mille jaoks sessioon puudub, saadetakse see konsistentse häshimise sõlme. Seal toimub tasakaalustamine kasutades konsistentset häshtimist: valime ressursi loendist, mis koosneb 'elavatest' ressurssidest. Seejärel saadetakse paketid NAT sõlme, mis teostab tegeliku sihtaadressi asendamise ja kontrollsummade uuesti arvutamise. Nagu näete, järgime VPP reegleid — sarnane sarnasele, koondame sarnased arvutused protsessori vahemähi efektiivsuse suurendamiseks.

Konsistentne häshtimine

Miks me valisime just selle ja mis see üldse on? Alustame eelmise ülesande vaatamisest — ressursi valimine loendist. 

Yandex.Cloudi võrgu koormuse tasakaalustaja arhitektuur

Ebaühtlise häshtimise korral arvutatakse sissetuleva paketi hash ja ressurss valitakse loendist jäägi alusel selle hash’i jagamisel ressursside arvuga. Niikaua kui loend jääb muutumatuks, töötab see skeem hästi: saadame alati paketid samade 5-tuple tunnustega samale instantsile. Kui mõni ressurss ei vasta healthcheck’idele, siis suure osa hash’ide valik muutub. Kliendi TCP-ühendused katkenevad: pakk, mis varem läks instantsile A, võib hakata minema instantsile B, millel ei ole selle paketi sessioonist teadlikkust.

Konsistentne häshtimine lahendab kirjeldatud probleemi. Lihtsaim on selle kontseptsioon seletada nii: kujutage ette, et teil on ring, millele jaotatakse ressursid hash’i põhjal (näiteks IP:port). Ressursside valimine on ratta pööramine nurga suunas, mis määratakse paketi hash’i alusel.

Yandex.Cloudi võrgu koormuse tasakaalustaja arhitektuur

Sellega minimeeritakse liikluse ümberjaotamine ressursside koostise muutumisel. Ressursi eemaldamine mõjutab ainult seda osa konsistentse häshtimise ringist, kus see ressurss asus. Ressursi lisamine muutab jaotust, kuid meil on sticky sessions sõlm, mis võimaldab juba asutatud sessioone uutele ressurssidele mitte vahetada.

Oleme arutanud, mis juhtub otse liiklusega tasakaalustaja ja ressursside vahel. Nüüd vaatame tagasi liiklust. See järgib sama skeemi nagu healthchecks — läbi algoritmilise NAT, see tähendab tagasi NAT 44 klientide liikluse jaoks ja NAT 46 healthchecks liikluse jaoks. Jätkame oma skeemi: unifitseerime healthchecks liikluse ja tegeliku kasutajate liikluse.

Loadbalancer-node ja komponendid kombinatsioonis

VPP tasakaalustajate ja ressursside kohta teavitab kohalik teenus — loadbalancer-node. See subscribeerib loadbalancer-controller'i sündmuste voogule, suudab konstrueerida erinevuse praeguse VPP oleku ja eesmärgi oleku vahel, mida saadakse kontrollerilt. Saame suletud süsteemi: API-st tulevad sündmused jõuavad tasakaalustaja kontrollerisse, mis paneb healthcheck-kontrollerile ülesanded ressursside "elujõudluse" kontrollimiseks. See omakorda suunab ülesandeid healthcheck-node'ile ja kogub tulemused, seejärel saadab need tagasi tasakaalustajate kontrollerile. Loadbalancer-node subscribeerib sündmustele kontrollerist ja muudab VPP olekut. Sellises süsteemis teab iga teenus ainult vajalikku teavet naabrite kohta. Suhete arv on piiratud, ja meil on võimalus sõltumatult hallata ja skaleerida erinevaid segmente.

Yandex.Cloudi võrgu koormuse tasakaalustaja arhitektuur

Millised küsimused õnnestus vältida

Kõik meie teenused control plane'is on kirjutatud Go-s ja neil on head omadused skaleeritavuse ja usaldusväärsuse osas. Go-s on palju avatud lähtekoodiga raamatukogusid jaotatud süsteemide loomiseks. Kasutame aktiivselt GRPC-d, kõik komponendid sisaldavad avatud lähtekoodiga teenuste avastamise teostust — meie teenused jälgivad üksteise töökorras olekut, saavad oma koosseisu dünaamiliselt muuta ning oleme selle sidunud GRPC tasakaalustamisega. Meie mõõdikute jaoks kasutame samuti avatud lähtekoodiga lahendust. Data plane'is saavutasime väärika jõudluse ja suure ressursivaruga: seinast seina seade oli raske kokku panna, et jõuda VPP jõudluse piirini, mitte raudvara piirideni.

Probleemid ja lahendused

Mis ei toiminud väga hästi? Go-s on mälu haldamine automaatne, kuid mälu lekkeid siiski esineb. Lihtsaim viis nendega toime tulla on käivitada gorutiinid ja mitte unustada neid lõpetada. Järeldus: jälgige Go programmide mälu tarbimist. Sage indikaator on käimasolevate gorutiinide arv. Selles loos on ka pluss: Go-s on lihtne saada andmeid runtime'i kohta — mälu tarbimise, käimasolevate gorutiinide arvu ja paljude teiste parameetrite kohta.

Lisaks, Go ei pruugi olla parim valik funktsionaalsete testide jaoks. Need on üsna sõnakad ja standardne lähenemine "käivitada kõik CI-s koos" ei sobi nende jaoks väga hästi. Asi on selles, et funktsionaalsed testid on ressursinõudlikumad, nendega kaasnevad tõelised ajaliselt piiritletud viivitused. Sellest tulenevalt võivad testid ebaõnnestuda, kuna CPU on hõivatud üksustestidega. Järeldus: võimalusel käitage "raskeid" teste eraldi üksustestidest. 

Mikroteenuste sündmuslik arhitektuur on keerulisem kui monoliit: logide otsimine kümnete erinevate masinate vahel ei ole väga mugav. Järeldus: kui loote mikroteenuseid, mõelge kohe jälgimisele.

Meie plaanid

Kavatseme käivitada sisemise tasakaalustaja, IPv6 tasakaalustaja, lisada Kubernetes'i stsenaariumite tugi, jätkata meie teenuste jagamist (praegu on jagatud vaid healthcheck-node ja healthcheck-ctrl), lisada uusi healthcheck'e ning luua nutikas kontrollide koondamine. Me kaalume võimalust teha meie teenused veelgi iseseisvamaks — et nad suhtleksid mitte otse omavahel, vaid sõnumijärjekorra kaudu. Pilves on hiljuti ilmunud SQS-i ühilduv teenus. Yandex Message Queue.

Hiljuti toimus avalik Yandex Load Balancer'i väljaanne. Uurige dokumentatsiooni teenust, haldage tasakaalustajaid mugaval viisil ja suurendage oma projektide veatust!

Allikas: habr.com

Osta usaldusväärne veebihosting DDoS kaitsega, VPS VDS serverid 🔥 Osta usaldusväärne veebihosting DDoS kaitsega, VPS VDS serverid | ProHoster