Тиндер транзиција кон Кубернетес

Забелешка. превод.: Вработените во светски познатиот сервис Tinder неодамна споделија некои технички детали за мигрирање на нивната инфраструктура во Кубернет. Процесот траеше речиси две години и резултираше со лансирање на многу голема платформа на K8, составена од 200 услуги сместени на 48 илјади контејнери. Со какви интересни тешкотии наидоа инженерите на Тиндер и до какви резултати дојдоа?Прочитајте го овој превод.

Тиндер транзиција кон Кубернетес

Зошто?

Пред речиси две години, Тиндер одлучи да ја пресели својата платформа во Кубернетес. Kubernetes ќе му дозволи на тимот на Tinder да контејнеризира и да се префрли на производство со минимален напор преку непроменливо распоредување (непроменливо распоредување). Во овој случај, склопувањето на апликациите, нивното распоредување и самата инфраструктура би биле уникатно дефинирани со код.

Баравме и решение за проблемот со приспособливост и стабилност. Кога скалирањето стана критично, честопати моравме да чекаме неколку минути за да се вртат новите EC2 примероци. Идејата за пуштање контејнери и започнување со опслужување на сообраќајот за неколку секунди, наместо за минути, ни стана многу привлечна.

Процесот се покажа тежок. За време на нашата миграција на почетокот на 2019 година, кластерот Kubernetes достигна критична маса и почнавме да се среќаваме со различни проблеми поради обемот на сообраќај, големината на кластерот и DNS. На патот, решивме многу интересни проблеми поврзани со мигрирање на 200 услуги и одржување на кластерот Kubernetes составен од 1000 јазли, 15000 подлоги и 48000 контејнери што работат.

Како?

Од јануари 2018 година, поминавме низ различни фази на миграција. Започнавме со контејнеризирање на сите наши услуги и нивно распоредување во облак околини за тестирање на Kubernetes. Почнувајќи од октомври, почнавме методично да ги мигрираме сите постоечки услуги во Kubernetes. До март следната година, ја завршивме миграцијата и сега платформата Tinder работи исклучиво на Kubernetes.

Градење слики за Kubernetes

Имаме повеќе од 30 складишта за изворни кодови за микросервиси кои работат на кластерот Kubernetes. Кодот во овие складишта е напишан на различни јазици (на пример, Node.js, Java, Scala, Go) со повеќе опкружувања за извршување на истиот јазик.

Системот за градење е дизајниран да обезбеди целосно приспособлив „контекс на градење“ за секоја микросервис. Обично се состои од Dockerfile и листа на команди на школка. Нивната содржина е целосно приспособлива, а во исто време, сите овие контексти за градење се напишани според стандардизиран формат. Стандардизирањето на контекстите за градење овозможува еден единствен систем за градење да се справи со сите микроуслуги.

Тиндер транзиција кон Кубернетес
Слика 1-1. Стандардизиран процес на градење преку контејнер на Builder

За да се постигне максимална конзистентност помеѓу работните времиња (околини за траење) истиот процес на градење се користи за време на развојот и тестирањето. Се соочивме со многу интересен предизвик: моравме да развиеме начин да обезбедиме доследност на градежната средина низ целата платформа. За да се постигне ова, сите процеси на склопување се изведуваат во посебен сад. Градител.

Неговата имплементација на контејнери бараше напредни Docker техники. Builder го наследува локалниот кориснички ID и тајните (како што се клучот SSH, ингеренциите AWS итн.) потребни за пристап до приватните складишта на Tinder. Тој монтира локални директориуми што содржат извори за природно складирање на артефакти на градба. Овој пристап ги подобрува перформансите бидејќи ја елиминира потребата од копирање на артефакти на градење помеѓу контејнерот на Builder и домаќинот. Зачуваните артефакти на градбата може повторно да се користат без дополнителна конфигурација.

За некои услуги, моравме да создадеме друг контејнер за да ја мапираме околината за компилација во околината за извршување (на пример, библиотеката Node.js bcrypt генерира бинарни артефакти специфични за платформата за време на инсталацијата). За време на процесот на компилација, барањата може да варираат помеѓу услугите, а конечниот Dockerfile се компајлира веднаш.

Кубернетска архитектура и миграција на кластерот

Управување со големината на кластерот

Решивме да користиме кубе-авс за автоматско распоредување на кластери на примероци на Amazon EC2. На самиот почеток, сè работеше во еден заеднички базен на јазли. Брзо ја сфативме потребата да се одделат оптоварувањата по големина и тип на пример за поефикасно користење на ресурсите. Логиката беше дека извршувањето на неколку наполнети мешунки со повеќе нишки се покажа како попредвидливо во однос на перформансите отколку нивното коегзистирање со голем број на мешунки со една нишка.

На крајот се решивме на:

  • m5.4xголем — за мониторинг (Прометеј);
  • c5.4xголем - за обемот на работа Node.js (обем на работа со една нишка);
  • c5.2xголем - за Java и Go (обем на работа со повеќе нишки);
  • c5.4xголем — за контролната табла (3 јазли).

Миграција

Еден од подготвителните чекори за мигрирање од старата инфраструктура кон Кубернетес беше пренасочување на постоечката директна комуникација помеѓу услугите кон новите балансери на оптоварување (Еластички баланси на оптоварување (ELB). Тие беа креирани на одредена подмрежа на виртуелен приватен облак (VPC). Оваа подмрежа беше поврзана со Kubernetes VPC. Ова ни овозможи постепено да мигрираме модули, без да го земеме предвид специфичниот редослед на зависности на услугите.

Овие крајни точки беа креирани со користење на пондерирани збирки на записи DNS кои имаа CNAME кои укажуваат на секоја нова ELB. За да се префрлиме, додадовме нов запис што укажува на новиот ELB на услугата Kubernetes со тежина од 0. Потоа го поставивме Time To Live (TTL) на записот на 0. После ова, старите и новите тежини беа полека се прилагодува, и на крајот 100% од оптоварувањето беше испратено до нов сервер. По завршувањето на префрлувањето, вредноста на TTL се врати на посоодветно ниво.

Јава модулите што ги имавме можеа да се справат со низок TTL DNS, но апликациите Node не можеа. Еден од инженерите преработил дел од кодот за поврзување на базенот и го завиткал во менаџер кој ги ажурирал базените на секои 60 секунди. Избраниот пристап функционираше многу добро и без забележлива деградација на перформансите.

Лекциите

Границите на мрежната ткаенина

Во раните утрински часови на 8 јануари 2019 година, платформата Тиндер неочекувано се урна. Како одговор на неповрзаното зголемување на латентноста на платформата порано истото утро, бројот на мешунки и јазли во кластерот се зголеми. Ова предизвика ARP кешот да се исцрпи на сите наши јазли.

Постојат три опции за Linux поврзани со кешот на ARP:

Тиндер транзиција кон Кубернетес
(извор)

gc_thresh3 - ова е тешко ограничување. Појавата на записи „прелевање на табела со соседи“ во дневникот значеше дека дури и по синхроно собирање ѓубре (GC), немаше доволно простор во кешот на ARP за складирање на соседниот запис. Во овој случај, кернелот едноставно го отфрли пакетот целосно.

Ние користиме Фланел како мрежна ткаенина во Kubernetes. Пакетите се пренесуваат преку VXLAN. VXLAN е L2 тунел подигнат на врвот на мрежата L3. Технологијата користи енкапсулација MAC-in-UDP (MAC Address-in-User Datagram Protocol) и овозможува проширување на мрежните сегменти на слојот 2. Транспортниот протокол на мрежата на физичкиот центар за податоци е IP плус UDP.

Тиндер транзиција кон Кубернетес
Слика 2-1. Фланелен дијаграм (извор)

Тиндер транзиција кон Кубернетес
Слика 2-2. VXLAN пакет (извор)

Секој работник на Kubernetes доделува виртуелен адресен простор со /24 маска од поголем /9 блок. За секој јазол ова е значи еден запис во рутирачката табела, еден запис во табелата ARP (на интерфејсот flannel.1) и еден запис во табелата за префрлување (FDB). Тие се додаваат првиот пат кога ќе се стартува работничкиот јазол или секој пат кога ќе се открие нов јазол.

Дополнително, комуникацијата node-pod (или pod-pod) на крајот оди преку интерфејсот eth0 (како што е прикажано на фланелниот дијаграм погоре). Ова резултира со дополнителен запис во табелата ARP за секој соодветен извор и дестинација домаќин.

Во нашата средина, овој тип на комуникација е многу чест. За услужни објекти во Kubernetes, се креира ELB и Kubernetes го регистрира секој јазол со ELB. ELB не знае ништо за pods и избраниот јазол можеби не е крајната дестинација на пакетот. Поентата е дека кога еден јазол добива пакет од ELB, тој го смета земајќи ги предвид правилата iptables за одредена услуга и по случаен избор избира pod на друг јазол.

Во моментот на неуспехот, имаше 605 јазли во кластерот. Од причините наведени погоре, ова беше доволно за да се надмине значењето gc_thresh3, што е стандардно. Кога тоа ќе се случи, не само што пакетите почнуваат да се испуштаат, туку целиот виртуелен адресен простор на Flannel со маска /24 исчезнува од табелата ARP. Комуникацијата Node-pod и барањата за DNS се прекинати (DNS е хостиран во кластер; прочитајте подоцна во оваа статија за детали).

За да го решите овој проблем, треба да ги зголемите вредностите gc_thresh1, gc_thresh2 и gc_thresh3 и рестартирајте го Flannel за повторно да ги регистрирате мрежите што недостасуваат.

Неочекувано скалирање на DNS

За време на процесот на миграција, активно користевме DNS за управување со сообраќајот и постепено пренесување услуги од старата инфраструктура во Кубернет. Поставивме релативно ниски TTL вредности за поврзаните RecordSets во Route53. Кога старата инфраструктура работеше на примероци на EC2, нашата конфигурација на резолуторот покажа на Amazon DNS. Го земавме ова здраво за готово и влијанието на нискиот TTL врз нашите услуги и услугите на Амазон (како DynamoDB) помина во голема мера незабележано.

Како што ги мигриравме услугите во Кубернетес, откривме дека DNS обработува 250 илјади барања во секунда. Како резултат на тоа, апликациите почнаа да искусуваат постојани и сериозни временски прекини за прашањата за DNS. Ова се случи и покрај неверојатните напори да се оптимизира и префрли DNS-провајдерот на CoreDNS (кој при максимално оптоварување достигна 1000 подови кои работат на 120 јадра).

Додека истражувавме други можни причини и решенија, откривме статью, опишувајќи ги условите на трката кои влијаат на рамката за филтрирање на пакети нетфилтер во Linux. Тајмаутите што ги забележавме, заедно со зголемениот бројач insert_failed во Flannel интерфејсот беа во согласност со наодите од статијата.

Проблемот се јавува во фазата на превод на мрежна адреса на извор и дестинација (SNAT и DNAT) и последователно внесување во табелата контра. Еден од заобиколните решенија за кои се дискутираше внатре и беше предложен од заедницата беше да се премести DNS во самиот работнички јазол. Во овој случај:

  • SNAT не е потребен бидејќи сообраќајот останува внатре во јазолот. Не треба да се пренасочува низ интерфејсот eth0.
  • DNAT не е потребен бидејќи одредишната IP адреса е локална на јазолот, а не случајно избрана подлога според правилата iptables.

Решивме да се држиме до овој пристап. CoreDNS беше распореден како DaemonSet во Kubernetes и имплементиравме локален јазол DNS сервер во резолуција.конф секој под со поставување знаме --кластер-dns команди кубелет . Ова решение се покажа како ефикасно за истекувања на DNS.

Сепак, сепак видовме загуба на пакети и зголемување на бројачот insert_failed во интерфејсот Flannel. Ова продолжи откако беше имплементирана заобиколницата бидејќи можевме да ги елиминираме SNAT и/или DNAT само за сообраќај DNS. Условите за трка беа зачувани за други видови сообраќај. За среќа, повеќето од нашите пакети се TCP, и ако се појави проблем тие едноставно се реемитуваат. Сè уште се обидуваме да најдеме соодветно решение за сите видови сообраќај.

Користење на „Envoy“ за подобро балансирање на товарот

Како што мигриравме задни услуги во Kubernetes, почнавме да страдаме од неурамнотежено оптоварување помеѓу местата. Откривме дека HTTP Keepalive предизвика ELB конекциите да се закачат на првите подготвени подлоги од секое распоредување. Така, најголемиот дел од сообраќајот помина низ мал процент од достапните мешунки. Првото решение што го тестиравме беше поставувањето на MaxSurge на 100% при нови распоредувања за најлошите сценарија. Ефектот се покажа како незначителен и неперспективен во однос на поголеми распоредувања.

Друго решение што го користевме беше вештачки да ги зголемиме барањата за ресурси за критични услуги. Во овој случај, мешунките поставени во близина ќе имаат повеќе простор за маневрирање во споредба со другите тешки мешунки. Тоа не би функционирало ниту на долг рок бидејќи би било губење ресурси. Покрај тоа, нашите апликации Node беа со една нишка и, соодветно, можеа да користат само едно јадро. Единственото вистинско решение беше да се користи подобро балансирање на товарот.

Одамна сакаме целосно да го цениме пратеник. Сегашната ситуација ни овозможи да го распоредиме на многу ограничен начин и да добиеме непосредни резултати. Envoy е прокси со високи перформанси, со отворен код, слој-XNUMX, дизајниран за големи SOA апликации. Може да имплементира напредни техники за балансирање на оптоварување, вклучувајќи автоматски повторувања, прекинувачи и глобално ограничување на брзината. (Забелешка. превод.: Можете да прочитате повеќе за ова во овој напис за Истио, кој се базира на Пратеник.)

Дојдовме до следнава конфигурација: имајте Envoy sidecar за секој pod и единствена рута, и поврзете го кластерот со контејнерот локално преку пристаништето. За да го минимизираме потенцијалното каскадирање и да одржиме мал радиус на удар, користевме флота од предни посредници на Envoy, по еден по зона на достапност (AZ) за секоја услуга. Тие се потпираа на едноставен мотор за откривање на услуги, напишан од еден од нашите инженери, кој едноставно враќаше список на мешунки во секоја АЗ за дадена услуга.

Service front-Envoys потоа го користеа овој механизам за откривање на услугата со еден возводно кластер и рута. Поставивме соодветни временски прекини, ги зголемивме сите поставки за прекинувачот и додадовме минимална конфигурација за повторно обид за да помогнеме при поединечни неуспеси и да обезбедиме непречено распоредување. Поставивме TCP ELB пред секој од овие сервисни предни пратеници. Дури и ако keepalive од нашиот главен прокси слој беше заглавен на некои подлоги на Envoy, тие сепак можеа многу подобро да се справат со оптоварувањето и беа конфигурирани да балансираат преку minimum_request во задниот дел.

За распоредување, ја користевме куката preStop и на лопатките за апликации и на лопатките за странични картички. Куката предизвика грешка во проверката на статусот на крајната точка на администраторот лоцирана на контејнерот на страничната кола и лежеше некое време за да се овозможи прекинување на активните врски.

Една од причините зошто можевме да се движиме толку брзо се должи на деталните метрики што можевме лесно да ги интегрираме во типична инсталација на Prometheus. Ова ни овозможи да видиме што точно се случува додека ги прилагодувавме параметрите за конфигурација и редистрибуиравме сообраќај.

Резултатите беа моментални и очигледни. Почнавме со најнеизбалансирани услуги, а во моментов работи пред 12-те најважни служби во кластерот. Оваа година планираме транзиција кон целосна сервисна мрежа со понапредно откривање на услугата, прекин на кола, откривање на оддалеченост, ограничување на брзината и следење.

Тиндер транзиција кон Кубернетес
Слика 3-1. Конвергенција на процесорот на една услуга за време на транзицијата кон Envoy

Тиндер транзиција кон Кубернетес

Тиндер транзиција кон Кубернетес

Конечен резултат

Преку ова искуство и дополнително истражување, изградивме силен инфраструктурен тим со силни вештини за дизајнирање, распоредување и управување со големи кластери Kubernetes. Сите инженери на Tinder сега имаат знаење и искуство за пакување контејнери и распоредување апликации на Кубернетес.

Кога се појави потребата за дополнителен капацитет на старата инфраструктура, моравме да чекаме неколку минути за да се стартуваат новите примероци на EC2. Сега контејнерите почнуваат да работат и почнуваат да го обработуваат сообраќајот за неколку секунди наместо за неколку минути. Распоредот на повеќе контејнери на еден примерок EC2, исто така, обезбедува подобрена хоризонтална концентрација. Како резултат на тоа, предвидуваме значително намалување на трошоците за EC2019 во 2 година во споредба со минатата година.

Миграцијата траеше речиси две години, но ја завршивме во март 2019 година. Во моментов, платформата Tinder работи исклучиво на кластерот Kubernetes кој се состои од 200 услуги, 1000 јазли, 15 подлоги и 000 контејнери што работат. Инфраструктурата повеќе не е единствен домен на оперативните тимови. Сите наши инженери ја споделуваат оваа одговорност и го контролираат процесот на градење и распоредување на нивните апликации користејќи само код.

PS од преведувач

Прочитајте и серија написи на нашиот блог:

Извор: www.habr.com

Додадете коментар