Tinder sa presťahoval do Kubernetes

Autor: Chris O'Brien, inžiniersky manažér | Chris Thomas, inžiniersky manažér | Jinyong Lee, hlavný softvérový inžinier | Editoval: Cooper Jackson, softvérový inžinier

prečo

Takmer pred dvoma rokmi sa Tinder rozhodol presunúť svoju platformu na Kubernetes. Spoločnosť Kubernetes nám poskytla príležitosť nasmerovať spoločnosť Tinder Engineering k kontajnerizácii a nízko dotykovým operáciám prostredníctvom nemenného nasadenia. Budovanie, zavádzanie a infraštruktúra aplikácií by boli definované ako kód.

Taktiež sme sa snažili riešiť výzvy z rozsahu a stability. Keď sa škálovanie stalo kritickým, často sme trpeli niekoľkými minútami čakania na nové prípady EC2. Myšlienka plánovania kontajnerov a obsluhy prepravy v priebehu niekoľkých sekúnd na rozdiel od minút bola pre nás lákavá.

Nebolo to ľahké. Počas našej migrácie začiatkom roku 2019 sme dosiahli kritické množstvo v rámci nášho klastra Kubernetes a začali sme čeliť rôznym výzvam v dôsledku objemu prenosu, veľkosti klastra a DNS. Riešili sme zaujímavé výzvy na migráciu 200 služieb a spustenie klastra Kubernetes v rozsahu 1 000 uzlov, 15 000 toboliek a 48 000 bežných kontajnerov.

ako

Od januára 2018 sme prešli rôznymi fázami migračného úsilia. Začali sme kontajnerizáciou všetkých našich služieb a ich nasadením v sérii inscenovaných prostredí Kubernetes. Začiatkom októbra sme začali systematicky presúvať všetky naše staršie služby do Kubernetes. Do marca nasledujúceho roku sme dokončili našu migráciu a platforma Tinder Platform teraz beží výlučne na Kubernetes.

Vytváranie obrázkov pre Kubernetes

Existuje viac ako 30 archívov zdrojového kódu pre mikroservisy, ktoré sú spustené v klastri Kubernetes. Kód v týchto úložiskách je napísaný v rôznych jazykoch (napr. Node.js, Java, Scala, Go) s viacerými runtime prostrediami pre ten istý jazyk.

Zostavovací systém je navrhnutý tak, aby fungoval v plne prispôsobiteľnom „kontexte vytvárania“ pre každú mikroservis, ktorý zvyčajne pozostáva z Dockerfile a série príkazov shellu. Aj keď je ich obsah plne prispôsobiteľný, všetky tieto kontexty vytvárania sú napísané podľa štandardizovaného formátu. Normalizácia kontextov zostavovania umožňuje systému s jednou zostavou spracovať všetky mikroservisy.

Obrázok 1-1 Štandardizovaný proces zostavovania prostredníctvom kontajnera Builder

Aby sa dosiahla maximálna konzistencia medzi runtime prostrediami, používa sa počas fázy vývoja a testovania rovnaký proces zostavovania. Toto vyžadovalo jedinečnú výzvu, keď sme potrebovali navrhnúť spôsob, ako zaručiť konzistentné prostredie na zostavenie na celej platforme. Výsledkom je, že všetky procesy zostavovania sa vykonávajú vo vnútri špeciálneho kontajnera „Builder“.

Implementácia kontajnera Builder si vyžadovala množstvo pokročilých Dockerových techník. Tento kontajner Builder zdedí miestne užívateľské ID a tajomstvá (napr. Kľúč SSH, poverenia AWS atď.), Ako sa vyžaduje na prístup k súkromným archívom Tinder. Pripojí miestne adresáre obsahujúce zdrojový kód, aby mal prirodzený spôsob ukladania artefaktov zostavenia. Tento prístup zvyšuje výkon, pretože eliminuje kopírovanie zabudovaných artefaktov medzi kontajnerom Builder a hostiteľským počítačom. Uložené artefakty zostavy sa nabudúce znovu použijú bez ďalšej konfigurácie.

Pre určité služby sme v Builderi potrebovali vytvoriť ďalší kontajner, ktorý bude porovnávať prostredie kompilácie s prostredím run-time (napr. Inštalácia knižnice bcrypt Node.js generuje binárne artefakty špecifické pre platformu). Požiadavky na čas kompilácie sa môžu medzi službami líšiť a konečný Dockerfile sa skladá za behu.

Klasterová architektúra a migrácia Kubernetes

Cluster Sizing

Rozhodli sme sa použiť kube-aws na automatizované vytváranie klastrov v inštanciách Amazon EC2. Čoskoro sme bežali všetko v jednom všeobecnom fonde uzlov. Rýchlo sme identifikovali potrebu rozdeliť pracovné zaťaženie do rôznych veľkostí a typov prípadov, aby sme lepšie využívali zdroje. Dôvodom bolo to, že prevádzka menšieho počtu silnozávitových toboliek spolu nám priniesla predvídateľnejšie výsledky výkonnosti, ako nechať ich koexistovať s väčším počtom jednovláknových toboliek.

Usadili sme sa:

  • m5,4 násobok na monitorovanie (Prometheus)
  • c5.4xlarge pre pracovné za aženie Node.js (jednozávitové pracovné za aženie)
  • c5.2xlarge pre Java a Go (viacvláknové pracovné za aženie)
  • c5.4násobok pre riadiacu rovinu (3 uzly)

sťahovanie

Jedným z prípravných krokov na migráciu z našej starej infraštruktúry na Kubernetes bolo zmeniť existujúcu komunikáciu medzi službami a poukazovať na nové pružné vyrovnávacie zaťaženia (ELB), ktoré boli vytvorené v špecifickej podsieti Virtual Private Cloud (VPC). Táto podsieť bola nahliadnutá do súboru Kubernetes VPC. To nám umožnilo podrobne migrovať moduly bez ohľadu na konkrétne objednávanie závislostí od služieb.

Tieto koncové body boli vytvorené pomocou vážených množín záznamov DNS, ktoré mali CNAME smerujúci na každú novú ELB. Na preklenutie sme pridali nový záznam, ktorý ukázal na novú službu Kubernetes ELB, s váhou 0. Potom sme nastavili čas do života (TTL) na záznam nastavený na 0. Staré a nové závažia sa potom pomaly upravili na nakoniec skončí so 100% na novom serveri. Po dokončení prechodu bol TTL nastavený na niečo primeranejšie.

Naše Java moduly ocenili nízku DNS TTL, ale naše aplikácie Node nie. Jeden z našich inžinierov prepísal časť kódu oblasti pripojení tak, aby bol zabalený do manažéra, ktorý bude obnovovať bazény každých 60 rokov. Fungovalo to pre nás veľmi dobre bez výrazného zásahu do výkonu.

znalosti

Limity sieťových tkanín

V skorých ranných hodinách 8. januára 2019 mala Tinderova platforma trvalý výpadok. V reakcii na nesúvisiace zvýšenie latencie platformy skôr v dopoludňajších hodinách boli počty podov a uzlov na klastri upravené. To malo za následok vyčerpanie vyrovnávacej pamäte ARP na všetkých našich uzloch.

Pre medzipamäť ARP existujú tri hodnoty Linuxu:

úver

gc_thresh3 je pevný kryt. Ak sa zobrazujú položky denníka „pretečenie tabuľky susedov“, znamená to, že ani po synchronizovanej zbierke odpadov (GC) vyrovnávacej pamäte ARP nebolo dosť miesta na uloženie záznamu suseda. V tomto prípade jadro jednoducho úplne vypustí paket.

Flannel používame ako sieťovú štruktúru v Kubernetes. Pakety sa posielajú ďalej prostredníctvom VXLAN. VXLAN je schéma prekrytia vrstvy 2 v sieti vrstvy 3. Používa zapuzdrenie protokolu MAC v užívateľskom datagrame (MAC-in-UDP) na zabezpečenie prostriedkov na rozšírenie sieťových segmentov vrstvy 2. Transportný protokol cez sieť fyzických dátových centier je IP plus UDP.

Obrázok 2-1 Flanellov diagram (kredit)

Obrázok 2–2 VXLAN paket (kredit)

Každý pracovný uzol Kubernetes prideľuje svoj vlastný / 24 virtuálneho adresného priestoru z väčšieho / 9 bloku. Výsledkom je pre každý uzol 1 záznam tabuľky trasy, 1 záznam tabuľky ARP (na rozhraní flannel.1) a 1 záznam databázy preposielania (FDB). Pridajú sa pri prvom spustení pracovného uzla alebo pri objavení každého nového uzla.

Okrem toho komunikácia uzol-pod-pod (alebo pod-pod-pod) nakoniec tečie cez rozhranie eth0 (zobrazené vo Flanellovom diagrame vyššie). Výsledkom bude ďalší záznam v tabuľke ARP pre každý zodpovedajúci zdroj uzla a cieľový uzol.

V našom prostredí je tento typ komunikácie veľmi bežný. Pre naše servisné objekty Kubernetes sa vytvorí ELB a Kubernetes registruje každý uzol v ELB. ELB si nie je vedomý a vybraný uzol nemusí byť konečným cieľom paketu. Je to tak preto, že keď uzol prijme paket z ELB, vyhodnotí svoje pravidlá iptables pre službu a náhodne vyberie modul na inom uzle.

V čase výpadku bolo v klastri celkom 605 uzlov. Z dôvodov uvedených vyššie to stačilo na zatmenie predvolenej hodnoty gc_thresh3. Akonáhle sa to stane, nielen sa vyhodia pakety, ale v tabuľke ARP chýba celý Flannel / 24s virtuálneho adresného priestoru. Neúspešná komunikácia medzi uzlami a servermi DNS. (DNS je hosťovaná v klastri, ako bude podrobnejšie vysvetlené ďalej v tomto článku.)

Na vyriešenie sa hodnoty gc_thresh1, gc_thresh2 a gc_thresh3 zvýšia a Flannel sa musí reštartovať, aby sa znovu registrovali chýbajúce siete.

Neočakávane spustené DNS na stupnici

Aby sme vyhoveli našej migrácii, využívali sme služby DNS intenzívne, aby sme našim službám uľahčili formovanie prenosu a postupné prerušenie z pôvodného spojenia na Kubernetes. Na priradené sady záznamov Route53 sme nastavili relatívne nízke hodnoty TTL. Keď sme bežali svoju starú infraštruktúru v prípadoch EC2, naša konfigurácia prekladača ukázala na server Amazon DNS. Toto sme považovali za samozrejmé a náklady na relatívne nízke TTL za naše služby a služby Amazonu (napr. DynamoDB) zostali do značnej miery nepovšimnuté.

Keď sme nastupovali stále viac služieb do spoločnosti Kubernetes, zistili sme, že prevádzkujeme službu DNS, ktorá odpovedala 250 000 žiadostí za sekundu. V našich aplikáciách sa vyskytli prerušované a pôsobivé časové limity vyhľadávania DNS. K tomu došlo napriek vyčerpávajúcemu úsiliu o ladenie a poskytovateľ DNS prešiel na nasadenie CoreDNS, ktoré v tom istom čase vyvrcholilo na 1 000 tobolkách, ktoré spotrebovali 120 jadier.

Pri skúmaní ďalších možných príčin a riešení sme našli článok popisujúci stav rasy ovplyvňujúci netfilter rámca filtrovania paketov systému Linux. Časové limity DNS, ktoré sme videli, spolu s prírastkovým čítačom insert_failed na rozhraní Flannel boli zarovnané so zisteniami článku.

Problém sa vyskytuje počas prekladu zdrojových a cieľových sieťových adries (SNAT a DNAT) a následného vkladania do tabuľky conntrack. Jedno riešenie interne diskutované a navrhnuté komunitou bolo presunúť DNS do samotného pracovného uzla. V tomto prípade:

  • SNAT nie je potrebný, pretože prevádzka ostáva lokálne v uzle. Nemusí sa prenášať cez rozhranie eth0.
  • DNAT nie je potrebný, pretože cieľová IP je lokálna pre uzol a nie náhodne vybrané pravidlá pre jednotlivé iptables.

Tento prístup sme sa rozhodli posunúť vpred. CoreDNS bol nasadený ako DaemonSet v Kubernetes a do servera resolv.conf každého podu sme vstrekli lokálny server DNS uzla nakonfigurovaním príznaku príkazu kubelet - cluster-dns. Riešenie bolo efektívne pre časové limity DNS.

Stále však vidíme vyradené pakety a prírastok počítadla insert_failed rozhrania Flannel. To bude pretrvávať aj po vyššie uvedenom riešení, pretože sme sa vyhýbali iba SNAT a / alebo DNAT pre prenos DNS. Závodný stav bude naďalej existovať pre iné typy premávky. Našťastie väčšina našich paketov je TCP a keď dôjde k stavu, pakety budú úspešne znovu odoslané. O dlhodobej oprave pre všetky typy prenosu je niečo, o čom stále diskutujeme.

Použitie vyslanca na dosiahnutie lepšieho vyváženia zaťaženia

Keď sme migrovali naše backendové služby do Kubernetes, začali sme trpieť nevyváženým zaťažením medzi strukmi. Zistili sme, že v dôsledku protokolu HTTP Keepalive sa pripojenia ELB prilepili k prvým pripraveným modulom každého nasadenia, takže väčšina prenosu prúdila cez malé percento dostupných modulov. Jedným z prvých zmiernení, ktoré sme vyskúšali, bolo použitie 100% MaxSurge na nové nasadenia pre najhorších páchateľov. Pri niektorých väčších nasadeniach to bolo okrajovo efektívne a dlhodobo neudržateľné.

Ďalším zmierňovaním, ktoré sme použili, bolo umelé nafúknutie žiadostí o zdroje na kritické služby, takže farebné tobolky by mali väčší priestor popri iných ťažkých tobolkách. To sa tiež z dlhodobého hľadiska nestane udržateľným kvôli plytvaniu zdrojmi a naše aplikácie Node boli jednozávitové, a teda účinne obmedzené na jedno jadro. Jediným jasným riešením bolo využitie lepšieho vyváženia záťaže.

Interne sme sa snažili hodnotiť vyslanca. To nám dalo šancu nasadiť ho vo veľmi obmedzenej miere a okamžite využiť výhody. Envoy je vysoko výkonný proxy server vrstvy 7 navrhnutý pre veľké architektúry orientované na služby. Je schopný implementovať pokročilé techniky vyrovnávania záťaže, vrátane automatického opakovania, prerušenia obvodu a obmedzenia globálnej rýchlosti.

Konfigurácia, ktorú sme prišli, spočívala v tom, že vedľa každého stojanu, ktorý mal jednu trasu a klaster, zasiahla vedľajší vozík Envoy, aby zasiahla lokálny kontajnerový port. Aby sme minimalizovali potenciálne kaskádovanie a udržali malý polomer, sme pre každú službu použili flotilu predkov proxy Envoy, jedno nasadenie v každej zóne dostupnosti (AZ). Tieto zasiahli malý mechanizmus objavenia služby, ktorý zostavil jeden z našich inžinierov a ktorý jednoducho vrátil zoznam toboliek v každej AZ pre danú službu.

Klienti front-envoys potom využili tento mechanizmus na vyhľadávanie služieb s jedným klastrom a trasou proti prúdu. Nakonfigurovali sme primerané časové limity, vylepšili sme všetky nastavenia ističa a potom sme nastavili minimálnu opakovanú konfiguráciu, aby sme pomohli pri prechodných zlyhaniach a hladkom nasadení. Každú z týchto predných služieb vyslanca sme postavili pred TCP ELB. Aj keď sa udržovací modul z našej hlavnej prednej vrstvy proxy pripnul na určité podložky Envoy, boli oveľa lepšie schopné zvládnuť záťaž a boli nakonfigurované tak, aby vyvážili prostredníctvom najmenej najmenšej požiadavky na koncovú stanicu.

Pre nasadenie sme použili hák preStop na oboch aplikáciách a na postrannom vozíku. Tento háčik sa nazýval kontrola stavu postranného vozíka a zlyhal správca spolu s malým spánkom, aby poskytol nejaký čas, aby sa umožnilo dokončenie a odtok spojenia pre let.

Jedným z dôvodov, prečo sme sa dokázali pohybovať tak rýchlo, bolo vďaka bohatým metrikám, ktoré sme boli schopní ľahko integrovať do nášho normálneho nastavenia Prometheus. To nám umožnilo presne vidieť, čo sa deje, keď sme opakovali konfiguračné nastavenia a obmedzili prenos.

Výsledky boli okamžité a zrejmé. Začali sme s najviac nevyváženými službami av tomto okamihu ich prevádzkujeme pred dvanástimi najdôležitejšími službami v našom klastri. Tento rok plánujeme prechod na sieť s úplnými službami s pokročilejším vyhľadaním služby, prerušením obvodu, detekciou odľahlých hodnôt, obmedzením rýchlosti a sledovaním.

Obrázok 3-1 Konvergencia CPU jednej služby počas prechodu na vyslanca

Konečný výsledok

Prostredníctvom týchto poznatkov a ďalšieho výskumu sme vyvinuli silný interný tím pre infraštruktúru s veľkou znalosťou toho, ako navrhnúť, nasadiť a prevádzkovať veľké klastre Kubernetes. Celá inžinierska organizácia spoločnosti Tinder má teraz vedomosti a skúsenosti o tom, ako kontajnerizovať a nasadiť svoje aplikácie na serveri Kubernetes.

Pokiaľ ide o našu starú infraštruktúru, keď sa vyžadovala ďalšia škála, často sme trpeli niekoľkými minútami čakania na nové prípady EC2. Kontajnery teraz naplánujú a obsluhujú prevádzku v priebehu niekoľkých sekúnd na rozdiel od minút. Plánovanie viacerých kontajnerov na jednu inštanciu EC2 tiež poskytuje vylepšenú horizontálnu hustotu. Výsledkom je, že v roku 2019 v porovnaní s predchádzajúcim rokom očakávame značné úspory nákladov na EC2.

Trvalo to takmer dva roky, našu migráciu sme však dokončili v marci 2019. Platforma Tinder Platform beží výlučne na klastri Kubernetes, ktorý sa skladá z 200 služieb, 1 000 uzlov, 15 000 toboliek a 48 000 bežných kontajnerov. Infraštruktúra už nie je vyhradená pre naše operačné tímy. Namiesto toho sa inžinieri v celej organizácii podieľajú na tejto zodpovednosti a majú kontrolu nad tým, ako sú ich aplikácie budované a rozmiestnené so všetkým ako kód.