Tinderin muutto Kubernetesiin

Kirjoittanut: Chris O'Brien, suunnittelupäällikkö | Chris Thomas, suunnittelupäällikkö | Jinyong Lee, vanhempi ohjelmistoinsinööri | Toimittaja: Cooper Jackson, ohjelmistoinsinööri

Miksi

Melkein kaksi vuotta sitten Tinder päätti siirtää alustaansa Kubernetesiin. Kubernetes antoi meille mahdollisuuden ohjata Tinder Engineeringiä kohti konttien valmistamista ja alhaisen kosketuksen toimintaa muuttumattoman käyttöönoton kautta. Sovellusten rakennus, käyttöönotto ja infrastruktuuri määritetään koodina.

Etsimme myös puuttua mittakaavan ja vakauden haasteisiin. Kun skaalaus muuttui kriittiseksi, kärsimme usein useita minuutteja odottaessamme uusien EC2-tapausten tuloa verkkoon. Ajatus konttien ajoittamisesta ja liikenteen paljastamisesta muutamassa sekunnissa sen sijaan, että minuuteissa vetoaa meihin.

Se ei ollut helppoa. Vuoden 2019 alkupuolella tapahtuvan muuttoprosessimme aikana saavutimme kriittisen massan Kubernetes-klusterissamme ja aloimme kohdata erilaisia ​​haasteita liikenteen määrän, klusterin koon ja DNS: n takia. Ratkaisimme mielenkiintoisia haasteita 200 palvelun siirtämiseksi ja Kubernetes-klusterin ajamiseksi mittakaavassa yhteensä 1000 solmua, 15 000 palkkia ja 48 000 käynnissä olevaa konttia.

Miten

Tammikuusta 2018 lähtien työskentelimme läpi muuttovaiheen eri vaiheiden. Aloitimme säiliöimällä kaikki palvelut ja ottamalla ne käyttöön sarjassa Kubernetes-isäntäympäristöjä. Lokakuun alusta lähtien aloitimme metodologisesti siirtää kaikki vanhat palvelumme Kubernetesiin. Seuraavan vuoden maaliskuuhun mennessä saatiin päätökseen muuttoliike ja Tinder-alusta toimii nyt yksinomaan Kubernetesilla.

Kuvien rakentaminen Kubernetesille

Kubernetes-klusterissa käynnissä oleville mikropalveluille on yli 30 lähdekoodin säilytystilaa. Näiden arkistojen koodi on kirjoitettu eri kielillä (esim. Node.js, Java, Scala, Go) useilla ajonaikaisympäristöillä samalle kielelle.

Kokoonpanojärjestelmä on suunniteltu toimimaan täysin mukautettavissa olevassa “rakennuskontekstissa” jokaiselle mikropalvelulle, joka koostuu tyypillisesti Dockerfilestä ja sarjasta komentorivikomentoja. Vaikka niiden sisältö on täysin muokattavissa, nämä rakennuskonteksit kirjoitetaan kaikki noudattamalla standardisoitua muotoa. Rakennuskontekstien standardisointi mahdollistaa yhden rakennusjärjestelmän käsittelemään kaikkia mikropalveluita.

Kuva 1–1 Standardoitu rakennusprosessi Builder-säiliön kautta

Maksimaalisen yhdenmukaisuuden saavuttamiseksi ajonaikaisten ympäristöjen välillä, samaa rakennusprosessia käytetään kehitys- ja testausvaiheessa. Tämä asetti ainutlaatuisen haasteen, kun meidän piti suunnitella tapa taata yhdenmukainen rakennusympäristö koko alustalla. Seurauksena on, että kaikki rakennusprosessit suoritetaan erityisessä “Builder” -säiliössä.

Builder-säilön toteutus vaati useita edistyneitä Docker-tekniikoita. Tämä Builder-säilö perii paikallisen käyttäjätunnuksen ja salaisuudet (esim. SSH-avain, AWS-käyttöoikeustiedot jne.) Tinderin yksityisten arkistojen käyttämiseksi. Se liittää lähdekoodia sisältävät paikalliset hakemistot luonnolliseen tapaan rakentaa esineitä. Tämä lähestymistapa parantaa suorituskykyä, koska se eliminoi rakennettujen esineiden kopioinnin Builder-säilön ja isäntäkoneen välillä. Tallennetut rakennuksen esineet käytetään uudelleen seuraavan kerran ilman lisämäärityksiä.

Tietyille palveluille jouduimme luomaan uuden säilön Builder-sovelluksessa, jotta sovittamaan käännösaikaympäristö ajonaikaiseen ympäristöön (esimerkiksi Node.js-bcrypt-kirjaston asentaminen tuottaa alustakohtaisia ​​binaariesineitä). Kokoonpanoaikavaatimukset voivat vaihdella palveluiden välillä ja lopullinen Docker-tiedosto koostuu lennossa.

Kubernetes-klusteriarkkitehtuuri ja muutto

Klusterin koko

Päätimme käyttää kube-aws -sovelluksia automaattiseen klusterin valmistukseen Amazon EC2 -tapauksissa. Aiemmin olimme ajamassa kaikkea yhdessä yleisessä solmupoolissa. Tunnistimme nopeasti tarpeen erottaa työkuormat eri kokoisiin ja tyyppisiin ilmentymiin resurssien paremman hyödyntämisen kannalta. Perusteena oli, että vähemmän raskaasti kierteitettyjen palkojen ajaminen yhdessä tuotti meille paremmin ennustettavissa olevia suoritustuloksia kuin sen, että annimme niiden rinnakkain olemassa olevan määrän kanssa yksisäikeisiä palkoja.

Olemme asettuneet:

  • m5.4xxlarge tarkkailuun (Prometheus)
  • c5.4xlarge Node.js-työmäärälle (yksisäikeinen työmäärä)
  • c5.2xlarge Java ja Go (monisäikeinen työmäärä)
  • c5.4xx suurempi ohjaustasolle (3 solmua)

muutto

Yksi valmisteluvaiheista siirtymiselle vanhasta infrastruktuuristamme Kubernetesiin oli muuttaa nykyistä palveluiden välistä viestintää osoittamaan uusille joustaville kuormituksen tasapainoille (ELB), jotka on luotu tiettyyn Virtual Private Cloud (VPC) -aliverkkoon. Tämä aliverkko on katsottu Kubernetes VPC: hen. Tämän ansiosta voimme siirtää moduulit yksityiskohtaisesti ottamatta huomioon palveluiden riippuvuuksien tilaamista.

Nämä päätepisteet luotiin käyttämällä painotettuja DNS-tietuejoukkoja, joissa CNAME osoitti jokaiselle uudelle ELB: lle. Vaihteen lisäämiseksi lisäsimme uuden tietueen osoittamalla uuteen Kubernetes-palvelun ELB: hen, jonka paino oli 0. Asetimme sitten ennätyshetkelle (TTL) ennätyslukemaan 0. Vanhat ja uudet painot säädettiin sitten hitaasti lopulta lopulta 100% uudelle palvelimelle. Kun vaihto oli valmis, TTL asetettiin jotain kohtuullisempaa.

Java-moduulimme kunnioittivat alhaista DNS TTL: ää, mutta Solmu-sovelluksemme eivät. Yksi insinööreistämme kirjoitti osan liitosryhmän koodista käärittämällä sen johtajaan, joka päivittäisi altaat 60-luvun välein. Tämä toimi meille erittäin hyvin ilman merkittävää suorituskykyä.

opit

Verkkokankaan rajoitukset

8. tammikuuta 2019 varhain aamuna, Tinder's Platform kärsi jatkuvasta seisokista. Vastauksena alustan latenssin etuyhteydettömään lisääntymiseen aiemmin samana aamuna, pod- ja solmumäärät mitattiin klusterissa. Tämä johti ARP-välimuistin loppumiseen kaikissa solmuissamme.

ARP-välimuistissa on kolme Linux-arvoa:

Luotto

gc_thresh3 on kova korkki. Jos saat "naapuripöydän ylivuoto" -lokitietoja, tämä osoittaa, että jopa ARP-välimuistin synkronisen roskien keräyksen (GC) jälkeen ei ollut riittävästi tilaa naapurimerkinnän tallentamiseen. Tässä tapauksessa ydin pudottaa paketin kokonaan.

Käytämme Flannelia verkkokankaana Kubernetesissa. Paketit välitetään edelleen VXLAN: n kautta. VXLAN on kerroksen 2 peittokuvajärjestelmä kerroksen 3 verkon kautta. Se käyttää MAC-osoitteen-käyttäjän datagrammi (MAC-in-UDP) -kapselointia tarjotakseen välineet kerroksen 2 verkkosegmenttien laajentamiseksi. Fyysisen datakeskuksen verkon kautta kuljetusprotokolla on IP plus UDP.

Kuva 2–1 Flanelli-kaavio (hyvitys)

Kuva 2–2 VXLAN-paketti (hyvitys)

Jokainen Kubernetes-työntekijän solmu allokoi oman / 24 virtuaalisen osoitealueen suuremmasta / 9 lohkosta. Jokaiselle solmulle tämä johtaa 1 reittitaulukkotietoon, 1 ARP-taulukkotietoon (flannel.1-rajapinnalla) ja 1 edelleenlähetystietokantaan (FDB). Ne lisätään, kun työntekijän solmu käynnistyy ensimmäisen kerran tai kun jokainen uusi solmu löydetään.

Lisäksi solmujen välinen (tai pod-to-pod) viestintä kulkee lopulta eth0-rajapinnan yli (kuvattu yllä olevassa Flannel-kaaviossa). Tämä johtaa lisämerkintään ARP-taulukossa jokaiselle vastaavalle solmun lähteelle ja solmukohteelle.

Ympäristössämme tämäntyyppinen viestintä on hyvin yleistä. Kubernetes-palvelukohteillemme luodaan ELB ja Kubernetes rekisteröi jokaisen solmun ELB: llä. ELB ei ole pod-tietoinen, ja valittu solmu ei ehkä ole paketin lopullinen määränpää. Tämä johtuu siitä, että kun solmu vastaanottaa paketin ELB: ltä, se arvioi palvelun iptable-säännöt ja valitsee satunnaisesti pod toisen palvelimen kohdalta.

Katkaisuhetkellä klusterissa oli yhteensä 605 solmua. Edellä esitetyistä syistä tämä riitti hävittämään oletusarvon gc_thresh3. Kun tämä tapahtuu, paitsi pakettien pudottaminen myös, että kokonaiset Flannel / 24-virtuaaliset osoitetilat puuttuvat ARP-taulukosta. Solmu pod-viestintään ja DNS-haut epäonnistuvat. (DNS isännöi klusterissa, kuten selitetään tarkemmin tässä artikkelissa myöhemmin.)

Ratkaisemiseksi gc_thresh1-, gc_thresh2- ja gc_thresh3-arvoja nostetaan ja Flannel on käynnistettävä uudelleen puuttuvien verkkojen rekisteröimiseksi uudelleen.

DNS yllättäen ajaa mittakaavassa

Siirtymämme mukauttamiseksi hyödynnimme DNS-palvelua voimakkaasti helpottamaan palveluidemme liikenteen muotoilua ja asteittaista siirtymistä vanhasta Kubernetes-palveluun. Asetimme suhteellisen alhaiset TTL-arvot niihin liittyvään Route53 RecordSets -sovellukseen. Kun käytimme vanhaa infrastruktuuriamme EC2-esiintymissä, ratkaisumäärityksemme osoitti Amazonin DNS: ään. Pidimme tätä itsestäänselvyytenä ja suhteellisen alhaisen TTL: n kustannukset palveluillemme ja Amazonin palveluille (esim. DynamoDB) jäivät suurelta osin huomaamatta.

Kun saavuimme yhä useampia palveluita Kubernetesille, huomasimme olevan DNS-palvelun, joka vastasi 250 000 pyyntöön sekunnissa. Havaitsimme sovelluksissamme ajoittaista ja vaikuttavaa DNS-hakuaikakatkaisua. Tämä tapahtui huolimatta tyhjentävästä virityspyrkimyksestä ja DNS-palveluntarjoajan siirtymisestä CoreDNS-käyttöönottoon, joka saavutti huippunsa 1000 palkissa, jotka kuluttavat 120 ydintä.

Tutkiessaan muita mahdollisia syitä ja ratkaisuja löysimme artikkelin, joka kuvaa kilpailuolosuhteita, jotka vaikuttavat Linux-pakettien suodatuskehyksen netfilteriin. Näkemämme DNS-aikakatkaisut sekä kasvava insert_failed -laskuri Flannel-käyttöliittymässä, linjassa artikkelin havaintoihin.

Ongelma ilmenee lähde- ja kohdeverkon osoitteiden kääntämisen (SNAT ja DNAT) ja sitä seuraavan lisäyksen yhteydessä conntrack-taulukkoon. Yksi sisäisesti keskusteltu ja yhteisön ehdottama kiertotapa oli siirtää DNS itse työntekijän solmuun. Tässä tapauksessa:

  • SNAT ei ole välttämätön, koska liikenne pysyy paikallisesti solmulla. Sitä ei tarvitse lähettää eth0-rajapinnan kautta.
  • DNAT ei ole välttämätön, koska kohde-IP on paikallinen solmulle eikä satunnaisesti valittua podia per iptable-säännöt.

Päätimme siirtyä eteenpäin tämän lähestymistavan kanssa. CoreDNS otettiin käyttöön DaemonSet-sovelluksena Kubernetesissa, ja injektoimme solmun paikallisen DNS-palvelimen kunkin pod-laitteen resolv.conf-tiedostoon määrittämällä kubelet - cluster-dns -komennon lipun. Kiertotapa oli tehokas DNS-aikakatkaisuille.

Näemme kuitenkin edelleen pudonneet paketit ja Flannel-käyttöliittymän insert_failed -laskurin lisäyksen. Tämä jatkuu myös yllä mainitun kiertotavan jälkeen, koska vältimme SNAT- ja / tai DNAT-tietoja vain DNS-liikenteessä. Kilpailuolosuhteet esiintyvät edelleen muun tyyppisissä liikenteissä. Onneksi suurin osa paketeistamme on TCP ja kun tilanne ilmenee, paketit lähetetään onnistuneesti uudelleen. Kaiken tyyppiselle liikenteelle on pitkäaikainen ratkaisu, josta keskustelemme edelleen.

Lähetystön käyttäminen paremman kuormituksen tasapainottamiseksi

Kun muutimme taustapalvelumme Kubernetesiin, meille alkoi kärsiä epätasapainoisesta kuormasta palkojen välillä. Havaitsimme, että HTTP Keepalive -sovelluksen takia ELB-yhteydet kiinnittyivät kunkin liikkuvan käyttöönoton ensimmäisiin valmiisiin palkkeihin, joten suurin osa liikenteestä kulki pienen prosenttiosuuden käytettävissä olevista poddeista. Yksi ensimmäisistä lieventämistä, jota yritimme, oli käyttää 100% MaxSurge -sovellusta uusimmissa asennuksissa pahimmille rikoksentekijöille. Tämä oli hiukan tehokasta eikä kestävää pitkällä aikavälillä joidenkin suurten käyttöönottojen yhteydessä.

Toinen lieventäjä, jota käytimme, oli paisuttaa keinotekoisesti resurssipyyntöjä kriittisiin palveluihin, jotta sijoitettujen palkkien päällä olisi enemmän tilaa muiden raskaiden palkkien rinnalla. Tämä ei myöskään tule pitkällä tähtäimellä kestäväksi resurssien tuhlaamisen takia, ja Solmu-sovelluksemme olivat yksittäiskierteisiä ja siksi tehokkaasti korjattu yhteen ytimeen. Ainoa selkeä ratkaisu oli paremman kuormituksen tasapainotuksen hyödyntäminen.

Olimme sisäisesti halunneet arvioida lähettilään. Tämä antoi meille mahdollisuuden käyttää sitä hyvin rajoitetusti ja saada välittömiä etuja. Envoy on avoimen lähdekoodin korkealaatuinen Layer 7 -välityspalvelin, joka on suunniteltu suurille palvelukeskeisille arkkitehtuureille. Se pystyy toteuttamaan edistyneitä kuormituksen tasapainotustekniikoita, mukaan lukien automaattiset uudelleenyritykset, piirin katkaisut ja globaalin nopeuden rajoittamisen.

Kokoonpano, jonka keksimme, oli, että jokaisen podin vieressä oli Envoyn sivuvaunu, jolla oli yksi reitti ja klusteri osuakseen paikalliseen konttisatamaan. Jotta voisimme minimoida mahdollisen CSS: n ja pitääksemme pienen räjähdyssäteen, käytimme etuosan välityspalvelimen Envoy-palkoja, yksi käyttöönotto jokaisessa saatavuusvyöhykkeessä (AZ) jokaiselle palvelulle. Ne osuivat pieneen palvelun löytämismekanismiin, jonka yksi insinööreistämme koonnut yhteen ja palautti yksinkertaisesti luettelon paloista kussakin AZ: ssa tietyn palvelun osalta.

Palvelun etulähettiläiset käyttivät sitten tätä palvelun etsintämekanismia yhdellä ylävirran klusterilla ja reitillä. Konfiguroimme kohtuulliset aikakatkaisut, lisäsimme kaikkia virrankatkaisimen asetuksia ja panimme sitten minimaalisen uudelleenyrityskokoonpanon väliaikaisten vikojen ja sujuvien käyttöönottojen helpottamiseksi. Fronsoimme jokaisen näistä etulähettiläspalveluista TCP ELB: llä. Vaikka päävälityspalvelinkerroksestamme peräisin olevat aineet kiinnitettiin tiettyihin Envoy-palkkeihin, ne pystyivät paljon paremmin käsittelemään kuormaa ja ne määritettiin tasapainottamaan vähimmäispyynnön kautta taustalle.

Asennuksissa käytimme preStop-koukkua sekä sovellukseen että sivuvaunun kiinnittimeen. Tämä koukku, jota kutsuttiin sivuvaunun terveystarkastukseksi epäonnistui järjestelmän päätepiste yhdessä pienen unen kanssa, antaa aikaa, jotta lentoyhteydet voivat valmistua ja tyhjentyä.

Yksi syy siihen, että pystyimme liikkumaan niin nopeasti, johtui rikkaista mittareista, jotka pystyimme helposti integroimaan normaaliin Prometheus-asetukseemme. Tämän ansiosta voimme nähdä tarkalleen mitä tapahtui, kun toistimme kokoonpanoasetuksista ja leikkasimme liikennettä.

Tulokset olivat välittömiä ja ilmeisiä. Aloitimme epätasapainoisimmilla palveluilla, ja tällä hetkellä se toimitetaan klusterimme kahdentoista tärkeimmän palvelun edessä. Tänä vuonna aiomme siirtyä täyden palvelun verkkoon edistyneemmällä palvelutunnistuksella, piirin katkaisulla, ulkopuolisella havainnoinnilla, nopeuden rajoituksella ja jäljittämisellä.

Kuva 3–1 Yhden palvelun CPU: n konvergenssi lähetyskierroksen aikana

Lopputulos

Näiden opintojen ja lisätutkimuksen avulla olemme kehittäneet vahvan sisäisen infrastruktuuritiimin, jolla on suuri tuntemus suurten Kubernetes-klusterien suunnittelusta, käyttöönotosta ja käytöstä. Tinderin koko suunnitteluorganisaatiolla on nyt tietoa ja kokemusta siitä, kuinka niiden sovellukset voidaan koota ja asentaa Kubernetesiin.

Vanhassa infrastruktuurissamme, kun tarvittiin ylimääräistä mittakaavaa, kärsimme usein useita minuutteja odottaessamme uusien EC2-tapausten saapumista verkkoon. Kontit ajoittavat ja palvelevat liikennettä muutamassa sekunnissa minuutteihin nähden. Useiden säiliöiden ajoittaminen yhdelle EC2-tapaukselle tarjoaa myös parannetun vaakatiheyden. Seurauksena on, että arvioimme EC2: lle huomattavia kustannussäästöjä vuonna 2019 verrattuna edelliseen vuoteen.

Kesti melkein kaksi vuotta, mutta muutto saatiin päätökseen maaliskuussa 2019. Tinder-alusta toimii yksinomaan Kubernetes-klusterissa, joka koostuu 200 palvelusta, 1000 solmusta, 15 000 palkista ja 48 000 käynnissä olevasta kontista. Infrastruktuuri ei ole enää operatiivisille ryhmillemme varattu tehtävä. Sen sijaan koko organisaation insinöörit jakavat tämän vastuun ja hallitsevat sitä, kuinka heidän sovelluksensa rakennetaan ja otetaan käyttöön kaiken koodilla.