Prijelaz Tindera na Kubernetes

Bilješka. prev.: Zaposlenici svjetski poznatog servisa Tinder nedavno su podijelili neke tehničke detalje migracije svoje infrastrukture na Kubernetes. Proces je trajao gotovo dvije godine i rezultirao je lansiranjem vrlo velike platforme na K8s, koja se sastoji od 200 usluga smještenih na 48 tisuća spremnika. Na koje su zanimljive poteškoće naišli inženjeri Tindera i do kakvih su rezultata došli, pročitajte ovaj prijevod.

Prijelaz Tindera na Kubernetes

Zašto?

Prije gotovo dvije godine Tinder je odlučio preseliti svoju platformu na Kubernetes. Kubernetes bi Tinder timu omogućio kontejneriziranje i prelazak na proizvodnju uz minimalan napor kroz nepromjenjivu implementaciju (nepromjenjiva implementacija). U ovom slučaju, sklop aplikacija, njihova implementacija i sama infrastruktura bili bi jedinstveno definirani kodom.

Također smo tražili rješenje za problem skalabilnosti i stabilnosti. Kad je skaliranje postalo kritično, često smo morali čekati nekoliko minuta da se pokrenu nove EC2 instance. Ideja o pokretanju kontejnera i započinjanju opsluživanja prometa u sekundama umjesto u minutama postala nam je vrlo atraktivna.

Proces se pokazao teškim. Tijekom naše migracije početkom 2019., Kubernetes klaster je dosegao kritičnu masu i počeli smo nailaziti na razne probleme zbog količine prometa, veličine klastera i DNS-a. Usput smo riješili puno zanimljivih problema vezanih uz migraciju 200 servisa i održavanje Kubernetes klastera koji se sastoji od 1000 čvorova, 15000 podova i 48000 spremnika koji rade.

Kako?

Od siječnja 2018. prošli smo kroz različite faze migracije. Započeli smo kontejneriziranjem svih naših usluga i njihovom implementacijom u Kubernetes testna okruženja oblaka. Počevši od listopada, započeli smo metodičnu migraciju svih postojećih usluga na Kubernetes. Do ožujka sljedeće godine dovršili smo migraciju i sada platforma Tinder radi isključivo na Kubernetesu.

Izrada slika za Kubernetes

Imamo preko 30 repozitorija izvornog koda za mikroservise koji se izvode na Kubernetes klasteru. Kod u tim spremištima napisan je na različitim jezicima (na primjer, Node.js, Java, Scala, Go) s više runtime okruženja za isti jezik.

Sustav izgradnje osmišljen je za pružanje potpuno prilagodljivog "konteksta izrade" za svaku mikrouslugu. Obično se sastoji od Dockerfilea i popisa naredbi ljuske. Njihov sadržaj je potpuno prilagodljiv, a istovremeno su svi ti konteksti izgradnje napisani prema standardiziranom formatu. Standardiziranje konteksta izgradnje omogućuje da jedan sustav izrade upravlja svim mikroservisima.

Prijelaz Tindera na Kubernetes
Slika 1-1. Standardizirani proces izrade putem Builder spremnika

Za postizanje maksimalne dosljednosti između vremena izvođenja (izvršna okruženja) tijekom razvoja i testiranja koristi se isti proces izrade. Suočili smo se s vrlo zanimljivim izazovom: morali smo razviti način da osiguramo dosljednost okruženja za izgradnju na cijeloj platformi. Kako bi se to postiglo, svi procesi montaže provode se unutar posebnog spremnika. Graditelj.

Njegova implementacija spremnika zahtijevala je napredne Docker tehnike. Builder nasljeđuje lokalni korisnički ID i tajne (kao što su SSH ključ, AWS vjerodajnice itd.) potrebne za pristup privatnim Tinder repozitoriju. Montira lokalne direktorije koji sadrže izvore za prirodno pohranjivanje artefakata izrade. Ovaj pristup poboljšava izvedbu jer eliminira potrebu za kopiranjem artefakata izgradnje između spremnika Builder i glavnog računala. Pohranjeni artefakti građenja mogu se ponovno upotrijebiti bez dodatne konfiguracije.

Za neke smo usluge morali stvoriti još jedan spremnik za mapiranje okruženja kompilacije u okruženje za izvođenje (na primjer, biblioteka Node.js bcrypt generira binarne artefakte specifične za platformu tijekom instalacije). Tijekom procesa kompilacije zahtjevi se mogu razlikovati od usluge do usluge, a konačna Dockerfile se kompilira u hodu.

Arhitektura Kubernetes klastera i migracija

Upravljanje veličinom klastera

Odlučili smo koristiti kube-aws za automatiziranu implementaciju klastera na instancama Amazon EC2. Na samom početku sve je radilo u jednom zajedničkom skupu čvorova. Brzo smo shvatili potrebu za odvajanjem radnih opterećenja prema veličini i vrsti instance kako bismo učinkovitije koristili resurse. Logika je bila da se pokretanje nekoliko učitanih multi-threaded modula pokazalo predvidljivijim u smislu izvedbe od njihovog koegzistencije s velikim brojem jednonitnih modula.

Na kraju smo se odlučili za:

  • m5.4xveliki — za praćenje (Prometej);
  • c5.4xveliki - za Node.js radno opterećenje (jednonitno radno opterećenje);
  • c5.2xveliki - za Java i Go (višenitno radno opterećenje);
  • c5.4xveliki — za upravljačku ploču (3 čvora).

migracija

Jedan od pripremnih koraka za prelazak sa stare infrastrukture na Kubernetes bilo je preusmjeravanje postojeće izravne komunikacije između servisa na nove balansere opterećenja (Elastic Load Balancers (ELB). Stvoreni su na određenoj podmreži virtualnog privatnog oblaka (VPC). Ova je podmreža bila spojena na Kubernetes VPC. To nam je omogućilo postupnu migraciju modula, bez razmatranja specifičnog redoslijeda ovisnosti o uslugama.

Ove su krajnje točke stvorene korištenjem ponderiranih skupova DNS zapisa koji su imali CNAME koji su upućivali na svaki novi ELB. Da bismo se prebacili, dodali smo novi unos koji pokazuje na novi ELB usluge Kubernetes s težinom 0. Zatim smo postavili Time To Live (TTL) skupa unosa na 0. Nakon toga, stare i nove težine su polako prilagođavao i na kraju je 100% opterećenja poslano na novi poslužitelj. Nakon završenog prebacivanja TTL vrijednost se vratila na adekvatniju razinu.

Java moduli koje smo imali mogli su se nositi s niskim TTL DNS-om, ali Node aplikacije nisu mogle. Jedan od inženjera prepisao je dio koda skupa veze i zamotao ga u upravitelj koji je ažurirao skupove svakih 60 sekundi. Odabrani pristup funkcionirao je vrlo dobro i bez ikakvog primjetnog pada performansi.

Lekcije

Ograničenja mrežne strukture

U rano jutro 8. siječnja 2019. Tinder platforma se neočekivano srušila. Kao odgovor na nepovezano povećanje latencije platforme ranije tog jutra, povećao se broj podova i čvorova u klasteru. Zbog toga je ARP predmemorija bila iscrpljena na svim našim čvorovima.

Postoje tri Linux opcije koje se odnose na ARP predmemoriju:

Prijelaz Tindera na Kubernetes
(izvor)

gc_thresh3 - ovo je teška granica. Pojava unosa "preljeva susjedne tablice" u dnevniku značila je da čak i nakon sinkronog skupljanja smeća (GC), nije bilo dovoljno prostora u ARP predmemorije za pohranjivanje susjednog unosa. U ovom slučaju, kernel je jednostavno potpuno odbacio paket.

Koristimo Flanel kao mrežno tkivo u Kubernetesu. Paketi se prenose preko VXLAN-a. VXLAN je L2 tunel podignut na vrhu L3 mreže. Tehnologija koristi enkapsulaciju MAC-in-UDP (MAC Address-in-User Datagram Protocol) i omogućuje proširenje Layer 2 mrežnih segmenata. Prijenosni protokol na fizičkoj mreži podatkovnog centra je IP plus UDP.

Prijelaz Tindera na Kubernetes
Slika 2–1. Flanel dijagram (izvor)

Prijelaz Tindera na Kubernetes
Slika 2-2. VXLAN paket (izvor)

Svaki Kubernetes radnički čvor dodjeljuje virtualni adresni prostor s maskom /24 iz većeg bloka /9. Za svaki čvor ovo je sredstva jedan unos u tablici usmjeravanja, jedan unos u ARP tablici (na sučelju flanel.1) i jedan unos u tablici prebacivanja (FDB). Dodaju se kada se prvi put pokrene radni čvor ili svaki put kada se otkrije novi čvor.

Dodatno, komunikacija čvor-pod (ili pod-pod) u konačnici ide kroz sučelje eth0 (kao što je prikazano na gornjem dijagramu Flannel). To rezultira dodatnim unosom u ARP tablici za svaki odgovarajući izvorni i odredišni host.

U našoj sredini ovakva komunikacija vrlo je česta. Za servisne objekte u Kubernetesu kreira se ELB i Kubernetes registrira svaki čvor s ELB-om. ELB ne zna ništa o podovima i odabrani čvor možda nije konačno odredište paketa. Poanta je da kada čvor primi paket od ELB-a, on ga razmatra uzimajući u obzir pravila iptables za određenu uslugu i nasumično odabire pod na drugom čvoru.

U trenutku kvara u klasteru je bilo 605 čvorova. Iz gore navedenih razloga, to je bilo dovoljno za prevladavanje značaja gc_thresh3, što je zadano. Kada se to dogodi, ne samo da se paketi počinju ispuštati, već cijeli Flannel virtualni adresni prostor s /24 maskom nestaje iz ARP tablice. Komunikacija između čvorova i DNS upiti su prekinuti (DNS je smješten u klasteru; pojedinosti pročitajte kasnije u ovom članku).

Da biste riješili ovaj problem, morate povećati vrijednosti gc_thresh1, gc_thresh2 и gc_thresh3 i ponovno pokrenite Flannel kako biste ponovno registrirali mreže koje nedostaju.

Neočekivano DNS skaliranje

Tijekom procesa migracije aktivno smo koristili DNS za upravljanje prometom i postupni prijenos usluga sa stare infrastrukture na Kubernetes. Postavili smo relativno niske TTL vrijednosti za pridružene RecordSetove u Route53. Kad je stara infrastruktura radila na EC2 instancama, naša konfiguracija razrješitelja je upućivala na Amazon DNS. Uzeli smo to zdravo za gotovo i utjecaj niskog TTL-a na naše usluge i usluge Amazona (kao što je DynamoDB) prošao je uglavnom nezapaženo.

Dok smo migrirali usluge na Kubernetes, otkrili smo da DNS obrađuje 250 tisuća zahtjeva u sekundi. Kao rezultat toga, aplikacije su počele doživljavati stalna i ozbiljna isteka vremena za DNS upite. To se dogodilo unatoč nevjerojatnim naporima da se optimizira i prebaci DNS davatelj na CoreDNS (koji je pri najvećem opterećenju dosegao 1000 podova koji rade na 120 jezgri).

Dok smo istraživali druge moguće uzroke i rješenja, otkrili smo članak, opisujući uvjete utrke koji utječu na okvir za filtriranje paketa neto filter u Linuxu. Timeouts koje smo primijetili, zajedno sa sve većim brojačem umetanje nije uspjelo u sučelju Flannel bili su u skladu s nalazima članka.

Problem se javlja u fazi prijevoda izvorne i odredišne ​​mrežne adrese (SNAT i DNAT) i naknadnog unosa u tablicu conntrack. Jedno od rješenja o kojima se raspravljalo interno i koje je predložila zajednica bilo je premještanje DNS-a na sam radni čvor. U ovom slučaju:

  • SNAT nije potreban jer promet ostaje unutar čvora. Ne treba ga usmjeravati kroz sučelje eth0.
  • DNAT nije potreban budući da je odredišni IP lokalan za čvor, a ne nasumično odabran pod u skladu s pravilima iptables.

Odlučili smo ostati pri ovom pristupu. CoreDNS je implementiran kao DaemonSet u Kubernetesu i implementirali smo DNS poslužitelj lokalnog čvora u razriješiti.konf svaku mahunu postavljanjem zastavice --cluster-dns naredbe kubelet . Ovo se rješenje pokazalo učinkovitim za DNS timeout.

Međutim, i dalje smo vidjeli gubitak paketa i povećanje brojača umetanje nije uspjelo u Flannel sučelju. To se nastavilo nakon što je implementirano zaobilazno rješenje jer smo uspjeli eliminirati SNAT i/ili DNAT samo za DNS promet. Uvjeti utrke sačuvani su za ostale vrste prometa. Srećom, većina naših paketa su TCP, a ako se pojavi problem jednostavno se ponovno šalju. I dalje pokušavamo pronaći odgovarajuće rješenje za sve vrste prometa.

Korištenje Envoya za bolje balansiranje opterećenja

Kako smo migrirali pozadinske usluge na Kubernetes, počeli smo patiti od neuravnoteženog opterećenja između grupa. Otkrili smo da je HTTP Keepalive uzrokovao da ELB veze vise na prvim spremnim modulima svake implementacije. Stoga je većina prometa prolazila kroz mali postotak dostupnih grupa. Prvo rješenje koje smo testirali bilo je postavljanje MaxSurgea na 100% na novim implementacijama za najgore scenarije. Ispostavilo se da je učinak beznačajan i neobećavajući u smislu većih postavljanja.

Još jedno rješenje koje smo koristili bilo je umjetno povećanje zahtjeva za resursima za kritične usluge. U ovom slučaju, mahune postavljene u blizini imale bi više prostora za manevriranje u usporedbi s drugim teškim mahunama. Ne bi uspjelo ni dugoročno jer bi to bio gubitak resursa. Osim toga, naše Node aplikacije bile su jednonitne i, sukladno tome, mogle su koristiti samo jednu jezgru. Jedino pravo rješenje bilo je korištenje boljeg balansiranja opterećenja.

Dugo smo htjeli u potpunosti cijeniti Izaslanik. Trenutna situacija omogućila nam je da ga primijenimo na vrlo ograničen način i dobijemo trenutne rezultate. Envoy je visokoučinkoviti, open-source, layer-XNUMX proxy dizajniran za velike SOA aplikacije. Može implementirati napredne tehnike balansiranja opterećenja, uključujući automatske ponovne pokušaje, prekidače i globalno ograničenje brzine. (Bilješka. prev.: Više o tome možete pročitati u ovaj članak o Istio, koji se temelji na Envoyu.)

Smislili smo sljedeću konfiguraciju: imati bočnu prikolicu Envoy za svaku mahunu i jednu rutu i povezati klaster sa spremnikom lokalno putem priključka. Kako bismo smanjili potencijalno kaskadiranje i održali mali radijus pogotka, koristili smo flotu Envoy prednjih proxy jedinica, jednu po zoni dostupnosti (AZ) za svaku uslugu. Oslonili su se na jednostavan mehanizam za otkrivanje usluga koji je napisao jedan od naših inženjera koji je jednostavno vratio popis podova u svakoj AZ za danu uslugu.

Service front-Envoys zatim je koristio ovaj mehanizam otkrivanja usluge s jednim uzvodnim klasterom i rutom. Postavili smo odgovarajuća vremena čekanja, povećali sve postavke prekidača i dodali minimalnu konfiguraciju ponovnog pokušaja kako bismo pomogli kod pojedinačnih kvarova i osigurali glatku implementaciju. Postavili smo TCP ELB ispred svakog od ovih front-Envoys usluga. Čak i ako je keepalive s našeg glavnog proxy sloja zapeo na nekim Envoy modulima, oni su i dalje bili u mogućnosti podnijeti opterećenje puno bolje i konfigurirani su za balansiranje kroz najmanji zahtjev u pozadini.

Za postavljanje smo upotrijebili preStop kuku na obje aplikacije i bočne prikolice. Priključak je pokrenuo pogrešku u provjeri statusa krajnje točke administratora koja se nalazi na kontejneru prikolice i otišla je u stanje mirovanja na neko vrijeme kako bi omogućila prekid aktivnih veza.

Jedan od razloga zašto smo uspjeli krenuti tako brzo su detaljne metrike koje smo mogli lako integrirati u tipičnu instalaciju Prometheusa. To nam je omogućilo da točno vidimo što se događa dok smo prilagođavali konfiguracijske parametre i redistribuirali promet.

Rezultati su bili trenutni i očiti. Krenuli smo s najneuravnoteženijim servisima, a trenutno djeluje ispred 12 najvažnijih servisa u klasteru. Ove godine planiramo prijelaz na mrežnu mrežu s punom uslugom s naprednijim otkrivanjem usluga, prekidom strujnog kruga, otkrivanjem izvanrednih vrijednosti, ograničavanjem brzine i praćenjem.

Prijelaz Tindera na Kubernetes
Slika 3–1. CPU konvergencija jedne usluge tijekom prijelaza na Envoy

Prijelaz Tindera na Kubernetes

Prijelaz Tindera na Kubernetes

Konačni rezultat

Kroz ovo iskustvo i dodatna istraživanja, izgradili smo snažan infrastrukturni tim sa snažnim vještinama u projektiranju, implementaciji i radu velikih Kubernetes klastera. Svi inženjeri Tindera sada imaju znanje i iskustvo za pakiranje spremnika i postavljanje aplikacija u Kubernetes.

Kada se pojavila potreba za dodatnim kapacitetom na staroj infrastrukturi, morali smo čekati nekoliko minuta za pokretanje novih EC2 instanci. Sada se kontejneri pokreću i počinju obrađivati ​​promet za nekoliko sekundi umjesto za minute. Zakazivanje više spremnika na jednoj EC2 instanci također pruža poboljšanu horizontalnu koncentraciju. Kao rezultat toga, predviđamo značajno smanjenje troškova EC2019 u 2. u usporedbi s prošlom godinom.

Migracija je trajala gotovo dvije godine, ali smo je dovršili u ožujku 2019. Trenutačno platforma Tinder radi isključivo na Kubernetes klasteru koji se sastoji od 200 usluga, 1000 čvorova, 15 000 podova i 48 000 aktivnih spremnika. Infrastruktura više nije jedina domena operativnih timova. Svi naši inženjeri dijele ovu odgovornost i kontroliraju proces izgradnje i implementacije svojih aplikacija samo pomoću koda.

PS od prevoditelja

Pročitajte i niz članaka na našem blogu:

Izvor: www.habr.com

Dodajte komentar