Kalimi i Tinder në Kubernetes

Shënim. përkth.: Punonjësit e shërbimit të famshëm botëror Tinder së fundmi ndanë disa detaje teknike të migrimit të infrastrukturës së tyre në Kubernetes. Procesi zgjati gati dy vjet dhe rezultoi në lançimin e një platforme shumë të gjerë në K8, e përbërë nga 200 shërbime të vendosura në 48 mijë kontejnerë. Çfarë vështirësish interesante hasën inxhinierët e Tinder dhe në çfarë rezultatesh arritën? Lexoni këtë përkthim.

Kalimi i Tinder në Kubernetes

Pse?

Pothuajse dy vjet më parë, Tinder vendosi të zhvendosë platformën e saj në Kubernetes. Kubernetes do të lejonte ekipin e Tinder të kontejneronte dhe të kalonte në prodhim me përpjekje minimale përmes vendosjes së pandryshueshme (vendosja e pandryshueshme). Në këtë rast, montimi i aplikacioneve, vendosja e tyre dhe vetë infrastruktura do të përcaktohen në mënyrë unike me kod.

Ne po kërkonim gjithashtu një zgjidhje për problemin e shkallëzueshmërisë dhe stabilitetit. Kur shkallëzimi u bë kritik, shpesh na duhej të prisnim disa minuta që instancat e reja EC2 të rrotulloheshin. Ideja për të hedhur kontejnerët dhe për të filluar shërbimin e trafikut në sekonda në vend të minutave u bë shumë tërheqëse për ne.

Procesi doli të ishte i vështirë. Gjatë migrimit tonë në fillim të vitit 2019, grupi Kubernetes arriti masën kritike dhe filluam të hasim probleme të ndryshme për shkak të vëllimit të trafikut, madhësisë së grupit dhe DNS. Gjatë rrugës, ne zgjidhëm shumë probleme interesante në lidhje me migrimin e 200 shërbimeve dhe mbajtjen e një grupi Kubernetes të përbërë nga 1000 nyje, 15000 pods dhe 48000 kontejnerë që funksionojnë.

Si?

Që nga janari 2018, ne kemi kaluar nëpër faza të ndryshme migrimi. Ne filluam duke kontejneruar të gjitha shërbimet tona dhe duke i vendosur ato në mjediset e testimit të resë së Kubernetes. Duke filluar nga tetori, ne filluam të migronim në mënyrë metodike të gjitha shërbimet ekzistuese në Kubernetes. Deri në mars të vitit të ardhshëm, ne përfunduam migrimin dhe tani platforma Tinder funksionon ekskluzivisht në Kubernetes.

Ndërtimi i imazheve për Kubernetes

Ne kemi mbi 30 depo të kodit burimor për mikroshërbimet që funksionojnë në një grupim Kubernetes. Kodi në këto depo është shkruar në gjuhë të ndryshme (për shembull, Node.js, Java, Scala, Go) me mjedise të shumëfishta kohëzgjatjeje për të njëjtën gjuhë.

Sistemi i ndërtimit është krijuar për të ofruar një "kontekst ndërtimi" plotësisht të personalizueshëm për çdo mikroshërbim. Zakonisht përbëhet nga një Dockerfile dhe një listë komandash shell. Përmbajtja e tyre është plotësisht e personalizueshme, dhe në të njëjtën kohë, të gjitha këto kontekste ndërtimi janë shkruar sipas një formati të standardizuar. Standardizimi i konteksteve të ndërtimit lejon një sistem të vetëm ndërtimi për të trajtuar të gjitha mikroshërbimet.

Kalimi i Tinder në Kubernetes
Figura 1-1. Procesi i standardizuar i ndërtimit nëpërmjet kontejnerit Builder

Për të arritur konsistencën maksimale ndërmjet kohëzgjatjeve (mjediset e kohës së ekzekutimit) i njëjti proces ndërtimi përdoret gjatë zhvillimit dhe testimit. Ne u përballëm me një sfidë shumë interesante: duhej të zhvillonim një mënyrë për të siguruar konsistencën e mjedisit të ndërtimit në të gjithë platformën. Për të arritur këtë, të gjitha proceset e montimit kryhen brenda një enë të veçantë. Ndërtues.

Zbatimi i tij i kontejnerit kërkonte teknika të avancuara Docker. Builder trashëgon ID-në e përdoruesit lokal dhe sekretet (siç është çelësi SSH, kredencialet AWS, etj.) që kërkohen për të hyrë në depot private të Tinder. Ai monton drejtoritë lokale që përmbajnë burime për të ruajtur natyrshëm objekte ndërtimi. Kjo qasje përmirëson performancën sepse eliminon nevojën për të kopjuar objekte ndërtimi midis kontejnerit Builder dhe hostit. Artefaktet e ruajtura të ndërtimit mund të ripërdoren pa konfigurim shtesë.

Për disa shërbime, na u desh të krijonim një kontejner tjetër për të hartuar mjedisin e përpilimit në mjedisin e ekzekutimit (për shembull, biblioteka bcrypt Node.js gjeneron objekte binare specifike për platformën gjatë instalimit). Gjatë procesit të përpilimit, kërkesat mund të ndryshojnë midis shërbimeve dhe Dockerfile përfundimtar përpilohet menjëherë.

Arkitektura dhe migrimi i grupimeve Kubernetes

Menaxhimi i madhësisë së grupit

Ne vendosëm të përdorim kube-aws për vendosjen e automatizuar të grupimeve në instancat e Amazon EC2. Në fillim, gjithçka funksionoi në një grup të përbashkët nyjesh. Ne e kuptuam shpejt nevojën për të ndarë ngarkesat e punës sipas madhësisë dhe llojit të shembullit për të bërë përdorimin më efikas të burimeve. Logjika ishte se drejtimi i disa podeve të ngarkuara me shumë fije doli të ishte më i parashikueshëm për sa i përket performancës sesa bashkëjetesa e tyre me një numër të madh pods me një fije.

Në fund u vendosëm në:

  • m5.4x i madh — për monitorim (Prometheus);
  • c5.4x i madh - për ngarkesën e punës Node.js (ngarkesa e punës me një fije);
  • c5.2x i madh - për Java dhe Go (ngarkesa e punës me shumë fije);
  • c5.4x i madh — për panelin e kontrollit (3 nyje).

shpërngulje

Një nga hapat përgatitorë për migrimin nga infrastruktura e vjetër në Kubernetes ishte ridrejtimi i komunikimit të drejtpërdrejtë ekzistues midis shërbimeve në balancuesit e rinj të ngarkesës (Elastic Load Balaners (ELB). Ato u krijuan në një nënrrjet specifik të një reje private virtuale (VPC). Ky nënrrjet ishte i lidhur me një Kubernetes VPC. Kjo na lejoi të migronim modulet gradualisht, pa marrë parasysh rendin specifik të varësive të shërbimit.

Këto pika fundore u krijuan duke përdorur grupe të peshuara të regjistrimeve DNS që kishin CNAME që tregonin çdo ELB të re. Për të kaluar, ne shtuam një hyrje të re që tregon ELB-në e re të shërbimit Kubernetes me një peshë 0. Më pas vendosëm Time To Live (TTL) të grupit të hyrjes në 0. Pas kësaj, peshat e vjetra dhe të reja u rregulluar ngadalë, dhe përfundimisht 100% e ngarkesës u dërgua në një server të ri. Pas përfundimit të ndërrimit, vlera TTL u kthye në një nivel më të përshtatshëm.

Modulet Java që kishim mund të përballonin DNS me TTL të ulët, por aplikacionet Node nuk mundën. Një nga inxhinierët rishkruan një pjesë të kodit të pishinës së lidhjes dhe e mbështjellë atë në një menaxher që përditësonte pishinat çdo 60 sekonda. Qasja e zgjedhur funksionoi shumë mirë dhe pa ndonjë degradim të dukshëm të performancës.

Mësimet

Kufijtë e pëlhurës së rrjetit

Në mëngjesin e hershëm të 8 janarit 2019, platforma Tinder u rrëzua papritur. Në përgjigje të një rritjeje të palidhur në vonesën e platformës më herët atë mëngjes, numri i pods dhe nyjeve në grup u rrit. Kjo bëri që cache ARP të shterohej në të gjitha nyjet tona.

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

Kalimi i Tinder në Kubernetes
(burim)

gc_thresh3 - ky është një kufi i vështirë. Shfaqja e hyrjeve të "mbushjes së tabelës fqinje" në regjistër nënkuptonte që edhe pas mbledhjes sinkron të mbeturinave (GC), nuk kishte hapësirë ​​të mjaftueshme në memorien e kujtesës ARP për të ruajtur hyrjen fqinje. Në këtë rast, kerneli thjesht e hodhi paketën plotësisht.

Ne përdorim fanellë si një strukturë rrjeti në Kubernetes. Paketat transmetohen përmes VXLAN. VXLAN është një tunel L2 i ngritur në krye të një rrjeti L3. Teknologjia përdor kapsulimin MAC-in-UDP (MAC Address-in-User Datagram Protocol) dhe lejon zgjerimin e segmenteve të rrjetit të Shtresës 2. Protokolli i transportit në rrjetin fizik të qendrës së të dhënave është IP plus UDP.

Kalimi i Tinder në Kubernetes
Figura 2-1. Diagrami fanellë (burim)

Kalimi i Tinder në Kubernetes
Figura 2-2. Paketa VXLAN (burim)

Çdo nyje punonjëse e Kubernetes cakton një hapësirë ​​​​virtuale adresash me një maskë /24 nga një bllok më i madh /9. Për çdo nyje kjo është mjete një hyrje në tabelën e rrugëzimit, një hyrje në tabelën ARP (në ndërfaqen flannel.1) dhe një hyrje në tabelën komutuese (FDB). Ato shtohen herën e parë kur fillon një nyje punëtore ose sa herë zbulohet një nyje e re.

Për më tepër, komunikimi node-pod (ose pod-pod) përfundimisht kalon përmes ndërfaqes eth0 (siç tregohet në diagramin Flannel më lart). Kjo rezulton në një hyrje shtesë në tabelën ARP për çdo host burimi dhe destinacioni përkatës.

Në mjedisin tonë, ky lloj komunikimi është shumë i zakonshëm. Për objektet e shërbimit në Kubernetes, krijohet një ELB dhe Kubernetes regjistron çdo nyje me ELB. ELB nuk di asgjë për pods dhe nyja e zgjedhur mund të mos jetë destinacioni përfundimtar i paketës. Çështja është se kur një nyje merr një paketë nga ELB, ajo e konsideron atë duke marrë parasysh rregullat iptables për një shërbim specifik dhe zgjedh rastësisht një pod në një nyje tjetër.

Në kohën e dështimit, kishte 605 nyje në grup. Për arsyet e përmendura më sipër, kjo ishte e mjaftueshme për të kapërcyer rëndësinë gc_thresh3, e cila është e paracaktuar. Kur kjo ndodh, jo vetëm që paketat fillojnë të hidhen, por e gjithë hapësira e adresave virtuale Flannel me një maskë /24 zhduket nga tabela ARP. Komunikimi Node-pod dhe pyetjet DNS ndërpriten (DNS është pritur në një grup; lexoni më vonë në këtë artikull për detaje).

Për të zgjidhur këtë problem, ju duhet të rritni vlerat gc_thresh1, gc_thresh2 и gc_thresh3 dhe rinisni Flannel për të riregjistruar rrjetet që mungojnë.

Shkallëzimi i papritur i DNS-së

Gjatë procesit të migrimit, ne përdorëm në mënyrë aktive DNS për të menaxhuar trafikun dhe për të transferuar gradualisht shërbimet nga infrastruktura e vjetër në Kubernetes. Ne vendosëm vlera relativisht të ulëta TTL për RecordSets të lidhura në Route53. Kur infrastruktura e vjetër po funksiononte në instancat EC2, konfigurimi ynë i zgjidhësit tregoi në Amazon DNS. Ne e morëm këtë si të mirëqenë dhe ndikimi i TTL-së së ulët në shërbimet tona dhe shërbimet e Amazon (si DynamoDB) kaloi kryesisht pa u vënë re.

Ndërsa migruam shërbimet në Kubernetes, zbuluam se DNS po përpunonte 250 mijë kërkesa në sekondë. Si rezultat, aplikacionet filluan të përjetojnë ndërprerje të vazhdueshme dhe serioze për pyetjet DNS. Kjo ndodhi pavarësisht përpjekjeve të jashtëzakonshme për të optimizuar dhe ndërruar ofruesin DNS në CoreDNS (i cili në ngarkesë maksimale arriti në 1000 pods që funksionojnë në 120 bërthama).

Duke hulumtuar shkaqe dhe zgjidhje të tjera të mundshme, ne zbuluam një artikull, duke përshkruar kushtet e garës që ndikojnë në kornizën e filtrimit të paketave filtër netfilm në Linux. Kohët e ndërprerjeve që vëzhguam, së bashku me një numërues në rritje fut_dështoi në ndërfaqen Flannel ishin në përputhje me gjetjet e artikullit.

Problemi ndodh në fazën e Përkthimit të Adresës së Rrjetit të Burimit dhe Destinacionit (SNAT dhe DNAT) dhe hyrjes pasuese në tabelë komplot. Një nga zgjidhjet e diskutuara brenda dhe sugjeruar nga komuniteti ishte zhvendosja e DNS në vetë nyjen e punës. Në këtë rast:

  • SNAT nuk është i nevojshëm sepse trafiku qëndron brenda nyjes. Nuk ka nevojë të kalohet përmes ndërfaqes eth0.
  • DNAT nuk nevojitet pasi IP-ja e destinacionit është lokale për nyjen dhe jo një pod i zgjedhur rastësisht sipas rregullave iptables.

Ne vendosëm t'i përmbahemi kësaj qasjeje. CoreDNS u vendos si një DaemonSet në Kubernetes dhe ne implementuam një server DNS me nyje lokale në zgjidh.konf çdo pod duke vendosur një flamur --grup-dns komandat kubelet . Kjo zgjidhje doli të jetë efektive për afatet e DNS.

Megjithatë, ne ende pamë humbje të paketave dhe një rritje në numërues fut_dështoi në ndërfaqen Flannel. Kjo vazhdoi pas zbatimit të zgjidhjes, sepse ne ishim në gjendje të eliminonim SNAT dhe/ose DNAT vetëm për trafikun DNS. Kushtet e garës u ruajtën për llojet e tjera të trafikut. Për fat të mirë, shumica e paketave tona janë TCP, dhe nëse shfaqet një problem, ato thjesht ritransmetohen. Ne ende po përpiqemi të gjejmë një zgjidhje të përshtatshme për të gjitha llojet e trafikut.

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

Ndërsa migruam shërbimet e backend-it në Kubernetes, filluam të vuanim nga ngarkesa e pabalancuar midis pods. Ne zbuluam se HTTP Keepalive bëri që lidhjet ELB të vareshin në podet e para të gatshme të çdo vendosjeje të lëshuar. Kështu, pjesa më e madhe e trafikut kaloi përmes një përqindje të vogël të podeve të disponueshme. Zgjidhja e parë që testuam ishte vendosja e MaxSurge në 100% në vendosjet e reja për skenarët më të keq. Efekti doli të ishte i parëndësishëm dhe jopremtues për sa i përket vendosjeve më të mëdha.

Një zgjidhje tjetër që përdorëm ishte rritja artificiale e kërkesave për burime për shërbime kritike. Në këtë rast, bishtajat e vendosura afër do të kishin më shumë hapësirë ​​për manovrim në krahasim me bishtajat e tjera të rënda. Nuk do të funksiononte as në planin afatgjatë, sepse do të ishte humbje e burimeve. Për më tepër, aplikacionet tona Node ishin me një fije të vetme dhe, në përputhje me rrethanat, mund të përdornin vetëm një bërthamë. E vetmja zgjidhje reale ishte përdorimi i balancimit më të mirë të ngarkesës.

Ne kemi dashur prej kohësh të vlerësojmë plotësisht i dërguar. Situata aktuale na lejoi ta vendosim atë në një mënyrë shumë të kufizuar dhe të marrim rezultate të menjëhershme. Envoy është një përfaqësues i nivelit XNUMX me performancë të lartë, me burim të hapur, i krijuar për aplikacione të mëdha SOA. Ai mund të zbatojë teknika të avancuara të balancimit të ngarkesës, duke përfshirë përsëritjet automatike, ndërprerësit dhe kufizimin global të normës. (Shënim. përkth.: Mund të lexoni më shumë rreth kësaj në Ky artikull për Istio, i cili bazohet në të Dërguarin.)

Ne dolëm me konfigurimin e mëposhtëm: kemi një karrige anësore të Envoy për çdo pod dhe një rrugë të vetme, dhe lidhim grupin me kontejnerin në vend nëpërmjet portit. Për të minimizuar kaskadimin e mundshëm dhe për të mbajtur një rreze të vogël goditjeje, ne përdorëm një flotë të pjesëve të përparme të përfaqësuesit të Envoy, një për Zonë të Disponueshmërisë (AZ) për çdo shërbim. Ata u mbështetën në një motor të thjeshtë zbulimi shërbimi të shkruar nga një prej inxhinierëve tanë që thjesht kthente një listë të grupeve në çdo AZ për një shërbim të caktuar.

Shërbimi front-Envoys përdori më pas këtë mekanizëm zbulimi të shërbimit me një grup dhe rrugë në rrjedhën e sipërme. Ne caktuam periudha kohore adekuate, rritëm të gjitha cilësimet e ndërprerësve dhe shtuam konfigurimin minimal të riprovës për të ndihmuar me dështimet e vetme dhe për të siguruar vendosje të qetë. Ne vendosëm një TCP ELB përpara secilit prej këtyre të dërguarve të shërbimit. Edhe nëse ruajtja nga shtresa jonë kryesore e proxy-it ishte e mbërthyer në disa pjesë të Envoy, ata ishin ende në gjendje ta trajtonin ngarkesën shumë më mirë dhe ishin konfiguruar për të balancuar përmes minimum_request në backend.

Për vendosjen, ne përdorëm grepin preStop si në podat e aplikacionit ashtu edhe në podat e karrigeve anësore. Gjuajtja shkaktoi një gabim në kontrollimin e statusit të pikës fundore të administratorit të vendosur në kontejnerin e karriges anësore dhe shkoi në gjumë për pak kohë për të lejuar përfundimin e lidhjeve aktive.

Një nga arsyet që ne mundëm të lëviznim kaq shpejt është për shkak të matjeve të detajuara që ne mundëm t'i integronim lehtësisht në një instalim tipik të Prometheus. Kjo na lejoi të shihnim saktësisht se çfarë po ndodhte ndërsa rregullonim parametrat e konfigurimit dhe rishpërndanim trafikun.

Rezultatet ishin të menjëhershme dhe të dukshme. Filluam me shërbimet më të çekuilibruara dhe për momentin operon përballë 12 shërbimeve më të rëndësishme të grupit. Këtë vit ne po planifikojmë një kalim në një rrjetë shërbimi të plotë me zbulim më të avancuar të shërbimit, ndërprerje të qarkut, zbulim të jashtëm, kufizim të shpejtësisë dhe gjurmim.

Kalimi i Tinder në Kubernetes
Figura 3-1. Konvergjenca e CPU-së e një shërbimi gjatë kalimit në Envoy

Kalimi i Tinder në Kubernetes

Kalimi i Tinder në Kubernetes

Rezultati përfundimtar

Nëpërmjet kësaj përvoje dhe kërkimesh shtesë, ne kemi ndërtuar një ekip të fortë infrastrukture me aftësi të forta në projektimin, vendosjen dhe funksionimin e grupimeve të mëdha Kubernetes. Të gjithë inxhinierët e Tinder tani kanë njohuri dhe përvojë për të paketuar kontejnerë dhe për të vendosur aplikacione në Kubernetes.

Kur lindi nevoja për kapacitet shtesë në infrastrukturën e vjetër, na u desh të prisnim disa minuta që të niseshin instancat e reja EC2. Tani kontejnerët fillojnë të funksionojnë dhe fillojnë të përpunojnë trafikun brenda sekondave në vend të minutave. Planifikimi i kontejnerëve të shumtë në një shembull të vetëm EC2 siguron gjithashtu përqendrim të përmirësuar horizontal. Si rezultat, ne parashikojmë një ulje të ndjeshme të kostove të EC2019 në 2 krahasuar me vitin e kaluar.

Migrimi zgjati gati dy vjet, por ne e përfunduam atë në mars 2019. Aktualisht, platforma Tinder funksionon ekskluzivisht në një grup Kubernetes të përbërë nga 200 shërbime, 1000 nyje, 15 pods dhe 000 kontejnerë që funksionojnë. Infrastruktura nuk është më domeni i vetëm i ekipeve operative. Të gjithë inxhinierët tanë ndajnë këtë përgjegjësi dhe kontrollojnë procesin e ndërtimit dhe vendosjes së aplikacioneve të tyre duke përdorur vetëm kodin.

PS nga përkthyesi

Lexoni gjithashtu një seri artikujsh në blogun tonë:

Burimi: www.habr.com

Shto një koment