Ühe pilve – andmekeskuse taseme OS Odnoklassnikis

Ühe pilve – andmekeskuse taseme OS Odnoklassnikis

Aloha, inimesed! Minu nimi on Oleg Anastasjev, töötan Odnoklassnikis platvormi meeskonnas. Ja peale minu töötab Odnoklassnikis palju riistvara. Meil on neli andmekeskust, kus on umbes 500 riiulit enam kui 8 tuhande serveriga. Mingil hetkel mõistsime, et uue juhtimissüsteemi kasutuselevõtt võimaldab tõhusamalt laadida seadmeid, hõlbustada juurdepääsuhaldust, automatiseerida arvutusressursside (ümber)jaotamist, kiirendada uute teenuste käivitamist ja kiirendada reageerimist. suuremahuliste õnnetuste korral.

Mis sellest välja tuli?

Peale minu ja hulga riistvara töötavad selle riistvaraga ka inimesed: insenerid, kes asuvad otse andmekeskustes; võrgutöötajad, kes seadistavad võrgutarkvara; administraatorid ehk SRE-d, kes pakuvad infrastruktuuri vastupidavust; ja arendusmeeskonnad, igaüks neist vastutab osa portaali funktsioonide eest. Nende loodud tarkvara töötab umbes nii:

Ühe pilve – andmekeskuse taseme OS Odnoklassnikis

Kasutajate taotlusi võetakse vastu mõlemal põhiportaali esiküljel www.ok.ruja teistel, näiteks muusika API rindel. Äriloogika töötlemiseks kutsuvad nad rakendusserverit, mis päringu töötlemisel helistab vajalikele spetsialiseeritud mikroteenustele - one-graph (sotsiaalsete ühenduste graafik), kasutaja vahemälu (kasutajaprofiilide vahemälu) jne.

Kõik need teenused on juurutatud paljudele masinatele ja igaühel neist on vastutavad arendajad, kes vastutavad moodulite toimimise, töötamise ja tehnoloogilise arengu eest. Kõik need teenused töötavad riistvaraserverites ja kuni viimase ajani käivitasime iga serveri kohta täpselt ühe ülesande, st see oli spetsialiseerunud konkreetsele ülesandele.

Miks nii? Sellel lähenemisviisil oli mitmeid eeliseid:

  • Kergendust tundev massijuhtimine. Oletame, et ülesande jaoks on vaja mõnda teeki, mõnda seadet. Ja siis määratakse server täpselt ühte kindlasse rühma, kirjeldatakse selle grupi cfengine'i poliitikat (või seda on juba kirjeldatud) ning see konfiguratsioon levitatakse tsentraalselt ja automaatselt kõikidesse selle rühma serveritesse.
  • Lihtsustatud diagnostika. Oletame, et vaatate keskprotsessori suurenenud koormust ja mõistate, et selle koormuse saab genereerida ainult sellel riistvaraprotsessoril töötav ülesanne. Süüdistaja otsimine lõpeb väga kiiresti.
  • Lihtsustatud jälgimine. Kui serveriga on midagi valesti, annab monitor sellest teada ja sa tead täpselt, kes on süüdi.

Mitmest koopiast koosnevale teenusele eraldatakse mitu serverit – igaühele üks. Seejärel jaotatakse teenuse arvutusressurss väga lihtsalt: teenuses olevate serverite arv, maksimaalne ressursside hulk, mida see võib tarbida. “Lihtne” ei tähenda siin seda, et seda oleks lihtne kasutada, vaid selles mõttes, et ressursside jaotamine toimub käsitsi.

See lähenemine võimaldas ka meil seda teha spetsiaalsed raua konfiguratsioonid selles serveris töötava ülesande jaoks. Kui ülesanne salvestab suuri andmemahtusid, siis kasutame 4U serverit, mille šassii on 38 kettaga. Kui ülesanne on puhtalt arvutuslik, siis saame osta odavama 1U serveri. See on arvutuslikult tõhus. Muuhulgas võimaldab selline lähenemine kasutada neli korda vähem masinaid, mille koormus on võrreldav ühe sõbraliku suhtlusvõrgustikuga.

Selline arvutusressursside kasutamise efektiivsus peaks tagama ka majandusliku efektiivsuse, kui lähtuda eeldusest, et kõige kallim on serverid. Pikka aega oli riistvara kõige kallim ja me nägime palju vaeva riistvara hinna alandamise nimel, mõeldes välja tõrketaluvuse algoritmid, et vähendada riistvara töökindluse nõudeid. Ja täna oleme jõudnud faasi, kus serveri hind ei ole enam määrav. Kui te ei arvesta uusima eksootikaga, pole riiulis olevate serverite konkreetne konfiguratsioon oluline. Nüüd on meil veel üks probleem - andmekeskuse serveri poolt hõivatud ruumi hind, see tähendab riiulis oleva ruumi hind.

Mõistes, et see nii on, otsustasime arvutada, kui tõhusalt me ​​nagid kasutame.
Võtsime majanduslikult põhjendatud serverite hulgast võimsaima serveri hinna, arvutasime välja, kui palju selliseid servereid saaksime rackidesse paigutada, kui palju ülesandeid nende peal jookseme vana mudeli “üks server = üks ülesanne” alusel ja kui palju selliseid. ülesanded saaksid seadmeid kasutada. Nad lugesid ja valasid pisaraid. Selgus, et meie efektiivsus nagide kasutamisel on umbes 11%. Järeldus on ilmne: peame suurendama andmekeskuste kasutamise efektiivsust. Tundub, et lahendus on ilmne: peate ühes serveris korraga käivitama mitu ülesannet. Kuid siit saavad alguse raskused.

Masskonfiguratsioon muutub oluliselt keerulisemaks – nüüd on võimatu serverile ühte rühma määrata. Nüüd saab ju ühes serveris käivitada mitu erinevate käskude ülesannet. Lisaks võib konfiguratsioon olla erinevate rakenduste jaoks vastuoluline. Samuti muutub diagnoosimine keerulisemaks: kui näete serveris suurenenud protsessori- või kettatarbimist, ei tea te, milline ülesanne probleeme põhjustab.

Kuid peamine on see, et samas masinas töötavate ülesannete vahel pole isolatsiooni. Siin on näiteks graafik serveriülesande keskmisest reageerimisajast enne ja pärast teise arvutusrakenduse käivitamist samas serveris, mis ei ole kuidagi seotud esimesega - põhiülesande reageerimisaeg on oluliselt suurenenud.

Ühe pilve – andmekeskuse taseme OS Odnoklassnikis

Ilmselt peate ülesandeid käivitama kas konteinerites või virtuaalsetes masinates. Kuna peaaegu kõik meie ülesanded töötavad ühe operatsioonisüsteemi (Linux) all või on selle jaoks kohandatud, ei pea me toetama paljusid erinevaid operatsioonisüsteeme. Seetõttu pole virtualiseerimine vajalik, kuna see on täiendava üldkulude tõttu vähem efektiivne kui konteineriseerimine.

Konteinerite teostusena ülesannete käitamiseks otse serverites on Docker hea kandidaat: failisüsteemi pildid lahendavad hästi konfliktsete konfiguratsioonidega seotud probleeme. Asjaolu, et kujutised võivad koosneda mitmest kihist, võimaldab meil oluliselt vähendada nende infrastruktuuris juurutamiseks vajalikku andmehulka, eraldades ühised osad eraldi aluskihtideks. Seejärel salvestatakse põhilised (ja kõige mahukamad) kihid kogu infrastruktuuris üsna kiiresti vahemällu ning paljude erinevat tüüpi rakenduste ja versioonide edastamiseks tuleb üle kanda vaid väikesed kihid.

Lisaks annavad Dockeri valmis register ja piltide märgistamine meile valmis primitiivid versioonide loomiseks ja koodi tootmisse edastamiseks.

Docker, nagu mis tahes muu sarnane tehnoloogia, pakub meile karbist väljavõetuna teatud mahuti isolatsiooni. Näiteks mälu isoleerimine – igale konteinerile antakse masina mälu kasutamise limiit, millest kaugemale see ei tarbi. Samuti saate konteinereid isoleerida protsessori kasutuse alusel. Meie jaoks aga standardsest isolatsioonist ei piisanud. Aga sellest lähemalt allpool.

Konteinerite otse käivitamine serverites on vaid osa probleemist. Teine osa on seotud konteinerite hostimisega serverites. Peate aru saama, millise konteineri millisesse serverisse saab paigutada. See pole nii lihtne ülesanne, sest konteinerid tuleb serveritesse paigutada võimalikult tihedalt, ilma nende kiirust vähendamata. Selline paigutus võib olla keeruline ka veataluvuse seisukohast. Tihti soovime sama teenuse koopiaid paigutada erinevatesse riiulitesse või isegi andmekeskuse erinevatesse ruumidesse, et riiuli või ruumi rikke korral ei kaotaks me kohe kõiki teenuse koopiaid.

Konteinerite käsitsi levitamine pole valik, kui teil on 8 tuhat serverit ja 8–16 tuhat konteinerit.

Lisaks soovisime anda arendajatele suuremat sõltumatust ressursside jaotamisel, et nad saaksid oma teenuseid tootmises ise, ilma administraatori abita majutada. Samas tahtsime säilitada kontrolli, et mõni pisiteenus ei tarbiks kõiki meie andmekeskuste ressursse.

Ilmselgelt vajame juhtkihti, mis teeks seda automaatselt.

Nii jõudsime lihtsa ja arusaadava pildini, mida kõik arhitektid jumaldavad: kolm ruutu.

Ühe pilve – andmekeskuse taseme OS Odnoklassnikis

one-cloud masters on pilve orkestreerimise eest vastutav tõrkesiirdeklaster. Arendaja saadab ülemale manifesti, mis sisaldab kogu teenuse hostimiseks vajalikku teavet. Selle põhjal annab kapten käsud valitud käsilastele (konteinerite käitamiseks mõeldud masinad). Minionidel on meie agent, kes saab käsu, annab selle käsud Dockerile ja Docker konfigureerib Linuxi tuuma vastava konteineri käivitamiseks. Lisaks käskude täitmisele annab agent kaptenile pidevalt aru muutustest nii minion masina kui ka sellel töötavate konteinerite olekus.

Ressursi eraldamine

Nüüd vaatame paljude käsilaste jaoks keerukama ressursside eraldamise probleemi.

Arvutusressurss ühes pilves on:

  • Protsessori võimsuse hulk, mida konkreetne ülesanne kulutab.
  • Ülesande jaoks saadaolev mälumaht.
  • Võrguliiklus. Igal minionil on konkreetne piiratud ribalaiusega võrguliides, mistõttu on võimatu ülesandeid jaotada, võtmata arvesse nende võrgu kaudu edastatavate andmete hulka.
  • kettad. Lisaks sellele eraldame nende ülesannete jaoks ilmselt ruumi ka ketta tüübi: HDD või SSD. Kettad võivad teenindada piiratud arvu päringuid sekundis – IOPS. Seetõttu eraldame ülesannete jaoks, mis genereerivad rohkem IOPS-i, kui üks ketas suudab hakkama saada, ka "spindlid" - st kettaseadmed, mis peavad olema ülesande jaoks reserveeritud.

Siis saame mõne teenuse, näiteks kasutaja vahemälu, kulutatud ressursid salvestada nii: 400 protsessorituuma, 2,5 TB mälu, 50 Gbit/s liiklus mõlemas suunas, 6 TB HDD ruumi, mis asub 100 spindlil. Või tuttavamal kujul nagu see:

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

Kasutaja vahemälu teenuse ressursid tarbivad ainult osa kõigist tootmisinfrastruktuuris saadaolevatest ressurssidest. Seetõttu tahan veenduda, et äkki, operaatori vea tõttu või mitte, ei tarbi kasutaja vahemälu rohkem ressursse, kui talle on eraldatud. See tähendab, et me peame piirama ressursse. Aga millega me saaksime kvoodi siduda?

Pöördume tagasi meie oluliselt lihtsustatud komponentide interaktsiooni diagrammi juurde ja joonistame selle üksikasjalikumalt ümber – näiteks järgmiselt:

Ühe pilve – andmekeskuse taseme OS Odnoklassnikis

Mis sulle silma jääb:

  • Veebi frontend ja muusika kasutavad sama rakendusserveri isoleeritud klastreid.
  • Saame eristada loogilisi kihte, kuhu need klastrid kuuluvad: esipaneelid, vahemälud, andmesalvestus- ja halduskiht.
  • Esiosa on heterogeenne; see koosneb erinevatest funktsionaalsetest alamsüsteemidest.
  • Vahemälud võivad olla hajutatud ka allsüsteemis, mille andmeid nad vahemällu salvestavad.

Joonistame pildi uuesti:

Ühe pilve – andmekeskuse taseme OS Odnoklassnikis

Bah! Jah, me näeme hierarhiat! See tähendab, et saate jagada ressursse suuremate tükkidena: määrata vastutav arendaja selle hierarhia sõlmele, mis vastab funktsionaalsele alamsüsteemile (nagu pildil "muusika"), ja lisada kvoot samale hierarhia tasemele. See hierarhia võimaldab meil ka haldamise hõlbustamiseks teenuseid paindlikumalt korraldada. Näiteks jagame kogu veebi, kuna see on väga suur serverite rühmitus, mitmeks väiksemaks rühmaks, mis on pildil näidatud kui rühm1, rühm2.

Lisaridade eemaldamisega saame kirjutada oma pildi iga sõlme lamedamal kujul: rühm1.veebiesine, api.muusika.ees, user-cache.cache.

Nii jõuame "hierarhilise järjekorra" mõisteni. Selle nimi on nagu "group1.web.front". Sellele on määratud ressursside ja kasutajaõiguste kvoot. Anname DevOpsi inimesele õiguse saata teenust järjekorda ja selline töötaja saab midagi järjekorda käivitada ning OpsDevi inimesel on administraatori õigused ja nüüd saab ta järjekorda hallata, sinna inimesi määrata, anda neile inimestele õigused jne. Selles järjekorras töötavad teenused töötavad järjekorra kvoodi piires. Kui järjekorra arvutuskvoodist ei piisa kõigi teenuste korraga täitmiseks, siis käivitatakse need järjestikku, moodustades seega järjekorra enda.

Vaatame teenuseid lähemalt. Teenusel on täielik nimi, mis sisaldab alati järjekorra nime. Siis saab esikülje veebiteenuse nimi ok-web.group1.web.front. Ja kutsutakse välja rakendusserveri teenus, millele see juurde pääseb ok-app.group1.web.front. Igal teenusel on manifest, mis määrab kogu vajaliku teabe konkreetsetele masinatele paigutamiseks: kui palju ressursse see ülesanne kulutab, millist konfiguratsiooni selleks vaja on, mitu koopiat peaks olema, selle teenuse rikete käsitlemise omadused. Ja pärast teenuse otse masinatesse paigutamist ilmuvad selle eksemplarid. Neid nimetatakse ka üheselt – eksemplari numbri ja teenuse nimena: 1.ok-web.group1.web.front, 2.ok-web.group1.web.front, …

See on väga mugav: vaadates ainult jooksva konteineri nime, saame kohe palju teada.

Vaatame nüüd lähemalt, mida need juhtumid tegelikult täidavad: ülesandeid.

Ülesande eraldamise klassid

Kõik OK ülesanded (ja tõenäoliselt kõikjal) saab jagada rühmadesse:

  • Lühikese latentsusega ülesanded – prod. Selliste ülesannete ja teenuste puhul on väga oluline vastuse viivitus (latentsus), kui kiiresti süsteem iga päringu töötleb. Näited ülesannetest: veebi esiküljed, vahemälud, rakendusserverid, OLTP-salvestus jne.
  • Arvutusprobleemid - partii. Siin ei ole iga konkreetse päringu töötlemise kiirus oluline. Nende jaoks on oluline, kui palju arvutusi see ülesanne teatud (pika) aja (läbilaskevõime) jooksul teeb. Need on kõik MapReduce'i, Hadoopi, masinõppe ja statistika ülesanded.
  • Taustaülesanded – jõude. Selliste ülesannete puhul pole latentsus ega läbilaskevõime kuigi olulised. See hõlmab erinevaid teste, migratsioone, ümberarvutusi ja andmete konverteerimist ühest vormingust teise. Ühest küljest on need sarnased arvutuslikega, teisalt pole meie jaoks oluline, kui kiiresti need valmivad.

Vaatame, kuidas sellised ülesanded tarbivad ressursse, näiteks keskprotsessorit.

Lühikese viivitusega ülesanded. Sellise ülesande protsessori tarbimismuster on sarnane järgmisele:

Ühe pilve – andmekeskuse taseme OS Odnoklassnikis

Kasutajalt võetakse töötlemiseks vastu taotlus, ülesanne hakkab kasutama kõiki saadaolevaid protsessori tuumasid, töötleb seda, tagastab vastuse, ootab järgmist päringut ja peatub. Saabus järgmine päring - jälle valisime kõik, mis oli, arvutasime välja ja ootame järgmist.

Sellise ülesande minimaalse latentsuse tagamiseks peame võtma maksimaalsed ressursid, mida see tarbib, ja reserveerima minionil (masinal, mis ülesande täitma hakkab) vajaliku arvu südamikke. Siis on meie probleemi broneerimisvalem järgmine:

alloc: cpu = 4 (max)

ja kui meil on 16 tuumaga minion masin, siis saab sinna panna täpselt neli sellist ülesannet. Märgime eriti, et selliste ülesannete keskmine protsessori tarbimine on sageli väga madal - mis on ilmne, kuna märkimisväärne osa ajast ootab ülesanne päringut ega tee midagi.

Arvutusülesanded. Nende muster on veidi erinev:

Ühe pilve – andmekeskuse taseme OS Odnoklassnikis

Keskmine protsessori ressursikulu selliste ülesannete jaoks on üsna kõrge. Sageli soovime, et arvutusülesanne saaks tehtud teatud aja jooksul, mistõttu peame reserveerima minimaalse arvu protsessoreid, mida see vajab, et kogu arvutus saaks lõpule viidud vastuvõetava aja jooksul. Selle broneeringu valem näeb välja selline:

alloc: cpu = [1,*)

"Palun asetage see minioni peale, kus on vähemalt üks vaba tuum, ja nii palju kui neid on, neelab see kõik."

Siin on kasutamise efektiivsus juba palju parem kui lühikese viivitusega ülesannete puhul. Kuid kasu on palju suurem, kui kombineerite mõlemat tüüpi ülesanded ühes minionimasinas ja jagate selle ressursse liikvel olles. Kui väikese viivitusega ülesanne nõuab protsessorit, võtab see selle kohe vastu ja kui ressursse enam ei vajata, kantakse need üle arvutusülesandesse, st umbes nii:

Ühe pilve – andmekeskuse taseme OS Odnoklassnikis

Aga kuidas seda teha?

Kõigepealt vaatame prod ja selle alloc: cpu = 4. Peame reserveerima neli tuuma. Dockeri käivitamisel saab seda teha kahel viisil:

  • Kasutades valikut --cpuset=1-4st eraldage ülesandele masinal neli kindlat tuuma.
  • Kasutage --cpuquota=400_000 --cpuperiod=100_000, määrake protsessoriajale kvoot, st näidake, et iga 100 ms reaalajas kulutab ülesanne mitte rohkem kui 400 ms protsessoriaega. Saadakse samad neli südamikku.

Kuid milline neist meetoditest sobib?

cpuset näeb üsna atraktiivne välja. Ülesandel on neli spetsiaalset tuuma, mis tähendab, et protsessori vahemälud töötavad võimalikult tõhusalt. Sellel on ka negatiivne külg: me peaksime võtma ülesande jaotada arvutused OS-i asemel masina tühjade tuumade vahel ja see on üsna mittetriviaalne ülesanne, eriti kui proovime paigutada pakettülesandeid sellisele süsteemile. masin. Testid on näidanud, et siin sobib paremini kvoodiga variant: nii on operatsioonisüsteemil rohkem vabadust praegusel hetkel ülesande täitmiseks tuuma valikul ja protsessoriaeg jaotub efektiivsemalt.

Mõelgem välja, kuidas teha Dockeris broneeringuid tuumade minimaalse arvu põhjal. Pakkülesannete kvoot enam ei kehti, sest pole vaja piirata maksimumi, piisab vaid miinimumi tagamisest. Ja siin sobib see valik hästi docker run --cpushares.

Leppisime kokku, et kui partii nõuab garantiid vähemalt ühele südamikule, siis anname märku --cpushares=1024, ja kui südamikke on vähemalt kaks, siis näitame --cpushares=2048. Protsessori jagamised ei sega kuidagi protsessori aja jaotust seni, kuni seda on piisavalt. Seega, kui prod ei kasuta praegu kõiki oma nelja tuuma, ei piira miski pakettülesandeid ja nad saavad kasutada täiendavat protsessori aega. Kuid olukorras, kus protsessoreid napib, kui prod on kõik neli tuuma ära tarbinud ja oma kvoodi täis saanud, jagatakse järelejäänud protsessoriaeg proportsionaalselt cpushares’idega, s.t kolme vaba tuuma olukorras on üks antud ülesandele 1024 cpushares'iga ja ülejäänud kaks antakse ülesandele 2048 cpushares'iga.

Kuid kvoodi ja aktsiate kasutamisest ei piisa. Peame tagama, et lühikese viivitusega ülesanne saaks protsessori aja jaotamisel prioriteetse pakettülesande ees. Ilma sellise prioriseerimiseta võtab pakettülesanne kogu protsessori aja hetkel, mil seda vajab toode. Dockeri käitamisel pole konteineri prioriseerimise valikuid, kuid Linuxi protsessori planeerija poliitikad on kasulikud. Nende kohta saate üksikasjalikult lugeda siin, ja selle artikli raames käsitleme neid lühidalt:

  • SCHED_OTHER
    Vaikimisi saavad Linuxi masinas kõik tavalised kasutajaprotsessid vastu.
  • SCHED_BATCH
    Mõeldud ressursimahukate protsesside jaoks. Ülesande asetamisel protsessorile rakendatakse nn aktiveerimistrahvi: selline ülesanne saab vähem tõenäoliselt protsessori ressursse, kui seda kasutab parasjagu ülesandega SCHED_OTHER
  • SCHED_IDLE
    Väga madala prioriteediga taustprotsess, isegi madalam kui kena -19. Kasutame oma avatud lähtekoodiga raamatukogu üks-nio, et määrata konteineri käivitamisel helistades vajalik poliitika

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

Kuid isegi kui te Java-s ei programmeeri, saate sama teha käsuga chrt:

chrt -i 0 $pid

Selguse huvides võtame kõik oma isolatsioonitasemed ühte tabelisse:

Isolatsiooniklass
Alloc näide
Dockeri käitamise valikud
sched_setscheduler chrt*

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

Partii
Protsessor = [1, *)
--cpushares=1024
SCHED_BATCH

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

*Kui teete chrt-i konteineri seest, võib teil vaja minna sys_nice'i võimalust, kuna vaikimisi eemaldab Docker selle võimaluse konteineri käivitamisel.

Kuid ülesanded ei tarbi mitte ainult protsessorit, vaid ka liiklust, mis mõjutab võrguülesande latentsust veelgi rohkem kui protsessori ressursside vale eraldamine. Seetõttu tahame loomulikult saada liikluses täpselt sama pilti. See tähendab, et kui tooteülesanne saadab võrku mõned paketid, piirame maksimaalset kiirust (valem jaotamine: lan=[*,500mbps) ), millega prod seda teha saab. Ja partii puhul garanteerime ainult minimaalse läbilaskevõime, kuid ei piira maksimaalset (valem eraldamine: lan=[10Mbps,*) ) Sel juhul peaks tooteliiklus olema pakettülesannete ees prioriteet.
Siin pole Dockeril primitiive, mida saaksime kasutada. Aga see tuleb meile appi Linuxi liikluskontroll. Distsipliini abil suutsime saavutada soovitud tulemuse Hierarhiline õiglase teenuse kõver. Selle abil eristame kahte liikluse klassi: kõrge prioriteediga prod ja madala prioriteediga partii / tühikäik. Selle tulemusena on väljamineva liikluse konfiguratsioon järgmine:

Ühe pilve – andmekeskuse taseme OS Odnoklassnikis

siin 1:0 on hsfc distsipliini "juurketas"; 1:1 - hsfc alamklass kogu ribalaiuse piiranguga 8 Gbit/s, mille alla on paigutatud kõigi konteinerite alamklassid; 1:2 - hsfc alamklass on ühine kõikidele pakett- ja jõudetoimingutele, millel on "dünaamiline" piirang, mida arutatakse allpool. Ülejäänud hsfc alamklassid on praegu töötavate tootekonteinerite jaoks spetsiaalsed klassid, mille piirangud vastavad nende manifestidele – 450 ja 400 Mbit/s. Igale hsfc-klassile määratakse olenevalt Linuxi kerneli versioonist qdisc järjekord fq või fq_code, et vältida pakettide kadumist liikluse katkestuste ajal.

Tc-distsipliinid eelistavad tavaliselt ainult väljuvat liiklust. Kuid me tahame ka sissetulevat liiklust prioriteediks seada - lõppude lõpuks saab mõni pakkülesanne lihtsalt valida kogu sissetuleva kanali, saades näiteks suure hulga sisendandmeid map&reduce jaoks. Selleks kasutame moodulit ifb, mis loob iga võrguliidese jaoks ifbX virtuaalse liidese ja suunab sissetuleva liikluse liidesest ifbX-i väljaminevale liiklusele. Lisaks töötavad ifbX puhul kõik samad distsipliinid väljuva liikluse juhtimiseks, mille hsfc konfiguratsioon on väga sarnane:

Ühe pilve – andmekeskuse taseme OS Odnoklassnikis

Katsete käigus saime teada, et hsfc näitab parimaid tulemusi, kui mitteprioriteetse partii/tühikäigu liikluse klass 1:2 on minion masinatel piiratud kindla vaba sõidurajaga. Vastasel juhul mõjutab mitteprioriteetne liiklus tootmisülesannete latentsusaega liiga palju. miniond määrab igas sekundis vaba ribalaiuse praeguse koguse, mõõtes antud minioni kõigi prod-ülesannete keskmist liikluskulu Ühe pilve – andmekeskuse taseme OS Odnoklassnikis ja lahutades selle võrguliidese ribalaiusest Ühe pilve – andmekeskuse taseme OS Odnoklassnikis väikese varuga, st.

Ühe pilve – andmekeskuse taseme OS Odnoklassnikis

Ribad määratakse sissetuleva ja väljuva liikluse jaoks eraldi. Ja vastavalt uutele väärtustele konfigureerib miniond mitteprioriteetse klassi piirangu 1:2 ümber.

Seega rakendasime kõik kolm isolatsiooniklassi: prod, batch ja idle. Need klassid mõjutavad suuresti ülesannete sooritusomadusi. Seetõttu otsustasime paigutada selle atribuudi hierarhia tippu, et hierarhilise järjekorra nime vaadates oleks kohe selge, millega tegu:

Ühe pilve – andmekeskuse taseme OS Odnoklassnikis

Kõik meie sõbrad web и muusika esiküljed paigutatakse seejärel hierarhiasse toote alla. Näiteks paigutame teenuse partii alla muusikakataloog, mis koostab perioodiliselt lugude kataloogi Odnoklassnikisse üles laaditud mp3-failide komplektist. Näide tühikäigul olevast teenusest oleks muusika trafo, mis normaliseerib muusika helitugevust.

Kui lisaread on uuesti eemaldatud, saame oma teenuste nimed lamedamaks kirjutada, lisades teenuse täisnime lõppu ülesande eraldamise klassi: web.front.prod, kataloog.muusika.partii, transformer.music.idle.

Ja nüüd, vaadates teenuse nime, mõistame mitte ainult seda, millist funktsiooni see täidab, vaid ka selle isolatsiooniklassi, mis tähendab selle kriitilisust jne.

Kõik on suurepärane, kuid on üks kibe tõde. Ühes masinas töötavaid ülesandeid on võimatu täielikult eraldada.

Mida meil õnnestus saavutada: kui partii intensiivselt tarbib ainult CPU ressursid, siis teeb sisseehitatud Linuxi CPU planeerija oma tööd väga hästi ja prod ülesandele praktiliselt mingit mõju ei avalda. Kuid kui see partiiülesanne hakkab aktiivselt mäluga töötama, ilmneb juba vastastikune mõju. See juhtub seetõttu, et tootmisülesanne "pestakse" protsessori mälu vahemäludest välja - selle tulemusena suurenevad vahemälu puudujäägid ja protsessor töötleb tootmistoimingut aeglasemalt. Selline partiiülesanne võib suurendada meie tüüpilise tootemahuti latentsusaega 10%.

Liikluse eraldamine on veelgi keerulisem, kuna tänapäevastel võrgukaartidel on sisemine pakettide järjekord. Kui pakkülesande pakett jõuab sinna esimesena, siis edastatakse see esimesena kaabli kaudu ja sellega ei saa midagi ette võtta.

Lisaks oleme siiani suutnud lahendada vaid TCP-liikluse prioriseerimise probleemi: hsfc-lähenemine UDP puhul ei tööta. Ja isegi TCP-liikluse korral, kui pakettülesanne tekitab palju liiklust, suurendab see ka tootmisülesande viivitust umbes 10%.

veataluvus

Ühe pilve arendamisel oli üks eesmärke parandada Odnoklassniki veataluvust. Seetõttu tahaksin järgmisena käsitleda üksikasjalikumalt võimalikke rikete ja õnnetuste stsenaariume. Alustame lihtsa stsenaariumiga – konteineri rike.

Konteiner ise võib ebaõnnestuda mitmel viisil. See võib olla mingi katse, viga või viga manifestis, mille tõttu hakkab tootmisülesanne kulutama rohkem ressursse, kui manifestis märgitud. Meil oli juhtum: arendaja rakendas ühte keerukat algoritmi, töötas seda mitu korda ümber, mõtles end üle ja sattus nii segadusse, et lõpuks sai probleem väga mittetriviaalsel viisil silmusesse. Ja kuna tootmisülesandel on kõrgem prioriteet kui kõigil teistel samadel käsilastel, hakkas see tarbima kõiki saadaolevaid protsessoriressursse. Selles olukorras päästis olukorra isolatsioon või õigemini protsessori aja kvoot. Kui ülesandele on määratud kvoot, ei tarbi see ülesanne rohkem. Seetõttu ei märganud samal masinal jooksnud partii- ja muud tooteülesanded midagi.

Teine võimalik probleem on konteineri kukkumine. Ja siin päästavad meid taaskäivituspoliitikad, kõik teavad neid, Docker ise teeb suurepärast tööd. Peaaegu kõigil tootmisülesannetel on alati taaskäivitamise poliitika. Mõnikord kasutame pakettülesannete või tootekonteinerite silumise funktsiooni on_failure.

Mida saate teha, kui terve minion pole saadaval?

Ilmselgelt käivitage konteiner teises masinas. Huvitav osa siin on see, mis juhtub konteinerile määratud IP-aadressi(de)ga.

Saame määrata konteineritele samad IP-aadressid, mis minion masinatele, millel need konteinerid töötavad. Seejärel konteineri käivitamisel teises masinas muutub selle IP-aadress ja kõik kliendid peavad aru saama, et konteiner on kolinud ning nüüd tuleb minna teisele aadressile, mis nõuab eraldi Service Discovery teenust.

Service Discovery on mugav. Teenuseregistri korraldamiseks on turul palju erineva tõrketaluvusega lahendusi. Sageli rakendavad sellised lahendused koormuse tasakaalustaja loogikat, salvestavad lisakonfiguratsiooni KV-salvestuse kujul jne.
Siiski soovime vältida eraldi registri juurutamist, sest see tähendaks kriitilise süsteemi juurutamist, mida kasutavad kõik tootmises olevad teenused. See tähendab, et see on potentsiaalne tõrkepunkt ja peate valima või välja töötama väga tõrketaluva lahenduse, mis on ilmselgelt väga keeruline, aeganõudev ja kallis.

Ja veel üks suur puudus: selleks, et meie vana infrastruktuur uuega töötaks, peaksime absoluutselt kõik ülesanded ümber kirjutama, et kasutada mingit Service Discovery süsteemi. Tööd on PALJU ja mõnes kohas on see peaaegu võimatu, kui tegemist on madala tasemega seadmetega, mis töötavad OS-i kerneli tasemel või otse riistvaraga. Selle funktsionaalsuse juurutamine, kasutades väljakujunenud lahendusmustreid, nt külgkorviga tähendaks mõnes kohas lisakoormust, teisal - töö komplikatsiooni ja täiendavaid rikkestsenaariume. Me ei tahtnud asju keeruliseks ajada, mistõttu otsustasime teenuse avastamise kasutamise teha valikuliseks.

Ühes pilves järgib IP konteinerit, st igal tegumi eksemplaril on oma IP-aadress. See aadress on "staatiline": see määratakse igale eksemplarile, kui teenus esmakordselt pilve saadetakse. Kui teenusel oli oma eluea jooksul erinev arv eksemplare, siis lõpuks määratakse talle nii palju IP-aadresse, kui palju eksemplare oli maksimaalselt.

Edaspidi need aadressid ei muutu: need määratakse üks kord ja need eksisteerivad kogu tootmises oleva teenuse kasutusaja jooksul. IP-aadressid järgivad konteinereid kogu võrgus. Kui konteiner viiakse teisele minionile, järgneb sellele aadress.

Seega muutub teenuse nime vastendamine selle IP-aadresside loendiga väga harva. Kui vaatate uuesti teenuse eksemplaride nimesid, mida mainisime artikli alguses (1.ok-web.group1.web.front.prod, 2.ok-web.group1.web.front.prod, …), märkame, et need sarnanevad DNS-is kasutatavate FQDN-idega. See on õige, teenusejuhtumite nimede vastendamiseks nende IP-aadressidega kasutame DNS-protokolli. Lisaks tagastab see DNS kõigi konteinerite kõik reserveeritud IP-aadressid – nii töötavate kui ka peatatud (oletame, et kasutatakse kolme koopiat ja meil on seal reserveeritud viis aadressi – kõik viis tagastatakse). Kliendid, olles saanud selle teabe, püüavad luua ühenduse kõigi viie koopiaga ja seega määrata need, mis töötavad. See kättesaadavuse määramise valik on palju usaldusväärsem, see ei hõlma DNS-i ega teenuse tuvastamist, mis tähendab, et teabe asjakohasuse ja nende süsteemide veataluvuse tagamisel pole keerulisi probleeme lahendada. Veelgi enam, kriitilistes teenustes, millest sõltub kogu portaali töö, ei saa me DNS-i üldse kasutada, vaid lihtsalt sisestada konfiguratsiooni IP-aadressid.

Sellise IP-edastuse rakendamine konteinerite taga võib olla mittetriviaalne – ja me vaatame, kuidas see toimib, järgmise näite abil:

Ühe pilve – andmekeskuse taseme OS Odnoklassnikis

Oletame, et ühe pilve juht annab minionile M1 käsu käivitada 1.ok-web.group1.web.front.prod aadressiga 1.1.1.1. Töötab minioni peal BIRD, mis reklaamib seda aadressi spetsiaalsetele serveritele marsruudi helkur. Viimastel on võrgu riistvaraga BGP seanss, millesse tõlgitakse M1.1.1.1 aadressi 1 marsruut. M1 suunab pakette konteineris Linuxi abil. Marsruudi peegeldajaservereid on kolm, kuna see on ühe pilve infrastruktuuri väga oluline osa – ilma nendeta ei tööta ühes pilves olev võrk. Asetame need erinevatesse riiulitesse, mis asuvad võimalusel andmekeskuse erinevates ruumides, et vähendada kõigi kolme samaaegse rikke tõenäosust.

Oletame nüüd, et ühendus ühe pilvemeistri ja M1 minioni vahel on kadunud. Ühe pilve meister tegutseb nüüd eeldusel, et M1 on täielikult läbi kukkunud. See tähendab, et see annab M2 minionile käsu käivitada web.group1.web.front.prod sama aadressiga 1.1.1.1. Nüüd on meil võrgus kaks vastandlikku marsruuti 1.1.1.1 jaoks: M1 ja M2. Selliste konfliktide lahendamiseks kasutame Multi Exit Diskriminaatorit, mis on märgitud BGP teadaandes. See on number, mis näitab reklaamitava marsruudi kaalu. Vastuoluliste marsruutide hulgast valitakse madalama MED väärtusega marsruut. Ühe pilvejuht toetab MED-i konteineri IP-aadresside lahutamatu osana. Esimest korda kirjutatakse aadress piisavalt suure MED = 1 000 000. Sellise erakorralise konteineri ülekande olukorras vähendab kapten MED-i ja M2 saab juba käsu reklaamida aadressi 1.1.1.1 tähisega MED = 999 999. M1 jooksev eksemplar jääb sellisel juhul ühendusse ja tema edasine saatus ei huvita meid vähe, kuni side peremehega taastub, mil ta peatatakse nagu vana võte.

õnnetusi

Kõik andmekeskuse haldussüsteemid käsitlevad väiksemaid tõrkeid alati vastuvõetavalt. Konteinerite ülevool on peaaegu kõikjal norm.

Vaatame, kuidas toimime hädaolukorras, näiteks andmekeskuse ühes või mitmes ruumis voolukatkestuse korral.

Mida tähendab õnnetus andmekeskuse haldussüsteemi jaoks? Esiteks on see paljude masinate massiivne ühekordne rike ja juhtimissüsteem peab migreerima palju konteinereid korraga. Aga kui katastroof on väga mastaapne, siis võib juhtuda, et kõiki ülesandeid ei saa teistele käsilastele ümber jaotada, sest andmekeskuse ressursivõimsus langeb alla 100% koormusest.

Sageli kaasneb õnnetustega kontrollkihi rike. See võib juhtuda selle seadmete rikke tõttu, kuid sagedamini seetõttu, et õnnetusi ei testita ja kontrollkiht ise langeb suurenenud koormuse tõttu.

Mida saate selle kõige vastu teha?

Massmigratsioonid tähendavad, et infrastruktuuris toimub suur hulk tegevusi, migratsioone ja kasutuselevõttu. Iga migratsioon võib võtta aega, mis kulub konteineripiltide käsilastele kohaletoimetamiseks ja lahtipakkimiseks, konteinerite käivitamiseks ja lähtestamiseks jne. Seetõttu on soovitav, et olulisemad toimingud käivitataks enne vähemtähtsaid ülesandeid.

Vaatame uuesti meile tuttavate teenuste hierarhiat ja proovime otsustada, milliseid ülesandeid tahame kõigepealt käivitada.

Ühe pilve – andmekeskuse taseme OS Odnoklassnikis

Loomulikult on need protsessid, mis on otseselt seotud kasutajate päringute töötlemisega, st prod. Me näitame seda tähisega paigutuse prioriteet — number, mille saab järjekorrale määrata. Kui järjekorra prioriteet on kõrgem, asetatakse selle teenused esikohale.

Tootmisel määrame kõrgemad prioriteedid, 0; partiil - veidi madalam, 100; tühikäigul - veelgi madalam, 200. Prioriteedid rakendatakse hierarhiliselt. Kõigil hierarhias madalamal asuvatel ülesannetel on vastav prioriteet. Kui tahame, et prod-i vahemälud käivitataks enne esiserve, siis määrame prioriteedid vahemälu = 0 ja eesmistele alamjärjekordadele = 1. Kui näiteks tahame, et põhiportaal käivitataks esmalt esiosast ja ainult muusikarind. siis saame viimasele määrata madalama prioriteedi - 10.

Järgmine probleem on ressursside nappus. Nii läks suur hulk seadmeid, terved andmekeskuse saalid üles ja käivitasime nii palju teenuseid, et nüüd ei jätku kõigile ressursse. Peate otsustama, millised ülesanded ohverdada, et peamised kriitilised teenused töötaksid.

Ühe pilve – andmekeskuse taseme OS Odnoklassnikis

Erinevalt paigutuse prioriteedist ei saa me kõiki pakkülesandeid valimatult ohverdada, mõned neist on portaali toimimiseks olulised. Seetõttu oleme eraldi välja toonud eelisostu eelisõigus ülesandeid. Paigutamisel võib kõrgema prioriteediga ülesanne ennetada, st peatada madalama prioriteediga ülesande, kui vabu käsilasi pole enam. Sel juhul jääb madala prioriteediga ülesanne tõenäoliselt paigutamata, st ei leidu sellele enam sobivat minioni, kellel on piisavalt vaba ressurssi.

Meie hierarhias on väga lihtne määrata eelisostu prioriteet, nii et tootmis- ja pakettülesanded ennetavad või peatavad jõudeoleku ülesandeid, kuid mitte üksteist, määrates jõudeoleku prioriteediks 200. Nii nagu paigutuse prioriteedi puhul, nii ka me saab kasutada meie hierarhiat keerukamate reeglite kirjeldamiseks. Näiteks näitame, et ohverdame muusikafunktsiooni, kui meil pole peamise veebiportaali jaoks piisavalt ressursse, seades vastavate sõlmede prioriteedi madalamaks: 10.

Terved alalisvoolu õnnetused

Miks võib kogu andmekeskus ebaõnnestuda? Element. Hea postitus oli orkaan mõjutas andmekeskuse tööd. Elemente võib pidada kodututeks, kes kunagi kollektoris optikat põletasid ja andmekeskus kaotas täielikult kontakti teiste saitidega. Rikke põhjuseks võib olla ka inimfaktor: operaator annab sellise käsu, et kogu andmekeskus kukub. See võib juhtuda suure vea tõttu. Üldiselt pole andmekeskuste kokkuvarisemine haruldane. Seda juhtub meil kord paari kuu jooksul.

Ja seda me teeme, et takistada kedagi #elus säutsumast.

Esimene strateegia on isolatsioon. Iga ühe pilve eksemplar on isoleeritud ja suudab hallata masinaid ainult ühes andmekeskuses. See tähendab, et pilve kadumine vigade või valede operaatorikäskude tõttu on ainult ühe andmekeskuse kadu. Oleme selleks valmis: meil on koondamispoliitika, mille kohaselt rakenduse ja andmete koopiad asuvad kõigis andmekeskustes. Kasutame tõrketaluvaid andmebaase ja testime perioodiliselt tõrkeid.
Tänasest on meil neli andmekeskust, mis tähendab nelja eraldiseisvat, täiesti isoleeritud ühe pilve juhtumit.

Selline lähenemine ei kaitse mitte ainult füüsilise rikke eest, vaid võib kaitsta ka operaatori vea eest.

Mida saab veel teha inimfaktoriga? Kui operaator annab pilvele kummalise või potentsiaalselt ohtliku käsu, võidakse tal äkki paluda lahendada väike probleem, et näha, kui hästi ta mõtles. Näiteks kui see on mingisugune paljude koopiate massiline peatamine või lihtsalt kummaline käsk - koopiate arvu vähendamine või pildi nime muutmine, mitte ainult versiooni number uues manifestis.

Ühe pilve – andmekeskuse taseme OS Odnoklassnikis

Tulemused

Ühe pilve iseloomulikud tunnused:

  • Teenuste ja konteinerite hierarhiline ja visuaalne nimetamisskeem, mis võimaldab väga kiiresti teada saada, mis ülesanne on, millega see seotud on ja kuidas see toimib ning kes selle eest vastutab.
  • Rakendame oma toote ja partii kombineerimise tehnikaülesanded käsilastel masinate jagamise tõhustamiseks. Cpuseti asemel kasutame protsessori kvoote, jagamisi, protsessori ajakava põhimõtteid ja Linuxi QoS-i.
  • Samal masinal töötavaid konteinereid ei olnud võimalik täielikult isoleerida, kuid nende vastastikune mõju jääb 20% piiresse.
  • Teenuste korraldamine hierarhiasse aitab automaatset avariitaastet kasutades paigutuse ja eelisostu prioriteedid.

KKK

Miks me ei võtnud valmis lahendust?

  • Erinevad ülesannete isoleerimise klassid nõuavad käsilastele asetamisel erinevat loogikat. Kui tootmisülesandeid saab paigutada lihtsalt ressursse reserveerides, tuleb paigutada pakett- ja jõudetoimingud, mis jälgivad minionimasinate ressursside tegelikku kasutamist.
  • Vajadus võtta arvesse ressursse, mida kulutavad ülesanded, näiteks:
    • võrgu ribalaius;
    • ketaste tüübid ja "spindlid".
  • Vajadus osutada hädaolukorras reageerimisel teenuste prioriteedid, ressursside käskude õigused ja kvoodid, mis lahendatakse hierarhiliste järjekordade abil ühes pilves.
  • Konteinerite inimnimede andmise vajadus, et vähendada õnnetustele ja vahejuhtumitele reageerimise aega
  • Service Discovery ühekordse laialdase juurutamise võimatus; vajadus eksisteerida pikka aega koos riistvarahostides hostitud ülesannetega - see on lahendatud konteineritele järgnevate "staatilise" IP-aadresside abil ja sellest tulenevalt vajadus ainulaadse integratsiooni järele suure võrguinfrastruktuuriga.

Kõik need funktsioonid eeldaksid olemasolevate lahenduste olulist ümberkujundamist meile sobivaks ning töömahtu hinnates mõistsime, et saame välja töötada oma lahenduse ligikaudu samade tööjõukuludega. Kuid teie lahendust on palju lihtsam kasutada ja arendada – see ei sisalda tarbetuid abstraktsioone, mis toetavad funktsionaalsust, mida me ei vaja.

Need, kes loevad viimaseid ridu, tänan teid kannatlikkuse ja tähelepanu eest!

Allikas: www.habr.com

Lisa kommentaar