Kalimi i Tinderit në Kubernetes

Shkruar nga: Chris O'Brien, Menaxher Inxhinierik | Chris Thomas, Menaxher Inxhinierik | Jinyong Lee, Inxhinier i Lartë i Programeve | Redaktuar nga: Cooper Jackson, Inxhinier Softuerësh

Përse

Pothuajse dy vjet më parë, Tinder vendosi të transferojë platformën e saj në Kubernetes. Kubernetes na dha një mundësi për të drejtuar Inxhinierinë Tinder drejt kontejnerizimit dhe operimit me prekje të ulët përmes vendosjes së pandryshueshme. Ndërtimi i aplikimit, vendosja dhe infrastruktura do të përcaktoheshin si kod.

Ne po kërkonim gjithashtu të adresojmë sfidat e shkallës dhe stabilitetit. Kur shkallëzimi u bë kritik, ne shpesh vuanim përmes disa minutave të pritjes për të ardhur në internet raste të reja të EC2. Ideja e kontejnerëve që caktonin dhe shërbenin për trafik brenda sekondave në krahasim me minutat ishte tërheqëse për ne.

Nuk ishte e lehtë. Gjatë migrimit tonë në fillim të vitit 2019, arritëm një masë kritike brenda grupit tonë Kubernetes dhe filluam të hasim sfida të ndryshme për shkak të vëllimit të trafikut, madhësisë së klasterit dhe DNS. Ne zgjidhëm sfida interesante për të migruar 200 shërbime dhe për të drejtuar një tufë Kubernetes në shkallë që arrin 1.000 nyje, 15,000 pod dhe 48,000 kontejnerë që funksionojnë.

si

Duke filluar nga Janari 2018, ne kemi punuar rrugën tonë nëpër faza të ndryshme të përpjekjes për migrim. Ne filluam me kontejnerizimin e të gjitha shërbimeve tona dhe vendosjen e tyre në një seri ambientesh skenike të Kubernetes. Duke filluar nga tetori, filluam të transferojmë në mënyrë metodike të gjitha shërbimet tona të trashëgimisë në Kubernetes. Deri në Mars të vitit të ardhshëm, ne përfunduam migrimin tonë dhe Platforma Tinder tani shkon ekskluzivisht në Kubernetes.

Ndërtimi i Imazheve për Kubernetes

Ekzistojnë më shumë se 30 depo të kodit burimor për mikrotrojtimet që janë duke punuar në grupin e Kubernetes. Kodi në këto depo është shkruar në gjuhë të ndryshme (p.sh., Node.js, Java, Scala, Go) me mjedise të shumta kohëzgjatjeje për të njëjtën gjuhë.

Sistemi i ndërtimit është krijuar për të operuar në një "kontekst të ndërtuar" plotësisht të përshtatshëm për secilën mikroservizion, i cili zakonisht përbëhet nga një Dockerfile dhe një seri komandash shell. Ndërsa përmbajtja e tyre është plotësisht e personalizueshme, këto kontekste ndërtimi janë shkruar të gjitha duke ndjekur një format të standardizuar. Standardizimi i konteksteve të ndërtimit lejon që një sistem i vetëm ndërtimi të trajtojë të gjitha mikroservizionet.

Figura 1–1 Procesi i standardizuar i ndërtimit përmes enës Builder

Për të arritur konsistencën maksimale midis mjediseve të ekzekutimit, i njëjti proces ndërtimi po përdoret gjatë fazës së zhvillimit dhe testimit. Kjo imponoi një sfidë unike kur na duhej të krijonim një mënyrë për të garantuar një mjedis të qëndrueshëm ndërtimi në të gjithë platformën. Si rezultat, të gjitha proceset e ndërtimit ekzekutohen brenda një enë speciale "Builder".

Zbatimi i enës Builder kërkonte një numër teknikash të përparuara Docker. Kjo enë Builder trashëgon ID dhe sekretet e përdoruesit lokal (p.sh. çelësin SSH, letrat kredenciale AWS, etj.) Siç kërkohet për të hyrë në depot private Tinder. Ajo monton drejtoritë lokale që përmbajnë kodin burimor për të pasur një mënyrë natyrale për të ruajtur artifakte të ndërtuara. Kjo qasje përmirëson performancën, sepse eliminon kopjimin e objekteve të ndërtuara midis enës Builder dhe makinës pritëse. Artifaktet e ruajtura të ruajtura ripërdoren herën tjetër pa konfigurim të mëtejshëm.

Për shërbime të caktuara, ne kemi nevojë të krijojmë një enë tjetër brenda Ndërtuesit që të përputhet me mjedisin e përpilimit të kohës me mjedisin e kohës së ekzekutimit (p.sh., instalimi i bibliotekës bcrypt të Node.js gjeneron objekte binare specifike për platformën). Kërkesat në kohë përpilimi mund të ndryshojnë në mesin e shërbimeve dhe Dockerfile përfundimtar është i përbërë në fluturim.

Arkitektura dhe Migracioni i Kllasterit Kubernetes

Madhësia e kllasterit

Ne vendosëm të përdorim kube-aws për sigurimin e grupimeve të automatizuara në shembujt e EC2 të Amazon. Herët, ne po drejtonim gjithçka në një pishinë të përgjithshme të nyjeve. Ne shpejt e identifikuam nevojën për të ndarë ngarkesat e punës në madhësi dhe lloje të ndryshme të instancave, për të shfrytëzuar më mirë burimet. Arsyetimi ishte që vrojtimi i më pak pod-ve me fije të mëdha së bashku dhanë rezultate më të parashikueshme të performancës për ne sesa t'i lejojmë ata të bashkëjetojnë me një numër më të madh pod-je me një fije.

Ne u vendosëm në:

  • m5.4xmadh për monitorim (Prometeu)
  • c5.4x zmadhuar për ngarkesën e punës në Node.js (ngarkesë pune me një fije)
  • c5.2xlarge për Java dhe Go (ngarkesë pune me shumë fije)
  • c5.4x zmadhim për rrafshin e kontrollit (3 nyje)

shpërngulje

Një nga hapat e përgatitjes për shpërnguljen nga infrastruktura jonë e trashëgimisë në Kubernetes ishte ndryshimi i komunikimit ekzistues të shërbimit në shërbim, për të treguar një balancues të ri të ngarkesave elastike (ELB) që u krijuan në një nën-rrjetë specifike Virtual Private Cloud (VPC). Kjo nën-rrjetë iu referua VPC-së Kubernetes. Kjo na lejoi të migronim në mënyrë modulare module pa marrë parasysh porositë specifike për varësi të shërbimit.

Këto pikat e fundit u krijuan duke përdorur grupe regjistrimi të ponderuar të DNS që kishin një CNAME duke treguar çdo ELB të ri. Për të ndaluar, shtuam një rekord të ri, duke treguar shërbimin e ri të Kubernetes ELB, me një peshë 0. Ne pastaj vendosëm Kohën për të jetuar (TTL) në rekordin e vendosur në 0. Peshat e vjetra dhe të reja u rregulluan ngadalë në përfundimisht përfundojnë me 100% në serverin e ri. Pasi ndërprerja të ishte e plotë, TTL ishte vendosur në diçka më të arsyeshme.

Modulet tona Java kanë nderuar me DNS TTL të ulët, por aplikacionet tona të Node nuk kanë. Një nga inxhinierët tanë rishkruaj një pjesë të kodit të pishinës së lidhjes për ta mbështetur atë në një menaxher që do të rifreskonte pishinat çdo 60-të. Kjo funksionoi shumë mirë për ne pa hit të performancës së ndjeshme.

mësimeve

Kufijtë e rrobave të rrjetit

Në orët e para të mëngjesit të 8 janarit 2019, Platforma e Tinderit pësoi një ndërprerje të vazhdueshme. Në përgjigje të një rritje të palidhur në vonesë të platformës më herët atë mëngjes, numërimet e pod dhe nyjeve u rritën në tufë. Kjo rezultoi në ezaurim të cache të ARP në të gjitha nyjet tona.

Ekzistojnë tre vlera Linux që lidhen me cache ARP:

kredi

gc_thresh3 është një kapak i vështirë. Nëse jeni duke hyrë në regjistrat e hyrjeve në "tryezën e tejmbushur të fqinjit", kjo tregon që edhe pas një koleksioni sinkron të mbeturinave (GC) të arkës së ARP, nuk kishte hapësirë ​​të mjaftueshme për të ruajtur hyrjen e fqinjit. Në këtë rast, kerneli thjesht hedh paketën tërësisht.

Ne përdorim Flannel si strukturën tonë të rrjetit në Kubernetes. Paketat përcillen përmes VXLAN. VXLAN është një skemë mbivendosjeje Layer 2 mbi një rrjet Layer 3. Ai përdor encapsulimin e protokollit të databazave të përdoruesve MAC Adresa në Përdorues (MAC-in-UDP) për të siguruar një mjet për të zgjatur segmentet e rrjetit Layer 2. Protokolli i transportit mbi rrjetin e qendrës fizike të të dhënave është IP plus UDP.

Figura 2–1 Diagrami i fanellës (kredia)

Figura 2–2 VXLAN Paketë (kredi)

Eachdo nyje e punëtorëve të Kubernetes alokon hapësirën e vet / 24 të adresave virtuale nga një bllok i madh / 9. Për secilën nyje, kjo rezulton në 1 hyrje të tabelës së rrugës, 1 hyrje në tryezën ARP (në ndërfaqen flannel.1), dhe 1 hyrje të bazës së të dhënave përcjellëse (FDB). Këto shtohen kur nyja e punëtorit fillon fillimisht ose pasi zbulohet secila nyje e re.

Përveç kësaj, komunikimi nyje-në-pod (ose pod-to-pod) përfundimisht rrjedh mbi ndërfaqen eth0 (përshkruar në diagramin Flannel më lart). Kjo do të rezultojë në një hyrje shtesë në tabelën ARP për secilin burim përkatës të nyjes dhe destinacionin e nyjes.

Në mjedisin tonë, ky lloj komunikimi është shumë i zakonshëm. Për objektet e shërbimit tonë Kubernetes, krijohet një ELB dhe Kubernetes regjistron çdo nyje me ELB. ELB nuk është i vetëdijshëm dhe nyja e zgjedhur mund të mos jetë destinacioni përfundimtar i paketës. Kjo sepse kur nyja merr paketën nga ELB, ajo vlerëson rregullat e saj të iptables për shërbimin dhe zgjedh rastësisht një pod në një nyje tjetër.

Në kohën e ndërprerjes, kishte 605 nyje totale në tufë. Për arsyet e përshkruara më lart, kjo ishte e mjaftueshme për të eklipsuar vlerën e paracaktuar të gc_thresh3. Sapo të ndodhë kjo, jo vetëm që paketat janë hedhur, por i gjithë Flannel / 24-të e hapësirës së adresave virtuale mungojnë nga tabela e ARP-së. Komunikimi i nyjeve në pod dhe kërkimet e DNS dështojnë. (DNS është pritur brenda grupit, siç do të shpjegohet më në detaje më vonë në këtë artikull.)

Për të zgjidhur, vlerat gc_thresh1, gc_thresh2 dhe gc_thresh3 janë ngritur dhe Flannel duhet të rindizet për të ri-regjistruar rrjetet që mungojnë.

Papritur Running DNS në shkallë

Për të akomoduar migracionin tonë, ne përfituam shumë DNS për të lehtësuar formësimin e trafikut dhe ndërprerjen shtesë nga trashëgimia në Kubernetes për shërbimet tona. Ne vendosëm vlera relativisht të ulëta TTL në Route53 RecordSets të shoqëruara. Kur drejtuam infrastrukturën tonë të trashëgimisë në shembuj të EC2, konfigurimi ynë zgjidhës tregoi për DNS të Amazon. Ne e morëm si të mirëqenë dhe kostoja e një TTL relativisht të ulët për shërbimet tona dhe shërbimet e Amazon (p.sh. DynamoDB) shkoi kryesisht pa u vënë re.

Ndërsa ne hipnim gjithnjë e më shumë shërbime për Kubernetes, ne e gjetëm veten duke drejtuar një shërbim DNS që po i përgjigjej 250,000 kërkesa për sekondë. Ne po hasnim në afatet kohore për kërkime të përkohshme dhe me ndikim të DNS brenda aplikacioneve tona. Kjo ndodhi pavarësisht nga një përpjekje gjithëpërfshirëse për akordimin dhe një ofrues i DNS kaloni në një vendosje CoreDNS që në një kohë arriti kulmin në 1000 pods duke konsumuar 120 bërthama.

Ndërsa hulumtuam shkaqe dhe zgjidhje të tjera të mundshme, gjetëm një artikull që përshkruan një gjendje gare që ndikon në kornizën e filtrimit të paketave Linux netfilter. Kohëzgjatjet DNS që po i shihnim, së bashku me një kundër-shtim në rritje të ndërfaqes Flannel, të përafruar me gjetjet e artikullit.

Issueështja shfaqet gjatë Përkthimit të Adresës së Rrjetit Burim dhe Destinacionit (SNAT dhe DNAT) dhe futjen pasuese në tabelën e kontrollit. Një rrugëdalje e diskutuar brenda dhe propozuar nga komuniteti ishte të zhvendoset DNS në vetë nyjen e punëtorit. Në këtë rast:

  • SNAT nuk është i nevojshëm, sepse trafiku po qëndron lokalisht në nyjen. Nuk ka nevojë të transmetohet në të gjithë ndërfaqen eth0.
  • DNAT nuk është i domosdoshëm sepse IP-ja e destinacionit është lokale për nyjen dhe jo rregulla e një pod të zgjedhur rastësisht.

Ne vendosëm të ecim përpara me këtë qasje. CoreDNS u vendos si DaemonSet në Kubernetes dhe ne injektuam serverin lokal të nyjës së nyjës në rezv.conf të secilit pod duke konfiguruar flamurin e komandës kubelet - cluster-dns. Përmbledhja ishte efektive për pushimet kohore të DNS.

Sidoqoftë, ne ende shohim paketat e rënë dhe rritja e counter-it të ndërfaqes Flannel. Kjo do të vazhdojë edhe pas zgjidhjes së mësipërme sepse vetëm kemi shmangur SNAT dhe / ose DNAT për trafikun DNS. Gjendja e garës do të ndodhë akoma për llojet e tjera të trafikut. Për fat të mirë, shumica e paketave tona janë TCP dhe kur të shfaqet gjendja, paketat do të ritransmetohen me sukses. Një rregullim afatgjatë për të gjitha llojet e trafikut është diçka që ne jemi ende duke diskutuar.

Përdorimi i të dërguarit për të arritur balancimin më të mirë të ngarkesës

Ndërsa migruam shërbimet tona të backend në Kubernetes, filluam të vuajmë nga një ngarkesë e pabalancuar nëpër pods. Ne zbuluam se për shkak të HTTP Keepalive, lidhjet ELB mbërthyer në kutitë e para të gatshme të çdo dislokimi, kështu që shumica e trafikut rrodhën përmes një përqindje të vogël të bishtajave në dispozicion. Një nga lehtësimet e para që u përpoqëm ishte të përdorim një 100% MaxSurge në dislokime të reja për shkelësit më të këqij. Ky ishte margjinalisht i efektshëm dhe jo afatgjatë i qëndrueshëm me disa prej vendosjeve më të mëdha.

Një tjetër zbutje që kemi përdorur ishte që të fryjmë artificialisht kërkesat e burimeve në shërbime kritike në mënyrë që podat e kolokuar të kishin më shumë hapësirë ​​për krah, krahas kutive të tjera të rënda. Kjo gjithashtu nuk do të jetë e durueshme në planin afatgjatë për shkak të mbeturinave të burimeve dhe aplikacionet tona të Nyjes u filetuan me një fije të vetme dhe kështu u kapën në mënyrë efektive në 1 thelb. E vetmja zgjidhje e qartë ishte të shfrytëzoni balancimin më të mirë të ngarkesës.

Ne kishim kërkuar që të vlerësonin të Dërguarin. Kjo na dha një shans ta vendosim atë në një mënyrë shumë të kufizuar dhe të përfitojmë përfitime të menjëhershme. I dërguari është një proxy Layer 7 me burim të hapur, me performancë të lartë, i dizajnuar për arkitektura të mëdha të orientuara nga shërbimi. Isshtë në gjendje të zbatojë teknika të avancuara të balancimit të ngarkesës, duke përfshirë provat automatike, thyerjen e qarkut dhe kufizimin global të normës.

Konfigurimi që erdhëm ishte të kishim një anësor të të Dërguarit së bashku me secilën pod që kishte një rrugë dhe grupim për të goditur portin lokal të kontejnerëve. Për të minimizuar kaskadimin e mundshëm dhe për të mbajtur një rreze të vogël shpërthimi, ne shfrytëzuam një flotë prej pods të Dërgimit të Proxy, një vendosje në secilën Zonë të Disponueshmërisë (AZ) për secilin shërbim. Këta goditën një mekanizëm të vogël zbulimi shërbimi që një prej inxhinierëve tanë vendosi së bashku që thjesht kthyen një listë të bishtajave në secilën AZ për një shërbim të caktuar.

Më pas, të dërguarit e shërbimit shfrytëzuan këtë mekanizëm të zbulimit të shërbimit me një grup dhe rrjedhë në rrjedhën e sipërme. Ne konfiguruam afatet kohore të arsyeshme, forcuam të gjitha cilësimet e ndërprerësit, dhe pastaj vendosëm një konfigurim minimal provimi për të ndihmuar me dështimet e përkohshme dhe vendosjet e qetë. Ne përballuam secilën nga këto shërbime të Dërguarit përpara me një TCP ELB. Edhe në qoftë se mbajtësi nga shtresa jonë kryesore e parë e proxy u vidh në disa podiume të të Dërguarit, ata do të ishin shumë më të aftë për të përballuar ngarkesën dhe ishin të konfiguruara që të ekuilibronin përmes minimumit të kërkesave të sfondit.

Për dislokimet, ne kemi përdorur një goditje preStop në të dyja aplikacionin dhe pod anësore. Kjo goditje e quajtur kontrolli shëndetësor i sidecar-it dështon në pikën përfundimtare të administratorit, së bashku me një gjumë të vogël, për t’i dhënë kohë që të lejojnë lidhjet e dritës të përfundojnë dhe kullojnë.

Një arsye për të cilën ne mund të lëviznim kaq shpejt ishte për shkak të metrikës së pasur që ishim në gjendje t'i integronim lehtësisht me vendosjen tonë normale të Prometeut. Kjo na lejoi të shohim saktësisht se çfarë po ndodhte ndërsa përsërisnim në cilësimet e konfigurimit dhe shkurtuam trafikun.

Rezultatet ishin të menjëhershme dhe të dukshme. Ne kemi filluar me shërbimet më të pabalancuara dhe në këtë pikë i kemi ato duke ecur përpara dymbëdhjetë prej shërbimeve më të rëndësishme në grupin tonë. Këtë vit ne kemi në plan për të kaluar në një rrjetë të shërbimit të plotë, me zbulimin më të përparuar të shërbimit, thyer qark, zbulimin e jashtëm, kufizimin e normës, dhe gjurmimin.

Figura 3–1 konvergjenca e CPU-së e një shërbimi gjatë ndërrimit të të dërguarit

Rezultati i Fundit

Përmes këtyre mësimeve dhe hulumtimeve shtesë, ne kemi zhvilluar një ekip të fortë brenda infrastrukturës me një familjaritet të madh se si të projektojmë, vendosim dhe operojmë grupime të mëdha Kubernetes. E gjithë organizata inxhinierike e Tinder tani ka njohuri dhe përvojë se si të kontejnerizojë dhe vendosë aplikimet e tyre në Kubernetes.

Në infrastrukturën tonë të trashëgimisë, kur kërkohej shkallë shtesë, ne shpesh vuanim nëpër disa minuta duke pritur që të vinin në internet raste të reja EC2. Kontejnerët tani caktojnë dhe shërbejnë trafikun brenda sekondave, në krahasim me minutat. Caktimi i kontejnerëve të shumëfishtë në një rast të vetëm EC2 gjithashtu siguron dendësi horizontale të përmirësuar. Si rezultat, ne projektojmë kursime të konsiderueshme të kostos në EC2 në 2019 në krahasim me një vit më parë.

Kaluan gati dy vjet, por ne përfunduam migrimin tonë në Mars 2019. Platforma Tinder shkon ekskluzivisht në një grupim Kubernetes i përbërë nga 200 shërbime, 1.000 nyje, 15,000 pod dhe 48,000 kontejnerë që funksionojnë. Infrastruktura nuk është më një detyrë e rezervuar për ekipet tona të operacionit. Në vend të kësaj, inxhinierët në të gjithë organizatën ndajnë këtë përgjegjësi dhe kanë kontroll mbi mënyrën se si aplikimet e tyre ndërtohen dhe vendosen me gjithçka si kod.