Tinder költözik Kubernetesbe

Írta: Chris O'Brien, mérnöki vezető | Chris Thomas, mérnöki vezető | Jinyong Lee, vezető szoftvermérnök | Szerkesztette: Cooper Jackson, szoftvermérnök

Miért

Majdnem két évvel ezelőtt a Tinder úgy döntött, hogy platformját Kubernetesbe költözteti. A Kubernetes lehetőséget adott számunkra, hogy a Tinder Engineering-et megmozdíthatatlan telepítés révén a konténerek és az alacsony tapintású működés felé mozdítsuk el. Az alkalmazáskészítés, a telepítés és az infrastruktúra kódként kerül meghatározásra.

Arra is gondolkodtunk, hogy foglalkozzunk a méret és a stabilitás kihívásaival. Amikor a méretarány kritikussá vált, gyakran percek alatt vártuk az új EC2 példányok online megjelenését. Az a gondolat, hogy a konténerek másodpercek alatt, a percekkel szemben ütemezzenek és kiszolgálhassák a perceket, vonzó számunkra.

Nem volt könnyű. 2019 elején történő migrációnk során elértünk kritikus tömeget a Kubernetes-klaszterünkön belül, és különféle kihívásokkal kezdett szembekerülni a forgalom mennyisége, a fürt mérete és a DNS miatt. Érdekes kihívásokat oldottunk meg a 200 szolgáltatás migrálásával és egy Kubernetes-fürt futtatásával, összesen 1000 csomópont, 15 000 pod és 48 000 futó konténer számára.

Hogyan

2018 januárjától kezdve megmunkáltuk a migrációs erőfeszítések különböző szakaszaiban. Kezdetben az összes szolgáltatásunk tárolását és a Kubernetes által üzemeltetett színpadi környezetek sorozatába történő telepítését kezdtük. Október elejétől kezdve módszeresen áthelyeztük minden régi szolgáltatásunkat Kubernetesbe. A következő év márciusára befejeztük az áttérést és a Tinder Platform most kizárólag Kubernetes-en működik.

Képek építése a Kubernetes számára

Több mint 30 forráskód-tárhely található a Kubernetes-fürtben futó mikroszolgáltatások számára. Az ezen lerakatokban szereplő kód különböző nyelveken (pl. Node.js, Java, Scala, Go) van írva, ugyanahhoz a nyelvhez több futási környezettel.

Az összeállítási rendszert úgy tervezték, hogy az minden mikroszolgáltatás számára teljesen testreszabható „összeállítási környezetben” működjön, ami általában Dockerfile-ből és shell parancsok sorozatából áll. Miközben tartalmuk teljesen testreszabható, ezek az összeállítási összefüggések mind szabványos formátum alapján készülnek. Az összeállítási összefüggések szabványosítása lehetővé teszi, hogy egyetlen összeállítási rendszer kezelje az összes mikroszolgáltatást.

1–1. Ábra: Egységes szerkesztési folyamat a Builder tárolón keresztül

A futásidejű környezetek közötti maximális konzisztencia elérése érdekében ugyanazt az összeállítási folyamatot használják a fejlesztési és tesztelési szakaszban. Ez egyedülálló kihívást jelentett, amikor olyan módszert kellett kidolgoznunk, amely a platform egész területén biztosítja a következetes építkezési környezetet. Ennek eredményeként az összes építkezési folyamat egy speciális “Builder” tárolóban kerül végrehajtásra.

A Builder tároló implementálása számos fejlett Docker technikát igényelt. Ez a Builder tároló örökli a helyi felhasználói azonosítót és a titkokat (pl. SSH kulcs, AWS hitelesítő adatok stb.), Amire a Tinder magánlerakatokhoz való hozzáféréshez szükség van. A forráskódot tartalmazó helyi könyvtárakat felveszi, hogy természetes módon tárolhassák az összetevők műtárgyait. Ez a megközelítés javítja a teljesítményt, mert kiküszöböli a beépített műtárgyak másolását az építőtároló és a gazdagép között. A tárolt összetevőket legközelebb újra felhasználják további konfiguráció nélkül.

Bizonyos szolgáltatásokhoz létre kellett hoznunk egy új tárolót az Építőben, hogy összeállítsuk a fordítási idő környezetet a futási környezettel (pl. A Node.js bcrypt könyvtár telepítése generál platformspecifikus bináris mellékleteket). A fordítási idő követelményei szolgáltatásokonként eltérhetnek, és a végleges Dockerfile menet közben készül.

Kubernetes klaszter építészet és migráció

Klaszter méretezés

Úgy döntöttünk, hogy a kube-aws-t használja az automatikus fürtszolgáltatáshoz az Amazon EC2 példányokon. Korán mindent egy általános csomópont-készletben futottunk. Gyorsan felismertük annak szükségességét, hogy a munkaterheléseket különféle méretekre és példánytípusokra bontjuk, az erőforrások jobb felhasználása érdekében. Az érvelés az volt, hogy kevesebb, erősen menetes hüvely együttes futtatása kiszámíthatóbb teljesítményt eredményezett számunkra, mint ha nagyobb számú egyszálú hüvely lenne.

Megállapodtunk:

  • m5.4xl nagyítás megfigyeléshez (Prometheus)
  • c5.4xlarge a Node.js munkaterheléshez (egyszálú munkaterhelés)
  • c5.2xlarge Java és Go számára (többszálú munkaterhelés)
  • c5.4x nagyság a vezérlősíkon (3 csomópont)

elvándorlás

A régi infrastruktúránkból a Kubernetesbe történő áttérés egyik előkészítő lépése a meglévő szolgáltatás-szolgáltatás kommunikáció megváltoztatása olyan új rugalmas rugalmas terheléselosztókra (ELB) való mutatásra, amelyeket egy adott virtuális magán felhő (VPC) alhálózatban hoztak létre. Ezt az alhálózatot a Kubernetes VPC-hez társították. Ez lehetővé tette számunkra, hogy a modulokat granulátumosan áttelepítsük, tekintet nélkül a szolgáltatási függőségek speciális megrendelésére.

Ezeket a végpontokat súlyozott DNS-rekordkészletek felhasználásával hozták létre, amelyeknek CNAME-je minden új ELB-re mutatott. A váltáshoz új rekordot adtunk, amely az új Kubernetes szolgáltató ELB-re mutatott, amelynek súlya 0 volt. Ezt követően a rekordon a Time To Live (TTL) értékét 0-ra állítottuk. A régi és az új súlyokat lassan beállítottuk végül 100% -kal végül az új szerveren. Az átállás befejezése után a TTL-t valami ésszerűbbre állították.

Java moduljaink tiszteletben tartották az alacsony DNS TTL-t, de a Node alkalmazásunk nem. Az egyik mérnökünk újraírja a csatlakozási készlet kódját, hogy becsomagolja azt egy kezelőbe, amely 60-as években frissítené a medencéket. Ez nagyon jól működött számunkra, és nem volt észlelhető teljesítmény.

tanulsággal

A hálózati szövet korlátozásai

2019. január 8-i kora reggeli órákban a Tinder's Platform tartósan leállt. A periódus korábban reggel nem összefüggő növekedésére adott válaszként a hüvely és a csomópontok számát méretezték a fürtön. Ez az összes ARD gyorsítótár-kimerülést eredményezte minden csomópontunkon.

Három Linux érték releváns az ARP gyorsítótárban:

Hitel

A gc_thresh3 egy kemény sapka. Ha „szomszéd tábla túlcsordulása” naplóbejegyzéseket kap, ez azt jelzi, hogy még az ARP gyorsítótár szinkron hulladékgyűjtése (GC) után sem volt elég hely a szomszéd bejegyzés tárolására. Ebben az esetben a kernel teljes egészében eldobja a csomagot.

A Klannetes-ben hálózati anyagként a Flannel-t használjuk. A csomagok továbbítása a VXLAN-on keresztül történik. A VXLAN egy 2. rétegű overlay séma a 3. réteg hálózatán. A MAC cím-a-felhasználói adatgram protokollban (MAC-in-UDP) beágyazását használja a 2. rétegű hálózati szegmensek kiterjesztésének biztosítására. A fizikai adatközpont-hálózaton keresztüli szállítási protokoll IP plusz UDP.

2–1. Ábra Flanel diagram (kredit)

2–2. Ábra VXLAN csomag (jóváírás)

Minden Kubernetes munkáscsomópont kiosztja saját / 24 virtuális címterét egy nagyobb / 9 blokkból. Mindegyik csomópontnál ez 1 útvonalatábla-bejegyzést, 1 ARP-táblabejegyzést (a flannel.1 felületen) és 1 továbbító adatbázis (FDB) bejegyzést eredményez. Ezek hozzáadódnak, amikor a dolgozó csomópont először elindul, vagy amikor minden új csomópont felfedezésre kerül.

Ezen felül a csomópontok közötti pod (vagy pod-to-pod) kommunikáció végül az eth0 interfészen megy keresztül (a fenti Flannel diagram ábrázolja). Ez további bejegyzést eredményez az ARP táblában minden megfelelő csomópont forrás és csomópont rendeltetési helyére.

Környezetünkben az ilyen típusú kommunikáció nagyon gyakori. A Kubernetes szolgáltatási objektumainkhoz egy ELB jön létre, és a Kubernetes minden csomópontot regisztrál az ELB-n. Az ELB nem ismeri a pod-ot, és a kiválasztott csomópont lehet, hogy nem a csomag végső rendeltetési helye. Ennek oka az, hogy amikor a csomópont megkapja a csomagot az ELB-től, akkor kiértékeli a szolgáltatás iptable szabályait, és véletlenszerűen választ egy másik csomóponton lévő pod-ot.

A leállás idején összesen 605 csomópont volt a klaszterben. A fentebb vázolt okokból ez elegendő volt az alapértelmezett gc_thresh3 érték elsötétítéséhez. Ha ez megtörténik, nem csak a csomagok esnek le, hanem a teljes Flannel / 24 virtuális címtér hiányzik az ARP táblából. Nem sikerül a csomópont közötti kommunikáció és a DNS-keresések. (A DNS a fürtön található, amint ezt a cikk később részletesebben ismertetjük.)

A megoldás érdekében a gc_thresh1, gc_thresh2 és gc_thresh3 értékeket emelik, és a Flannel-t újra kell indítani a hiányzó hálózatok újraregisztrálása érdekében.

Váratlanul fut a DNS skálán

A migrációnk beépítéséhez erőteljesen kihasználtuk a DNS-t, hogy megkönnyítsük a forgalom kialakítását és az örökségről a Kubernetes-re történő fokozatos átállást szolgáltatásaink számára. Viszonylag alacsony TTL-értékeket állítottunk be a társított Route53 RecordSets-ben. Amikor az örökölt infrastruktúránkat EC2 példányokon futtattuk, a feloldó konfigurációnk az Amazon DNS-jére mutatott. Ezt magától értetődőnek tekintettük, és a viszonylag alacsony TTL költségei szolgáltatásaink és az Amazon szolgáltatásainak (pl. DynamoDB) költségei nagyrészt észrevétlenül maradtak.

Ahogy egyre több szolgáltatást telepítettünk a Kubernetes-hez, találtunk egy DNS-szolgáltatást, amely másodpercenként 250 000 kérésre válaszol. Időszakos és hatásos DNS-keresési időtúllépéseket tapasztaltunk alkalmazásunkban. Ez annak ellenére történt, hogy kimerítő hangolási erőfeszítéseket végeztünk, és egy DNS szolgáltató váltott a CoreDNS telepítésre, amely egyszerre tetőzött 1000 pod mellett, 120 energiát fogyasztva.

Miközben más lehetséges okokat és megoldásokat kutattunk, egy cikket találtunk, amely leírja a versenyfeltételeket, amelyek befolyásolják a netfilter Linux csomagszűrő keretét. A látott DNS-időtúllépések, valamint a növekvő insert_failed számláló a Flannel felületen, összhangban a cikk megállapításaival.

A probléma a forrás- és célhálózati címek fordításakor (SNAT és DNAT), majd az azt követő beillesztéskor jelentkezik a conntrack táblába. Az egyik belsőleg megvitatott és a közösség által javasolt megoldás az volt, hogy a DNS-t maga a munkavállaló csomópontra helyezze. Ebben az esetben:

  • A SNAT nem szükséges, mivel a forgalom lokálisan marad a csomóponton. Nem kell továbbítani az eth0 felületen.
  • A DNAT nem szükséges, mivel a cél IP helyi a csomóponton, és nem egy véletlenszerűen kiválasztott pod az iptable szabályok szerint.

Úgy döntöttünk, hogy továbblépünk ezzel a megközelítéssel. A CoreDNS-t DaemonSet-ként telepítettük a Kubernetes-be, és a kubelet - cluster-dns parancsjelző konfigurálásával beinjektáltuk a csomópont helyi DNS-kiszolgálóját minden egyes pod resv.conf fájljába. A megoldás hatékony volt a DNS időkorlátozásokra.

Még mindig látunk eldobott csomagokat és a Flannel felület insert_failed számlálójának növekedését. Ez továbbra is fennáll a fenti megoldás után is, mivel a SNAT és / vagy a DNAT csak a DNS forgalom esetében kerüljük el. A verseny feltételei továbbra is fennállnak más típusú forgalom esetén. Szerencsére a legtöbb csomagunk TCP, és amikor a feltétel bekövetkezik, akkor a csomagok sikeresen továbbadódnak. Az összes típusú forgalomra vonatkozó hosszú távú javításról még mindig beszélünk.

A megbízott használata a jobb terheléselosztás elérése érdekében

Ahogy a háttérszolgáltatásainkat Kubernetesbe telepítettük, elkezdtük szenvedni az egyenetlen terhelést a hüvelyek között. Felfedeztük, hogy a HTTP Keepalive miatt az ELB kapcsolatok az egyes gördülő üzembe helyezés első kész podjaihoz ragaszkodtak, így a legtöbb forgalom a rendelkezésre álló pod-ok kis hányadán haladt keresztül. Az egyik első enyhítés, amelyet megpróbáltunk, egy 100% -os MaxSurge alkalmazását volt az új telepítéseknél a legrosszabb elkövetők számára. Ez kissé hatékony és hosszú távon nem volt fenntartható néhány nagyobb telepítésnél.

Egy másik enyhítés az volt, hogy a kritikus szolgáltatások erőforrás-igényeit mesterségesen megfojtottuk, hogy a colocated hüvelyeknek több hely maradjon a többi nehéz pod mellett. Ez az erőforrás-pazarlás miatt hosszú távon nem is lesz képes megvalósíthatónak lenni, és a Node alkalmazásaink egyszeres meneteket képeztek, és így hatékonyan korlátozták az 1 magot. Az egyetlen világos megoldás a jobb terheléskiegyenlítés volt.

Belsőleg arra számítottunk, hogy értékeljük a követet. Ez lehetőséget adott nekünk egy nagyon korlátozott módon történő alkalmazásra, és azonnali előnyök elérésére. Az Envoy egy nyílt forráskódú, nagy teljesítményű Layer 7 proxy, amelyet nagy szolgáltatás-orientált architektúrákhoz terveztek. Speciális terheléselosztási technikákat képes végrehajtani, beleértve az automatikus újrapróbáltatást, az áramköri megszakítást és a globális sebességkorlátozást.

Az a konfiguráció, amelyet felállítottuk, az volt, hogy minden egyes pod mellett egy Envoy oldalkocsi legyen, amelynek egy útja és fürtje volt, hogy elérje a helyi konténer portját. A lehetséges lépcsőzés minimalizálása és a kis robbantási sugár fenntartása érdekében az első proxy Envoy hüvelyeket használtuk, egy-egy telepítést az egyes rendelkezésre állási zónákban (AZ) minden szolgáltatáshoz. Ezek egy kis szolgáltatáskeresési mechanizmust értek el, amelyet az egyik mérnökünk összeállított, és egyszerűen csak egy adott szolgáltatáshoz adta vissza az egyes AZ-ban lévő hüvelyek listáját.

A szolgálati front-envoys ezt követően kihasználta ezt a szolgáltatás-felfedező mechanizmust egy upstream fürtön és útvonalon. Konfiguráltuk az ésszerű időtúllépéseket, javítottuk az összes megszakító beállítást, majd minimális újrapróbálkozási konfigurációt tettünk be az átmeneti hibák és a zökkenőmentes telepítés érdekében. Mindezeket az első Envoy szolgáltatásokat egy TCP ELB-vel csináltuk. Még akkor is, ha a fő elülső proxy rétegből származó megőrző anyagot egyes Envoy hüvelyekre rögzítettük, sokkal jobban tudták kezelni a rakományt, és úgy vannak konfigurálva, hogy a legkevesebb_küldés révén egyensúlyba kerüljenek a háttérrendszerrel.

A telepítéshez egy PreStop horgot használtunk mind az alkalmazáson, mind az oldalkocsi hüvelyén. Ez a kampó, amelyet oldalkocsi állapotfelmérésnek hívtak, meghiúsult az admin végpontja mellett, egy kis alvással együtt, hogy adjon egy kis időt ahhoz, hogy a repülési összeköttetések létrejöhessenek és kiürüljenek.

Az egyik ok, amiért olyan gyorsan tudtunk mozogni, a gazdag mérőszámoknak volt köszönhető, amelyeket könnyen integrálhattunk a szokásos Prometheus-rendszerünkbe. Ez lehetővé tette számunkra, hogy pontosan megnézhessük, mi történik, amikor a konfigurációs beállításokon ismétlődöttünk, és lecsökkentük a forgalmat.

Az eredmények azonnali és nyilvánvalóak voltak. A leginkább kiegyensúlyozatlan szolgáltatásokkal kezdtük, és ezen a ponton a klaszterünk legfontosabb szolgáltatásainak tizenkét legfontosabb szolgáltatása előtt futtathatjuk. Ebben az évben azt tervezzük, hogy egy teljes körű szolgáltatási hálóra költözzünk, fejlettebb szolgáltatási felfedezéssel, áramkör-megszakítással, külsõ észleléssel, sebességkorlátozással és nyomon követéssel.

3–1. Ábra Egy szolgálat CPU-konvergenciája a küldöttség átvitele során

A végeredmény

Ezeknek a tanulásoknak és a további kutatásoknak köszönhetően egy erős házon belüli infrastruktúra-csoportot fejlesztettünk ki, amely nagy ismeretekkel rendelkezik a nagy Kubernetes-klaszterek tervezésének, telepítésének és működtetésének módjairól. A Tinder teljes mérnöki szervezete ismeretekkel és tapasztalatokkal rendelkezik az alkalmazások tárolására és telepítésére a Kubernetes-en.

Régi infrastruktúránkon, amikor további léptékre volt szükség, gyakran percek alatt vártuk az új EC2 példányok online megjelenését. A konténerek most ütemezik és szolgálják a forgalmat másodpercek alatt, a percekkel ellentétben. Több konténer ütemezése egyetlen EC2 példányon szintén javítja a vízszintes sűrűséget. Ennek eredményeként 2019-ben jelentős költségmegtakarítást tervezzünk az EC2-re az előző évhez képest.

Közel két év telt el, de az áttérésünket 2019 márciusában fejeztük be. A Tinder Platform kizárólag egy Kubernetes-fürtön működik, amely 200 szolgáltatást, 1000 csomópontot, 15 000 pod-ot és 48 000 futó konténert tartalmaz. Az infrastruktúra már nem a műveleti csapataink számára fenntartott feladat. Ehelyett a mérnökök a szervezet egészében megosztják ezt a felelősséget, és ellenőrzik az alkalmazások felépítését és telepítését, mindegyik kódként.