«Кубернетес кешіктіруді 10 есе арттырды»: бұған кім кінәлі?

Ескерту. аударма: Еуропалық Adevinta компаниясында бағдарламалық қамтамасыз ету жөніндегі бас инженер лауазымын атқаратын Гало Наварро жазған бұл мақала инфрақұрылымдық операциялар саласындағы қызықты және нұсқау беретін «зерттеу» болып табылады. Оның түпнұсқа атауы автор ең басында түсіндіретін себеппен аудармада сәл кеңейтілді.

«Кубернетес кешіктіруді 10 есе арттырды»: бұған кім кінәлі?

Автордан ескерту: Бұл постқа ұқсайды тартылған күткеннен әлдеқайда көп көңіл бөледі. Мақаланың аты жаңылысып, кейбір оқырмандар мұңаяды деген ашулы пікірлер әлі де бар. Мен не болып жатқанын түсінемін, сондықтан бүкіл интриганы бұзу қаупіне қарамастан, мен сізге бұл мақаланың не туралы екенін бірден айтқым келеді. Командалар Кубернетеске көшкен кезде мен көрген қызық нәрсе, мәселе туындаған кезде (мысалы, көшіруден кейінгі кідірістің жоғарылауы), бірінші кезекте Кубернетес кінәлі болады, бірақ содан кейін оркестр шынымен де жұмыс істемейтіні белгілі болды. кінә. Бұл мақалада осындай бір жағдай туралы айтылады. Оның аты біздің әзірлеушілеріміздің бірінің лебізін қайталайды (кейінірек сіз Кубернетестің бұған ешқандай қатысы жоқ екенін көресіз). Мұнда сіз Кубернетес туралы таңқаларлық ашуларды таба алмайсыз, бірақ күрделі жүйелер туралы бірнеше жақсы сабақ күтуге болады.

Бірнеше апта бұрын менің командам бір микросервисті CI/CD, Kubernetes негізіндегі жұмыс уақыты, метрика және басқа да пайдалы нәрселерді қамтитын негізгі платформаға көшірді. Бұл көшу сынақтық сипатта болды: біз оны негізге алып, алдағы айларда тағы 150-ге жуық қызмет көрсетуді жоспарладық. Олардың барлығы Испаниядағы ең ірі онлайн платформалардың (Infojobs, Fotocasa және т.б.) жұмысына жауапты.

Қолданбаны Kubernetes-ке орналастырып, оған трафикті қайта бағыттағаннан кейін бізді алаңдатарлық тосынсый күтіп тұрды. Кешіктіру (кідіріс) Кубернетестегі сұраулар EC10-ге қарағанда 2 есе жоғары болды. Жалпы, бұл мәселенің шешімін табу немесе микросервистің (және, мүмкін, бүкіл жобаның) көшіруінен бас тарту керек болды.

Неліктен EC2-ге қарағанда Kubernetes-те кідіріс соншалықты жоғары?

Қиындықты табу үшін біз бүкіл сұрау жолы бойынша көрсеткіштерді жинадық. Біздің архитектура қарапайым: API шлюзі (Zuul) EC2 немесе Kubernetes ішіндегі микросервис даналарына сұрауларды жібереді. Kubernetes-те біз NGINX Ingress Controller пайдаланамыз, ал серверлер сияқты қарапайым нысандар Орналастыру Spring платформасындағы JVM қолданбасымен.

                                  EC2
                            +---------------+
                            |  +---------+  |
                            |  |         |  |
                       +-------> BACKEND |  |
                       |    |  |         |  |
                       |    |  +---------+  |                   
                       |    +---------------+
             +------+  |
Public       |      |  |
      -------> ZUUL +--+
traffic      |      |  |              Kubernetes
             +------+  |    +-----------------------------+
                       |    |  +-------+      +---------+ |
                       |    |  |       |  xx  |         | |
                       +-------> NGINX +------> BACKEND | |
                            |  |       |  xx  |         | |
                            |  +-------+      +---------+ |
                            +-----------------------------+

Мәселе сервердегі бастапқы кідіріспен байланысты болды (мен графиктегі мәселе аймағын «xx» деп белгіледім). EC2-де қолданбаның жауабы шамамен 20 мс алды. Кубернетесте кідіріс 100-200 мс дейін өсті.

Орындау уақытын өзгертуге қатысты ықтимал күдіктілерді тез арада жоққа шығардық. JVM нұсқасы өзгеріссіз қалады. Контейнерлеу проблемалары да оған ешқандай қатысы жоқ: қолданба EC2 контейнерлерінде сәтті жұмыс істеп тұрды. Жүктелуде ме? Бірақ біз секундына 1 сұраныс болса да жоғары кідірістерді байқадық. Қоқыс жинауға арналған үзілістерді де елемеуге болады.

Біздің Kubernetes әкімшілерінің бірі қолданбада сыртқы тәуелділіктер бар ма деп сұрады, себебі DNS сұраулары бұрын ұқсас мәселелерді тудырған.

Гипотеза 1: DNS атауының рұқсаты

Әрбір сұрау үшін біздің қолданба AWS Elasticsearch данасына бір-үш рет сияқты доменде қатынасады elastic.spain.adevinta.com. Біздің контейнерлердің ішінде қабық бар, осылайша біз доменді іздеу ұзақ уақытты қажет ететінін тексере аламыз.

Контейнерден DNS сұраулары:

[root@be-851c76f696-alf8z /]# while true; do dig "elastic.spain.adevinta.com" | grep time; sleep 2; done
;; Query time: 22 msec
;; Query time: 22 msec
;; Query time: 29 msec
;; Query time: 21 msec
;; Query time: 28 msec
;; Query time: 43 msec
;; Query time: 39 msec

Қолданба іске қосылған EC2 даналарының бірінің ұқсас сұраулары:

bash-4.4# while true; do dig "elastic.spain.adevinta.com" | grep time; sleep 2; done
;; Query time: 77 msec
;; Query time: 0 msec
;; Query time: 0 msec
;; Query time: 0 msec
;; Query time: 0 msec

Іздеуге шамамен 30 мс уақыт кететінін ескерсек, Elasticsearch-ке кіру кезінде DNS ажыратымдылығы кідірістің артуына шынымен де ықпал ететіні белгілі болды.

Дегенмен, бұл екі себеп бойынша оғаш болды:

  1. Бізде жоғары кідіріссіз AWS ресурстарымен өзара әрекеттесетін көптеген Kubernetes қолданбалары бар. Қандай себеп болмасын, бұл нақты осы іске қатысты.
  2. JVM жадтағы DNS кэштеуін жасайтынын білеміз. Біздің суреттерімізде TTL мәні жазылған $JAVA_HOME/jre/lib/security/java.security және 10 секундқа орнатыңыз: networkaddress.cache.ttl = 10. Басқаша айтқанда, JVM барлық DNS сұрауларын 10 секунд ішінде кэштеу керек.

Бірінші гипотезаны растау үшін біз біраз уақытқа DNS қоңырауын тоқтатуды және мәселенің жойылғанын көруді шештік. Біріншіден, біз қолданбаны домендік атау арқылы емес, IP мекенжайы бойынша тікелей Elasticsearch-пен байланысатындай етіп қайта конфигурациялауды шештік. Бұл кодты өзгертуді және жаңа орналастыруды қажет етеді, сондықтан біз жай ғана доменді оның IP мекенжайымен салыстырдық /etc/hosts:

34.55.5.111 elastic.spain.adevinta.com

Енді контейнер бірден дерлік IP алды. Бұл біршама жақсартуға әкелді, бірақ біз күтілетін кідіріс деңгейлеріне сәл ғана жақындадық. DNS ажыратымдылығы ұзақ уақыт алғанымен, нақты себеп әлі де бізге түсініксіз болды.

Желі арқылы диагностика

Біз контейнерден трафикті талдауды шештік tcpdumpжеліде не болып жатқанын көру үшін:

[root@be-851c76f696-alf8z /]# tcpdump -leni any -w capture.pcap

Содан кейін біз бірнеше сұрау жіберіп, олардың түсірілімін жүктеп алдық (kubectl cp my-service:/capture.pcap capture.pcap) қосымша талдау үшін Wireshark.

DNS сұрауларында күдікті ештеңе болған жоқ (мен кейінірек айтатын бір кішкентай нәрсені қоспағанда). Бірақ біздің қызметіміздің әрбір сұрауды өңдеуінде белгілі бір ерекшеліктер болды. Төменде жауап басталар алдында сұраудың қабылданғанын көрсететін түсірудің скриншоты берілген:

«Кубернетес кешіктіруді 10 есе арттырды»: бұған кім кінәлі?

Пакет нөмірлері бірінші бағанда көрсетілген. Түсінікті болу үшін мен әртүрлі TCP ағындарын түсті кодтадым.

328 пакетінен басталатын жасыл ағын клиенттің (172.17.22.150) контейнерге (172.17.36.147) TCP қосылымын қалай орнатқанын көрсетеді. Алғашқы қол алысудан кейін (328-330), 331-бума әкелінді HTTP GET /v1/.. — біздің қызметімізге кіріс сұрау. Бүкіл процесс 1 мс алды.

Сұр ағын (339-пакеттен) біздің қызметіміздің Elasticsearch данасына HTTP сұрауын жібергенін көрсетеді (ол бар қосылымды пайдаланып жатқандықтан TCP қол алысуы жоқ). Бұл 18 мс уақытты алды.

Әзірге бәрі жақсы және уақыт күтілетін кідірістерге сәйкес келеді (клиенттен өлшенгенде 20-30 мс).

Дегенмен, көк бөлік 86 мс алады. Онда не болып жатыр? 333 пакетімен біздің қызмет HTTP GET сұрауын жіберді /latest/meta-data/iam/security-credentials, және одан кейін бірден сол TCP қосылымы арқылы басқа GET сұрауы /latest/meta-data/iam/security-credentials/arn:...

Біз бұл ізденіс барысында әрбір сұраумен қайталанатынын анықтадық. Біздің контейнерлерде DNS рұқсаты шынымен де баяуырақ (бұл құбылыстың түсіндірмесі өте қызықты, бірақ мен оны жеке мақалаға сақтаймын). Ұзақ кешігулердің себебі әрбір сұрау бойынша AWS Instance метадеректер қызметіне қоңыраулар болғаны белгілі болды.

Гипотеза 2: AWS-ке қажетсіз қоңыраулар

Екі соңғы нүкте де тиесілі AWS даналық метадеректер API. Біздің микросервис бұл қызметті Elasticsearch іске қосу кезінде пайдаланады. Екі қоңырау да негізгі авторизация процесінің бөлігі болып табылады. Бірінші сұрауда қол жеткізілетін соңғы нүкте данаға байланысты IAM рөлін шығарады.

/ # curl http://169.254.169.254/latest/meta-data/iam/security-credentials/
arn:aws:iam::<account_id>:role/some_role

Екінші сұрау екінші соңғы нүктеден осы данаға уақытша рұқсаттарды сұрайды:

/ # curl http://169.254.169.254/latest/meta-data/iam/security-credentials/arn:aws:iam::<account_id>:role/some_role`
{
    "Code" : "Success",
    "LastUpdated" : "2012-04-26T16:39:16Z",
    "Type" : "AWS-HMAC",
    "AccessKeyId" : "ASIAIOSFODNN7EXAMPLE",
    "SecretAccessKey" : "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
    "Token" : "token",
    "Expiration" : "2017-05-17T15:09:54Z"
}

Клиент оларды қысқа уақыт ішінде пайдалана алады және мерзімді түрде жаңа сертификаттарды алуы керек (олар болғанға дейін Expiration). Модель қарапайым: AWS қауіпсіздік мақсатында уақытша кілттерді жиі айналдырады, бірақ жаңа сертификаттарды алуға байланысты өнімділік айыппұлын өтеу үшін клиенттер оларды бірнеше минутқа кэштей алады.

AWS Java SDK бұл процесті ұйымдастыру жауапкершілігін өз мойнына алуы керек, бірақ қандай да бір себептермен бұл болмайды.

GitHub-те мәселелерді іздегеннен кейін біз мәселеге тап болдық #1921. Ол бізге әрі қарай «қазудың» бағытын анықтауға көмектесті.

AWS SDK келесі жағдайлардың бірі орын алған кезде сертификаттарды жаңартады:

  • Мерзімнің өту күні (Expiration) Түсіңіз EXPIRATION_THRESHOLD, 15 минутқа дейін қатты кодталған.
  • Сертификаттарды жаңартуға соңғы әрекеттен бері көп уақыт өтті REFRESH_THRESHOLD, 60 минутқа қатты кодталған.

Біз алатын сертификаттардың нақты жарамдылық мерзімін көру үшін контейнерден де, EC2 данасынан да жоғарыдағы cURL пәрмендерін орындадық. Контейнерден алынған сертификаттың әрекет ету мерзімі әлдеқайда қысқа болып шықты: тура 15 минут.

Енді бәрі түсінікті болды: бірінші сұраныс бойынша біздің қызмет уақытша сертификаттар алды. Олар 15 минуттан артық жарамсыз болғандықтан, AWS SDK оларды келесі сұрау бойынша жаңартуды шешеді. Және бұл әр өтініште болды.

Неліктен сертификаттардың қолданылу мерзімі қысқарды?

AWS даналық метадеректер Kubernetes емес, EC2 даналарымен жұмыс істеуге арналған. Екінші жағынан, біз қолданба интерфейсін өзгерткіміз келмеді. Ол үшін біз қолдандық KIAM - әрбір Kubernetes түйінінде агенттерді пайдалана отырып, пайдаланушыларға (кластерге қолданбаларды орналастыратын инженерлер) IAM рөлдерін EC2 даналары сияқты подкасттардағы контейнерлерге тағайындауға мүмкіндік беретін құрал. KIAM AWS Instance метадеректер қызметіне қоңырауларды ұстайды және оларды бұрын AWS жүйесінен алған оның кэшінен өңдейді. Қолдану тұрғысынан ештеңе өзгермейді.

KIAM кондукторларға қысқа мерзімді сертификаттарды береді. Бұрыштың орташа қызмет ету мерзімі EC2 данасына қарағанда қысқа екенін ескерсек, бұл мағынасы бар. Сертификаттардың әдепкі жарамдылық мерзімі бірдей 15 минутқа тең.

Нәтижесінде екі әдепкі мәнді бір-бірінің үстіне қойсаңыз, мәселе туындайды. Өтінімге берілген әрбір сертификаттың мерзімі 15 минуттан кейін аяқталады. Дегенмен, AWS Java SDK жарамдылық мерзіміне 15 минуттан аз уақыт қалған кез келген сертификатты жаңартуға мәжбүр етеді.

Нәтижесінде, уақытша сертификатты әрбір сұраумен ұзартуға мәжбүр болады, бұл AWS API-ге бірнеше қоңырауларды тудырады және кідірістің айтарлықтай артуына әкеледі. AWS Java SDK ішінде біз таптық мүмкіндік туралы сұрау, ол ұқсас мәселе туралы айтады.

Шешім қарапайым болып шықты. Біз жарамдылық мерзімі ұзағырақ сертификаттарды сұрау үшін KIAM-ды жай ғана қайта конфигурацияладық. Бұл орын алған соң, сұраулар AWS метадеректер қызметінің қатысуынсыз келе бастады және кідіріс EC2-ге қарағанда одан да төмен деңгейге дейін төмендеді.

қорытындылар

Көшіру тәжірибесіне сүйене отырып, мәселелердің ең көп тараған көздерінің бірі Kubernetes немесе платформаның басқа элементтеріндегі қателер емес. Ол сондай-ақ біз тасымалдайтын микросервистердің негізгі кемшіліктерін қарастырмайды. Мәселелер көбінесе әртүрлі элементтерді біріктіргендіктен туындайды.

Біз бұрын-соңды бір-бірімен өзара әрекеттеспейтін күрделі жүйелерді біріктіреміз, олар бірігіп, біртұтас, үлкенірек жүйені құрайды деп күтеміз. Өкінішке орай, элементтер неғұрлым көп болса, соғұрлым қателер үшін орын көп болса, энтропия соғұрлым жоғары болады.

Біздің жағдайда, жоғары кідіріс Kubernetes, KIAM, AWS Java SDK немесе біздің микросервистегі қателердің немесе қате шешімдердің нәтижесі емес. Бұл екі тәуелсіз әдепкі параметрді біріктірудің нәтижесі болды: біреуі KIAM жүйесінде, екіншісі AWS Java SDK. Бөлек алғанда, екі параметрдің де мағынасы бар: AWS Java SDK жүйесіндегі белсенді сертификатты жаңарту саясаты және KAIM сертификаттарының қысқа жарамдылық мерзімі. Бірақ сіз оларды біріктірген кезде нәтиже күтпеген болады. Екі тәуелсіз және логикалық шешім біріктірілген кезде мағынасы болмауы керек.

Аудармашыдан PS

AWS IAM-ті Kubernetes-пен біріктіруге арналған KIAM утилитасының архитектурасы туралы толығырақ мына жерден біле аласыз. Бұл мақала оны жасаушылардан.

Сондай-ақ біздің блогта оқыңыз:

Ақпарат көзі: www.habr.com

пікір қалдыру