One-cloud — datacenter-nivĂ„ OS i Odnoklassniki

One-cloud — datacenter-nivĂ„ OS i Odnoklassniki

Aloha, folk! Jag heter Oleg Anastasyev, och jag arbetar i Odnoklassniki i plattformsteamet. Utöver mig finns det massor av hÄrdvara i Odnoklassniki. Vi har fyra datacenter, och i dem finns det cirka 500 rack med mer Àn 8000 servrar. Vid en viss tidpunkt insÄg vi att implementeringen av ett nytt hanteringssystem skulle lÄta oss utnyttja tekniken mer effektivt, underlÀtta Ätkomsthantering, automatisera (om)fördelning av berÀkningsresurser, pÄskynda lanseringen av nya tjÀnster och öka reaktionshastigheten vid stora haverier.

Vad blev resultatet av detta?

Förutom mig och massor av hÄrdvara finns det ocksÄ mÀnniskor som arbetar med dessa maskiner: ingenjörer som Àr direkt i datacentren; nÀtverkstekniker som konfigurerar nÀtverkslösningarna; administratörer eller SRE som ser till att infrastrukturen Àr fel-tolerant; och team av utvecklare, dÀr varje team ansvarar för en del av portalens funktioner. Programvaran de skapar fungerar ungefÀr sÄ hÀr:

One-cloud — datacenter-nivĂ„ OS i Odnoklassniki

AnvĂ€ndarförfrĂ„gningar kommer bĂ„de till fronten av den huvudsakliga portalen www.ok.ru, sĂ„vĂ€l som till andra, till exempel till API:ets musikfronter. För att bearbeta affĂ€rslogik anropar de applikationsservern, som vid behandling av förfrĂ„gningar anropar nödvĂ€ndiga specialiserade mikrotjĂ€nster — one-graph (graf över sociala relationer), user-cache (cache för anvĂ€ndarprofiler) och sĂ„ vidare.

Varje av dessa tjÀnster Àr distribuerade över mÄnga maskiner, och var och en har ansvariga utvecklare som ansvarar för driften av modulerna, deras underhÄll och teknologisk utveckling. Alla dessa tjÀnster körs pÄ fysiska servrar, och fram till nyligen körde vi exakt en uppgift pÄ varje server, dvs. den var specialiserad för en specifik uppgift.

Varför sÄ? Denna strategi hade flera fördelar:

  • Det underlĂ€ttar massförvaltning. Anta att en uppgift krĂ€ver vissa bibliotek, vissa instĂ€llningar. DĂ„ kopplas servern just till en viss grupp, och en policy för cfengine beskriver denna grupp (eller sĂ„ Ă€r den redan beskriven), och denna konfiguration distribueras centralt och automatiskt till alla servrar i den gruppen.
  • Det förenklar diagnostik. Anta att du tittar pĂ„ en ökad belastning pĂ„ centralprocessorn och inser att denna belastning endast kan ha orsakats av den uppgift som körs pĂ„ den hĂ€r kraftfulla processorn. Att finna den skyldige gĂ„r vĂ€ldigt snabbt.
  • Det förenklar övervakning. Om det Ă€r nĂ„got fel med servern meddelar monitorn detta, och du vet exakt vem som Ă€r skyldig.

En tjĂ€nst som bestĂ„r av flera repliker tilldelas flera servrar – en för varje. DĂ„ tilldelas berĂ€kningsresursen för tjĂ€nsten vĂ€ldigt enkelt: sĂ„ mĂ„nga servrar som tjĂ€nsten har, sĂ„ mycket resurser kan den maximalt konsumera. "Enkelt" hĂ€r betyder inte att det Ă€r lĂ€tt att anvĂ€nda, utan att resursfördelningen sker manuellt.

Denna metod tillÀt oss ocksÄ att skapa specialiserade hÄrdvarukonfigurationer för uppgiften som körs pÄ denna server. Om uppgiften lagrar stora datamÀngder, anvÀnder vi en 4U-server med chassi för 38 diskar. Om uppgiften Àr rent berÀkningsmÀssig kan vi köpa en billigare 1U-server. Detta Àr effektivt sett till berÀkningsresurser. Denna metod tillÄter oss ocksÄ att anvÀnda fyra gÄnger fÀrre maskiner vid en belastning jÀmförbar med ett av vÄra vÀnliga sociala nÀtverk.

Denna effektivitet i anvĂ€ndningen av berĂ€kningsresurser bör ocksĂ„ ge ekonomisk effektivitet, om man utgĂ„r frĂ„n antagandet att det dyraste Ă€r servrar. Under lĂ„ng tid var hĂ„rdvara det dyraste, och vi har lagt ner mycket energi pĂ„ att minska kostnaden för hĂ„rdvara genom att utveckla algoritmer för att sĂ€kerstĂ€lla redundans och minska kraven pĂ„ hĂ„rdvarans tillförlitlighet. Idag har vi nĂ„tt en punkt dĂ€r serverpriserna inte lĂ€ngre Ă€r avgörande. Om vi inte rĂ€knar med den senaste exoteran, spelar den specifika konfigurationen av servrar i en rack ingen roll. Nu har vi ett annat problem – kostnaden för den plats servern upptar i datacentret, dvs. platsen i rack.

NÀr vi insÄg detta beslutade vi att rÀkna pÄ hur effektivt vi anvÀnder racken.
Vi tog priset för den mest kraftfulla servern som Àr ekonomiskt motiverad, berÀknade hur mÄnga sÄdana servrar vi kan placera i rack, vilka uppgifter vi skulle köra pÄ dem baserat pÄ den gamla modellen 'en server = en uppgift' och hur effektivt utrustningen skulle kunna anvÀnda sÄdana uppgifter. Det visade sig att vÄr rackanvÀndning Àr cirka 11 %. Slutsatsen Àr uppenbar: vi mÄste öka effektiviteten i datacenteranvÀndningen. Det skulle tyckas att lösningen Àr sjÀlvklart: man bör köra flera uppgifter pÄ en och samma server. Men hÀr börjar svÄrigheterna.

Masskonfigurationen blir plötsligt mycket mer komplicerad - nu gÄr det inte att tilldela en server till en specifik grupp. Eftersom det nu kan köras flera uppgifter frÄn olika team pÄ en och samma server. Dessutom kan konfigurationen vara konfliktfylld för olika applikationer. Diagnostiken blir ocksÄ mer komplicerad: om du ser ökad processor- eller diskförbrukning pÄ servern, vet du inte vilken av uppgifterna som orsakar problemen.

Men det frÀmsta problemet Àr att det inte finns nÄgon isolering mellan uppgifter som körs pÄ samma maskin. HÀr Àr till exempel grafen över genomsnittlig svarstid för en serveruppgift före och efter att ett annat, helt orelaterat berÀkningsprogram har körts pÄ samma server - svarstiden för den primÀra uppgiften ökade kraftigt.

One-cloud — datacenter-nivĂ„ OS i Odnoklassniki

Det Àr uppenbart att uppgifter antingen mÄste köras i containrar eller virtuella maskiner. Eftersom nÀstan alla vÄra uppgifter körs under en och samma OS (Linux) eller anpassas för den, behöver vi inte stödja mÄnga olika operativsystem. DÀrför behövs inte virtualisering, och pÄ grund av de extra kostnaderna kommer den att vara mindre effektiv Àn containrar.

Som en implementation av containrar för att köra uppgifter direkt pÄ servrar Àr Docker en bra kandidat: filsystemavbilder löser bra problem med konfliktande konfigurationer. Det faktum att avbilder kan bestÄ av flera lager gör att vi kraftigt kan minska mÀngden data som krÀvs för att distribuera dem pÄ infrastrukturen, genom att separera gemensamma delar i egna baslager. DÄ kommer de grundlÀggande (och mest omfattande) lagren att cachas snabbt pÄ hela infrastrukturen, och för att leverera mÄnga olika typer av applikationer och versioner behövs endast smÄ lager av överföring.

Dessutom ger den fÀrdiga registratorn och taggningen av avbilder i Docker oss fÀrdiga primitiva för versionering och leverans av kod till produktion.

Docker, precis som vilken annan liknande teknologi som helst, ger oss en viss nivĂ„ av isolering för containrar direkt ur lĂ„dan. Till exempel, minnesisolering — varje container fĂ„r en grĂ€ns för hur mycket minne den kan anvĂ€nda, som inte fĂ„r överskridas. Det gĂ„r ocksĂ„ att isolera containrar baserat pĂ„ CPU-anvĂ€ndning. För oss var dock den standardiserade isoleringen otillrĂ€cklig. Men mer om det nedan.

Att direkt köra containrar pÄ servrar Àr bara en del av problemen. En annan del handlar om att placera containrar pÄ servrar. Vi mÄste förstÄ vilken container som kan placeras pÄ vilken server. Det Àr ingen enkel uppgift, eftersom containrar mÄste placeras sÄ tÀtt som möjligt pÄ servrarna utan att sÀnka hastigheten pÄ deras arbete. SÄdan placering kan vara komplex och ur perspektivet av fel tolerans. Ofta vill vi placera repliker av samma tjÀnst i olika stÀll och till och med i olika hallar i datacentret, för att vi inte ska förlora alla repliker av tjÀnsten vid ett fel.

Att distribuera containrar manuellt Ă€r inget alternativ nĂ€r du har 8000 servrar och 8000–16000 containrar.

Dessutom ville vi ge utvecklarna mer sjÀlvstÀndighet i resursfördelningen, sÄ att de kan placera sina tjÀnster i produktion sjÀlva, utan administratörens hjÀlp. Samtidigt ville vi behÄlla kontrollen, sÄ att ingen sekundÀr tjÀnst konsumerar alla resurser i vÄra datacenter.

Det Àr uppenbart att vi behöver ett hanteringslager som automatiskt skulle hantera detta.

HÀr har vi kommit till en enkel och tydlig bild som alla arkitekter Àlskar: tre rutor.

One-cloud — datacenter-nivĂ„ OS i Odnoklassniki

one-cloud-mĂ€stare — en redundanskluster som ansvarar för orkestrering av molnet. Utvecklaren skickar en manifest till mĂ€staren, som innehĂ„ller all nödvĂ€ndig information för att placera tjĂ€nsten. MĂ€staren ger order till de valda minionerna (maskiner som Ă€r avsedda att köra behĂ„llare) baserat pĂ„ denna information. PĂ„ minionerna finns vĂ„r agent, som tar emot order, ger sina egna order till Docker, och Docker konfigurerar Linux-kĂ€rnan för att köra den relevanta behĂ„llaren. Förutom att utföra kommandon, rapporterar agenten kontinuerligt till mĂ€staren om statusĂ€ndringar för bĂ„de minionmaskinen och de behĂ„llare som körs pĂ„ den.

Resursfördelning

LÄt oss nu titta nÀrmare pÄ uppgiften med mer komplex resursfördelning för mÄnga minioner.

BerÀkningsresurs i one-cloud Àr:

  • Den berĂ€kningskraft som en specifik uppgift anvĂ€nder.
  • MĂ€ngden minne som Ă€r tillgĂ€nglig för uppgiften.
  • NĂ€tverkstrafik. Varje minion har ett specifikt nĂ€tverksgrĂ€nssnitt med begrĂ€nsad bandbredd, sĂ„ uppgifter kan inte fördelas utan att ta hĂ€nsyn till den data som överförs via nĂ€tverket.
  • Diskenheter. Förutom, uppenbarligen, utrymmet för uppgiftens data tilldelar vi Ă€ven typ av disk: HDD eller SSD. Diskenheter kan hantera ett bestĂ€mt antal förfrĂ„gningar per sekund — IOPS. DĂ€rför, för uppgifter som genererar mer IOPS Ă€n vad en disk kan hantera, tilldelar vi Ă€ven ''spindlar'' — det vill sĂ€ga disk-enheter som mĂ„ste reserveras specifikt för uppgiften.

SÄ för nÄgon tjÀnst, till exempel user-cache, kan vi skriva de anvÀnda resurserna sÄ hÀr: 400 CPU-kÀrnor, 2,5 TB minne, 50 Gbit/s trafik i bÄda riktningarna, 6 TB utrymme pÄ HDD, placerad pÄ 100 spindlar. Eller i en mer bekant form enligt nedan:

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

Resurserna för tjÀnsten user-cache anvÀnder endast en del av alla tillgÀngliga resurser i produktionsinfrastruktur. DÀrför vill vi se till att user-cache inte plötsligt förbrukar mer resurser Àn det tilldelats, varken pÄ grund av en operatörsfel eller av annan anledning. Det vill sÀga att vi mÄste begrÀnsa resurserna. Men vad skulle vi kunna knyta kvoten till?

LĂ„t oss Ă„tervĂ€nda till vĂ„r starkt förenklade schematiska representation av komponenternas interaktion och rita om med fler detaljer – sĂ„ hĂ€r:

One-cloud — datacenter-nivĂ„ OS i Odnoklassniki

Vad som sticker ut:

  • Webbfrontend och musik anvĂ€nder isolerade kluster av samma applikationsserver.
  • Det gĂ„r att sĂ€rskilja logiska lager, som omfattar dessa kluster: fronter, cacher, lagrings- och datastyrningslager.
  • Frontend Ă€r heterogen, det Ă€r olika funktionella delsystem.
  • Cacher kan ocksĂ„ fördelas över delsystemet vars data de cachar.

LÄt oss rita om bilden igen:

One-cloud — datacenter-nivĂ„ OS i Odnoklassniki

Oj! Vi ser en hierarki! Det betyder att vi kan fördela resurser i större bitar: tillsÀtta en ansvarig utvecklare pÄ noden i denna hierarki, som motsvarar det funktionella delsystemet (som 'music' i bilden), och knyta en kvot till samma nivÄ i hierarkin. En sÄdan hierarki gör det ocksÄ möjligt att mer flexibelt organisera tjÀnster för att underlÀtta hanteringen. Till exempel, alla web, eftersom det Àr en mycket stor gruppering av servrar, delar vi upp i flera mindre grupper, som visas pÄ bilden som group1, group2.

Genom att ta bort överflödiga linjer kan vi skriva ner varje nod i vÄr bild mer platt: group1.web.front, api.music.front, user-cache.cache.

SÄ kommer vi till begreppet 'hierarkisk kö'. Den har ett namn, som 'group1.web.front'. Den tilldelas en kvot pÄ resurser och anvÀndarrÀttigheter. En person frÄn DevOps fÄr rÀtt att skicka tjÀnsten till kön, och en sÄdan medarbetare kan köra nÄgot i kön, medan en person frÄn OpsDev fÄr administratörsrÀttigheter, och nu kan han hantera kön, tilldela personer dit, ge dessa personer rÀttigheter och sÄ vidare. TjÀnster som körs i denna kö kommer att utföras inom kökvoten. Om den berÀknade kvoten för kö Àr otillrÀcklig för att köra alla tjÀnster samtidigt, kommer de att köras sekventiellt, och skapar dÀrmed sjÀlva kön.

LĂ„t oss titta nĂ€rmare pĂ„ tjĂ€nsterna. En tjĂ€nst har ett helt namn, vilket alltid inkluderar namnet pĂ„ kön. DĂ„ kommer tjĂ€nsten webbfronten att ha namnet ok-web.group1.web.front. Och tjĂ€nsten för applikationsservern som den kontaktar kommer att kallas ok-app.group1.web.frontVarje tjĂ€nst har en manifestfil som innehĂ„ller all nödvĂ€ndig information för distribution pĂ„ specifika maskiner: hur mycket resurser den hĂ€r uppgiften förbrukar, vilken konfiguration som krĂ€vs, hur mĂ„nga repliker som bör finnas, egenskaper för felhantering av denna tjĂ€nst. Efter distributionen av tjĂ€nsten pĂ„ maskinerna skapas dess instanser. De namnges ocksĂ„ entydigt — med instansnumret och tjĂ€nstens namn: 1.ok-web.group1.web.front, 2.ok-web.group1.web.front, 


Detta Àr mycket praktiskt: genom att bara titta pÄ namnet pÄ den körda containern kan vi genast ta reda pÄ mycket.

LÄt oss nu lÀra kÀnna vad dessa instanser faktiskt utför: uppgifterna.

Klassificering av uppgifter

Alla uppgifter i OK (och sÀkerligen pÄ andra stÀllen) kan delas in i grupper:

  • Uppgifter med kort svarstid — prod. För sĂ„dana uppgifter och tjĂ€nster Ă€r svarstid (latens) mycket viktig; hur snabbt varje begĂ€ran hanteras av systemet. Exempel pĂ„ uppgifter: webfrontend, cache, applikationsservrar, OLTP-lagring m.m.
  • BerĂ€kningsuppgifter — batch. HĂ€r Ă€r hastigheten för bearbetning av varje specifik begĂ€ran inte sĂ„ viktig. Det som betyder mest Ă€r hur mĂ„nga berĂ€kningar den hĂ€r uppgiften gör under en viss (lĂ„ng) tidsperiod (genomströmning). Detta omfattar alla MapReduce-uppgifter, Hadoop, maskininlĂ€rning, statistik.
  • Bakgrundsuppgifter — idle. För dessa uppgifter Ă€r varken latens eller genomströmning sĂ€rskilt viktig. HĂ€r ingĂ„r olika tester, migrationer, omberĂ€kningar, konverteringar av data frĂ„n ett format till ett annat. Å ena sidan liknar de berĂ€kningsuppgifter, Ă„ andra sidan Ă€r det inte sĂ„ viktigt hur snabbt de avslutas.

LÄt oss titta pÄ hur sÄdana uppgifter förbrukar resurser, till exempel centrala processorn.

Uppgifter med kort svarstid. Mönstret för CPU-förbrukning för en sÄdan uppgift ser ut som följande:

One-cloud — datacenter-nivĂ„ OS i Odnoklassniki

En begÀran kommer in frÄn anvÀndaren, uppgiften börjar anvÀnda alla tillgÀngliga CPU-kÀrnor, bearbetar, returnerar svaret, vÀntar pÄ nÀsta begÀran och stÄr stilla. NÀr nÀsta begÀran kommer in tar vi Äterigen allt som finns, berÀknar, och vÀntar pÄ nÀsta.

För att garantera minimi latens för en sÄdan uppgift mÄste vi ta maximalt förbrukade resurser och reservera rÀtt antal kÀrnor pÄ minionen (maskinen som kommer att utföra uppgiften). DÄ kommer formeln för reservationen för vÄr uppgift att bli:

alloc: cpu = 4 (max)

Och om vi har en minionmaskin med 16 kĂ€rnor kan vi exakt köra fyra sĂ„dana uppgifter pĂ„ den. Det Ă€r vĂ€rt att notera att medelprocessorförbrukningen för sĂ„dana uppgifter ofta Ă€r mycket lĂ„g — vilket Ă€r uppenbart eftersom en stor del av tiden uppgiften vĂ€ntar pĂ„ förfrĂ„gan och inte gör nĂ„got.

BerÀkningsuppgifter. De kommer att ha ett nÄgot annat mönster:

One-cloud — datacenter-nivĂ„ OS i Odnoklassniki

Medelresursförbrukningen för processorn för sÄdana uppgifter Àr ganska hög. Ofta vill vi att berÀkningsuppgiften ska slutföras inom en viss tid, sÄ vi mÄste reservera det minimiantal processorer som krÀvs för att hela berÀkningen ska slutföras pÄ en acceptabel tid. Formeln för reservering kommer att se ut sÄ hÀr:

alloc: cpu = [1,* )

"SnÀlla placera detta pÄ minionen dÀr det finns Ätminstone en ledig kÀrna, och sedan fÄr den anvÀnda allt som finns."

HĂ€r Ă€r effektiviteten redan betydligt bĂ€ttre Ă€n med uppgifter med kort försening. Men vinsten blir mycket större om man kombinerar bĂ„da typer av uppgifter pĂ„ samma minionmaskin och distribuerar dess resurser dynamiskt. NĂ€r en uppgift med kort försening behöver processorn — fĂ„r den den omedelbart, och nĂ€r resurserna inte lĂ€ngre behövs — överförs de till berĂ€kningsuppgiften, det vill sĂ€ga ungefĂ€r sĂ„ hĂ€r:

One-cloud — datacenter-nivĂ„ OS i Odnoklassniki

Men hur gör man detta?

Först, lÄt oss förstÄ prod och dess alloc: cpu = 4. Vi behöver reservera fyra kÀrnor. I Docker run kan detta göras pÄ tvÄ sÀtt:

  • Genom alternativet --cpuset=1-4, det vill sĂ€ga att tilldela uppgiften fyra bestĂ€mda kĂ€rnor pĂ„ maskinen.
  • AnvĂ€nd --cpuquota=400_000 --cpuperiod=100_000, tilldela en kvot för processor-tid, det vill sĂ€ga att ange att varje 100 ms verklig tid fĂ„r uppgiften inte konsumera mer Ă€n 400 ms processor-tid. Det blir samma fyra kĂ€rnor.

Men vilket av dessa sÀtt Àr rÀtt?

Cpuset ser ganska lockande ut. Om uppgiften har fyra dedikerade kÀrnor, betyder det att processorcacheminnet kommer att fungera maximalt effektivt. Detta har dock en bakside: vi skulle ta pÄ oss uppgiften att fördela berÀkningarna över de avlastade kÀrnorna istÀllet för operativsystemet, vilket Àr en ganska icke-trivial uppgift, sÀrskilt om vi försöker placera batchuppgifter pÄ en sÄdan maskin. Tester har visat att en kvotvariant passar bÀttre hÀr: dÄ har operativsystemet mer frihet att vÀlja kÀrna för att utföra uppgiften för stunden och processortiden fördelas mer effektivt.

LÄt oss ta reda pÄ hur man gör reservreservation i Docker för det minimiantal kÀrnor. En kvot för batchuppgifter Àr redan otillÀmplig, eftersom det inte Àr nödvÀndigt att begrÀnsa ett maximum, det rÀcker med att garantera ett minimum. HÀr passar alternativet bra: docker run --cpushares.

Vi har kommit överens om att om en batch krĂ€ver garantin för minst ett kĂ€rn, anger vi --cpushares=1024, och om det behövs minst tvĂ„ kĂ€rnor, anger vi --cpushares=2048. Cpu shares pĂ„verkar inte fördelningen av processortid sĂ„ lĂ€nge det finns tillrĂ€ckligt. SĂ„ledes, om prod för nĂ€rvarande inte anvĂ€nder alla sina fyra kĂ€rnor — begrĂ€nsar inget batchuppgifterna, och de kan anvĂ€nda extra processortid. Men i en situation med brist pĂ„ CPU, om prod har anvĂ€nt alla sina fyra kĂ€rnor och nĂ„tt sin kvot — kommer den Ă„terstĂ„ende processortiden att delas proportionellt mellan cpushares, det vill sĂ€ga i en situation med tre lediga kĂ€rnor fĂ„r uppgiften med 1024 cpushares en kĂ€rna, medan de andra tvĂ„ fĂ„r uppgiften med 2048 cpushares.

Men anvÀndning av kvota och shares Àr inte tillrÀckligt. Vi mÄste se till att en uppgift med kort fördröjning fÄr prioritet över batchuppgiften vid fördelningen av processortid. Utan sÄdan prioritering kommer batchuppgiften att ta all processortid i det ögonblick nÀr det behövs av prod. I Docker run finns det inga alternativ för prioritering av containrar, men CPU-schemalÀggarpolicyer i Linux kommer till hjÀlp. Du kan lÀsa mer om dem ). Ett typiskt exempel pÄ anvÀndning:, men inom ramen för denna artikel ska vi kort gÄ igenom dem:

  • SCHED_OTHER
    FÄr som standard alla vanliga anvÀndarprocesser pÄ en Linux-maskin.
  • SCHED_BATCH
    Avsedd för resurskrÀvande processer. NÀr en uppgift placeras i processorn införs sÄ kallad aktiveringsstraff: en sÄdan uppgift har större sannolikhet att mindre fÄ processorns resurser om den för nÀrvarande anvÀnds av en uppgift med SCHED_OTHER.
  • SCHED_IDLE
    En bakgrundsprocess med mycket lĂ„g prioritet, till och med lĂ€gre Ă€n nice –19. Vi anvĂ€nder vĂ„rt öppna kĂ€llkods-bibliotek. one-nio, för att sĂ€tta nödvĂ€ndig policy vid start av containern genom att kalla pĂ„

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

Men Àven om du inte programmerar i Java kan samma sak göras med kommandot chrt:

chrt -i 0 $pid

LÄt oss sammanfatta alla vÄra isoleringsnivÄer i en tabell för tydlighet:

Isoleringsklass
Exempel alloc
Docker run-alternativ
sched_setscheduler chrt*

Prod
cpu = 4
--cpuquota=400000 --cpuperiod=100000
SCHED_OTHER

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

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

*Om du kör chrt inuti containern kan du behöva capability sys_nice, eftersom Docker som standard tar bort denna capability vid start av containern.

Men uppgifter konsumerar inte bara processor, utan ocksÄ trafik, vilket pÄverkar fördröjningen av nÀtverksuppgiften Ànnu mer Àn felaktig fördelning av processorresurser. DÀrför vill vi naturligtvis fÄ exakt samma bild för trafiken. Det vill sÀga, nÀr prod-uppgiften skickar paket till nÀtverket, kvoterar vi den maximala hastigheten (formel alloc: lan=[*,500mbps) ), som prod kan göra detta. För batch garanterar vi endast en minimal överföringskapacitet, men begrÀnsar inte den maximala (formel alloc: lan=[10Mbps,*) ) I detta fall ska trafiken frÄn prod prioriteras över batch-uppgifter.
HÀr har Docker inga primitiva verktyg som vi kan anvÀnda. Men vi fÄr hjÀlp av Linux Traffic Control. Vi lyckades uppnÄ önskat resultat med hjÀlp av disciplinen Hierarchical Fair Service Curve. Med den delar vi upp trafiken i tvÄ klasser: högprioriterad prod och lÄgt prioriterad batch/idle. Sammanfattningsvis blir konfigurationen för utgÄende trafik sÄ hÀr:

One-cloud — datacenter-nivĂ„ OS i Odnoklassniki

hĂ€r 1:0 — "rot qdisc" för disciplinen hsfc; 1:1 — underklass hsfc med en gemensam bandbredd pĂ„ 8 Gbit/s, under vilken alla underklasser av containrar placeras; 1:2 — underklass hsfc som Ă€r gemensam för alla batch- och idle-uppgifter med en "dynamisk" grĂ€ns som beskrivs nedan. Övriga underklasser av hsfc Ă€r dedikerade klasser för aktuellt körande prod-containrar med grĂ€nser som motsvarar deras manifester, — 450 och 400 Mbit/s. Varje hsfc-klass tilldelas en qdisc-kö fq eller fq_codel beroende pĂ„ versionen av linux-kĂ€rnan, för att undvika paketförluster vid trafiktoppar.

Vanligtvis anvĂ€nds tc-discipliner för att prioritera endast utgĂ„ende trafik. Men vi vill Ă€ven prioritera inkommande trafik — eftersom en batch-uppgift helt kan ta över hela den inkommande kanalen genom att fĂ„, till exempel, ett stort paket inkommande data för map&reduce. För detta anvĂ€nder vi modulen ifb, som skapar en virtuell ifbX-grĂ€nssnitt för varje nĂ€tverksgrĂ€nssnitt och omdirigerar inkommande trafik frĂ„n grĂ€nssnittet till utgĂ„ende pĂ„ ifbX. DĂ€refter fungerar alla de samma disciplinerna för kontroll av utgĂ„ende trafik för ifbX, för vilken hsfc-konfigurationen kommer att vara mycket lik:

One-cloud — datacenter-nivĂ„ OS i Odnoklassniki

Under experimenten har vi faststĂ€llt att hsfc ger de bĂ€sta resultaten nĂ€r klass 1:2 för icke-prioriterad batch/idle-trafik begrĂ€nsas pĂ„ minion-maskiner till högst en viss fri bandbredd. Annars pĂ„verkar icke-prioriterad trafik latensen för prod-uppgifter för mycket. Den aktuella fri bandbredden bestĂ€ms av miniond varje sekund genom att mĂ€ta den genomsnittliga trafikförbrukningen för alla prod-uppgifter pĂ„ den aktuella minionen One-cloud — datacenter-nivĂ„ OS i Odnoklassniki och subtrahera den frĂ„n bandbredden för nĂ€tverksgrĂ€nssnittet One-cloud — datacenter-nivĂ„ OS i Odnoklassniki med en liten marginal, det vill sĂ€ga.

One-cloud — datacenter-nivĂ„ OS i Odnoklassniki

Bandbredden bestÀms för inkommande och utgÄende trafik oberoende. I enlighet med de nya vÀrdena Äterkonfigurerar miniond grÀnsen för den icke-prioriterade klassen 1:2.

SÄledes har vi implementerat alla tre isoleringsklasser: prod, batch och idle. Dessa klasser pÄverkar kraftigt prestanda för uppgifterna. DÀrför har vi beslutat att placera denna egenskap högst upp i hierarkin, sÄ att nÀr man tittar pÄ namnet pÄ den hierarkiska kön kan man direkt se vad vi har att göra med:

One-cloud — datacenter-nivĂ„ OS i Odnoklassniki

Alla vÄra bekanta web och music fronter placeras dÄ i hierarkin under prod. För exempel under batch lÄt oss placera tjÀnsten music catalog, som periodiskt skapar en katalog över lÄtar frÄn uppsÀttningen av mp3-filer som laddats upp till "Odnoklassniki". Ett exempel pÄ en tjÀnst under idle kan vara music transformer, som normaliserar ljudnivÄn pÄ musiken.

Genom att Äterigen ta bort överflödiga linjer kan vi skriva namnen pÄ vÄra tjÀnster mer platt, genom att lÀgga till klass för uppgiftsisolering i slutet av tjÀnstens fullstÀndiga namn: web.front.prod, catalog.music.batch, transformer.music.idle.

Och nu, nÀr vi ser pÄ tjÀnstens namn, förstÄr vi inte bara vilken funktion den utför, utan ocksÄ dess klass för isolering, vilket betyder dess kritikalitet etc.

Allt Àr fantastiskt, men det finns en bitter sanning. Det Àr omöjligt att helt isolera uppgifter som körs pÄ samma maskin.

Vad vi har lyckats Ă„stadkomma: om batch intensivt förbrukar bara CPU-resurser, sĂ„ fungerar Linux inbyggda CPU-scheduler mycket bra, och pĂ„verkan pĂ„ prod-uppgiften Ă€r nĂ€stan obefintlig. Men om denna batch-uppgift börjar arbeta aktivt med minnet, sĂ„ visar ömsesidig pĂ„verkan sig. Detta hĂ€nder eftersom prod-uppgiften "tappar" processorcacheminnet — med resultatet att trĂ€ffarna i cachen ökar, och processorn bearbetar prod-uppgiften lĂ„ngsammare. En sĂ„dan batch-uppgift kan öka latensen för vĂ„r typiska prod-container med 10%.

Att isolera trafiken Àr Ànnu svÄrare eftersom moderna nÀtverkskort har en intern kö för paket. Om ett paket frÄn batch-uppgiften kommer in först, sÄ skickas det först över kabeln, och det kan vi inte Àndra pÄ.

Dessutom har vi hittills bara lyckats lösa problemet med prioritering av TCP-trafik: för UDP fungerar inte strategin med hsfc. Även nĂ€r det kommer till TCP-trafik, om batch-uppgiften genererar mycket trafik, ger det ocksĂ„ omkring 10% ökning av latensen för prod-uppgiften.

Fel-tolerans

Ett av mĂ„len nĂ€r vi utvecklade one-cloud var att förbĂ€ttra motstĂ„ndskraften hos Odnoklassniki. DĂ€rför skulle jag nu vilja titta nĂ€rmare pĂ„ möjliga fel- och nödsituationer. LĂ„t oss börja med ett enkelt scenario — med ett containerfel.

En container kan misslyckas pÄ flera sÀtt. Det kan vara ett experiment, en bugg eller ett fel i manifestet, vilket gör att en produktuppgift börjar konsumera fler resurser Àn vad som anges i manifestet. Vi hade ett fall dÀr en utvecklare implementerade en komplicerad algoritm, omarbetade den flera gÄnger och förvirrade sig sÄ mycket att uppgiften slutligen blev ganska komplex och fastnade i en loop. Eftersom produktuppgiften har högre prioritet Àn alla andra pÄ samma noder, började den att konsumera alla tillgÀngliga processorresurser. I denna situation rÀddade isolering, specifikt en kvot pÄ processoranvÀndningen. Om uppgiften har en kvot, kommer den inte att konsumera mer. DÀrför mÀrkte batch- och andra produktuppgifter som kördes pÄ samma maskin ingenting.

Den andra möjliga problemet Àr att containern kraschar. HÀr rÀddas vi av restart-policyer, som alla kÀnner till, Docker hanterar detta utmÀrkt. NÀstan alla produktuppgifter har en restart-policy "always". Ibland anvÀnder vi "on_failure" för batchuppgifter eller för att felsöka produktcontainrar.

Vad kan man göra nÀr en hel nod Àr otillgÀnglig?

SjÀlvklart kan vi starta containern pÄ en annan maskin. Det mest intressanta hÀr Àr vad som hÀnder med IP-adressen (adresserna) som tilldelas containern.

Vi kan tilldela containrar samma IP-adresser som noderna dÀr dessa containrar körs. NÀr containern startas pÄ en annan maskin, Àndras dess IP-adress, och alla klienter mÄste förstÄ att containern har flyttat, och nu mÄste de anvÀnda en annan adress, vilket krÀver en separat Service Discovery-tjÀnst.

Service Discovery Àr praktiskt. Det finns mÄnga lösningar pÄ marknaden med olika grad av felresistens för att etablera en tjÀnsteregister. I mÄnga sÄdana lösningar implementeras logik för lastbalansering, lagring av ytterligare konfiguration i form av en nyckel-vÀrde-lager, etc.
Vi vill dock gÀrna undvika behovet av att införa ett separat register, eftersom det skulle innebÀra införandet av ett kritiskt system som anvÀnds av alla tjÀnster i produktion. Det innebÀr att det Àr en potentiell felpunkt, och man mÄste vÀlja eller utveckla en mycket felresistent lösning, vilket uppenbarligen Àr mycket komplicerat, tidskrÀvande och kostsamt.

En stor nackdel Àr att för att vÄr gamla infrastruktur ska kunna fungera med den nya mÄste vi omprogrammera alla uppgifter för att anvÀnda ett Service Discovery-system. Det finns EXTREMT mycket arbete, och ibland Àr det praktiskt taget omöjligt, sÀrskilt nÀr det gÀller lÄgnivÄ-enheter som arbetar pÄ OS-kÀrnnivÄ eller direkt med hÄrdvara. Att implementera denna funktionalitet med hjÀlp av etablerade lösningsmönster, som till exempel side-car , skulle innebÀra extra belastning pÄ vissa stÀllen, och i andra fall skulle det komplicera drift och skapa ytterligare felhanteringsscenarier. Vi ville inte komplicera saker, sÄ vi beslutade att göra anvÀndningen av Service Discovery valfri.

I one-cloud följer IP-adressen efter containern, det vill sÀga varje instans av uppgiften har sin egen IP-adress. Denna adress Àr "statisk": den knyts till varje instans vid första gÄngen tjÀnsten skickas till molnet. Om tjÀnsten under sin livstid har haft ett varierande antal instanser, kommer sÄ mÄnga IP-adresser att knytas till den som det maximala antalet instanser var.

Dessa adresser Àndras inte senare: de tilldelas en gÄng och kvarstÄr under hela tjÀnstens livstid i produktionen. IP-adresserna följer efter containrarna i nÀtverket. Om containern flyttas till en annan nod, följer ocksÄ adressen med.

SĂ„ledes förĂ€ndras sambandet mellan tjĂ€nstens namn och listan över dess IP-adresser mycket sĂ€llan. Om vi ser pĂ„ namnen pĂ„ tjĂ€nsteinstanserna som vi nĂ€mnde i början av artikeln (1.ok-web.group1.web.front.prod, 2.ok-web.group1.web.front.prod, 
), kommer vi att mĂ€rka att de liknar FQDN som anvĂ€nds i DNS. Det stĂ€mmer, för att visa namnen pĂ„ tjĂ€nsternas instanser i deras IP-adresser anvĂ€nder vi DNS-protokollet. Detta DNS returnerar Ă€ven alla reserverade IP-adresser för alla containrar — bĂ„de aktiva och inaktiva (antag att tre repliker anvĂ€nds, men fem adresser Ă€r reserverade — alla fem kommer att returneras). Kunderna, efter att ha fĂ„tt denna information, kommer att försöka etablera en anslutning till alla fem repliker — och pĂ„ sĂ„ sĂ€tt identifiera vilka som fungerar. Denna metod för tillgĂ€nglighetsbestĂ€mning Ă€r betydligt mer tillförlitlig, eftersom varken DNS eller Service Discovery Ă€r involverade, vilket eliminerar svĂ„ra problem med att sĂ€kerstĂ€lla informationens aktualitet och systemens redundans. Dessutom kan vi i kritiska tjĂ€nster, som Ă€r avgörande för hela portalens funktion, helt undvika att anvĂ€nda DNS och istĂ€llet direkt ange IP-adresser i konfigurationen.

Implementeringen av en sĂ„dan överföring av IP bakom containrarna kan vara en utmaning — och vi kommer att stanna vid hur detta fungerar i följande exempel:

One-cloud — datacenter-nivĂ„ OS i Odnoklassniki

Anta att one-cloud-mĂ€staren ger order till minion M1 att starta 1.ok-web.group1.web.front.prod med adressen 1.1.1.1. Minionen kör BIRD, som annonserar denna adress till specifika servrar route reflector. De har en BGP-session med nĂ€tverksutrustningen, som slussar vidare routen för adressen 1.1.1.1 till M1. M1 vidarebefordrar paketen inuti containern med hjĂ€lp av Linux. Det finns tre route reflectors, eftersom detta Ă€r en mycket kritisk del av one-cloud-infrastrukturen — utan dem kommer nĂ€tverket i one-cloud att sluta fungera. Vi placerar dem i olika rack, helst i olika datahallar, för att minska risken för att alla tre skulle sluta fungera samtidigt.

LÄt oss nu anta att kommunikationen mellan one-cloud-mÀstaren och minion M1 har brutits. One-cloud-mÀstaren kommer nu att agera under antagandet att M1 har gÄtt helt ner. Det vill sÀga, den kommer att ge order till minion M2 att starta web.group1.web.front.prod med samma IP-adress 1.1.1.1. Nu har vi tvÄ konfliktfulla rutter i nÀtverket för 1.1.1.1: pÄ M1 och M2. För att lösa sÄdana konflikter anvÀnder vi Multi Exit Discriminator, som anges i BGP-annonsen. Detta Àr ett nummer som visar vikten pÄ den annonserade rutten. Rutten med lÀgre MED-vÀrde kommer att vÀljas av de konfliktfulla. Masten one-cloud stöder MED som en integrerad del av container-IP-adresser. Första gÄngen skrivs adressen ut med ett tillrÀckligt stort MED = 1 000 000. I en situation med en nödflyttning av containern minskar masten MED, och M2 fÄr en order att annonsera IP-adressen 1.1.1.1 med MED = 999 999. Instansen som körs pÄ M1 kommer att förbli utan kontakt, och dess öde intresserar oss inte mycket förrÀn kontakten med masten ÄterstÀlls, dÄ den kommer att stoppas som en gammal dubblett.

Avbrott

Alla datacenterhanteringssystem hanterar alltid mindre fel pÄ ett acceptabelt sÀtt. Avbrott av en container Àr nÀstan normen överallt.

LÄt oss titta pÄ hur vi hanterar ett avbrott, till exempel strömavbrott i en eller flera hallar i datacentret.

Vad betyder ett avbrott för datacenterhanteringssystemet? Först och frÀmst betyder det en massiv samtidigt fel av mÄnga maskiner, och systemet mÄste migrera ett stort antal containrar samtidigt. Men om avbrottet Àr mycket omfattande kan det hÀnda att alla uppgifter inte kan omplaceras till andra noder, eftersom resurskapaciteten i datacentret faller under 100 % belastning.

Ofta Ätföljs avbrott av förlust av kontrollskiktet. Detta kan ske pÄ grund av att dess utrustning gÄr sönder, men oftare beror det pÄ att avbrotten inte testas, och kontrollskiktet sjÀlv kraschar pÄ grund av ökad belastning.

Vad kan göras med allt detta?

Massaöverföringar innebÀr att det uppstÄr ett stort antal ÄtgÀrder, överföringar och placeringar i infrastrukturen. Varje överföring kan ta tid som krÀvs för att överföra och packa upp containerbilder till noder, starta och initiera containrar etc. DÀrför Àr det önskvÀrt att mer viktiga uppgifter startas före mindre viktiga.

LÄt oss Äterigen titta pÄ den bekanta hierarkin av tjÀnster och försöka avgöra vilka uppgifter vi vill köra först.

One-cloud — datacenter-nivĂ„ OS i Odnoklassniki

SjĂ€lvklart, det Ă€r de processer som direkt deltar i behandlingen av anvĂ€ndarförfrĂ„gningar, dvs. prod. Vi anger detta med placeringsprioritet — ett nummer som kan tilldelas en kö. Om en kö har en högre prioritet placeras dess tjĂ€nster först.

PĂ„ prod tilldelar vi högre prioriteringar, 0; pĂ„ batch — nĂ„got lĂ€gre, 100; pĂ„ idle — Ă€nnu lĂ€gre, 200. Prioriteter tillĂ€mpas hierarkiskt. Alla uppgifter lĂ€ngre ner i hierarkin har motsvarande prioritet. Om vi vill att cache ska köras före front-end i prod, tilldelar vi prioriteten cache = 0 och front-subkön = 1. Om vi till exempel vill att huvudportalen ska starta först bland front-end, medan music front körs efterĂ„t, kan vi tilldela den senare en lĂ€gre prioritet — 10.

NÀsta problem Àr brist pÄ resurser. SÄ, vi har haft stora utrustningsavbrott, hela hallar i datacentret, och vi har startat sÄ mÄnga tjÀnster att det nu inte finns tillrÀckliga resurser för alla. Vi mÄste avgöra vilka uppgifter vi kan offra för att de viktigaste kritiska tjÀnsterna ska fungera.

One-cloud — datacenter-nivĂ„ OS i Odnoklassniki

Till skillnad frÄn placeringsprioriteten kan vi inte helt enkelt offra alla batch-uppgifter, vissa av dem Àr viktiga för portalen. DÀrför har vi sÀrskilt identifierat utdragningsprioritet För uppgifter. Vid placering kan en uppgift med högre prioritet utdriva, dvs. stoppa en uppgift med lÀgre prioritet, om det inte finns nÄgra lediga minions. Om sÄ Àr fallet kommer en uppgift med lÄg prioritet sannolikt att förbli oplacerad, dvs. det kommer inte att finnas nÄgon lÀmplig minion med tillrÀckliga lediga resurser för den.

I vÄr hierarki Àr det mycket enkelt att ange en sÄdan utdragningsprioritet sÄ att prod- och batch-uppgifter utdriver eller stoppar idle-uppgifter, men inte varandra, genom att ange en prioritet för idle som Àr 200. Precis som med placeringsprioritet kan vi anvÀnda vÄr hierarki för att beskriva mer komplexa regler. Till exempel kan vi ange att musikinnehÄll offras om vi inte har tillrÀckliga resurser för huvudwebbportalen, genom att sÀtta lÀgre prioritet pÄ de relevanta noderna: 10.

Hela datacentrets krascher

Varför kan hela datacentret fallera? Naturkatastrofer. Det fanns ett bra inlÀgg om hur orkanen pÄverkade datacentrets verksamhet. Elementet kan beskrivas som hemlösa, som en gÄng skadade fiberoptiken i en avloppsledning, vilket gjorde att datacentret helt förlorade kontakten med andra platser. Orsaken till haveri kan ocksÄ vara mÀnsklig: en operatör kan ge en sÄdan direktiv att hela datacentret gÄr ner. Detta kan hÀnda pÄ grund av allvarliga buggar. Sammanfattningsvis, datacenter kraschar - det Àr inte ovanligt. Det hÀnder hos oss nÄgra gÄnger per Är.

Och detta Àr vad vi gör för att ingen #överbrygga ska posta pÄ Twitter.

Den första strategin Àr isolering. Varje instans av one-cloud Àr isolerad och kan bara hantera maskiner frÄn ett enda datacenter. Det innebÀr att förlust av molnet pÄ grund av buggar eller felaktiga kommandon frÄn operatören - det Àr bara förlust av ett datacenter. Vi Àr förberedda för detta: det finns en reservpolicy dÀr applikations- och datakopior placeras i alla datacenter. Vi anvÀnder hög tillgÀnglighet i databaserna och testar regelbundet systemfel.
Eftersom vi idag har fyra datacenter, finns det ocksÄ fyra separata, helt isolerade instanser av one-cloud.

Denna metod skyddar inte bara mot fysiska fel, utan kan ocksÄ skydda mot operatörsfel.

Vad mer kan göras med det mÀnskliga faktorn? NÀr operatören ger molnet nÄgot konstigt eller potentiellt farligt kommando, kan de plötsligt behöva lösa en liten uppgift för att kontrollera hur noga de har tÀnkt. Till exempel, om det handlar om ett massavbrott av mÄnga kopior eller bara ett konstigt kommando - att minska antalet kopior eller Àndra namnet pÄ en bild i stÀllet för bara versionsnumret i den nya manifestfilen.

One-cloud — datacenter-nivĂ„ OS i Odnoklassniki

Sammanfattning

UtmÀrkande drag av one-cloud:

  • En hierarkisk och tydlig namngivningsschema för tjĂ€nster och containrar, vilket gör att man snabbt kan förstĂ„ vilken uppgift det handlar om, vad den relaterar till, hur den fungerar och vem som ansvarar för den.
  • Vi tillĂ€mpar vĂ„r teknik för att kombinera prod- och batch-uppgifter pĂ„ minioner för att öka effektiviteten av resursdelning. IstĂ€llet för cpuset anvĂ€nder vi CPU-quota, shares, policyer för CPU-schemalĂ€ggning och Linux QoS.
  • Att helt isolera containrar som körs pĂ„ en maskin har vi inte lyckats med, men deras ömsesidiga pĂ„verkan förblir inom 20%.
  • Att organisera tjĂ€nster i en hierarki hjĂ€lper vid automatiskt felavhjĂ€lpande med hjĂ€lp av placering och utstötningsprioriteter.

FAQ

Varför vi inte valde en fÀrdig lösning.

  • Olika klasser av isolering av uppgifter krĂ€ver olika logik för placering pĂ„ minioner. Medan produktionsuppgifter kan placeras enkelt genom resursreservering, behöver batch och idle placeras genom att övervaka den faktiska resursutnyttjandet pĂ„ maskinminionerna.
  • Behov av att ta hĂ€nsyn till resurser som uppgifterna konsumerar, sĂ„som:
    • nĂ€tverksbandbredd;
    • typer och "spindlar" av diskar.
  • Behov av att ange prioriteringar för tjĂ€nster vid avbrott, rĂ€ttigheter och kvoter för team pĂ„ resurser, vilket löses med hierarkiska köer i one-cloud.
  • Behov av att ha mĂ€nskliga namn för containrar för att minska reaktionstiden pĂ„ avbrott och incidenter.
  • OförmĂ„ga att implementera Service Discovery överallt samtidigt; behovet av att samexistera lĂ€nge med uppgifter som Ă€r placerade pĂ„ fysiska vĂ€rdar – vilket löses med "statisk" IP-adresser som följer containrarna, och dĂ€rmed behovet av unik integration med en stor nĂ€tverksinfrastruktur.

Alla dessa funktioner skulle krĂ€va betydande ombyggnader av de befintliga lösningarna, och efter att ha bedömt mĂ€ngden arbete insĂ„g vi att vi skulle kunna utveckla vĂ„r egen lösning med ungefĂ€r samma arbetsinsats. Men vĂ„r lösning kommer att vara mycket enklare att anvĂ€nda och utveckla – den har inga onödiga abstraktioner som stöder funktionalitet vi inte behöver.

Till alla som lĂ€ser de sista raderna – tack för ert tĂ„lamod och uppmĂ€rksamhet!

KĂ€lla: habr.com

Köp pĂ„litlig webbhotell med DDoS-skydd, VPS VDS-servrar đŸ”„ Köp pĂ„litlig webbhotell med DDoS-skydd, VPS VDS-servrar | ProHoster