One-cloud - OS na úrovni datového centra v Odnoklassniki

One-cloud - OS na úrovni datového centra v Odnoklassniki

Aloha, lidi! Jmenuji se Oleg Anastasyev, pracuji v Odnoklassniki v týmu Platform. A kromě mě v Odnoklassniki funguje spousta hardwaru. Máme čtyři datová centra s cca 500 racky s více než 8 tisíci servery. V určitém okamžiku jsme si uvědomili, že zavedení nového systému správy nám umožní efektivněji zatěžovat zařízení, usnadnit správu přístupu, automatizovat (pře)distribuci výpočetních zdrojů, urychlit spouštění nových služeb a zrychlit odezvy. k rozsáhlým nehodám.

co z toho vzešlo?

Kromě mě a hromady hardwaru jsou zde také lidé, kteří s tímto hardwarem pracují: inženýři, kteří sídlí přímo v datových centrech; síťaři, kteří nastavují síťový software; správci nebo SRE, kteří zajišťují odolnost infrastruktury; a vývojové týmy, každý z nich je zodpovědný za část funkcí portálu. Software, který vytvářejí, funguje asi takto:

One-cloud - OS na úrovni datového centra v Odnoklassniki

Požadavky uživatelů jsou přijímány na obou frontách hlavního portálu www.ok.rua na dalších, například na frontách hudebního API. Pro zpracování obchodní logiky volají aplikační server, který při zpracování požadavku volá potřebné specializované mikroslužby - jednograf (graf sociálních vazeb), user-cache (cache uživatelských profilů) atd.

Každá z těchto služeb je nasazena na mnoha strojích a každý z nich má odpovědné vývojáře odpovědné za fungování modulů, jejich provoz a technologický rozvoj. Všechny tyto služby běží na hardwarových serverech a donedávna jsme spouštěli přesně jednu úlohu na server, tedy specializovanou na konkrétní úlohu.

proč tomu tak je? Tento přístup měl několik výhod:

  • Uklidněný hromadné řízení. Řekněme, že úloha vyžaduje nějaké knihovny, nějaká nastavení. A pak je server přiřazen přesně jedné konkrétní skupině, je popsána politika cfengine pro tuto skupinu (nebo již byla popsána) a tato konfigurace je centrálně a automaticky zavedena na všechny servery v této skupině.
  • Zjednodušený diagnostika. Řekněme, že se podíváte na zvýšené zatížení centrálního procesoru a uvědomíte si, že toto zatížení může být generováno pouze úlohou, která běží na tomto hardwarovém procesoru. Hledání viníka velmi rychle končí.
  • Zjednodušený monitoring. Pokud je se serverem něco v nepořádku, monitor to nahlásí a vy přesně víte, kdo za to může.

Službě skládající se z několika replik je přiděleno několik serverů – pro každý jeden. Poté je výpočetní zdroj pro službu alokován velmi jednoduše: počet serverů, které služba má, maximální množství zdrojů, které může spotřebovat. „Snadné“ zde neznamená, že se snadno používá, ale v tom smyslu, že alokace zdrojů se provádí ručně.

Tento přístup nám to také umožnil specializované železné konfigurace pro úlohu spuštěnou na tomto serveru. Pokud úloha ukládá velké množství dat, pak používáme 4U server s šasi s 38 disky. Pokud je úloha čistě výpočetní, pak si můžeme koupit levnější 1U server. To je výpočetně efektivní. Tento přístup nám mimo jiné umožňuje využívat čtyřikrát méně strojů se zátěží srovnatelnou s jednou přátelskou sociální sítí.

Taková efektivita ve využití výpočetních zdrojů by měla zajistit i ekonomickou efektivitu, pokud vyjdeme z premisy, že nejdražší jsou servery. Dlouhou dobu byl hardware nejdražší a my jsme vynaložili velké úsilí na snížení ceny hardwaru, přišli jsme s algoritmy odolnosti proti chybám, abychom snížili požadavky na spolehlivost hardwaru. A dnes jsme se dostali do fáze, kdy cena serveru přestala být rozhodující. Pokud nepovažujete za nejnovější exotiku, pak na konkrétní konfiguraci serverů v racku nezáleží. Nyní máme další problém - cenu místa, které zabírá server v datovém centru, tedy místa v racku.

Když jsme si uvědomili, že tomu tak je, rozhodli jsme se spočítat, jak efektivně využíváme stojany.
Vzali jsme cenu nejvýkonnějšího serveru z ekonomicky ospravedlnitelných, spočítali, kolik takových serverů můžeme umístit do racků, kolik úloh na nich spustíme podle starého modelu „jeden server = jedna úloha“ a kolik takových úkoly mohly využívat zařízení. Počítali a ronili slzy. Ukázalo se, že naše efektivita při používání stojanů je asi 11 %. Závěr je zřejmý: musíme zvýšit efektivitu využívání datových center. Zdálo by se, že řešení je nasnadě: potřebujete spustit několik úloh na jednom serveru najednou. Ale tady začínají potíže.

Hromadná konfigurace se dramaticky zkomplikuje – nyní je nemožné přiřadit k serveru jakoukoli jednu skupinu. Koneckonců, nyní lze na jednom serveru spustit několik úloh různých příkazů. Kromě toho může být konfigurace pro různé aplikace konfliktní. Diagnostika se také komplikuje: pokud na serveru vidíte zvýšenou spotřebu CPU nebo disku, nevíte, která úloha způsobuje potíže.

Ale hlavní věc je, že neexistuje žádná izolace mezi úlohami běžícími na stejném počítači. Zde je například graf průměrné doby odezvy úlohy serveru před a po spuštění jiné výpočetní aplikace na stejném serveru, v žádném případě nesouvisející s tou první - doba odezvy hlavní úlohy se výrazně prodloužila.

One-cloud - OS na úrovni datového centra v Odnoklassniki

Je zřejmé, že úlohy musíte spouštět buď v kontejnerech, nebo ve virtuálních strojích. Protože téměř všechny naše úlohy běží pod jedním OS (Linux) nebo jsou pro něj přizpůsobeny, nepotřebujeme podporovat mnoho různých operačních systémů. V souladu s tím není virtualizace potřeba; kvůli dodatečné režii bude méně efektivní než kontejnerizace.

Jako implementace kontejnerů pro spouštění úloh přímo na serverech je Docker dobrým kandidátem: obrazy souborového systému dobře řeší problémy s konfliktními konfiguracemi. Skutečnost, že obrazy mohou být složeny z několika vrstev, nám umožňuje výrazně snížit množství dat potřebných k jejich nasazení v infrastruktuře a oddělit společné části do samostatných základních vrstev. Pak budou základní (a nejobjemnější) vrstvy poměrně rychle uloženy do mezipaměti v celé infrastruktuře a pro poskytování mnoha různých typů aplikací a verzí bude potřeba přenést pouze malé vrstvy.

Navíc připravený registr a značkování obrázků v Dockeru nám poskytují hotová primitiva pro verzování a dodávání kódu do produkce.

Docker, stejně jako jakákoli jiná podobná technologie, nám poskytuje určitou úroveň izolace kontejnerů hned po vybalení. Například izolace paměti – každý kontejner má daný limit na využití paměti stroje, po jehož překročení nebude spotřebovávat. Kontejnery můžete také izolovat na základě využití procesoru. Nám však standardní izolace nestačila. Ale o tom více níže.

Přímé spouštění kontejnerů na serverech je pouze částí problému. Další část se týká hostování kontejnerů na serverech. Musíte pochopit, který kontejner lze umístit na který server. To není tak snadný úkol, protože kontejnery je třeba umístit na servery co nejhustěji, aniž by se snížila jejich rychlost. Takové umístění může být také obtížné z hlediska odolnosti proti poruchám. Často chceme umístit repliky stejné služby do různých racků nebo dokonce do různých místností datového centra, abychom v případě selhání racku nebo místnosti nepřišli okamžitě o všechny repliky služeb.

Ruční distribuce kontejnerů není možnost, když máte 8 tisíc serverů a 8-16 tisíc kontejnerů.

Kromě toho jsme chtěli vývojářům poskytnout větší nezávislost při přidělování zdrojů, aby mohli své služby hostovat v produkci sami, bez pomoci správce. Zároveň jsme si chtěli zachovat kontrolu, aby nějaká drobná služba nespotřebovala všechny zdroje našich datových center.

Je zřejmé, že potřebujeme kontrolní vrstvu, která by to dělala automaticky.

Došli jsme tedy k jednoduchému a srozumitelnému obrázku, který všichni architekti zbožňují: tři čtverce.

One-cloud - OS na úrovni datového centra v Odnoklassniki

one-cloud masters je cluster s podporou převzetí služeb při selhání zodpovědný za cloudovou orchestraci. Vývojář odešle hlavnímu serveru manifest, který obsahuje všechny informace potřebné k hostování služby. Na jeho základě master dává příkazy vybraným minionům (strojům určeným ke spouštění kontejnerů). Přisluhovači mají našeho agenta, který přijímá příkaz, vydává své příkazy Dockeru a Docker konfiguruje linuxové jádro tak, aby spustilo odpovídající kontejner. Kromě provádění příkazů agent průběžně hlásí masterovi změny stavu jak stroje minion, tak kontejnerů, které na něm běží.

Přidělení zdrojů

Nyní se podívejme na problém složitější alokace zdrojů pro mnoho minionů.

Výpočetní zdroj v jednom cloudu je:

  • Množství výkonu procesoru spotřebovaného konkrétní úlohou.
  • Množství paměti dostupné pro úkol.
  • Síťový provoz. Každý z minionů má specifické síťové rozhraní s omezenou šířkou pásma, takže je nemožné distribuovat úkoly bez zohlednění množství dat, které po síti přenášejí.
  • Disky. Kromě toho samozřejmě k prostoru pro tyto úkoly přidělujeme také typ disku: HDD nebo SSD. Disky mohou obsloužit konečný počet požadavků za sekundu – IOPS. Proto pro úlohy, které generují více IOPS, než zvládne jeden disk, přidělujeme také „vřetena“ – tedy disková zařízení, která musí být pro úlohu výhradně rezervována.

Pak pro nějakou službu, například pro user-cache, můžeme zaznamenávat spotřebované zdroje takto: 400 procesorových jader, 2,5 TB paměti, 50 Gbit/s provoz v obou směrech, 6 TB místa na HDD umístěných na 100 vřetenech . Nebo ve známější podobě, jako je tato:

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

Prostředky služeb mezipaměti uživatele spotřebovávají pouze část všech dostupných zdrojů v produkční infrastruktuře. Proto se chci ujistit, že najednou, kvůli chybě operátora nebo ne, uživatelská mezipaměť nespotřebovává více zdrojů, než je jí přiděleno. To znamená, že musíme omezit zdroje. Ale s čím bychom mohli kvótu svázat?

Vraťme se k našemu značně zjednodušenému diagramu interakce komponent a překreslete jej s dalšími detaily – takto:

One-cloud - OS na úrovni datového centra v Odnoklassniki

Co padne do oka:

  • Webové rozhraní a hudba používají izolované clustery stejného aplikačního serveru.
  • Můžeme rozlišit logické vrstvy, do kterých tyto clustery patří: fronty, cache, datové úložiště a vrstva správy.
  • Frontend je heterogenní, skládá se z různých funkčních subsystémů.
  • Mezipaměti mohou být také rozptýleny v rámci subsystému, jehož data ukládají do mezipaměti.

Znovu překreslíme obrázek:

One-cloud - OS na úrovni datového centra v Odnoklassniki

Bah! Ano, vidíme hierarchii! To znamená, že můžete distribuovat zdroje po větších blocích: přiřaďte odpovědného vývojáře k uzlu této hierarchie, který odpovídá funkčnímu subsystému (jako je „hudba“ na obrázku), a ke stejné úrovni hierarchie připojte kvótu. Tato hierarchie nám také umožňuje pružněji organizovat služby pro usnadnění správy. Například celý web, protože se jedná o velmi velké seskupení serverů, rozdělíme do několika menších skupin, které jsou na obrázku znázorněny jako skupina1, skupina2.

Odstraněním nadbytečných řádků můžeme napsat každý uzel našeho obrázku v plošší podobě: group1.web.front, api.music.front, user-cache.cache.

Tak se dostáváme ke konceptu „hierarchické fronty“. Má název jako „group1.web.front“. Je mu přiřazena kvóta pro zdroje a uživatelská práva. Dáme tomu člověku z DevOps práva poslat službu do fronty a takový zaměstnanec může něco spustit ve frontě a člověk z OpsDev bude mít administrátorská práva a teď může frontu spravovat, přidělovat tam lidi, dát těmto lidem práva atd. Služby spuštěné v této frontě poběží v rámci kvóty fronty. Pokud výpočetní kvóta fronty nestačí ke spuštění všech služeb najednou, budou se spouštět postupně, čímž se vytvoří samotná fronta.

Pojďme se na služby podívat blíže. Služba má plně kvalifikovaný název, který vždy obsahuje název fronty. Poté bude mít název přední webová služba ok-web.group1.web.front. A bude volána služba aplikačního serveru, ke které přistupuje ok-app.group1.web.front. Každá služba má manifest, který specifikuje všechny potřebné informace pro umístění na konkrétních počítačích: kolik zdrojů tato úloha spotřebovává, jaká konfigurace je pro ni potřebná, kolik má být replik, vlastnosti pro řešení selhání této služby. A po umístění služby přímo na stroje se objeví její instance. Jsou také pojmenovány jednoznačně - jako číslo instance a název služby: 1.ok-web.group1.web.front, 2.ok-web.group1.web.front, …

To je velmi pohodlné: pohledem pouze na název běžícího kontejneru můžeme okamžitě zjistit mnoho.

Nyní se podívejme blíže na to, co tyto instance skutečně provádějí: úkoly.

Třídy izolace úkolů

Všechny úkoly v OK (a pravděpodobně všude) lze rozdělit do skupin:

  • Short Latency Tasks - prod. U takových úloh a služeb je velmi důležité zpoždění odezvy (latence), jak rychle bude každý z požadavků systémem zpracován. Příklady úloh: webové fronty, mezipaměti, aplikační servery, úložiště OLTP atd.
  • Výpočtové úlohy - dávka. Zde není důležitá rychlost zpracování každého konkrétního požadavku. Pro ně je důležité, kolik výpočtů tato úloha provede za určité (dlouhé) časové období (propustnost). Budou to libovolné úkoly MapReduce, Hadoop, strojové učení, statistiky.
  • Úlohy na pozadí - nečinné. U takových úloh není příliš důležitá latence ani propustnost. To zahrnuje různé testy, migrace, přepočty a převod dat z jednoho formátu do druhého. Na jednu stranu jsou podobné vypočítaným, na druhou stranu je pro nás úplně jedno, jak rychle jsou hotové.

Podívejme se, jak takové úlohy spotřebovávají zdroje, například centrální procesor.

Úlohy s krátkým zpožděním. Taková úloha bude mít vzorec spotřeby CPU podobný tomuto:

One-cloud - OS na úrovni datového centra v Odnoklassniki

Je přijat požadavek od uživatele ke zpracování, úloha začne využívat všechna dostupná jádra CPU, zpracuje ji, vrátí odpověď, čeká na další požadavek a zastaví se. Přišla další poptávka - opět jsme vybrali vše, co tam bylo, spočítali to a čekáme na další.

Abychom u takové úlohy zaručili minimální latenci, musíme vzít maximum zdrojů, které spotřebovává, a vyhradit požadovaný počet jader na minionu (stroji, který bude úlohu provádět). Pak bude rezervační vzorec pro náš problém následující:

alloc: cpu = 4 (max)

a pokud máme minion stroj se 16 jádry, tak na něj lze umístit právě čtyři takové úlohy. Zvláště si všimneme, že průměrná spotřeba procesoru u takových úloh je často velmi nízká - což je zřejmé, protože značnou část času úloha čeká na požadavek a nedělá nic.

Výpočtové úlohy. Jejich vzor se bude mírně lišit:

One-cloud - OS na úrovni datového centra v Odnoklassniki

Průměrná spotřeba prostředků CPU pro takové úkoly je poměrně vysoká. Často chceme, aby se výpočetní úloha dokončila za určitý čas, takže musíme vyhradit minimální počet procesorů, které potřebuje, aby byl celý výpočet dokončen v přijatelném čase. Jeho rezervační vzorec bude vypadat takto:

alloc: cpu = [1,*)

"Prosím, umístěte to na miniona, kde je alespoň jedno volné jádro, a pak, kolik jich bude, sežere všechno."

Zde je efektivita použití již mnohem lepší než u úloh s krátkým zpožděním. Zisk ale bude mnohem větší, pokud oba typy úkolů zkombinujete na jednom stroji miniona a jeho zdroje budete distribuovat na cestách. Když úloha s krátkým zpožděním vyžaduje procesor, ten jej okamžitě obdrží, a když prostředky již nejsou potřeba, jsou převedeny do výpočetní úlohy, tedy něco takového:

One-cloud - OS na úrovni datového centra v Odnoklassniki

Ale jak na to?

Nejprve se podívejme na prod a jeho alokaci: cpu = 4. Musíme si vyhradit čtyři jádra. Ve spuštění Dockeru to lze provést dvěma způsoby:

  • Pomocí možnosti --cpuset=1-4, tj. alokovat čtyři konkrétní jádra na stroji k úloze.
  • Chcete-li použít --cpuquota=400_000 --cpuperiod=100_000, přiřadit kvótu pro čas procesoru, to znamená, že každých 100 ms reálného času úloha nespotřebuje více než 400 ms času procesoru. Získají se stejná čtyři jádra.

Ale která z těchto metod je vhodná?

cpuset vypadá docela atraktivně. Úloha má čtyři vyhrazená jádra, což znamená, že mezipaměti procesoru budou fungovat co nejefektivněji. To má i stinnou stránku: museli bychom převzít úlohu distribuce výpočtů přes nezatížená jádra stroje místo OS, a to je docela netriviální úkol, zvláště pokud se pokusíme umístit dávkové úlohy na takový stroj. Testy ukázaly, že možnost s kvótou je zde vhodnější: operační systém tak má větší svobodu ve výběru jádra, které má v aktuálním okamžiku provést úlohu, a čas procesoru je distribuován efektivněji.

Pojďme zjistit, jak provádět rezervace v Dockeru na základě minimálního počtu jader. Kvóta pro dávkové úlohy již neplatí, protože není potřeba omezovat maximum, stačí jen garantovat minimum. A tady ta možnost dobře sedí docker run --cpushares.

Dohodli jsme se, že pokud šarže vyžaduje záruku alespoň na jedno jádro, pak uvádíme --cpushares=1024, a pokud existují alespoň dvě jádra, pak označíme --cpushares=2048. Sdílení CPU nijak nezasahuje do rozložení času procesoru, pokud je ho dostatek. Pokud tedy prod aktuálně nevyužívá všechna svá čtyři jádra, nic neomezuje dávkové úlohy a mohou využívat další procesorový čas. Ale v situaci, kdy je nedostatek procesorů, pokud produkt spotřeboval všechna čtyři svá jádra a dosáhl své kvóty, bude zbývající čas procesoru rozdělen proporcionálně mezi cpushares, tj. v situaci tří volných jader bude jedno přidělený úkolu s 1024 cpushares a zbývající dva budou přiděleny úkolu s 2048 cpushares.

Používání kvót a podílů však nestačí. Musíme se ujistit, že úloha s krátkým zpožděním má při přidělování času procesoru přednost před dávkovou úlohou. Bez takovéto prioritizace zabere dávková úloha veškerý čas procesoru v okamžiku, kdy ji produkt potřebuje. V běhu Dockeru nejsou žádné možnosti upřednostňování kontejnerů, ale zásady plánovače CPU pro Linux se hodí. Můžete si o nich přečíst podrobně zdea v rámci tohoto článku si je stručně projdeme:

  • SCHED_OTHER
    Ve výchozím nastavení přijímají všechny běžné uživatelské procesy na počítači se systémem Linux.
  • SCHED_BATCH
    Navrženo pro procesy náročné na zdroje. Při umístění úlohy na procesor je zavedena takzvaná aktivační penalizace: taková úloha s menší pravděpodobností obdrží prostředky procesoru, pokud je právě používána úlohou s SCHED_OTHER
  • SCHED_IDLE
    Proces na pozadí s velmi nízkou prioritou, dokonce nižší než pěkných -19. Používáme naši open source knihovnu jedna-nio, aby bylo možné nastavit potřebnou politiku při spouštění kontejneru voláním

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

Ale i když neprogramujete v Javě, totéž lze provést pomocí příkazu chrt:

chrt -i 0 $pid

Pojďme si pro přehlednost shrnout všechny naše úrovně izolace do jedné tabulky:

Třída izolace
Příklad Alloc
Možnosti spuštění Dockeru
sched_setscheduler chrt*

Podněcovat
CPU = 4
--cpuquota=400000 --cpuperiod=100000
SCHED_OTHER

Dávka
CPU = [1, *)
--cpushares=1024
SCHED_BATCH

Líný
CPU = [2, *)
--cpushares=2048
SCHED_IDLE

*Pokud provádíte chrt zevnitř kontejneru, možná budete potřebovat schopnost sys_nice, protože ve výchozím nastavení Docker tuto schopnost při spouštění kontejneru odstraňuje.

Úlohy ale spotřebovávají nejen procesor, ale také provoz, což ovlivňuje latenci síťové úlohy ještě více než nesprávná alokace zdrojů procesoru. Proto přirozeně chceme získat přesně stejný obrázek o provozu. To znamená, že když prod úloha odešle nějaké pakety do sítě, omezíme maximální rychlost (vzorec alloc: lan=[*,500 Mb/s) ), se kterým to prod umí. A u dávky garantujeme pouze minimální propustnost, ale neomezujeme maximální (vzorec alloc: lan=[10Mbps,*) ) V tomto případě by měl mít provoz prod přednost před dávkovými úlohami.
Zde Docker nemá žádná primitiva, která bychom mohli použít. Ale přichází nám na pomoc Řízení provozu Linuxu. Pomocí disciplíny jsme byli schopni dosáhnout požadovaného výsledku Hierarchická křivka spravedlivých služeb. S jeho pomocí rozlišujeme dvě třídy provozu: prod s vysokou prioritou a dávkový/nečinný provoz s nízkou prioritou. V důsledku toho je konfigurace pro odchozí provoz takto:

One-cloud - OS na úrovni datového centra v Odnoklassniki

zde 1:0 je „kořenový disk qdisc“ disciplíny hsfc; 1:1 - podřízená třída hsfc s celkovým limitem šířky pásma 8 Gbit/s, pod kterou jsou umístěny podřízené třídy všech kontejnerů; 1:2 - podřízená třída hsfc je společná pro všechny dávkové a nečinné úlohy s „dynamickým“ limitem, který je popsán níže. Zbývající podřízené třídy hsfc jsou vyhrazené třídy pro aktuálně spuštěné prod kontejnery s limity odpovídajícími jejich manifestům – 450 a 400 Mbit/s. Každé třídě hsfc je přiřazena fronta qdisc fq nebo fq_codel, v závislosti na verzi linuxového jádra, aby se zabránilo ztrátě paketů během shluků provozu.

Disciplíny tc obvykle slouží k upřednostnění pouze odchozího provozu. Chceme ale upřednostňovat i příchozí provoz – koneckonců některá dávková úloha může snadno vybrat celý příchozí kanál a přijímat například velkou dávku vstupních dat pro map&reduce. K tomu používáme modul ifb, který vytváří virtuální rozhraní ifbX pro každé síťové rozhraní a přesměrovává příchozí provoz z rozhraní na odchozí provoz na ifbX. Dále pro ifbX fungují všechny stejné disciplíny pro řízení odchozího provozu, pro který bude konfigurace hsfc velmi podobná:

One-cloud - OS na úrovni datového centra v Odnoklassniki

Během experimentů jsme zjistili, že hsfc vykazuje nejlepší výsledky, když je třída 1:2 neprioritního dávkového/nečinného provozu omezena na strojích minion na ne více než určitý volný pruh. Jinak má neprioritní provoz příliš velký dopad na latenci prod úloh. miniond určuje aktuální množství volné šířky pásma každou sekundu a měří průměrnou spotřebu provozu všech prod-úloh daného miniona One-cloud - OS na úrovni datového centra v Odnoklassniki a odečtením od šířky pásma síťového rozhraní One-cloud - OS na úrovni datového centra v Odnoklassniki s malou rezervou, tzn.

One-cloud - OS na úrovni datového centra v Odnoklassniki

Pásma jsou definována nezávisle pro příchozí a odchozí provoz. A podle nových hodnot miniond překonfiguruje limit neprioritní třídy 1:2.

Implementovali jsme tedy všechny tři třídy izolace: prod, batch a idle. Tyto třídy výrazně ovlivňují výkonnostní charakteristiky úloh. Proto jsme se rozhodli umístit tento atribut na vrchol hierarchie, aby při pohledu na název hierarchické fronty bylo hned jasné, s čím máme co do činění:

One-cloud - OS na úrovni datového centra v Odnoklassniki

Všichni naši přátelé web и hudba fronty jsou pak umístěny v hierarchii pod prod. Například pod dávku umístěte službu hudební katalog, která pravidelně sestavuje katalog skladeb ze sady mp3 souborů nahraných do Odnoklassniki. Příkladem služby v nečinnosti by bylo hudební transformátor, který normalizuje úroveň hlasitosti hudby.

Po opětovném odstranění dalších řádků můžeme názvy služeb psát plošší přidáním třídy izolace úloh na konec úplného názvu služby: web.front.prod, katalog.hudba.dávka, transformátor.hudba.nečiní.

A nyní, když se podíváme na název služby, chápeme nejen to, jakou funkci plní, ale také její třídu izolace, což znamená její kritičnost atd.

Všechno je skvělé, ale je tu jedna hořká pravda. Úlohy běžící na jednom počítači není možné zcela izolovat.

Čeho se nám podařilo dosáhnout: pokud šarže intenzivně spotřebovává pouze CPU zdroje, pak vestavěný linuxový plánovač CPU dělá svou práci velmi dobře a prakticky nemá žádný dopad na prod úlohu. Pokud ale tato dávková úloha začne aktivně pracovat s pamětí, pak se vzájemné ovlivňování již objevuje. K tomu dochází proto, že prod úloha je „vymyta“ z mezipamětí procesoru – v důsledku toho se zvyšuje počet vynechaných mezipamětí a procesor zpracovává prod úlohu pomaleji. Takový dávkový úkol může zvýšit latenci našeho typického kontejneru na produkty o 10 %.

Izolace provozu je ještě obtížnější kvůli skutečnosti, že moderní síťové karty mají interní frontu paketů. Pokud se tam paket z dávkové úlohy dostane jako první, pak bude první přenášený po kabelu a nelze s tím nic dělat.

Navíc se nám zatím podařilo vyřešit pouze problém s upřednostňováním TCP provozu: přístup hsfc nefunguje pro UDP. A dokonce i v případě TCP provozu, pokud dávková úloha generuje velký provoz, také to způsobí asi 10% nárůst zpoždění prod úlohy.

odolnost proti chybám

Jedním z cílů při vývoji jednoho cloudu bylo zlepšit odolnost Odnoklassniki proti chybám. Proto bych se dále rád podrobněji zabýval možnými scénáři poruch a havárií. Začněme jednoduchým scénářem – selháním kontejneru.

Samotný kontejner může selhat několika způsoby. Může to být nějaký druh experimentu, chyba nebo chyba v manifestu, kvůli které začne prod úkol spotřebovávat více zdrojů, než je uvedeno v manifestu. Měli jsme případ: vývojář implementoval jeden složitý algoritmus, mnohokrát ho přepracovával, přemýšlel a byl tak zmatený, že se problém nakonec dostal do smyčky velmi netriviálním způsobem. A protože úloha prod má vyšší prioritu než všechny ostatní na stejných minionech, začala spotřebovávat všechny dostupné zdroje procesoru. V této situaci izolace, nebo spíše kvóta času CPU, zachránila den. Pokud je úkolu přidělena kvóta, úkol nespotřebuje více. Proto dávkové a další prod úlohy, které běžely na stejném stroji, si ničeho nevšimly.

Druhým možným problémem je pád nádoby. A tady nás zachraňují politiky restartu, každý je zná, samotný Docker odvádí skvělou práci. Téměř všechny úlohy produktu mají zásadu vždy restartovat. Někdy používáme on_failure pro dávkové úlohy nebo pro ladění prod kontejnerů.

Co můžete dělat, když není dostupný celý minion?

Samozřejmě spusťte kontejner na jiném počítači. Zajímavá část zde je, co se stane s IP adresou (adresami) přiřazenými ke kontejneru.

Kontejnerům můžeme přiřadit stejné IP adresy jako minion strojům, na kterých tyto kontejnery běží. Poté, když je kontejner spuštěn na jiném počítači, jeho IP adresa se změní a všichni klienti musí pochopit, že se kontejner přesunul, a nyní musí přejít na jinou adresu, což vyžaduje samostatnou službu Service Discovery.

Zjišťování služeb je pohodlné. Na trhu existuje mnoho řešení s různým stupněm odolnosti proti chybám pro organizaci registru služeb. Často taková řešení implementují logiku load balanceru, ukládají další konfiguraci ve formě KV úložiště atd.
Rádi bychom se však vyhnuli nutnosti implementovat samostatný registr, protože by to znamenalo zavedení kritického systému, který využívají všechny služby v produkci. To znamená, že se jedná o potenciální bod selhání a musíte si vybrat nebo vyvinout řešení velmi odolné vůči chybám, což je samozřejmě velmi obtížné, časově náročné a drahé.

A ještě jeden velký nedostatek: aby naše stará infrastruktura fungovala s tou novou, museli bychom přepsat úplně všechny úkoly, abychom používali nějaký systém Service Discovery. Práce je hodně a na některých místech je to téměř nemožné, pokud jde o nízkoúrovňová zařízení, která pracují na úrovni jádra operačního systému nebo přímo s hardwarem. Implementace této funkcionality pomocí zavedených vzorů řešení, jako je např postranní vůz by na některých místech znamenalo dodatečné zatížení, jinde komplikaci provozu a další scénáře poruch. Nechtěli jsme věci komplikovat, a tak jsme se rozhodli, že použití vyhledávání služeb bude volitelné.

V jednom cloudu IP následuje kontejner, tj. každá instance úlohy má svou vlastní IP adresu. Tato adresa je „statická“: je přiřazena každé instanci při prvním odeslání služby do cloudu. Pokud měla služba během své životnosti jiný počet instancí, bude jí nakonec přiděleno tolik IP adres, kolik bylo maximálních instancí.

Následně se tyto adresy nemění: jsou přiděleny jednou a nadále existují po celou dobu životnosti služby ve výrobě. IP adresy následují kontejnery v celé síti. Pokud je kontejner přenesen na jiného miniona, adresa bude následovat za ním.

Mapování názvu služby na jeho seznam IP adres se tedy mění velmi zřídka. Pokud se znovu podíváte na názvy instancí služeb, které jsme zmínili na začátku článku (1.ok-web.group1.web.front.prod, 2.ok-web.group1.web.front.prod, …), všimneme si, že se podobají FQDN používaným v DNS. Správně, k mapování názvů instancí služeb na jejich IP adresy používáme protokol DNS. Navíc tento DNS vrací všechny rezervované IP adresy všech kontejnerů – běžících i zastavených (řekněme, že jsou použity tři repliky a máme tam rezervováno pět adres – všech pět se vrátí). Klienti se po obdržení této informace pokusí navázat spojení se všemi pěti replikami – a určit tak ty, které fungují. Tato možnost určování dostupnosti je mnohem spolehlivější, nezahrnuje DNS ani zjišťování služby, což znamená, že neexistují žádné složité problémy, které by se daly vyřešit při zajišťování relevance informací a odolnosti těchto systémů proti chybám. Navíc v kritických službách, na kterých závisí chod celého portálu, nemůžeme DNS vůbec používat, ale jednoduše zadávat IP adresy do konfigurace.

Implementace takového přenosu IP za kontejnery může být netriviální – a my se podíváme, jak to funguje na následujícím příkladu:

One-cloud - OS na úrovni datového centra v Odnoklassniki

Řekněme, že master jednoho cloudu dá příkaz minionu M1 ke spuštění 1.ok-web.group1.web.front.prod s adresou 1.1.1.1. Funguje na minionovi BIRD, který tuto adresu inzeruje na speciálních serverech reflektor trasy. Ty mají relaci BGP se síťovým hardwarem, do kterého je přeložena trasa adresy 1.1.1.1 na M1. M1 směruje pakety uvnitř kontejneru pomocí Linuxu. Existují tři servery s odrazem tras, protože se jedná o velmi kritickou část infrastruktury jednoho cloudu – bez nich nebude síť v jednom cloudu fungovat. Umístíme je do různých stojanů, pokud možno umístěných v různých místnostech datového centra, abychom snížili pravděpodobnost selhání všech tří současně.

Předpokládejme nyní, že se ztratilo spojení mezi masterem jednoho cloudu a minionem M1. Master jednoho cloudu bude nyní jednat za předpokladu, že M1 zcela selhal. To znamená, že dá příkaz minionu M2 ke spuštění web.group1.web.front.prod se stejnou adresou 1.1.1.1. Nyní máme dvě konfliktní trasy v síti pro 1.1.1.1: na M1 a na M2. K vyřešení takových konfliktů používáme Multi Exit Discriminator, který je uveden v oznámení BGP. Toto je číslo, které ukazuje váhu inzerované trasy. Mezi konfliktními trasami bude vybrána trasa s nižší hodnotou MED. Jednocloudový master podporuje MED jako nedílnou součást IP adres kontejneru. Poprvé je adresa zapsána s dostatečně velkým MED = 1 000 000. V situaci takového nouzového přesunu kontejneru velitel sníží MED a M2 již dostane příkaz inzerovat adresu 1.1.1.1 s MED = 999 999. Instance běžící na M1 zůstane v tomto případě bez spojení a jeho další osud nás málo zajímá, dokud nebude spojení s masterem obnoveno, kdy bude zastaven jako starý záběr.

Havaruje

Všechny systémy pro správu datových center vždy přijatelně zvládají menší selhání. Přetečení kontejnerů je normou téměř všude.

Podívejme se, jak řešíme nouzovou situaci, jako je výpadek proudu v jedné nebo více místnostech datového centra.

Co znamená nehoda pro systém správy datového centra? Za prvé se jedná o masivní jednorázové selhání mnoha strojů a řídicí systém potřebuje migrovat velké množství kontejnerů současně. Pokud je ale katastrofa velmi rozsáhlá, pak se může stát, že všechny úkoly nelze přerozdělit dalším minionům, protože kapacita zdrojů datového centra klesne pod 100 % zátěže.

Často jsou nehody doprovázeny selháním řídicí vrstvy. To se může stát kvůli poruše jeho zařízení, ale častěji kvůli tomu, že nehody nejsou testovány a samotná řídicí vrstva klesá kvůli zvýšené zátěži.

Co s tím vším můžete dělat?

Hromadné migrace znamenají, že v infrastruktuře probíhá velké množství aktivit, migrací a nasazení. Každá z migrací může nějakou dobu trvat, než se doručí a rozbalí obrazy kontejnerů přisluhovačům, spustí se a inicializují kontejnery atd. Proto je žádoucí, aby byly spouštěny důležitější úlohy před méně důležitými.

Podívejme se znovu na hierarchii služeb, které známe, a pokusme se rozhodnout, které úlohy chceme spustit jako první.

One-cloud - OS na úrovni datového centra v Odnoklassniki

Samozřejmě se jedná o procesy, které se přímo podílejí na zpracování požadavků uživatelů, tedy prod. To naznačujeme pomocí priorita umístění — číslo, které lze přiřadit do fronty. Pokud má fronta vyšší prioritu, jsou její služby umístěny jako první.

Na prod přiřadíme vyšší priority, 0; na dávce - o něco nižší, 100; na idle - ještě nižší, 200. Priority jsou aplikovány hierarchicky. Všechny úkoly níže v hierarchii budou mít odpovídající prioritu. Pokud chceme, aby byly cache uvnitř produktu spuštěny před frontendy, pak přiřadíme priority cache = 0 a front subqueues = 1. Pokud například chceme, aby byl hlavní portál spouštěn nejprve z fronty a pouze fronta hudby pak můžeme tomu druhému přiřadit nižší prioritu - 10.

Dalším problémem je nedostatek zdrojů. Selhalo tedy velké množství zařízení, celé haly datového centra a znovu jsme spustili tolik služeb, že nyní není dostatek zdrojů pro všechny. Musíte se rozhodnout, které úkoly obětujete, abyste udrželi hlavní kritické služby v chodu.

One-cloud - OS na úrovni datového centra v Odnoklassniki

Na rozdíl od priority umístění nemůžeme bez rozdílu obětovat všechny dávkové úkoly, některé z nich jsou důležité pro provoz portálu. Proto jsme zvýraznili samostatně přednost předkupního práva úkoly. Po zadání úkolu s vyšší prioritou může zabránit, tj. zastavit úkol s nižší prioritou, pokud již nejsou žádní volní minioni. V tomto případě úkol s nízkou prioritou pravděpodobně zůstane neumístěn, tj. již pro něj nebude vhodný minion s dostatkem volných zdrojů.

V naší hierarchii je velmi jednoduché určit prioritu preempce tak, aby prod a dávkové úlohy předcházely nebo zastavovaly nečinné úlohy, ale ne navzájem, zadáním priority pro nečinnost rovné 200. Stejně jako v případě priority umístění může použít naši hierarchii k popisu složitějších pravidel. Řekněme například, že obětujeme hudební funkci, pokud nemáme dostatek zdrojů pro hlavní webový portál, přičemž prioritu pro odpovídající uzly nastavíme níže: 10.

Celé nehody DC

Proč může selhat celé datové centrum? Živel. Byl to dobrý příspěvek hurikán ovlivnil práci datového centra. Za živly lze považovat bezdomovce, kteří kdysi spálili optiku v rozdělovači a datové centrum zcela ztratilo kontakt s jinými weby. Příčinou selhání může být i lidský faktor: operátor vydá takový příkaz, že spadne celé datové centrum. To se může stát kvůli velké chybě. Obecně není kolaps datových center neobvyklý. To se nám stává jednou za pár měsíců.

A to je to, co děláme, abychom zabránili komukoli tweetovat #živý.

První strategií je izolace. Každá instance jednoho cloudu je izolovaná a může spravovat stroje pouze v jednom datovém centru. To znamená, že ztráta cloudu kvůli chybám nebo nesprávným příkazům operátora je ztrátou pouze jednoho datového centra. Jsme na to připraveni: máme politiku redundance, ve které jsou repliky aplikace a dat umístěny ve všech datových centrech. Používáme databáze odolné proti chybám a pravidelně testujeme chyby.
Ode dneška máme čtyři datová centra, to znamená čtyři samostatné, zcela izolované instance jednoho cloudu.

Tento přístup nejen chrání před fyzickým selháním, ale může také chránit před chybou operátora.

Co jiného se dá dělat s lidským faktorem? Když operátor zadá cloudu nějaký podivný nebo potenciálně nebezpečný příkaz, může být náhle požádán, aby vyřešil malý problém, aby zjistil, jak dobře to myslel. Například, pokud se jedná o nějaké hromadné zastavení mnoha replik nebo jen podivný příkaz - snížení počtu replik nebo změna názvu obrázku, a nikoli pouze čísla verze v novém manifestu.

One-cloud - OS na úrovni datového centra v Odnoklassniki

Výsledky

Charakteristické rysy jednoho cloudu:

  • Hierarchické a vizuální schéma pojmenování pro služby a kontejnery, která umožňuje velmi rychle zjistit, o jaký úkol se jedná, čeho se týká a jak funguje a kdo je za něj zodpovědný.
  • Aplikujeme naše technika kombinace prod- a šarže-úkoly na minionech pro zlepšení efektivity sdílení strojů. Místo cpuset používáme CPU kvóty, sdílené složky, zásady plánovače CPU a Linux QoS.
  • Kontejnery běžící na stejném stroji nebylo možné zcela izolovat, ale jejich vzájemný vliv zůstává do 20 %.
  • Uspořádání služeb do hierarchie pomáhá s použitím automatického zotavení po havárii priority umístění a přednosti.

FAQ

Proč jsme nevzali hotové řešení?

  • Různé třídy izolace úkolů vyžadují při umístění na miniony různou logiku. Pokud lze prod úlohy umístit jednoduchou rezervací zdrojů, pak musí být umístěny dávkové a nečinné úlohy, které sledují skutečné využití zdrojů na strojích minion.
  • Potřeba vzít v úvahu zdroje spotřebované úkoly, jako jsou:
    • šířka pásma sítě;
    • typy a „vřetena“ disků.
  • Nutnost indikovat priority služeb při havarijní reakci, práva a kvóty příkazů pro zdroje, což je řešeno pomocí hierarchických front v jednom cloudu.
  • Potřeba mít kontejnery pojmenovávat lidmi, aby se zkrátila doba odezvy na nehody a incidenty
  • Nemožnost jednorázové rozsáhlé implementace Service Discovery; potřeba dlouhodobé koexistence s úlohami hostovanými na hardwarových hostitelích – něco, co je řešeno „statickými“ IP adresami za kontejnery, a v důsledku toho potřeba jedinečné integrace s rozsáhlou síťovou infrastrukturou.

Všechny tyto funkce by vyžadovaly značné úpravy stávajících řešení, aby nám vyhovovaly, a po zhodnocení množství práce jsme si uvědomili, že bychom mohli vyvinout vlastní řešení s přibližně stejnými mzdovými náklady. Vaše řešení ale bude mnohem jednodušší na obsluhu a vývoj – neobsahuje zbytečné abstrakce, které podporují funkcionalitu, kterou nepotřebujeme.

Těm, kteří čtou poslední řádky, děkuji za trpělivost a pozornost!

Zdroj: www.habr.com

Přidat komentář