Võrgukoormuse tasakaalustaja arhitektuur Yandex.Cloudis

Võrgukoormuse tasakaalustaja arhitektuur Yandex.Cloudis
Tere, mina olen Sergei Elantsev, arenen võrgu koormuse tasakaalustaja Yandex.Cloudis. Varem juhtisin Yandexi portaali L7 tasakaalustaja väljatöötamist – kolleegid naljatavad, et ükskõik mida ma ka ei teeks, osutub see tasakaalustajaks. Räägin Habri lugejatele, kuidas pilveplatvormil koormust hallata, milline on meie arvates ideaalne tööriist selle eesmärgi saavutamiseks ja kuidas me liigume selle tööriista loomise poole.

Esiteks tutvustame mõnda terminit:

  • VIP (Virtual IP) – tasakaalustaja IP-aadress
  • Server, taustaprogramm, eksemplar – rakendust töötav virtuaalmasin
  • RIP (Real IP) - serveri IP-aadress
  • Healthcheck – serveri valmisoleku kontrollimine
  • Kättesaadavustsoon, AZ – isoleeritud infrastruktuur andmekeskuses
  • Piirkond – erinevate AZ-de liit

Koormuse tasakaalustajad lahendavad kolm peamist ülesannet: nad teostavad ise tasakaalustamise, parandavad teenuse veataluvust ja lihtsustavad selle skaleerimist. Veataluvus on tagatud läbi automaatse liiklushalduse: tasakaaluliikur jälgib rakenduse olekut ja välistab tasakaalustamisest juhud, mis elujõulisuse kontrolli ei läbi. Skaleerimise tagab koormuse ühtlane jaotamine eksemplaride vahel, samuti eksemplaride loendi käigupealt uuendamine. Kui tasakaalustamine pole piisavalt ühtlane, saavad osad eksemplarid koormuse, mis ületab nende võimsuspiiri ja teenus muutub vähem töökindlaks.

Koormuse tasakaalustaja klassifitseeritakse sageli selle OSI mudeli protokollikihi järgi, millel see töötab. Cloud Balancer töötab TCP tasemel, mis vastab neljandale kihile L4.

Liigume edasi pilve tasakaalustaja arhitektuuri ülevaate juurde. Suurendame järk-järgult üksikasjalikkust. Jagame tasakaalustaja komponendid kolme klassi. Konfiguratsioonitasandi klass vastutab kasutaja suhtluse eest ja salvestab süsteemi sihtoleku. Juhttasand salvestab süsteemi hetkeoleku ja haldab süsteeme andmetasandi klassist, mis vastutavad otseselt liikluse edastamise eest klientidelt teie eksemplaridele.

Andmetasand

Liiklus jõuab kallitesse seadmetesse, mida nimetatakse piirimarsruuteriteks. Tõrgete taluvuse suurendamiseks töötab ühes andmekeskuses samaaegselt mitu sellist seadet. Järgmisena suunatakse liiklus tasakaalustajatele, mis teatavad klientidele BGP kaudu kõikidele AZ-dele anycasti IP-aadressid. 

Võrgukoormuse tasakaalustaja arhitektuur Yandex.Cloudis

Liiklus edastatakse üle ECMP - see on marsruutimisstrateegia, mille kohaselt võib sihtmärgini (meie puhul on sihtmärgiks sihtkoha IP-aadress) olla mitu võrdselt head marsruuti ja pakette saab saata ükskõik millist neist mööda. Toetame ka tööd mitmes saadavuse tsoonis järgmise skeemi järgi: igas tsoonis reklaamime aadressi, liiklus läheb lähimasse ega ületa selle piire. Hilisemas postituses vaatame lähemalt, mis liiklusega juhtub.

Konfiguratsioonitasand

 
Konfiguratsioonitasandi võtmekomponendiks on API, mille kaudu tehakse tasakaalustajatega põhitoiminguid: eksemplaride loomine, kustutamine, koostise muutmine, tervisekontrolli tulemuste saamine jne. Ühest küljest on see REST API ja teisest küljest muu, me pilves kasutame väga sageli raamistikku gRPC, seega "tõlgime" REST-i gRPC-ks ja kasutame seejärel ainult gRPC-d. Iga päring viib asünkroonsete idempotentide ülesannete seeria loomiseni, mida täidetakse Yandex.Cloudi töötajate ühises kogumis. Ülesanded on kirjutatud nii, et neid saab igal ajal peatada ja seejärel uuesti käivitada. See tagab skaleeritavuse, korratavuse ja toimingute logimise.

Võrgukoormuse tasakaalustaja arhitektuur Yandex.Cloudis

Selle tulemusel saadab API ülesanne tasakaalustusteenuse kontrollerile päringu, mis on kirjutatud Go-sse. See võib lisada ja eemaldada tasakaalustajaid, muuta taustaprogrammide ja seadete koostist. 

Võrgukoormuse tasakaalustaja arhitektuur Yandex.Cloudis

Teenus salvestab oma oleku Yandexi andmebaasis, hajutatud hallatavas andmebaasis, mida saate peagi kasutada. Yandex.Cloudis, nagu me juba rääkinud, kehtib koeratoidu kontseptsioon: kui meie ise oma teenuseid kasutame, siis kasutavad neid hea meelega ka meie kliendid. Sellise kontseptsiooni rakendamise näide on Yandexi andmebaas. Hoiame kõik oma andmed YDB-s ja me ei pea mõtlema andmebaasi hooldamisele ja skaleerimisele: need probleemid lahendatakse meie eest, kasutame andmebaasi teenusena.

Pöördume tagasi tasakaalustaja kontrolleri juurde. Selle ülesandeks on salvestada tasakaalustaja info ja saata tervisekontrolli kontrollerile ülesanne virtuaalmasina valmisoleku kontrollimiseks.

Tervisekontrolli kontroller

See võtab vastu taotlusi kontrollireeglite muutmiseks, salvestab need YDB-sse, jaotab ülesanded tervisekontrolli sõlmede vahel ja koondab tulemused, mis seejärel salvestatakse andmebaasi ja saadetakse koormusetasakaalu kontrollerile. See omakorda saadab loadbalancer-sõlmele andmetasandil oleva klastri koostise muutmise taotluse, mida käsitlen allpool.

Võrgukoormuse tasakaalustaja arhitektuur Yandex.Cloudis

Räägime lähemalt tervisekontrollist. Neid saab jagada mitmeks klassiks. Audititel on erinevad edukriteeriumid. TCP-kontrollid peavad kindla aja jooksul edukalt ühenduse looma. HTTP-kontrollid nõuavad nii edukat ühendust kui ka vastust olekukoodiga 200.

Samuti erinevad tšekid tegevusklassi poolest – need on aktiivsed ja passiivsed. Passiivsed kontrollid lihtsalt jälgivad liikluses toimuvat ilma erilisi meetmeid võtmata. L4 puhul see väga hästi ei tööta, sest see sõltub kõrgema taseme protokollide loogikast: L4 puhul pole infot, kui kaua toiming aega võttis või kas ühenduse lõpetamine oli hea või halb. Aktiivsed kontrollid nõuavad, et tasakaalustaja saadaks päringud igale serveri eksemplarile.

Enamik koormuse tasakaalustajaid kontrollib elujõudu ise. Cloudis otsustasime skaleeritavuse suurendamiseks süsteemi need osad eraldada. See lähenemisviis võimaldab meil suurendada tasakaalustajate arvu, säilitades samal ajal teenusele esitatavate tervisekontrolli taotluste arvu. Kontrolli teostavad eraldi tervisekontrolli sõlmed, mille kaudu kontrolli sihtmärgid killustatakse ja kopeeritakse. Te ei saa kontrollida ühest hostist, kuna see võib ebaõnnestuda. Siis me ei saa aru nende juhtumite seisu kohta, mida ta kontrollis. Kontrollime kõiki juhtumeid vähemalt kolmest tervisekontrolli sõlmest. Jagame sõlmedevahelise kontrolli eesmärgid, kasutades ühtseid räsimisalgoritme.

Võrgukoormuse tasakaalustaja arhitektuur Yandex.Cloudis

Tasakaalustamise ja tervisekontrolli eraldamine võib põhjustada probleeme. Kui tervisekontrolli sõlm esitab eksemplarile päringuid, minnes mööda tasakaalustajast (mis hetkel liiklust ei teeninda), siis tekib kummaline olukord: ressurss tundub olevat elus, aga liiklus selleni ei jõua. Me lahendame selle probleemi nii: tervisekontrolli liikluse algatamine on garanteeritud tasakaalustajate kaudu. Teisisõnu, klientide ja tervisekontrollide liiklusega pakettide teisaldamise skeem erineb minimaalselt: mõlemal juhul jõuavad paketid tasakaalustajateni, mis toimetavad need sihtressurssidesse.

Erinevus seisneb selles, et kliendid esitavad taotlusi VIP-ile, tervisekontrollid aga igale individuaalsele RIP-ile. Siin tekib huvitav probleem: anname oma kasutajatele võimaluse luua ressursse hallides IP võrkudes. Kujutagem ette, et on kaks erinevat pilveomanikku, kes on oma teenused tasakaalustajate taha peitnud. Igal neist on samade aadressidega alamvõrgus 10.0.0.1/24 ressursid. Peate suutma neid kuidagi eristada ja siin peate sukelduma Yandex.Cloud virtuaalse võrgu struktuuri. Parem on täpsemalt uurida siit video üritusest about:cloud, on meie jaoks praegu oluline, et võrk oleks mitmekihiline ja sellel oleks tunnelid, mida saab eristada alamvõrgu ID järgi.

Healthchecki sõlmed võtavad tasakaalustajatega ühendust, kasutades niinimetatud kvaasi-IPv6 aadresse. Kvaasiaadress on IPv6-aadress, mille sisse on manustatud IPv4-aadress ja kasutaja alamvõrgu ID. Liiklus jõuab tasakaalustajani, mis võtab sealt välja IPv4 ressursi aadressi, asendab IPv6 IPv4-ga ja saadab paketi kasutaja võrku.

Vastupidine liiklus käib samamoodi: tasakaalustaja näeb, et sihtkohaks on tervisekontrollijate hall võrk, ja teisendab IPv4 IPv6-ks.

VPP – andmetasandi süda

Tasakaalustaja on rakendatud VPP-tehnoloogia (Vector Packet Processing) abil, mis on Cisco raamistik võrguliikluse paketttöötluseks. Meie puhul töötab raamistik kasutajaruumi võrgu seadmehaldusteegi peal - Data Plane Development Kit (DPDK). See tagab kõrge pakettide töötlemise jõudluse: kernelis esineb palju vähem katkestusi ning tuumaruumi ja kasutajaruumi vahel ei toimu kontekstivahetusi. 

VPP läheb veelgi kaugemale ja pigistab süsteemist veelgi suurema jõudluse, ühendades paketid partiideks. Jõudluse kasv tuleneb vahemälu agressiivsest kasutamisest kaasaegsetes protsessorites. Kasutusel on nii andmevahemälu (pakette töödeldakse “vektorites”, andmed on üksteise lähedal) kui ka käskude vahemälu: VPP-s toimub pakettide töötlemine graafiku järgi, mille sõlmedes on sama ülesannet täitvad funktsioonid.

Näiteks IP-pakettide töötlemine VPP-s toimub järgmises järjekorras: esmalt sõelutakse parsimissõlmes pakettide päised ja seejärel saadetakse need sõlme, mis saadab paketid edasi vastavalt marsruutimistabelitele.

Natuke hardcore. VPP autorid ei salli järeleandmisi protsessori vahemälu kasutamisel, seetõttu sisaldab tüüpiline pakettide vektori töötlemise kood käsitsi vektoriseerimist: on töötlustsükkel, milles töödeldakse olukorda nagu "meil on järjekorras neli paketti", siis sama kahele, siis - ühele. Eellaadimisjuhiseid kasutatakse sageli andmete laadimiseks vahemällu, et kiirendada neile juurdepääsu järgmistel iteratsioonidel.

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);
}

Niisiis, Healthchecks suhtleb IPv6 kaudu VPP-ga, mis muudab need IPv4-ks. Seda teeb graafiku sõlm, mida me nimetame algoritmiliseks NAT-iks. Pöördliikluse jaoks (ja IPv6-lt IPv4-ks teisendamiseks) on sama algoritmiline NAT-sõlm.

Võrgukoormuse tasakaalustaja arhitektuur Yandex.Cloudis

Tasakaalustaja klientide otseliiklus läheb läbi graafiku sõlmede, mis teostavad tasakaalustamise ise. 

Võrgukoormuse tasakaalustaja arhitektuur Yandex.Cloudis

Esimene sõlm on kleepuvad seansid. See salvestab räsi 5-kordne kehtestatud seansside jaoks. 5-korter sisaldab kliendi aadressi ja porti, kust teavet edastatakse, liikluse vastuvõtmiseks saadaolevate ressursside aadressi ja porte, samuti võrguprotokolli. 

5-kordse räsi abil saame järgnevas järjekindlas räsisõlmes vähem arvutusi teha ja paremini hallata tasakaalustaja taga toimuvaid ressursside loendi muudatusi. Kui tasakaalustajasse saabub pakett, mille jaoks seanssi pole, saadetakse see järjekindlale räsisõlmele. Siin toimub tasakaalustamine järjepideva räsimise abil: valime ressursi saadaolevate "reaalajas" ressursside loendist. Järgmisena saadetakse paketid NAT-sõlme, mis tegelikult asendab sihtkoha aadressi ja arvutab kontrollsummad ümber. Nagu näete, järgime VPP reegleid - meeldib meeldida, rühmitades sarnased arvutused protsessori vahemälude tõhususe suurendamiseks.

Järjepidev räsimine

Miks me selle valisime ja mis see üldse on? Kõigepealt kaalume eelmist ülesannet – ressursi valimist loendist. 

Võrgukoormuse tasakaalustaja arhitektuur Yandex.Cloudis

Ebajärjekindla räsi korral arvutatakse sissetuleva paketi räsi ja loendist valitakse ressurss, mille jääk jagatakse räsi ressursside arvuga. Kuni loendit ei muudeta, töötab see skeem hästi: saadame sama 5-korruse paketid alati samale eksemplarile. Kui näiteks mõni ressurss lõpetas tervisekontrollidele reageerimise, siis olulise osa räsidest valik muutub. Kliendi TCP-ühendused katkevad: varem eksemplari A jõudnud pakett võib hakata jõudma eksemplari B, mis ei ole selle paketi seansiga tuttav.

Järjepidev räsimine lahendab kirjeldatud probleemi. Lihtsaim viis selle kontseptsiooni selgitamiseks on järgmine: kujutage ette, et teil on ring, millele jagate ressursse räsi järgi (näiteks IP:porti). Ressursi valimine on ratta pööramine nurga võrra, mille määrab paketi räsi.

Võrgukoormuse tasakaalustaja arhitektuur Yandex.Cloudis

See minimeerib liikluse ümberjaotamist, kui ressursside koostis muutub. Ressursi kustutamine mõjutab ainult seda osa järjepidevast räsiringist, milles ressurss asus. Ressursi lisamine muudab ka jaotust, kuid meil on seansside sõlm, mis võimaldab meil juba loodud seansse uutele ressurssidele mitte lülitada.

Vaatasime, mis juhtub liikluse suunamisega tasakaalustaja ja ressursside vahel. Vaatame nüüd tagasiliiklust. See järgib sama mustrit nagu liikluse kontrollimine – läbi algoritmilise NAT-i, st läbi vastupidise NAT 44 kliendiliikluse jaoks ja läbi NAT 46 tervisekontrolli liikluse puhul. Peame kinni oma skeemist: ühendame tervisekontrolli liikluse ja tegeliku kasutajaliikluse.

Loadbalancer-sõlm ja kokkupandud komponendid

VPP tasakaalustajate ja ressursside koostist teatab kohalik teenus - loadbalancer-node. See tellib sündmuste voo loadbalancer-kontrollerilt ja suudab joonistada erinevuse praeguse VPP oleku ja kontrollerilt saadud sihtoleku vahel. Saame suletud süsteemi: API-st tulevad sündmused tasakaalustuskontrollerisse, mis määrab tervisekontrolli kontrollerile ülesanded ressursside “elavuse” kontrollimiseks. See omakorda määrab tervisekontrolli sõlmele ülesanded ja koondab tulemused, misjärel saadab need tagasi tasakaalustaja kontrollerile. Loadbalancer-sõlm tellib kontrolleri sündmusi ja muudab VPP olekut. Sellises süsteemis teab iga teenus naaberteenuste kohta ainult seda, mis on vajalik. Ühenduste arv on piiratud ja meil on võimalus erinevaid segmente iseseisvalt opereerida ja skaleerida.

Võrgukoormuse tasakaalustaja arhitektuur Yandex.Cloudis

Milliseid probleeme välditi?

Kõik meie teenused juhttasandil on kirjutatud Go ja neil on head skaleerimis- ja töökindlusomadused. Go-l on hajutatud süsteemide loomiseks palju avatud lähtekoodiga teeke. Kasutame aktiivselt GRPC-d, kõik komponendid sisaldavad teenusetuvastuse avatud lähtekoodiga rakendust – meie teenused jälgivad üksteise jõudlust, saavad oma koostist dünaamiliselt muuta ja sidusime selle GRPC tasakaalustamisega. Mõõdikute jaoks kasutame ka avatud lähtekoodiga lahendust. Andmetasandil saime korraliku jõudluse ja suure ressursireservi: väga keeruliseks osutus statiivi kokkupanek, mille peale saaks loota pigem VPP kui raudse võrgukaardi jõudlusele.

Probleemid ja lahendused

Mis ei töötanud nii hästi? Go-l on automaatne mäluhaldus, kuid mälulekkeid juhtub ikka. Lihtsaim viis nendega toimetulemiseks on käivitada gorutiinid ja meeles pidada, et need tuleb lõpetada. Takeaway: jälgige oma Go programmide mälutarbimist. Sageli on heaks näitajaks gorutiinide arv. Sellel lool on pluss: Go's on lihtne hankida käitusaegseid andmeid - mälukulu, jooksvate gorutiinide arv ja palju muid parameetreid.

Samuti ei pruugi Go funktsionaalsete testide jaoks parim valik. Need on üsna paljusõnalised ja standardne lähenemine "käivitage kõik CI-s partiina" neile eriti ei sobi. Fakt on see, et funktsionaalsed testid on ressursinõudlikumad ja põhjustavad reaalseid aegumistähtajaid. Seetõttu võivad testid ebaõnnestuda, kuna protsessor on seadmetestidega hõivatud. Järeldus: võimalusel tehke "rasked" testid ühikutestidest eraldi. 

Mikroteenuse sündmuste arhitektuur on keerulisem kui monoliit: logide kogumine kümnetel erinevatel masinatel pole eriti mugav. Järeldus: kui teete mikroteenuseid, mõelge kohe jälgimisele.

Meie plaanid

Käivitame sisemise tasakaalustaja, IPv6 tasakaalustaja, lisame Kubernetese skriptide toe, jätkame oma teenuste killustamist (praegu on killustatud ainult Healthcheck-node ja Healthcheck-ctrl), lisame uusi tervisekontrolle ja rakendame ka kontrollide nutikat koondamist. Kaalume võimalust muuta oma teenused veelgi sõltumatumaks – et nad ei suhtleks omavahel otse, vaid sõnumijärjekorda kasutades. Hiljuti ilmus pilves SQS-iga ühilduv teenus Yandexi sõnumite järjekord.

Hiljuti toimus Yandex Load Balanceri avalik väljalase. Uurige dokumentatsioon teenusele, hallata tasakaalustajaid endale sobival viisil ja tõsta oma projektide veataluvust!

Allikas: www.habr.com

Lisa kommentaar