Prehod Tinderja na Kubernetes

Opomba. prevod: Zaposleni v svetovno znani storitvi Tinder so pred kratkim delili nekaj tehničnih podrobnosti o selitvi svoje infrastrukture na Kubernetes. Proces je trajal skoraj dve leti in privedel do lansiranja zelo obsežne platforme na K8s, sestavljene iz 200 storitev, ki gostujejo v 48 tisoč vsebnikih. Na katere zanimive težave so naleteli Tinderjevi inženirji in do kakšnih rezultatov so prišli? Preberite ta prevod.

Prehod Tinderja na Kubernetes

Zakaj?

Pred skoraj dvema letoma se je Tinder odločil svojo platformo preseliti na Kubernetes. Kubernetes bi ekipi Tinder z nespremenljivo uvedbo omogočil kontejneriziranje in prehod na proizvodnjo z minimalnim naporom (nespremenljiva uvedba). V tem primeru bi bilo sestavljanje aplikacij, njihova namestitev in sama infrastruktura enolično definirana s kodo.

Iskali smo tudi rešitev za problem razširljivosti in stabilnosti. Ko je skaliranje postalo kritično, smo morali pogosto čakati nekaj minut, da so se novi primerki EC2 zavrteli. Ideja o lansiranju zabojnikov in pričetku oskrbe prometa v nekaj sekundah namesto v minutah nam je postala zelo privlačna.

Izkazalo se je, da je postopek težak. Med našo selitvijo v začetku leta 2019 je gruča Kubernetes dosegla kritično maso in začeli smo se srečevati z različnimi težavami zaradi količine prometa, velikosti gruče in DNS-ja. Na tej poti smo rešili veliko zanimivih problemov, povezanih s selitvijo 200 storitev in vzdrževanjem gruče Kubernetes, ki jo sestavlja 1000 vozlišč, 15000 podov in 48000 delujočih vsebnikov.

Kako?

Od januarja 2018 smo šli skozi različne faze selitve. Začeli smo z zbiranjem vseh naših storitev v kontejnerje in njihovo uvedbo v testna oblačna okolja Kubernetes. Z oktobrom smo začeli metodično seliti vse obstoječe storitve v Kubernetes. Do marca naslednjega leta smo zaključili selitev in zdaj platforma Tinder deluje izključno na Kubernetesu.

Gradnja slik za Kubernetes

Imamo več kot 30 repozitorijev izvorne kode za mikrostoritve, ki se izvajajo v gruči Kubernetes. Koda v teh repozitorijih je napisana v različnih jezikih (na primer Node.js, Java, Scala, Go) z več izvajalnimi okolji za isti jezik.

Sistem gradnje je zasnovan tako, da zagotavlja popolnoma prilagodljiv »kontekst gradnje« za vsako mikrostoritev. Običajno je sestavljen iz datoteke Docker in seznama ukazov lupine. Njihova vsebina je popolnoma prilagodljiva, hkrati pa so vsi ti gradbeni konteksti napisani v skladu s standardizirano obliko. Standardizacija gradbenih kontekstov omogoča, da en sam gradbeni sistem obravnava vse mikrostoritve.

Prehod Tinderja na Kubernetes
Slika 1-1. Standardiziran postopek gradnje prek vsebnika Builder

Za doseganje največje doslednosti med izvajalnimi časi (izvajalna okolja) Med razvojem in testiranjem se uporablja isti postopek gradnje. Soočili smo se z zelo zanimivim izzivom: razviti smo morali način, kako zagotoviti skladnost gradbenega okolja na celotni platformi. Da bi to dosegli, se vsi postopki sestavljanja izvajajo v posebnem zabojniku. Builder.

Njegova implementacija vsebnika je zahtevala napredne Dockerjeve tehnike. Builder podeduje lokalni ID uporabnika in skrivnosti (kot so ključ SSH, poverilnice AWS itd.), potrebne za dostop do zasebnih repozitorijev Tinder. Pripne lokalne imenike, ki vsebujejo vire, da naravno shrani artefakte gradnje. Ta pristop izboljša zmogljivost, ker odpravlja potrebo po kopiranju gradbenih artefaktov med vsebnikom Builder in gostiteljem. Shranjene artefakte gradnje je mogoče ponovno uporabiti brez dodatne konfiguracije.

Za nekatere storitve smo morali ustvariti drug vsebnik za preslikavo okolja prevajanja v okolje izvajalnega časa (na primer, knjižnica bcrypt Node.js med namestitvijo ustvari binarne artefakte, specifične za platformo). Med postopkom prevajanja se lahko zahteve med storitvami razlikujejo, končna datoteka Dockerfile pa se prevede sproti.

Arhitektura gruče Kubernetes in migracija

Upravljanje velikosti gruče

Odločili smo se za uporabo kube-aws za avtomatizirano uvajanje gruč na primerkih Amazon EC2. Na samem začetku je vse delovalo v enem skupnem bazenu vozlišč. Hitro smo spoznali potrebo po ločevanju delovnih obremenitev glede na velikost in vrsto instance, da bi učinkoviteje uporabljali vire. Logika je bila, da se je izvajanje več naloženih večnitnih podov izkazalo za bolj predvidljivo v smislu zmogljivosti kot njihov soobstoj z velikim številom enonitnih podov.

Na koncu smo se dogovorili za:

  • m5.4 večji — za spremljanje (Prometheus);
  • c5.4 večji - za obremenitev Node.js (obremenitev z eno nitjo);
  • c5.2 večji - za Java in Go (večnitna obremenitev);
  • c5.4 večji — za nadzorno ploščo (3 vozlišča).

Migracija

Eden od pripravljalnih korakov za selitev s stare infrastrukture na Kubernetes je bila preusmeritev obstoječe neposredne komunikacije med storitvami na nove uravnavalnike obremenitve (Elastic Load Balancers (ELB). Ustvarjeni so bili v določenem podomrežju virtualnega zasebnega oblaka (VPC). To podomrežje je bilo povezano s Kubernetes VPC. To nam je omogočilo postopno selitev modulov, ne da bi upoštevali poseben vrstni red odvisnosti storitev.

Te končne točke so bile ustvarjene z uporabo uteženih nizov zapisov DNS, ki so imeli CNAME, ki kažejo na vsak nov ELB. Za preklop smo dodali nov vnos, ki kaže na novi ELB storitve Kubernetes z utežjo 0. Nato smo nastavili čas življenja (TTL) nabora vnosov na 0. Po tem sta bili stari in novi uteži počasi prilagodil in na koncu je bilo 100 % obremenitve poslano na nov strežnik. Po končanem preklopu se je vrednost TTL vrnila na ustreznejšo raven.

Moduli Java, ki smo jih imeli, so se lahko spopadli z nizkim TTL DNS, aplikacije Node pa ne. Eden od inženirjev je prepisal del kode povezovalnega bazena in ga zavil v upravitelja, ki je bazene posodabljal vsakih 60 sekund. Izbrani pristop je deloval zelo dobro in brez opaznega poslabšanja zmogljivosti.

Lekcije

Omejitve omrežne strukture

Zgodaj zjutraj 8. januarja 2019 se je platforma Tinder nepričakovano zrušila. Kot odziv na nepovezano povečanje zakasnitve platforme zgodaj zjutraj se je povečalo število podov in vozlišč v gruči. To je povzročilo, da je bil predpomnilnik ARP izčrpan na vseh naših vozliščih.

Obstajajo tri možnosti Linuxa, povezane s predpomnilnikom ARP:

Prehod Tinderja na Kubernetes
(Vir)

gc_thresh3 - to je trda omejitev. Pojav vnosov »prelivanje sosednje tabele« v dnevniku je pomenil, da tudi po sinhronem zbiranju smeti (GC) v predpomnilniku ARP ni bilo dovolj prostora za shranjevanje sosednjega vnosa. V tem primeru je jedro preprosto popolnoma zavrglo paket.

Uporabljamo Flannel kot omrežno tkivo v Kubernetesu. Paketi se prenašajo preko VXLAN. VXLAN je tunel L2, dvignjen na vrhu omrežja L3. Tehnologija uporablja enkapsulacijo MAC-in-UDP (MAC Address-in-User Datagram Protocol) in omogoča razširitev omrežnih segmentov plasti 2. Transportni protokol v omrežju fizičnega podatkovnega centra je IP plus UDP.

Prehod Tinderja na Kubernetes
Slika 2–1. Flanel diagram (Vir)

Prehod Tinderja na Kubernetes
Slika 2-2. paket VXLAN (Vir)

Vsako delovno vozlišče Kubernetes dodeli virtualni naslovni prostor z masko /24 iz večjega bloka /9. Za vsako vozlišče je to pomeni en vnos v usmerjevalni tabeli, en vnos v tabeli ARP (na vmesniku flanel.1) in en vnos v preklopni tabeli (FDB). Dodani so ob prvem zagonu delovnega vozlišča ali vsakič, ko je odkrito novo vozlišče.

Poleg tega komunikacija vozlišča-pod (ali pod-pod) na koncu poteka prek vmesnika eth0 (kot je prikazano na zgornjem diagramu Flannel). Posledica tega je dodaten vnos v tabeli ARP za vsakega ustreznega izvornega in ciljnega gostitelja.

V našem okolju je tovrstna komunikacija zelo pogosta. Za storitvene objekte v Kubernetesu se ustvari ELB in Kubernetes registrira vsako vozlišče z ELB. ELB ne ve ničesar o podih in izbrano vozlišče morda ni končni cilj paketa. Bistvo je, da ko vozlišče prejme paket od ELB, ga obravnava ob upoštevanju pravil iptables za določeno storitev in naključno izbere pod na drugem vozlišču.

V času okvare je bilo v gruči 605 vozlišč. Zaradi zgoraj navedenih razlogov je to zadostovalo za preseganje pomena gc_thresh3, kar je privzeto. Ko se to zgodi, ne samo, da začnejo padati paketi, ampak celoten virtualni naslovni prostor Flannel z masko /24 izgine iz tabele ARP. Komunikacija med vozlišči in poizvedbe DNS so prekinjene (DNS gostuje v gruči; podrobnosti preberite pozneje v tem članku).

Če želite rešiti to težavo, morate povečati vrednosti gc_thresh1, gc_thresh2 и gc_thresh3 in znova zaženite Flannel, da ponovno registrirate manjkajoča omrežja.

Nepričakovano skaliranje DNS

V procesu migracije smo aktivno uporabljali DNS za upravljanje prometa in postopen prenos storitev iz stare infrastrukture v Kubernetes. Nastavili smo relativno nizke vrednosti TTL za povezane RecordSets v Route53. Ko se je stara infrastruktura izvajala na primerkih EC2, je naša konfiguracija razreševalnika kazala na Amazon DNS. To smo vzeli za samoumevno in vpliv nizkega TTL na naše storitve in storitve Amazon (kot je DynamoDB) je ostal večinoma neopažen.

Ko smo storitve preselili na Kubernetes, smo ugotovili, da DNS obdeluje 250 tisoč zahtev na sekundo. Posledično so aplikacije začele doživljati stalne in resne časovne omejitve za poizvedbe DNS. To se je zgodilo kljub neverjetnim prizadevanjem za optimizacijo in preklop ponudnika DNS na CoreDNS (ki je ob največji obremenitvi dosegel 1000 podov, ki delujejo na 120 jedrih).

Med raziskovanjem drugih možnih vzrokov in rešitev smo odkrili статью, ki opisuje pogoje tekmovanja, ki vplivajo na ogrodje za filtriranje paketov netfilter v sistemu Linux. Časovne omejitve, ki smo jih opazili, skupaj z naraščajočim števcem vstavljanje ni uspelo v vmesniku Flannel so bile skladne z ugotovitvami članka.

Težava se pojavi v fazi prevajanja izvornega in ciljnega omrežnega naslova (SNAT in DNAT) in kasnejšega vnosa v tabelo conntrack. Ena od rešitev, o kateri so interno razpravljali in jo je predlagala skupnost, je bila premakniti DNS v samo delovno vozlišče. V tem primeru:

  • SNAT ni potreben, ker promet ostane znotraj vozlišča. Ni ga treba usmeriti skozi vmesnik eth0.
  • DNAT ni potreben, ker je ciljni IP lokalen za vozlišče in ne naključno izbran pod v skladu s pravili iptables.

Odločili smo se, da bomo ostali pri tem pristopu. CoreDNS je bil razporejen kot DaemonSet v Kubernetes in implementirali smo strežnik DNS lokalnega vozlišča v razreši.conf vsak pod z nastavitvijo zastavice --cluster-dns ukazi kocka . Ta rešitev se je izkazala za učinkovito pri časovnih omejitvah DNS.

Vendar smo še vedno opazili izgubo paketov in povečanje števca vstavljanje ni uspelo v vmesniku Flannel. To se je nadaljevalo, potem ko je bila uvedena rešitev, ker smo lahko odstranili SNAT in/ali DNAT samo za promet DNS. Pogoji dirke so bili ohranjeni za druge vrste prometa. Na srečo je večina naših paketov TCP in če pride do težave, se preprosto ponovno prenesejo. Še vedno iščemo ustrezno rešitev za vse vrste prometa.

Uporaba programa Envoy za boljše uravnoteženje obremenitve

Ko smo zaledne storitve preselili v Kubernetes, smo začeli trpeti zaradi neuravnotežene obremenitve med sklopi. Ugotovili smo, da je HTTP Keepalive povzročil, da so povezave ELB visele na prvih pripravljenih blokih vsake uvedene uvedbe. Tako je večina prometa potekala skozi majhen odstotek razpoložljivih sklopov. Prva rešitev, ki smo jo preizkusili, je bila nastavitev MaxSurge na 100 % pri novih uvedbah za najslabše možne scenarije. Učinek se je izkazal za nepomembnega in neobetavnega z vidika večjih uvedb.

Druga rešitev, ki smo jo uporabili, je bilo umetno povečanje zahtev po virih za kritične storitve. V tem primeru bi imeli stroki, postavljeni v bližini, več manevrskega prostora v primerjavi z drugimi težkimi stroki. Tudi na dolgi rok ne bi delovalo, ker bi bilo zapravljanje virov. Poleg tega so bile naše aplikacije Node enonitne in so zato lahko uporabljale samo eno jedro. Edina prava rešitev je bila uporaba boljšega uravnoteženja obremenitve.

Že dolgo smo želeli v celoti ceniti Odposlanec. Trenutne razmere so nam omogočile, da smo ga uporabili na zelo omejen način in dosegli takojšnje rezultate. Envoy je visoko zmogljiv, odprtokoden proxy sloja XNUMX, zasnovan za velike aplikacije SOA. Izvaja lahko napredne tehnike uravnoteženja obremenitve, vključno s samodejnimi ponovnimi poskusi, odklopniki in globalno omejitvijo hitrosti. (Opomba. prevod: Več o tem si lahko preberete v ta članek o Istio, ki temelji na Envoy.)

Prišli smo do naslednje konfiguracije: imeti stransko prikolico Envoy za vsak pod in eno pot ter povezati gručo z vsebnikom lokalno prek vrat. Da bi čim bolj zmanjšali morebitno kaskadiranje in ohranili majhen radij zadetka, smo uporabili floto enot Envoy front-proxy, enega na območje razpoložljivosti (AZ) za vsako storitev. Zanašali so se na preprost mehanizem za odkrivanje storitev, ki ga je napisal eden od naših inženirjev, ki je preprosto vrnil seznam podov v vsakem AZ za določeno storitev.

Service front-Envoys je nato uporabil ta mehanizem odkrivanja storitev z eno gornjo gručo in potjo. Nastavili smo ustrezne časovne omejitve, povečali vse nastavitve odklopnikov in dodali minimalno konfiguracijo ponovnih poskusov za pomoč pri posameznih okvarah in zagotovili nemoteno uvajanje. TCP ELB smo postavili pred vsakega od teh servisnih front-Envoys. Tudi če je bil keepalive iz našega glavnega posredniškega sloja obtičal na nekaterih podih Envoy, so še vedno lahko veliko bolje obvladovali obremenitev in so bili konfigurirani za uravnoteženje prek najmanjše zahteve v ozadju.

Za uvajanje smo uporabili kavelj preStop tako na podstavkih za aplikacije kot na podstavkih s prikolico. Kavelj je sprožil napako pri preverjanju statusa skrbniške končne točke, ki se nahaja na vsebniku stranske prikolice, in je za nekaj časa prešel v stanje mirovanja, da je omogočil prekinitev aktivnih povezav.

Eden od razlogov, zakaj smo se tako hitro premaknili, je podrobna metrika, ki smo jo lahko enostavno integrirali v tipično namestitev Prometheusa. To nam je omogočilo, da smo natančno videli, kaj se dogaja, medtem ko smo prilagajali konfiguracijske parametre in prerazporejali promet.

Rezultati so bili takojšnji in očitni. Začeli smo z najbolj neuravnoteženimi storitvami, trenutno pa deluje pred 12 najpomembnejšimi storitvami v grozdu. Letos načrtujemo prehod na mrežo s polnimi storitvami z naprednejšim odkrivanjem storitev, prekinitvijo tokokroga, zaznavanjem izstopov, omejevanjem hitrosti in sledenjem.

Prehod Tinderja na Kubernetes
Slika 3–1. CPU konvergenca ene storitve med prehodom na Envoy

Prehod Tinderja na Kubernetes

Prehod Tinderja na Kubernetes

Končni rezultat

S temi izkušnjami in dodatnimi raziskavami smo zgradili močno infrastrukturno ekipo z močnimi veščinami pri načrtovanju, uvajanju in upravljanju velikih gruč Kubernetes. Vsi Tinder inženirji imajo zdaj znanje in izkušnje za pakiranje vsebnikov in uvajanje aplikacij v Kubernetes.

Ko se je pojavila potreba po dodatni zmogljivosti na stari infrastrukturi, smo morali počakati nekaj minut, da so se zagnale nove instance EC2. Zdaj se kontejnerji začnejo izvajati in začnejo obdelovati promet v nekaj sekundah namesto v minutah. Načrtovanje več vsebnikov na enem primerku EC2 zagotavlja tudi izboljšano vodoravno koncentracijo. Posledično napovedujemo znatno zmanjšanje stroškov EC2019 v letu 2 v primerjavi z lanskim letom.

Selitev je trajala skoraj dve leti, vendar smo jo zaključili marca 2019. Trenutno platforma Tinder deluje izključno na gruči Kubernetes, ki jo sestavlja 200 storitev, 1000 vozlišč, 15 podov in 000 tekočih vsebnikov. Infrastruktura ni več edina domena operativnih skupin. Vsi naši inženirji si delijo to odgovornost in nadzorujejo proces gradnje in uvajanja svojih aplikacij samo z uporabo kode.

PS od prevajalca

Preberite tudi vrsto člankov na našem blogu:

Vir: www.habr.com

Dodaj komentar