Egyfelhős – adatközponti szintű operációs rendszer az Odnoklassnikiben

Egyfelhős – adatközponti szintű operációs rendszer az Odnoklassnikiben

Aloha, emberek! A nevem Oleg Anastasyev, az Odnoklassnikinél dolgozom a Platform csapatában. És rajtam kívül nagyon sok hardver dolgozik az Odnoklassnikiben. Négy adatközpontunk van, mintegy 500 rackkel, több mint 8 ezer szerverrel. Egy bizonyos ponton felismertük, hogy egy új irányítási rendszer bevezetése lehetővé teszi a berendezések hatékonyabb terhelését, megkönnyíti a hozzáférés-kezelést, automatizálja a számítási erőforrások (újra)elosztását, felgyorsítja az új szolgáltatások elindítását, és felgyorsítja a válaszadást. nagyszabású balesetekre.

mi lett belőle?

Rajtam és egy csomó hardveren kívül még emberek dolgoznak ezzel a hardverrel: mérnökök, akik közvetlenül az adatközpontokban dolgoznak; Hálózati szoftvereket beállító hálózatépítők; rendszergazdák vagy SRE-k, akik az infrastruktúra rugalmasságát biztosítják; és fejlesztői csapatok, mindegyikük felelős a portál funkcióinak egy részéért. Az általuk készített szoftver valahogy így működik:

Egyfelhős – adatközponti szintű operációs rendszer az Odnoklassnikiben

A felhasználói kérések a fő portál mindkét oldalán érkeznek www.ok.ru, és másokon, például a zenei API frontokon. Az üzleti logika feldolgozásához meghívják az alkalmazáskiszolgálót, amely a kérés feldolgozásakor meghívja a szükséges speciális mikroszolgáltatásokat - egygrafikus (a közösségi kapcsolatok grafikonja), felhasználói gyorsítótár (felhasználói profilok gyorsítótára) stb.

Ezen szolgáltatások mindegyike számos gépen van telepítve, és mindegyikhez tartozik felelős fejlesztő, aki a modulok működéséért, működéséért és technológiai fejlesztéséért felelős. Mindezek a szolgáltatások hardveres szervereken futnak, és egészen a közelmúltig szerverenként pontosan egy feladatot indítottunk el, azaz egy adott feladatra volt specializálva.

Miert van az? Ennek a megközelítésnek számos előnye volt:

  • Megkönnyebbülten tömeggazdálkodás. Tegyük fel, hogy egy feladathoz bizonyos könyvtárak, beállítások szükségesek. Ezután a kiszolgáló pontosan egy adott csoporthoz van hozzárendelve, le van írva (vagy már leírtuk) a csoport cfengine házirendje, és ez a konfiguráció központilag és automatikusan megjelenik a csoport összes kiszolgálóján.
  • Egyszerűsített diagnosztika. Tegyük fel, hogy megnézi a központi processzor megnövekedett terhelését, és rájön, hogy ezt a terhelést csak az ezen a hardveres processzoron futó feladat generálhatja. A hibáztató keresése nagyon gyorsan véget ér.
  • Egyszerűsített megfigyelés. Ha valami nem stimmel a szerverrel, a monitor jelzi, és pontosan tudja, ki a hibás.

Egy több replikából álló szolgáltatáshoz több szerver is hozzá van rendelve – mindegyikhez egy. Ezután a szolgáltatás számítási erőforrását nagyon egyszerűen allokálják: hány szerverrel rendelkezik a szolgáltatás, mennyi erőforrást fogyaszthat. Az „egyszerű” itt nem azt jelenti, hogy könnyen használható, hanem abban az értelemben, hogy az erőforrások elosztása manuálisan történik.

Ez a megközelítés lehetővé tette számunkra is speciális vas konfigurációk ezen a szerveren futó feladathoz. Ha a feladat nagy mennyiségű adatot tárol, akkor 4U-s szervert használunk 38 lemezes házzal. Ha a feladat tisztán számítási jellegű, akkor vehetünk olcsóbb 1U-s szervert. Ez számítási szempontból hatékony. Többek között ez a megközelítés lehetővé teszi, hogy négyszer kevesebb gépet használjunk egyetlen barátságos közösségi hálózathoz hasonló terhelés mellett.

A számítási erőforrások ilyen hatékonyságának a gazdasági hatékonyságot is biztosítania kell, ha abból indulunk ki, hogy a szerverek a legdrágábbak. Sokáig a hardver volt a legdrágább, és nagy erőfeszítéseket tettünk a hardver árának csökkentésére, hibatűrő algoritmusokkal dolgoztunk ki a hardver megbízhatósági követelményeinek csökkentésére. És ma elérkeztünk ahhoz a szakaszhoz, amikor a szerver ára már nem meghatározó. Ha nem veszi figyelembe a legújabb egzotikumokat, akkor a rack-ben lévő szerverek konkrét konfigurációja nem számít. Most egy másik problémánk van: az adatközpontban lévő szerver által elfoglalt hely ára, vagyis a rackben lévő hely ára.

Felismertük, hogy ez a helyzet, úgy döntöttünk, hogy kiszámítjuk, milyen hatékonyan használjuk az állványokat.
A legerősebb szerver árát a gazdaságilag indokolhatóak közül vettük, kiszámoltuk, hogy hány ilyen szervert tudunk rackbe helyezni, hány feladatot fogunk futtatni rajtuk a régi „egy szerver = egy feladat” modell alapján és mennyi ilyen. feladatok hasznosíthatták a berendezést. Számoltak és könnyeket hullattak. Kiderült, hogy az állványok használatának hatékonysága körülbelül 11%. A következtetés nyilvánvaló: növelnünk kell az adatközpontok használatának hatékonyságát. Úgy tűnik, hogy a megoldás kézenfekvő: egyszerre több feladatot kell futtatnia egy szerveren. De itt kezdődnek a nehézségek.

A tömeges konfiguráció drámaian bonyolultabbá válik – ma már lehetetlen egyetlen csoportot sem hozzárendelni egy szerverhez. Hiszen ma már több, különböző parancsból álló feladat is elindítható egy szerveren. Ezenkívül a konfiguráció ütközhet különböző alkalmazások esetén. A diagnosztika is bonyolultabbá válik: ha megnövekedett CPU- vagy lemezfogyasztást tapasztal egy szerveren, nem tudja, melyik feladat okoz problémát.

De a lényeg az, hogy nincs elszigeteltség az ugyanazon a gépen futó feladatok között. Itt van például egy grafikon, amely egy szerverfeladat átlagos válaszidejét mutatja, mielőtt és miután egy másik számítási alkalmazás elindult ugyanazon a szerveren, semmiképpen sem kapcsolódik az elsőhöz - a fő feladat válaszideje jelentősen megnőtt.

Egyfelhős – adatközponti szintű operációs rendszer az Odnoklassnikiben

Nyilvánvalóan a feladatokat konténerekben vagy virtuális gépeken kell futtatnia. Mivel szinte minden feladatunk egy operációs rendszer (Linux) alatt fut, vagy arra van adaptálva, nem kell sok különböző operációs rendszert támogatnunk. Ennek megfelelően virtualizációra nincs szükség, a többletköltség miatt ez kevésbé lesz hatékony, mint a konténerezés.

A feladatokat közvetlenül a szervereken futtató konténerek megvalósításaként a Docker jó jelölt: a fájlrendszerképek jól megoldják az ütköző konfigurációk problémáit. Az a tény, hogy a képek több rétegből is összeállíthatók, lehetővé teszi, hogy jelentősen csökkentsük az infrastruktúrán való elhelyezésükhöz szükséges adatmennyiséget, a közös részeket külön alaprétegekre bontva. Ekkor az alap (és a legterjedelmesebb) rétegek gyorsítótárazásra kerülnek a teljes infrastruktúrában, és sok különböző típusú alkalmazás és verzió szállításához csak kis rétegeket kell átvinni.

Ráadásul a Dockerben található kész regisztrációs adatbázis és képcímkék kész primitíveket adnak nekünk a verziószámításhoz és a kód termelésbe való eljuttatásához.

A Docker, mint bármely más hasonló technológia, bizonyos szintű konténerszigetelést biztosít számunkra a dobozból. Például a memória elkülönítése – minden tárolónak van egy korlátja a gépmemória használatára, amelyen túl nem fogyaszt. A tárolókat a CPU-használat alapján is elkülönítheti. Számunkra azonban a szabványos szigetelés nem volt elég. De erről lentebb bővebben.

A konténerek szervereken való közvetlen futtatása csak egy része a problémának. A másik rész a konténerek szervereken való elhelyezésével kapcsolatos. Meg kell értenie, hogy melyik tároló melyik szerveren helyezhető el. Ez nem olyan egyszerű feladat, mert a konténereket a lehető legsűrűbben kell elhelyezni a szervereken anélkül, hogy a sebességük csökkenne. Az ilyen elhelyezés hibatűrési szempontból is nehézkes lehet. Gyakran szeretnénk ugyanannak a szolgáltatásnak a replikáit különböző rackekben, vagy akár az adatközpont különböző helyiségeiben elhelyezni, hogy ha egy rack vagy helyiség meghibásodik, ne veszítsük el azonnal az összes szolgáltatási replikát.

A konténerek kézi szétosztása nem választható, ha 8 ezer szerverünk és 8-16 ezer konténerünk van.

Ezen túlmenően nagyobb függetlenséget akartunk biztosítani a fejlesztőknek az erőforrások elosztásában, hogy saját maguk, adminisztrátor segítsége nélkül üzemeltethessék szolgáltatásaikat termelésben. Ugyanakkor meg akartuk őrizni az irányítást, hogy egy-egy kisebb szolgáltatás ne emésztse fel adatközpontjaink összes erőforrását.

Nyilvánvalóan szükségünk van egy vezérlőrétegre, amely ezt automatikusan megteszi.

Így egy egyszerű és érthető képhez jutottunk, amelyet minden építész imád: három négyzet.

Egyfelhős – adatközponti szintű operációs rendszer az Odnoklassnikiben

Az one-cloud masters egy feladatátvételi fürt, amely a felhő hangszereléséért felelős. A fejlesztő jegyzéket küld a mesternek, amely tartalmazza a szolgáltatás hosztolásához szükséges összes információt. Ez alapján a mester parancsokat ad a kiválasztott minionoknak (konténerek futtatására tervezett gépeknek). A csatlósoknak van ügynökünk, amely megkapja a parancsot, kiadja parancsait a Dockernek, és a Docker beállítja a linux kernelt, hogy elindítsa a megfelelő tárolót. Az ügynök a parancsok végrehajtása mellett folyamatosan jelentést tesz a masternek mind a minion gép, mind a rajta futó konténerek állapotában bekövetkezett változásokról.

Forráselosztás

Most nézzük meg a bonyolultabb erőforrás-allokáció problémáját sok csatlós számára.

Egy számítási erőforrás egy felhőben:

  • Egy adott feladat által felhasznált processzorteljesítmény mennyisége.
  • A feladathoz rendelkezésre álló memória mennyisége.
  • Hálózati forgalom. A minionok mindegyike rendelkezik saját, korlátozott sávszélességű hálózati interfésszel, így lehetetlen a feladatok elosztása anélkül, hogy figyelembe vennénk a hálózaton továbbított adatmennyiséget.
  • Lemezek. Ezen túlmenően természetesen az ezekhez a feladatokhoz szükséges helyhez hozzárendeljük a lemez típusát is: HDD vagy SSD. A lemezek másodpercenként véges számú kérést tudnak kiszolgálni – IOPS. Ezért azokhoz a feladatokhoz, amelyek több IOPS-t generálnak, mint amennyit egyetlen lemez képes kezelni, „orsókat” is kijelölünk - vagyis olyan lemezeszközöket, amelyeket kizárólag a feladathoz kell lefoglalni.

Aztán egyes szolgáltatásoknál, például felhasználói gyorsítótárnál így rögzíthetjük az elfogyasztott erőforrásokat: 400 processzormag, 2,5 TB memória, 50 Gbit/s forgalom mindkét irányban, 6 TB HDD-terület 100 orsón elhelyezve. Vagy egy ismertebb formában, mint ez:

alloc:
    cpu: 400
    mem: 2500
    lan_in: 50g
    lan_out: 50g
    hdd:100x6T

A felhasználói gyorsítótár szolgáltatás erőforrásai az éles infrastruktúra összes elérhető erőforrásának csak egy részét fogyasztják. Ezért szeretném megbizonyosodni arról, hogy hirtelen, operátori hiba miatt vagy sem, a felhasználói gyorsítótár nem fogyaszt több erőforrást, mint amennyit a számára lefoglaltak. Vagyis korlátoznunk kell az erőforrásokat. De mihez köthetnénk a kvótát?

Térjünk vissza a komponensek kölcsönhatásának nagymértékben leegyszerűsített diagramjához, és rajzoljuk át további részletekkel – így:

Egyfelhős – adatközponti szintű operációs rendszer az Odnoklassnikiben

Ami megragadja a szemet:

  • A webes előtér és a zene ugyanazon alkalmazáskiszolgáló elszigetelt fürtjeit használja.
  • Megkülönböztethetjük azokat a logikai rétegeket, amelyekhez ezek a klaszterek tartoznak: frontok, gyorsítótárak, adattároló és kezelési réteg.
  • A frontend heterogén, különböző funkcionális alrendszerekből áll.
  • A gyorsítótárak szétszórva helyezkedhetnek el azon az alrendszeren, amelynek adatait gyorsítótárazzák.

Rajzoljuk újra a képet:

Egyfelhős – adatközponti szintű operációs rendszer az Odnoklassnikiben

Bah! Igen, hierarchiát látunk! Ez azt jelenti, hogy az erőforrásokat nagyobb darabokban is eloszthatja: rendeljen felelős fejlesztőt ennek a hierarchiának a funkcionális alrendszernek megfelelő csomópontjához (mint a képen a „zene”), és rendeljen hozzá kvótát a hierarchia ugyanazon szintjéhez. Ez a hierarchia lehetővé teszi a szolgáltatások rugalmasabb megszervezését is az egyszerű kezelés érdekében. Például felosztjuk az egész webet, mivel ez egy nagyon nagy szervercsoport, több kisebb csoportra osztjuk, amelyek a képen csoport1, csoport2 néven láthatók.

A felesleges sorok eltávolításával képünk minden csomópontját laposabb formában írhatjuk fel: csoport1.web.front, api.zene.front, user-cache.cache.

Így jutunk el a „hierarchikus sor” fogalmához. Olyan neve van, mint "csoport1.web.front". Az erőforrásokra és a felhasználói jogokra vonatkozó kvóta hozzá van rendelve. A DevOps személyének megadjuk a jogot, hogy szolgáltatást küldjön a sorba, és egy ilyen alkalmazott elindíthat valamit a sorban, és az OpsDev személyének rendszergazdai jogai lesznek, és most már kezelheti a sort, hozzárendelheti az embereket, jogokat adjon ezeknek az embereknek stb. Az ezen a sorban futó szolgáltatások a sor kvótáján belül futnak. Ha a sor számítási kvótája nem elegendő az összes szolgáltatás egyszerre történő végrehajtásához, akkor azok egymás után kerülnek végrehajtásra, így maga a sor jön létre.

Nézzük meg közelebbről a szolgáltatásokat. A szolgáltatásnak teljesen minősített neve van, amely mindig tartalmazza a sor nevét. Ekkor az elülső webszolgáltatás neve lesz ok-web.group1.web.front. És az általa elért alkalmazáskiszolgáló szolgáltatás meg lesz hívva ok-app.group1.web.front. Minden szolgáltatáshoz tartozik egy manifest, amely megadja az összes szükséges információt az adott gépeken történő elhelyezéshez: mennyi erőforrást fogyaszt ez a feladat, milyen konfiguráció szükséges hozzá, hány replikának kell lennie, a szolgáltatás meghibásodásának kezelésére szolgáló tulajdonságokat. És miután a szolgáltatás közvetlenül a gépekre kerül, megjelennek a példányai. Elnevezésük is egyértelműen – példányszámként és szolgáltatásnévként: 1.ok-web.group1.web.front, 2.ok-web.group1.web.front, …

Ez nagyon kényelmes: ha csak a futó konténer nevét nézzük, azonnal sok mindent megtudhatunk.

Most pedig nézzük meg közelebbről, mit is hajtanak végre ezek a példányok: feladatokat.

Feladat Izolációs osztályok

Az OK-ban lévő összes feladat (és valószínűleg mindenhol) csoportokra osztható:

  • Rövid késleltetésű feladatok – prod. Az ilyen feladatoknál, szolgáltatásoknál nagyon fontos a válaszkésleltetés (latencia), vagyis, hogy az egyes kéréseket milyen gyorsan dolgozza fel a rendszer. Példák a feladatokra: webes frontok, gyorsítótárak, alkalmazásszerverek, OLTP-tárhely stb.
  • Számítási problémák - köteg. Itt nem fontos az egyes kérések feldolgozási sebessége. Számukra fontos, hogy ez a feladat egy bizonyos (hosszú) idő (áteresztőképesség) alatt hány számítást végez el. Ezek a MapReduce, a Hadoop, a gépi tanulás, a statisztikák tetszőleges feladatai lesznek.
  • Háttérfeladatok – tétlen. Az ilyen feladatoknál sem a késleltetés, sem az átviteli sebesség nem nagyon fontos. Ez magában foglalja a különféle teszteket, áttelepítéseket, újraszámításokat és az adatok egyik formátumból a másikba való konvertálását. Egyrészt hasonlítanak a kiszámítottakhoz, másrészt számunkra teljesen mindegy, hogy milyen gyorsan készülnek el.

Nézzük meg, hogyan fogyasztják az ilyen feladatok erőforrásokat, például a központi processzort.

Rövid késleltetésű feladatok. Egy ilyen feladat CPU-fogyasztási mintája hasonló lesz:

Egyfelhős – adatközponti szintű operációs rendszer az Odnoklassnikiben

A felhasználótól kérés érkezik feldolgozásra, a feladat elkezdi használni az összes rendelkezésre álló CPU magot, feldolgozza, választ ad, megvárja a következő kérést és leáll. Megérkezett a következő kérés - ismét kiválasztottunk mindent, ami volt, kiszámoltuk, és várjuk a következőt.

Egy ilyen feladat minimális késleltetésének garantálásához fel kell vennünk az általa fogyasztott maximális erőforrásokat, és le kell foglalnunk a szükséges számú magot a minionon (a feladatot végrehajtó gépen). Akkor a problémánk foglalási képlete a következő lesz:

alloc: cpu = 4 (max)

és ha van egy minion gépünk 16 maggal, akkor pontosan négy ilyen feladatot lehet rá rakni. Külön megjegyezzük, hogy az ilyen feladatok átlagos processzorfogyasztása gyakran nagyon alacsony - ami nyilvánvaló, hiszen az idő jelentős részében a feladat vár egy kérésre, és nem csinál semmit.

Számítási feladatok. A mintájuk kissé eltérő lesz:

Egyfelhős – adatközponti szintű operációs rendszer az Odnoklassnikiben

Az ilyen feladatok átlagos CPU-erőforrás-fogyasztása meglehetősen magas. Gyakran azt szeretnénk, hogy egy számítási feladat egy bizonyos idő alatt elkészüljön, ezért le kell foglalnunk a szükséges minimális számú processzort, hogy a teljes számítás elfogadható időn belül elkészüljön. A foglalási képlet így fog kinézni:

alloc: cpu = [1,*)

"Kérlek, helyezd egy minionra, ahol van legalább egy szabad mag, és ahányan vannak, mindent felemészt."

Itt a felhasználás hatékonysága már sokkal jobb, mint a kis késéssel végzett feladatoknál. De a haszon sokkal nagyobb lesz, ha mindkét típusú feladatot egy minion gépen kombinálja, és menet közben osztja el erőforrásait. Amikor egy kis késleltetésű feladathoz processzorra van szükség, azt azonnal megkapja, és amikor már nincs szükség az erőforrásokra, átkerül a számítási feladatba, azaz valami ilyesmi:

Egyfelhős – adatközponti szintű operációs rendszer az Odnoklassnikiben

De hogyan lehet ezt megtenni?

Először nézzük meg a prod-ot és annak alloc-ját: cpu = 4. Négy magot kell lefoglalnunk. A Docker futtatásában ezt kétféleképpen lehet megtenni:

  • Az opció használatával --cpuset=1-4, azaz négy meghatározott magot rendeljen a gépen a feladathoz.
  • Használat --cpuquota=400_000 --cpuperiod=100_000, rendeljen hozzá kvótát a processzoridőhöz, azaz jelezze, hogy minden 100 ms valós időben a feladat legfeljebb 400 ms processzoridőt vesz igénybe. Ugyanazt a négy magot kapjuk.

De ezek közül a módszerek közül melyik a megfelelő?

A cpuset elég vonzónak tűnik. A feladatnak négy dedikált magja van, ami azt jelenti, hogy a processzor gyorsítótárai a lehető leghatékonyabban fognak működni. Ennek van egy árnyoldala is: vállalnunk kellene azt a feladatot, hogy az operációs rendszer helyett a számításokat a gép betöltetlen magjai között osszuk el, és ez meglehetősen nem triviális feladat, főleg, ha kötegelt feladatokat próbálunk elhelyezni egy ilyen gépen. gép. A tesztek azt mutatták, hogy a kvótával való opció itt jobban megfelel: így az operációs rendszer nagyobb szabadságot kap az adott pillanatban a feladat végrehajtásához szükséges mag kiválasztásában, és hatékonyabban oszlik el a processzoridő.

Találjuk ki, hogyan foglalhatunk a Dockerben a minimális magszám alapján. A kötegelt feladatok kvótája már nem érvényes, mert nem kell a maximumot korlátozni, elég csak a minimumot garantálni. És itt a lehetőség jól illeszkedik docker run --cpushares.

Megállapodtunk, hogy ha egy tétel legalább egy magra garanciát igényel, akkor jelezzük --cpushares=1024, és ha legalább két mag van, akkor jelezzük --cpushares=2048. A CPU-megosztások semmilyen módon nem zavarják a processzoridő elosztását, amíg van belőle elég. Így, ha a prod jelenleg nem használja mind a négy magját, semmi sem korlátozza a kötegelt feladatokat, és további processzoridőt is igénybe vehet. De egy processzorhiány esetén, ha a prod mind a négy magját elfogyasztotta, és elérte a kvótát, akkor a maradék processzoridő arányosan megoszlik a cpushares-el, azaz három szabad mag esetén egy 1024 cpushares feladatra, a maradék kettő pedig 2048 cpushares feladatra kerül.

De a kvóta és a részvények használata nem elég. Gondoskodnunk kell arról, hogy a rövid késleltetésű feladat prioritást élvezzen a kötegelt feladattal szemben a processzoridő kiosztásánál. Ilyen prioritás nélkül a kötegelt feladat a processzor teljes idejét lefoglalja abban a pillanatban, amikor a prod-nak szüksége van rá. A Docker futtatásakor nincsenek tárolók prioritási beállításai, de a Linux CPU ütemező házirendjei hasznosak. Részletesen olvashat róluk itt, és e cikk keretein belül röviden áttekintjük őket:

  • SCHED_OTHER
    Alapértelmezés szerint egy Linux gépen minden normál felhasználói folyamat fogad.
  • SCHED_BATCH
    Erőforrás-igényes folyamatokhoz tervezték. Feladat processzorra helyezésekor úgynevezett aktiválási büntetés kerül bevezetésre: egy ilyen feladat kisebb valószínűséggel kap processzor erőforrásokat, ha éppen egy feladat használja a SCHED_OTHER
  • SCHED_IDLE
    Nagyon alacsony prioritású háttérfolyamat, még a szép -19-nél is alacsonyabb. Nyílt forráskódú könyvtárunkat használjuk one-nio, hogy beállíthassa a szükséges házirendet a tároló indításakor hívással

one.nio.os.Proc.sched_setscheduler( pid, Proc.SCHED_IDLE )

De még ha nem is Java nyelven programoz, ugyanezt megteheti a chrt paranccsal:

chrt -i 0 $pid

Foglaljuk össze az összes elkülönítési szintünket egy táblázatban az egyértelműség kedvéért:

Szigetelési osztály
Alloc példa
Docker futtatási lehetőségek
sched_setscheduler chrt*

Döf
cpu = 4
--cpuquota=400000 --cpuperiod=100000
SCHED_OTHER

Batch
Cpu = [1, *)
--cpushares=1024
SCHED_BATCH

Idle
Cpu= [2, *)
--cpushares=2048
SCHED_IDLE

*Ha a chrt-t egy tároló belsejéből végzi, szükség lehet a sys_nice képességre, mert a Docker alapértelmezés szerint eltávolítja ezt a képességet a tároló indításakor.

A feladatok azonban nem csak a processzort fogyasztják, hanem a forgalmat is, ami még jobban befolyásolja a hálózati feladatok késését, mint a processzorerőforrások helytelen kiosztása. Ezért természetesen pontosan ugyanazt a képet szeretnénk kapni a forgalomról. Ez azt jelenti, hogy amikor egy prod feladat néhány csomagot küld a hálózatnak, korlátozzuk a maximális sebességet (formula kiosztás: lan=[*,500mbps) ), amellyel a prod ezt megteheti. Tételenként pedig csak a minimális áteresztőképességet garantáljuk, de nem korlátozzuk a maximumot (képlet kiosztás: lan=[10Mbps,*) ) Ebben az esetben a termékforgalomnak elsőbbséget kell élveznie a kötegelt feladatokkal szemben.
Itt a Dockernek nincs semmiféle primitíve, amit használhatnánk. De a segítségünkre van Linux forgalomirányítás. A kívánt eredményt a fegyelem segítségével tudtuk elérni Hierarchikus méltányos szolgáltatási görbe. Segítségével a forgalom két osztályát különböztetjük meg: magas prioritású prod és alacsony prioritású batch/idle. Ennek eredményeként a kimenő forgalom konfigurációja a következő:

Egyfelhős – adatközponti szintű operációs rendszer az Odnoklassnikiben

itt 1:0 a hsfc diszciplína „gyökér qdiscje”; 1:1 - hsfc gyermekosztály 8 Gbit/s teljes sávszélesség-korláttal, amely alá az összes konténer gyermekosztálya kerül; 1:2 - a hsfc gyermekosztály közös minden kötegelt és tétlen feladatnál, „dinamikus” korláttal, amelyet alább tárgyalunk. A fennmaradó hsfc utódosztályok dedikált osztályok a jelenleg futó prod-tárolókhoz, a jegyzéküknek megfelelő korlátokkal - 450 és 400 Mbit/s. Minden hsfc osztályhoz hozzá van rendelve egy fq vagy fq_code qdisc queue, a Linux kernel verziójától függően, hogy elkerülje a forgalom törések során bekövetkező csomagvesztést.

A tc szakterületek jellemzően csak a kimenő forgalom prioritásait szolgálják. De szeretnénk a bejövő forgalmat is priorizálni – elvégre néhány kötegelt feladat könnyen kiválaszthatja a teljes bejövő csatornát, például nagy köteg bemeneti adatot fogadva a map&reduce-hoz. Ehhez a modult használjuk ifb, amely minden hálózati interfészhez létrehoz egy ifbX virtuális interfészt, és átirányítja a bejövő forgalmat az interfészről az ifbX kimenő forgalmára. Továbbá az ifbX esetében ugyanazok a diszciplínák működnek a kimenő forgalom szabályozásában, amelyhez a hsfc konfiguráció nagyon hasonló lesz:

Egyfelhős – adatközponti szintű operációs rendszer az Odnoklassnikiben

A kísérletek során rájöttünk, hogy a hsfc akkor mutatja a legjobb eredményeket, ha a nem prioritást élvező kötegelt/tétlen forgalom 1:2 osztálya a minion gépeken legfeljebb egy bizonyos szabad sávra korlátozódik. Ellenkező esetben a nem prioritású forgalom túlságosan nagy hatással van a termelési feladatok várakozási idejére. A miniond másodpercenként meghatározza a szabad sávszélesség aktuális mennyiségét, mérve egy adott minion összes prod-feladatának átlagos forgalmát Egyfelhős – adatközponti szintű operációs rendszer az Odnoklassnikiben és kivonjuk a hálózati interfész sávszélességéből Egyfelhős – adatközponti szintű operációs rendszer az Odnoklassnikiben kis margóval, i.e.

Egyfelhős – adatközponti szintű operációs rendszer az Odnoklassnikiben

A sávok egymástól függetlenül vannak meghatározva a bejövő és a kimenő forgalom számára. És az új értékek szerint a miniond újrakonfigurálja a nem prioritásos osztályhatárt 1:2 arányban.

Így mindhárom izolációs osztályt megvalósítottuk: prod, batch és idle. Ezek az osztályok nagyban befolyásolják a feladatok teljesítési jellemzőit. Ezért úgy döntöttünk, hogy ezt az attribútumot a hierarchia tetejére helyezzük, hogy a hierarchikus sor nevére nézve azonnal világos legyen, hogy mivel is van dolgunk:

Egyfelhős – adatközponti szintű operációs rendszer az Odnoklassnikiben

Minden barátunk háló и zene a frontok ezután a hierarchiában a prod alá kerülnek. Például a köteg alatt helyezzük el a szolgáltatást zenei katalógus, amely az Odnoklassnikibe feltöltött mp3 fájlok készletéből időszakonként zeneszámkatalógust állít össze. Példa az üresjárati szolgáltatásra zenei transzformátor, amely normalizálja a zene hangerejét.

Az extra sorok ismételt eltávolításával laposabbá tudjuk írni szolgáltatásaink nevét, ha a teljes szolgáltatásnév végére hozzáadjuk a feladat elkülönítési osztályt: web.front.prod, katalógus.zene.köteg, transzformátor.zene.tétlen.

És most a szolgáltatás nevére nézve nem csak azt értjük, hogy milyen funkciót lát el, hanem az elkülönítési osztályát is, ami a kritikusságát stb.

Minden nagyszerű, de van egy keserű igazság. Az egy gépen futó feladatokat lehetetlen teljesen elkülöníteni.

Amit sikerült elérni: ha a tétel intenzíven fogyaszt csak CPU erőforrások, akkor a beépített Linux CPU ütemező nagyon jól teszi a dolgát, és gyakorlatilag nincs hatással a prod feladatra. De ha ez a kötegelt feladat elkezd aktívan dolgozni a memóriával, akkor a kölcsönös hatás már megjelenik. Ez azért történik, mert a prod-feladat „kimosódik” a processzor memória-gyorsítótáraiból – ennek eredményeként a gyorsítótár-kihagyások növekednek, és a processzor lassabban dolgozza fel a prod-feladatot. Egy ilyen kötegelt feladat 10%-kal növelheti tipikus terméktárolónk késleltetését.

A forgalom elkülönítése még nehezebb, mivel a modern hálózati kártyák belső csomagsorral rendelkeznek. Ha a kötegfeladatból a csomag először kerül oda, akkor az lesz az első, amelyik a kábelen keresztül kerül továbbításra, és ez ellen nem lehet mit tenni.

Ráadásul eddig csak a TCP forgalom prioritásának problémáját sikerült megoldanunk: a hsfc megközelítés nem működik UDP esetén. És még TCP forgalom esetén is, ha a kötegelt feladat nagy forgalmat generál, ez is körülbelül 10%-kal növeli a prod feladat késleltetését.

hibatűrés

A one-cloud fejlesztése során az egyik cél az Odnoklassniki hibatűrésének javítása volt. Ezért a következőkben szeretném részletesebben megvizsgálni a meghibásodások és balesetek lehetséges forgatókönyveit. Kezdjük egy egyszerű forgatókönyvvel – a konténer meghibásodásával.

Maga a tároló többféleképpen is meghibásodhat. Ez lehet valamiféle kísérlet, hiba vagy hiba a jegyzékben, amelynek következtében a prod-feladat több erőforrást fogyaszt, mint amennyi a jegyzékben szerepel. Volt egy esetünk: egy fejlesztő implementált egy összetett algoritmust, sokszor átdolgozta, túlgondolta magát, és annyira összezavarodott, hogy végül a probléma egy nagyon nem triviális körbe került. És mivel a prod feladat magasabb prioritású, mint az összes többi ugyanazon minionon, elkezdte felemészteni az összes elérhető processzorerőforrást. Ebben a helyzetben az elszigeteltség, vagy inkább a CPU időkvóta mentette meg a napot. Ha egy feladathoz kvótát rendelnek, a feladat nem fogyaszt többet. Ezért az ugyanazon a gépen futó kötegelt és egyéb prod feladatok nem vettek észre semmit.

A második lehetséges probléma a konténer leesése. És itt az újraindítási szabályzatok megmentenek minket, mindenki ismeri őket, a Docker maga is nagyszerű munkát végez. Szinte minden prod-feladatnak mindig újraindítási szabályzata van. Néha az on_failure-t használjuk kötegelt feladatokhoz vagy prod-tárolók hibakereséséhez.

Mit tehetsz, ha egy egész minion nem elérhető?

Nyilvánvalóan futtassa a tárolót egy másik gépen. Az érdekes rész itt az, hogy mi történik a konténerhez rendelt IP-címekkel.

A konténerekhez ugyanazokat az IP-címeket rendelhetjük, mint a minion gépekhez, amelyeken ezek a konténerek futnak. Ezután, amikor a tárolót elindítják egy másik gépen, annak IP-címe megváltozik, és minden kliensnek meg kell értenie, hogy a tároló elköltözött, és most egy másik címre kell mennie, amihez külön Service Discovery szolgáltatás szükséges.

A Service Discovery kényelmes. A piacon számos, különböző fokú hibatűrő megoldás létezik a szolgáltatásnyilvántartás szervezésére. Az ilyen megoldások gyakran terheléselosztó logikát valósítanak meg, további konfigurációkat tárolnak KV tároló formájában stb.
Szeretnénk azonban elkerülni a külön nyilvántartás bevezetését, mert ez egy olyan kritikus rendszer bevezetését jelentené, amelyet a termelésben minden szolgáltatás használ. Ez azt jelenti, hogy ez egy lehetséges meghibásodási pont, és nagyon hibatűrő megoldást kell választani vagy ki kell dolgozni, ami nyilvánvalóan nagyon nehéz, időigényes és költséges.

És még egy nagy hátrány: ahhoz, hogy a régi infrastruktúránk működjön az újjal, abszolút minden feladatot át kellene írnunk, hogy valamilyen Service Discovery rendszert használjunk. Rengeteg munka van, és bizonyos helyeken szinte lehetetlen, ha olyan alacsony szintű eszközökről van szó, amelyek az operációs rendszer kernel szintjén vagy közvetlenül a hardverrel működnek. Ennek a funkciónak a megvalósítása kialakított megoldási minták segítségével, mint pl oldalkocsi egyes helyeken további terhelést jelentene, máshol - a működés bonyolítását és további meghibásodási forgatókönyveket. Nem akartuk bonyolítani a dolgokat, ezért úgy döntöttünk, hogy a Service Discovery használatát nem kötelezővé tesszük.

Egyfelhőben az IP követi a konténert, azaz minden feladatpéldánynak saját IP-címe van. Ez a cím „statikus”: minden egyes példányhoz hozzárendelődik, amikor a szolgáltatást először elküldik a felhőbe. Ha egy szolgáltatásnak eltérő számú példánya volt élete során, akkor végül annyi IP-címet rendelnek hozzá, ahány példány volt.

A későbbiekben ezek a címek nem változnak: egyszer hozzá vannak rendelve, és a termelési szolgáltatás teljes élettartama alatt fennmaradnak. Az IP-címek a konténereket követik a hálózaton keresztül. Ha a konténert áthelyezik egy másik minionba, akkor a cím követni fogja.

Így egy szolgáltatásnév hozzárendelése az IP-címek listájához nagyon ritkán változik. Ha újra megnézi a szolgáltatáspéldányok nevét, amelyeket a cikk elején említettünk (1.ok-web.group1.web.front.prod, 2.ok-web.group1.web.front.prod, …), észre fogjuk venni, hogy hasonlítanak a DNS-ben használt FQDN-ekre. Így van, hogy a szolgáltatáspéldányok nevét leképezzük az IP-címükre, a DNS protokollt használjuk. Sőt, ez a DNS visszaadja az összes lefoglalt IP-címet az összes konténerhez – mind a futó, mind a leállított tárolókhoz (tegyük fel, hogy három replikát használunk, és öt címet foglaltunk le ott – mind az öt visszaadásra kerül). Az ügyfelek, miután megkapták ezt az információt, megpróbálnak kapcsolatot létesíteni mind az öt replikával - és így meghatározzák azokat, amelyek működnek. Ez a rendelkezésre állás meghatározására szolgáló lehetőség sokkal megbízhatóbb, nem foglal magában sem DNS-t, sem szolgáltatásfelderítést, ami azt jelenti, hogy nincs nehéz megoldani az információk relevanciájának és a rendszerek hibatűrésének biztosításában. Sőt, azokban a kritikus szolgáltatásokban, amelyektől a teljes portál működése függ, egyáltalán nem használhatjuk a DNS-t, hanem egyszerűen IP-címeket írhatunk be a konfigurációba.

Az ilyen IP-átvitel konténerek mögött való megvalósítása nem triviális – és a következő példán keresztül nézzük meg, hogyan működik:

Egyfelhős – adatközponti szintű operációs rendszer az Odnoklassnikiben

Tegyük fel, hogy a one-cloud master parancsot ad az M1 minionnak, hogy futtasson 1.ok-web.group1.web.front.prod 1.1.1.1 címmel. Egy minionon működik BIRD, amely ezt a címet hirdeti speciális szervereknek útvonal reflektor. Ez utóbbiaknak van egy BGP szekciója a hálózati hardverrel, amelybe az M1.1.1.1-en az 1 cím útvonalát fordítják. Az M1 Linux segítségével irányítja a csomagokat a konténeren belül. Három routereflektor szerver van, mivel ez nagyon kritikus része az egyfelhős infrastruktúrának – nélkülük nem működik az egyfelhős hálózat. Különböző állványokba helyezzük őket, lehetőség szerint az adatközpont különböző helyiségeiben, hogy csökkentsük mindhárom egyidejű meghibásodásának valószínűségét.

Tegyük fel most, hogy megszakadt a kapcsolat az egyfelhő-mester és az M1 minion között. Az egyfelhő-mester most abból a feltételezésből fog cselekedni, hogy az M1 teljesen megbukott. Vagyis kiadja a parancsot az M2 minionnak az indításhoz web.group1.web.front.prod azonos címmel 1.1.1.1. Most két ütköző útvonal van a hálózaton az 1.1.1.1-hez: az M1-en és az M2-n. Az ilyen konfliktusok feloldására a Multi Exit Diskriminátort használjuk, amely a BGP közleményében található. Ez egy szám, amely a meghirdetett útvonal súlyát mutatja. Az ütköző útvonalak közül az alacsonyabb MED értékű útvonal kerül kiválasztásra. A one-cloud master támogatja a MED-t a konténer IP-címek szerves részeként. Első alkalommal kellően nagy MED = 1 000 000 értékkel írják ki a címet. Ilyen vészhelyzeti konténerátvitel esetén a master csökkenti a MED-t, és az M2 már megkapja az 1.1.1.1 cím hirdetésére vonatkozó parancsot a MED = jelzéssel. 999 999. Az M1-en futó példány ebben az esetben kapcsolat nélkül marad, további sorsa pedig nemigen érdekel minket, amíg helyre nem áll a kapcsolat a mesterrel, amikor is leállítják, mint egy régi felvételt.

összeomlik

Valamennyi adatközpont-felügyeleti rendszer mindig elfogadhatóan kezeli a kisebb hibákat. A tartály túlcsordulása szinte mindenhol jellemző.

Nézzük meg, hogyan kezeljük a vészhelyzeteket, például egy adatközpont egy vagy több helyiségében bekövetkező áramszünetet.

Mit jelent egy baleset egy adatközponti menedzsment rendszer számára? Először is, ez sok gép masszív, egyszeri meghibásodása, és a vezérlőrendszernek egyszerre sok konténert kell migrálnia. De ha nagyon nagy léptékű a katasztrófa, akkor előfordulhat, hogy nem lehet minden feladatot átcsoportosítani más minionokhoz, mert az adatközpont erőforráskapacitása a terhelés 100%-a alá csökken.

A baleseteket gyakran a vezérlőréteg meghibásodása kíséri. Ez történhet berendezéseinek meghibásodása miatt, de gyakrabban azért, mert a baleseteket nem tesztelik, és maga a vezérlőréteg is leesik a megnövekedett terhelés miatt.

Mit tehet ez ellen?

A tömeges migráció azt jelenti, hogy nagyszámú tevékenység, áttelepítés és telepítés történik az infrastruktúrában. Minden egyes migráció eltarthat egy ideig, amíg a konténerképeket minionokhoz eljuttatja és kicsomagolja, elindítja és inicializálja stb. Ezért kívánatos, hogy a fontosabb feladatokat a kevésbé fontosak előtt indítsák el.

Nézzük újra az általunk ismert szolgáltatások hierarchiáját, és próbáljuk meg eldönteni, hogy mely feladatokat szeretnénk először futtatni.

Egyfelhős – adatközponti szintű operációs rendszer az Odnoklassnikiben

Természetesen ezek azok a folyamatok, amelyek közvetlenül részt vesznek a felhasználói kérések feldolgozásában, azaz a prod. Ezzel jelezzük elhelyezési prioritás — a sorhoz rendelhető szám. Ha egy várólista magasabb prioritású, akkor a szolgáltatásai kerülnek az első helyre.

Prod esetén magasabb prioritást adunk, 0; tételben - egy kicsit alacsonyabb, 100; üresjáratban - még alacsonyabb, 200. A prioritások hierarchikusan vannak alkalmazva. A hierarchiában lejjebb lévő összes feladatnak megfelelő prioritása lesz. Ha azt szeretnénk, hogy a prod-on belüli gyorsítótárak a frontendek előtt induljanak el, akkor a cache = 0-hoz és az elülső alsorokhoz = 1-hez rendelünk prioritást. Ha például azt szeretnénk, hogy először a fő portál induljon el az előlapokról, és csak a zenei front akkor az utóbbihoz alacsonyabb prioritást rendelhetünk - 10.

A következő probléma az erőforrások hiánya. Így nagy mennyiségű berendezés, az adatközpont egész csarnoka hibásodott meg, és annyi szolgáltatást indítottunk újra, hogy most már nem jut mindenkinek elegendő erőforrás. El kell döntenie, hogy mely feladatokat áldozza fel annak érdekében, hogy a főbb kritikus szolgáltatások továbbra is működjenek.

Egyfelhős – adatközponti szintű operációs rendszer az Odnoklassnikiben

Az elhelyezési prioritástól eltérően nem áldozhatunk fel válogatás nélkül minden kötegelt feladatot, ezek egy része fontos a portál működése szempontjából. Ezért külön kiemeltük elővásárlási elsőbbség feladatokat. Elhelyezéskor egy magasabb prioritású feladat megelőzheti, azaz leállíthatja az alacsonyabb prioritású feladatot, ha nincs több szabad csatlós. Ebben az esetben egy alacsony prioritású feladat valószínűleg elhelyezetlenül marad, vagyis már nem lesz megfelelő minion elegendő szabad erőforrással.

A mi hierarchiánkban nagyon egyszerű megadni egy elővásárlási prioritást úgy, hogy a gyártási és kötegelt feladatok megelőzik vagy leállítják a tétlen feladatokat, de nem egymást, az üresjárat prioritása 200-nak megfelelő prioritás megadásával. Csakúgy, mint az elhelyezési prioritás esetében, itt is használhatja hierarchiánkat bonyolultabb szabályok leírására. Például jelezzük, hogy feláldozzuk a zenei funkciót, ha nincs elegendő erőforrásunk a fő webportálhoz, a megfelelő csomópontok prioritását alacsonyabbra állítva: 10.

Teljes egyenáramú balesetek

Miért hibásodhat meg az egész adatközpont? Elem. Jó poszt volt a hurrikán az adatközpont munkáját érintette. Az elemek hajléktalanoknak tekinthetők, akik egykor az elosztó optikáját égették, az adatközpont pedig teljesen elvesztette a kapcsolatot más helyszínekkel. A meghibásodás oka emberi tényező is lehet: a kezelő olyan parancsot ad ki, hogy az egész adatközpont leesik. Ez egy nagy hiba miatt fordulhat elő. Általában véve az adatközpontok összeomlása nem ritka. Nálunk ez néhány havonta egyszer megtörténik.

És ezt tesszük, hogy megakadályozzuk, hogy bárki #élve tweeteljen.

Az első stratégia az elszigeteltség. Minden egyfelhő-példány elszigetelt, és csak egy adatközpontban tudja kezelni a gépeket. Vagyis egy felhő elvesztése hibák vagy helytelen kezelői parancsok miatt csak egy adatközpont elvesztését jelenti. Készen állunk erre: redundancia szabályzatunk van, amely szerint az alkalmazás és az adatok replikái minden adatközpontban megtalálhatók. Hibatűrő adatbázisokat használunk, és rendszeresen teszteljük a hibákat.
Ma már négy adatközpontunk van, ami az egyfelhő négy különálló, teljesen elszigetelt példányát jelenti.

Ez a megközelítés nemcsak a fizikai meghibásodás ellen véd, hanem a kezelői hibától is.

Mit lehet még tenni az emberi tényezővel? Amikor egy operátor furcsa vagy potenciálisan veszélyes parancsot ad a felhőnek, hirtelen felkérhetik, hogy oldjon meg egy kis problémát, hogy lássa, milyen jól gondolta. Például, ha ez a sok replika tömeges leállítása vagy csak egy furcsa parancs - a replikák számának csökkentése vagy a kép nevének megváltoztatása, és nem csak a verziószám az új manifestben.

Egyfelhős – adatközponti szintű operációs rendszer az Odnoklassnikiben

Eredményei

Az egyfelhő megkülönböztető jellemzői:

  • Hierarchikus és vizuális elnevezési séma szolgáltatásokhoz és konténerekhez, amely lehetővé teszi, hogy nagyon gyorsan megtudja, mi a feladat, mihez kapcsolódik és hogyan működik, és ki a felelős érte.
  • Alkalmazzuk a mi a termék és a tétel kombinálásának technikájafeladatok a minionokon a gépmegosztás hatékonyságának javítása érdekében. A cpuset helyett CPU-kvótákat, megosztásokat, CPU-ütemező házirendeket és Linux QoS-t használunk.
  • Az ugyanazon a gépen futó konténereket nem sikerült teljesen elkülöníteni, de kölcsönös befolyásuk 20%-on belül marad.
  • A szolgáltatások hierarchiába szervezése segít az automatikus katasztrófa utáni helyreállításban elhelyezési és elővásárlási prioritások.

GYIK

Miért nem vettünk kész megoldást?

  • A feladatok elkülönítésének különböző osztályai eltérő logikát igényelnek, amikor a minionokra helyezik őket. Ha a prod feladatok elhelyezhetők az erőforrások egyszerű lefoglalásával, akkor kötegelt és tétlen feladatokat kell elhelyezni, nyomon követve az erőforrások tényleges felhasználását a minion gépeken.
  • Figyelembe kell venni a feladatok által felhasznált erőforrásokat, például:
    • hálózati sávszélesség;
    • lemezek típusai és „orsói”.
  • A vészhelyzeti reagálás során a szolgáltatások prioritásainak, az erőforrásokhoz tartozó parancsok jogainak és kvótáinak jelzésének szükségessége, amelyet hierarchikus sorok segítségével oldanak meg egy felhőben.
  • A konténerek emberi elnevezésének szükségessége a balesetekre és eseményekre adott válaszidő csökkentése érdekében
  • A Service Discovery egyszeri széles körű megvalósításának lehetetlensége; a hardver hosztokon tárolt feladatokkal való hosszú távú együttélés szükségessége – amit a konténereket követő „statikus” IP-címek oldanak meg, és ennek következtében egyedi integráció igénye egy nagy hálózati infrastruktúrával.

Mindezek a funkciók a meglévő megoldások jelentős módosítását igényelnék, hogy nekünk megfeleljenek, és a munka mennyiségét felmérve rájöttünk, hogy hozzávetőlegesen azonos munkaerőköltséggel kifejleszthetjük saját megoldásunkat. De az Ön megoldása sokkal könnyebben kezelhető és fejleszthető lesz – nem tartalmaz olyan szükségtelen absztrakciókat, amelyek olyan funkcionalitást támogatnak, amelyre nincs szükségünk.

Az utolsó sorokat olvasóknak köszönöm a türelmet és a figyelmet!

Forrás: will.com

Hozzászólás