Tinderov prelazak u Kubernetes

Napisao: Chris O'Brien, inženjerski direktor | Chris Thomas, inženjerski menadžer | Jinyong Lee, stariji softverski inženjer | Uredio: Cooper Jackson, softver inženjer

Zašto

Prije gotovo dvije godine Tinder je odlučio svoju platformu premjestiti u Kubernetes. Kubernetes nam je pružio priliku da Tinder Engineering krenemo u smjeru kontejnera i rada s malim dodirom kroz nepromjenjivu implementaciju. Izgradnja, implementacija i infrastruktura aplikacije definirali bi se kao kod.

Također smo tražili rješavanje izazova razmjera i stabilnosti. Kad je skaliranje postalo kritično, često smo patili od nekoliko minuta čekanja da se novi EC2 slučajevi jave na mreži. Ideja o rasporedu kontejnera i opsluživanju prometa u sekundi, za razliku od minuta bila nam je privlačna.

Nije bilo lako. Tijekom migracije početkom 2019. godine, dosegli smo kritičnu masu unutar našeg klastera Kubernetes i počeli nailaziti na različite izazove zbog količine prometa, veličine klastera i DNS-a. Riješili smo zanimljive izazove migrirati 200 usluga i pokrenuti Kubernetes klaster u mjerilu od ukupno 1.000 čvorova, 15.000 mahuna i 48.000 spremnika.

Kako

Počevši od siječnja 2018., radili smo na različitim stupnjevima migracijskih napora. Započeli smo sa spremanjem svih naših usluga i raspoređivanjem ih u niz okruženja u kojima su se nalazili Kubernetes. Početkom listopada započeli smo metodično premještanje svih naših zaostavljenih usluga u Kubernetes. Do ožujka sljedeće godine dovršili smo svoju migraciju i platforma Tinder sada radi isključivo na Kubernetesu.

Izrada slika za Kubernetes

Postoji više od 30 skladišta izvornog koda za mikroservise koji se izvode u klasteru Kubernetes. Kôd u tim spremištima je napisan na različitim jezicima (npr. Node.js, Java, Scala, Go) s više okruženja za istim jezikom.

Sustav gradnje dizajniran je za rad na potpuno prilagodljivom "kontekstu gradnje" za svaku mikroservis, koja se obično sastoji od Dockerfile-a i niza naredbi shell-a. Iako je njihov sadržaj u potpunosti prilagodljiv, svi ovi konteksti izrade pišu se slijedeći standardizirani format. Standardizacija konteksta gradnje omogućava da jedan sustav sastavljanja obrađuje sve mikroservise.

Slika 1–1 Standardizirani postupak sastavljanja kroz Builder spremnik

Da bi se postigla maksimalna usklađenost između okruženja za vrijeme izvođenja, u fazi razvoja i ispitivanja koristi se isti postupak sastavljanja. To nam je naložilo jedinstven izazov kada smo trebali osmisliti način koji će jamčiti dosljedno okruženje za izgradnju na čitavoj platformi. Kao rezultat, svi procesi sastavljanja se izvode unutar posebnog spremnika "Builder".

Implementacija spremnika Builder zahtijevala je niz naprednih Docker tehnika. Ovaj spremnik Builder nasljeđuje lokalni korisnički ID i tajne (npr. SSH ključ, AWS vjerodajnice itd.) Prema potrebi za pristup Tinder privatnim spremištima. Montira lokalne imenike koji sadrže izvorni kod kako bi imao prirodan način pohranjivanja artefakata. Ovaj pristup poboljšava performanse, jer eliminira kopiranje izgrađenih artefakata između spremnika Builder-a i glavnog računala. Spremljeni artefakti za izgradnju ponovo će se koristiti sljedeći put bez daljnje konfiguracije.

Za određene usluge morali smo stvoriti još jedan spremnik u Builderu da bi se uskladio okruženje vremena sastavljanja s okruženjem izvođenja (npr. Instaliranjem Node.js biblioteke bcrypt generira binarni artefakt specifičan za platformu). Zahtjevi za vrijeme kompilacije mogu se razlikovati između usluga i konačni Dockerfile sastavljen je u pokretu.

Kubernetes klaster arhitektura i migracije

Veličina klastera

Odlučili smo koristiti kube-aws za automatsko osiguravanje klastera na primjercima Amazon EC2. Rano smo sve pokrenuli u jednom zajedničkom bazenu čvorova. Brzo smo prepoznali potrebu za razdvajanjem radnih opterećenja u različite veličine i vrste primjeraka kako bismo bolje iskoristili resurse. Obrazloženje je bilo da trčanje manje mahuna s navojem zajedno daje više predvidljivih rezultata izvedbe za nas nego što im dopuštamo da koegzistiraju s većim brojem podloga s jednim navojem.

Smjestili smo se na:

  • m5.4već za nadzor (Prometej)
  • c5.4 povećati za Node.js radno opterećenje (jednostruko nanošenje)
  • c5.2 uvećanje za Java i Go (višeslojno opterećenje)
  • c5.4 uvećanje za upravljačku ravninu (3 čvora)

Migracija

Jedan od koraka za pripremu migracije s naše naslijeđene infrastrukture na Kubernetes bio je promjena postojeće komunikacije između usluge i servisa kako bi se ukazalo na nove elastične ravnoteže opterećenja (ELBs) koji su stvoreni u određenoj podmreži Virtual Private Cloud (VPC). Ova podmreža je zavirila u Kubernetes VPC. To nam je omogućilo granularnu migraciju modula bez obzira na posebno naručivanje ovisno o uslugama.

Te su krajnje točke kreirane korištenjem ponderiranih skupova zapisa DNS koji su CNAME usmjeravali prema svakom novom ELB-u. Za rezanje dodali smo novi rekord, pokazujući na novu Kubernetes uslugu ELB, s težinom od 0. Zatim smo postavili Time To Live (TTL) na rekordnoj postavi na 0. Stari i novi utezi su se polako prilagodili na na kraju završite sa 100% na novom poslužitelju. Nakon što je presjek bio dovršen, TTL je postavljen na nešto razumnije.

Naši Java moduli poštovali su nizak DNS TTL, ali naše aplikacije Node nisu. Jedan od naših inženjera napisao je dio koda baze podataka za spajanje kako bi ga omotao za upravitelja koji bi osvježavao bazene svakih 60-ih. To nam je jako dobro uspjelo, bez zapaženih performansi.

spoznaje

Granice mrežne tkanine

U ranim jutarnjim satima 8. siječnja 2019. Tinder's Platform pretrpio je trajan kvar. Kao odgovor na nepovezano povećanje kašnjenja platforme ranije tog jutra, broj podočnjaka i čvorova skaliran je na klasteru. To je rezultiralo iscrpljenjem ARP predmemorije na svim našim čvorovima.

Tri su Linux vrijednosti relevantne za ARP cache:

Kreditna

gc_thresh3 je tvrda kapa. Ako dobivate unose u dnevnik "susjedove prepune tablice", to ukazuje da čak i nakon sinkronog skupljanja smeća (GC) ARP cachea nije bilo dovoljno mjesta za spremanje susjedovog unosa. U ovom slučaju kernel samo u potpunosti ispusta paket.

Flannel koristimo kao mrežnu tkaninu u Kubernetesu. Paketi se prosljeđuju putem VXLAN-a. VXLAN je shema sloja sloja 2 preko mreže razine 3. Koristi enkapsulaciju MAC adrese u korisniku Datagram (MAC-in-UDP) za osiguravanje načina za proširenje mrežnih segmenata razine 2. Protokol prijevoza preko mreže fizičkih podataka je IP plus UDP.

Slika 2–1 Flannel dijagram (kreditni)

Slika 2–2 VXLAN paket (kreditni)

Svaki Kubernetesov radnički čvor dodjeljuje vlastiti / 24 virtualnog adresnog prostora iz većeg / 9 bloka. To za svaki čvor rezultira s 1 unosom tablice rute, 1 unosom tablice ARP (na sučelju flannel.1) i 1 unosom baze podataka prosljeđivanja (FDB). Oni se dodaju kada se prvi čvor radnika pokreće ili kada se otkrije svaki novi čvor.

Uz to, komunikacija čvor-do-pod (ili pod-do-pod) komunikacija na kraju teče preko eth0 sučelja (prikazanog na Flannelovom dijagramu gore). To će rezultirati dodatnim unosom u ARP tablici za svaki odgovarajući izvor čvora i odredišta čvora.

U našem okruženju je ova vrsta komunikacije vrlo česta. Za naše Kubernetes servisne objekte stvara se ELB i Kubernetes registrira svaki čvor s ELB. ELB nije svjestan i odabrani čvor možda nije krajnje odredište paketa. To je zato što kad čvor primi paket od ELB-a, on procjenjuje svoja iptables pravila za uslugu i nasumično odabire pod na drugom čvoru.

U trenutku kvara bilo je 605 ukupnih čvorova u klasteru. Iz gore navedenih razloga, ovo je bilo dovoljno za pomračenje zadane vrijednosti gc_thresh3. Kad se to dogodi, ne samo da se paketi odbacuju, već i cijeli Flannel / 24s virtualnog adresnog prostora nedostaje iz ARP tablice. Neuspjeh komunikacija čvorova prema područniku i DNS pretraživanja. (DNS se nalazi unutar klastera, kao što će biti detaljnije objašnjeno u ovom članku.)

Da bi se to riješilo, vrijednosti gc_thresh1, gc_thresh2 i gc_thresh3 podižu se i Flannel se mora ponovo pokrenuti kako bi se ponovno registrirala mreža koja nedostaje.

Neočekivano pokreće DNS na skali

Kako bismo prilagodili našoj migraciji, snažno smo iskoristili DNS kako bismo olakšali oblikovanje prometa i inkrementalni prelazak od ostavštine do Kubernetesa za naše usluge. Postavili smo relativno niske vrijednosti TTL-a na pridruženi Route53 RecordSets. Kad smo pokrenuli svoju naslijeđenu infrastrukturu na EC2 instancama, naša je rezolucijska konfiguracija ukazala na Amazonov DNS. To smo shvatili zdravo za gotovo i troškovi relativno niskog TTL-a za naše usluge i Amazonove usluge (npr. DynamoDB) ostali su uglavnom nezapaženi.

Kako smo se ukrcavali na sve više i više usluga Kubernetesu, našli smo se da vodimo DNS uslugu koja je odgovarala na 250 000 zahtjeva u sekundi. U našim se aplikacijama susretali s povremenim i snažnim DNS-ovim vremenskim intervalima pretraživanja. Do toga je došlo unatoč iscrpnom ugađanju i prelasku DNS dobavljača na CoreDNS implementaciju koja je odjednom dosegla 1.000 mahuna konzumirajući 120 jezgara.

Dok smo istraživali druge moguće uzroke i rješenja, pronašli smo članak koji opisuje stanje utrke koje utječe na Linux filtriranje okvira paketa netfilter. Istek vremena DNS-a koji smo vidjeli, zajedno s povećanjem brojača insert_failed na Flannel sučelju, usklađen s nalazima članka.

Do problema dolazi tijekom prijevoda izvorne i odredišne ​​mrežne adrese (SNAT i DNAT) i naknadnog umetanja u tablicu uklanjanja. Jedan od poteza koji se unutar raspravljao i koji je zajednica predložila je premještanje DNS-a na sam radnički čvor. U ovom slučaju:

  • SNAT nije potreban, jer promet ostaje lokalno na čvoru. Ne treba ga prenositi kroz eth0 sučelje.
  • DNAT nije potreban jer je odredišni IP lokalni čvor, a nije nasumično odabran pod za pravila iptables.

Odlučili smo krenuti naprijed s tim pristupom. CoreDNS je raspoređen kao DaemonSet u Kubernetesu i ubrizgali smo lokalni DNS poslužitelj čvora u svaki pod Resol.conf konfiguracijom konfigurirajući naredbenu zastavu kubelet - cluster-dns. Otkrivanje je bilo učinkovito za istek vremena DNS-a.

Ipak, i dalje vidimo odbačene pakete i priraštaj brojača inser_failed brojača sučelja Flannel. To će trajati i nakon gornjeg zaobilaženja, jer smo samo izbjegli SNAT i / ili DNAT za DNS promet. Stanje utrke i dalje će se pojaviti za druge vrste prometa. Srećom, većina naših paketa je TCP, a kada se stanje dogodi, paketi će se uspješno ponovno poslati. Dugoročno ispravljanje svih vrsta prometa nešto je što još uvijek raspravljamo.

Koristeći izaslanika za postizanje boljeg uravnoteženja opterećenja

Dok smo premještali svoje pomoćne usluge u Kubernetes, počeli smo patiti od neuravnoteženog opterećenja preko mahuna. Otkrili smo da se zahvaljujući HTTP Keepalive, ELB veze zadržale na prvim spremnim podsustavima svake implementacije, tako da je većina prometa tekla kroz mali postotak dostupnih mahuna. Jedno od prvih ublažavanja koje smo pokušali bilo je korištenje 100% MaxSurgea na novim razmještajima za najgore prekršitelje. Ovo je bilo neznatno učinkovito i dugoročno održivo vrijeme s nekim većim raspoređivanjima.

Drugo ublažavanje koje smo koristili bilo je da se na kritične usluge umjetno povećaju zahtjevi za resursima kako bi obojene mahune imale više prostora pored ostalih teških mahuna. Ovo također neće biti dugoročno održivo zbog gubitka resursa, a naše Node aplikacije su jednostruke i na taj način učinkovito su ograničene na 1 jezgru. Jedino jasno rješenje bilo je koristiti bolje uravnoteženje opterećenja.

Interno smo tražili procjenu izaslanika. To nam je omogućilo šansu da je implementiramo na vrlo ograničen način i da dobijemo neposredne koristi. Izaslanik je open source, visoko-performansni proxy Layer 7 dizajniran za velike uslužne arhitekture. U mogućnosti je primijeniti napredne tehnike uravnoteženja opterećenja, uključujući automatska pokušaja, prekid kruga i ograničenje globalne stope.

Konfiguracija koju smo smislili bila je da uz svaki luk koji je imao jednu rutu i grozd koji bi pogodio lokalni priključak kontejnera, stoji prikolica za izaslanika. Kako bismo umanjili potencijal kaskadne i zadržali mali radijus eksplozije, koristili smo flotu prednjih proxy-jevih izaslanica za podzemlje, jedno u svakoj zoni dostupnosti (AZ) za svaku uslugu. Ovi su pogodili mali mehanizam za otkrivanje servisa koji je jedan od naših inženjera sastavio, a koji su jednostavno vratili popis mahuna u svakoj AZ za određenu uslugu.

Prednji servisni izaslanici upotrebljavali su ovaj mehanizam otkrivanja usluge jednim nakupom i smjerom uzvodno. Konfigurirali smo razumne vremenske prekide, pojačali sve postavke prekidača, a zatim unijeli minimalnu konfiguraciju za ponovni pokušaj kako bismo pomogli pri prolaznim kvarovima i neometanim implementacijama. Svaku od ovih usluga prednjih izaslanika suočili smo s TCP ELB-om. Čak i ako se keepalive iz našeg glavnog prednjeg proxy sloja zakačio na određene mahune Envoy-a, oni su mnogo bolje mogli podnijeti opterećenje i bili su konfigurirani za ravnotežu preko najmanjeg zahtjeva za pomoćni prostor.

Za implementaciju smo koristili preStop kuku i na aplikaciji i na bočnoj podlozi. Ova kuka nazvana bočna provjera zdravstvenog stanja neuspjeha, zajedno s malo spavanja, kako bi se malo vremena omogućilo dovršavanje i odvod zrakoplovnih veza.

Jedan od razloga zašto smo se uspjeli tako brzo kretati bio je zbog bogatih metrika koje smo uspjeli lako integrirati s našim uobičajenim Prometejevim postavljanjem. To nam je omogućilo da vidimo točno što se događa dok smo ponavljali postavke konfiguracije i smanjili promet.

Rezultati su bili trenutačni i očiti. Započeli smo s najnebalansiranijim uslugama i u ovom trenutku smo je pokrenuli ispred dvanaest najvažnijih usluga u našem klasteru. Ove godine planiramo prijeći na mrežu s potpunom uslugom, s naprednijim otkrivanjem usluga, razbijanjem krugova, otkrivanjem vanjske vrijednosti, ograničavanjem brzine i praćenjem.

Slika 3–1 konvergencija CPU-a jedne usluge tijekom rezanja poslanika

Krajnji rezultat

Kroz ova učenja i dodatna istraživanja razvili smo snažan interni infrastrukturni tim s velikim poznavanjem načina dizajniranja, implementacije i upravljanja velikim klasterima Kubernetesa. Sada cijela inženjerska organizacija tvrtke Tinder ima znanje i iskustvo o tome kako spremiti i rasporediti njihove aplikacije na Kubernetes.

Na našoj naslijeđenoj infrastrukturi, kad nam je bila potrebna dodatna razmjera, često smo patili od nekoliko minuta čekanja da se novi EC2 slučajevi jave na mreži. Kontejneri sada planiraju i poslužuju promet u roku od nekoliko sekundi za razliku od minuta. Planiranje više spremnika na jednoj EC2 instanci također omogućuje poboljšanu vodoravnu gustoću. Kao rezultat, projiciramo znatne uštede troškova na EC2 u 2019. u odnosu na prethodnu godinu.

Prošle su gotovo dvije godine, ali smo migraciju dovršili u ožujku 2019. Tinder platforma radi isključivo na Kubernetes grupi koja se sastoji od 200 usluga, 1.000 čvorova, 15.000 mahuna i 48.000 spremnika. Infrastruktura više nije zadatak rezerviran za naše operativne timove. Umjesto toga, inženjeri u cijeloj organizaciji dijele ovu odgovornost i imaju kontrolu nad načinom izrade i implementacije njihovih aplikacija sa svime kao kodom.