"Kubernetes gecikmə müddətini 10 dəfə artırdı": bunun günahkarı kimdir?

Qeyd. tərcümə.: Avropanın Adevinta şirkətində Baş Proqram Mühəndisi vəzifəsini tutan Qalo Navarro tərəfindən yazılmış bu məqalə infrastruktur əməliyyatları sahəsində maraqlı və ibrətamiz “araşdırma”dır. Tərcümə zamanı onun orijinal başlığı müəllifin əvvəldən izah etdiyi səbəbə görə bir qədər genişləndirilmişdir.

"Kubernetes gecikmə müddətini 10 dəfə artırdı": bunun günahkarı kimdir?

Müəllifdən qeyd: Bu yazıya bənzəyir cəlb etdi gözlənildiyindən daha çox diqqət. Mən hələ də yazının başlığının çaşdırıcı olması və bəzi oxucuların kədərləndiyi barədə qəzəbli şərhlər alıram. Baş verənlərin səbəblərini başa düşürəm, buna görə də bütün intriqanı pozmaq riskinə baxmayaraq, bu məqalənin nədən bəhs etdiyini dərhal sizə demək istəyirəm. Komandaların Kubernetesə köçməsi zamanı gördüyüm maraqlı bir şey odur ki, hər hansı bir problem yarandıqda (miqrasiyadan sonra artan gecikmə kimi) günahlandırılan ilk şey Kubernetesdir, lakin sonra məlum olur ki, orkestr əslində günahlandırmaq. Bu məqalə belə bir hadisədən bəhs edir. Onun adı tərtibatçılarımızdan birinin nidasını təkrarlayır (daha sonra Kubernetesin bununla heç bir əlaqəsi olmadığını görəcəksiniz). Burada Kubernetes haqqında heç bir təəccüblü açıqlama tapa bilməyəcəksiniz, lakin mürəkkəb sistemlər haqqında bir neçə yaxşı dərs gözləmək olar.

Bir neçə həftə əvvəl komandam tək bir mikroxidməti CI/CD, Kubernetes əsaslı iş vaxtı, ölçülər və digər gözəllikləri ehtiva edən əsas platformaya köçürdü. Hərəkət sınaq xarakterli idi: biz bunu əsas götürməyi və yaxın aylarda daha 150 xidmət ötürməyi planlaşdırdıq. Onların hamısı İspaniyanın ən böyük onlayn platformalarından bəzilərinin (Infojobs, Fotocasa və s.) fəaliyyətinə cavabdehdirlər.

Tətbiqi Kubernetes-ə yerləşdirdikdən və bəzi trafiki ona yönləndirdikdən sonra bizi həyəcan verici bir sürpriz gözləyirdi. Gecikmə (gecikmə) Kubernetesdəki sorğular EC10 ilə müqayisədə 2 dəfə çox idi. Ümumiyyətlə, ya bu problemin həllini tapmaq, ya da mikroservisin (və bəlkə də bütün layihənin) miqrasiyasından imtina etmək lazım idi.

Niyə Kubernetesdə gecikmə EC2 ilə müqayisədə bu qədər yüksəkdir?

Darboğazı tapmaq üçün bütün sorğu yolu boyunca ölçüləri topladıq. Arxitekturamız sadədir: API şlüzü (Zuul) EC2 və ya Kubernetes-də mikroservis instansiyalarına sorğu göndərir. Kubernetes-də biz NGINX Ingress Controller-dən istifadə edirik və arxa tərəflər adi obyektlərdir. Deployment Spring platformasında JVM tətbiqi ilə.

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

Problem backenddəki ilkin gecikmə ilə əlaqəli görünürdü (qrafikdə problem sahəsini "xx" kimi qeyd etdim). EC2-də tətbiqin cavabı təxminən 20 ms çəkdi. Kubernetesdə gecikmə 100-200 ms-ə qədər artdı.

İş vaxtı dəyişikliyi ilə bağlı ehtimal olunan şübhəliləri tez bir zamanda rədd etdik. JVM versiyası eyni olaraq qalır. Konteynerləşdirmə problemlərinin də bununla heç bir əlaqəsi yox idi: proqram artıq EC2-də konteynerlərdə uğurla işləyirdi. Yüklənir? Ancaq saniyədə 1 sorğuda belə yüksək gecikmələr müşahidə etdik. Zibil yığımı üçün fasilələrə də laqeyd yanaşmaq olar.

Kubernetes adminlərimizdən biri tətbiqdə xarici asılılıqların olub-olmaması ilə maraqlandı, çünki DNS sorğuları keçmişdə oxşar problemlərə səbəb olub.

Hipotez 1: DNS adının həlli

Hər sorğu üçün tətbiqimiz AWS Elasticsearch instansiyasına bir-üç dəfə domen kimi daxil olur elastic.spain.adevinta.com. Konteynerlərimizin içərisində qabıq var, beləliklə, bir domen axtarışının həqiqətən uzun müddət çəkdiyini yoxlaya bilərik.

Konteynerdən DNS sorğuları:

[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

Tətbiqin işlədiyi EC2 instansiyalarından birindən oxşar sorğular:

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

Axtarışın təxminən 30 ms çəkdiyini nəzərə alsaq, məlum oldu ki, Elasticsearch-ə daxil olan zaman DNS həlli həqiqətən gecikmənin artmasına kömək edir.

Ancaq bu iki səbəbə görə qəribə idi:

  1. Artıq yüksək gecikmədən əziyyət çəkmədən AWS resursları ilə qarşılıqlı əlaqədə olan bir ton Kubernetes tətbiqimiz var. Səbəb nə olursa olsun, bu, konkret olaraq bu işə aiddir.
  2. Biz bilirik ki, JVM yaddaşdaxili DNS keşini həyata keçirir. Şəkillərimizdə TTL dəyəri yazılıb $JAVA_HOME/jre/lib/security/java.security və 10 saniyəyə təyin edin: networkaddress.cache.ttl = 10. Başqa sözlə, JVM bütün DNS sorğularını 10 saniyə ərzində yaddaşda saxlamalıdır.

Birinci fərziyyəni təsdiqləmək üçün bir müddət DNS-ə zəng etməyi dayandırmaq və problemin aradan qalxıb-yaxmadığını görmək qərarına gəldik. Birincisi, biz tətbiqi yenidən konfiqurasiya etmək qərarına gəldik ki, o, domen adı ilə deyil, birbaşa Elasticsearch ilə IP ünvanı ilə əlaqə saxlasın. Bunun üçün kod dəyişiklikləri və yeni yerləşdirmə tələb olunur, ona görə də biz sadəcə olaraq domeni onun IP ünvanına uyğunlaşdırdıq /etc/hosts:

34.55.5.111 elastic.spain.adevinta.com

İndi konteyner demək olar ki, dərhal bir IP aldı. Bu, müəyyən irəliləyişlə nəticələndi, lakin biz gözlənilən gecikmə səviyyələrinə bir qədər yaxınlaşdıq. DNS həlli çox vaxt aparsa da, əsl səbəb hələ də bizdən qaçırdı.

Şəbəkə vasitəsilə diaqnostika

Konteynerdən istifadə edərək trafiki təhlil etmək qərarına gəldik tcpdumpşəbəkədə tam olaraq nə baş verdiyini görmək üçün:

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

Sonra bir neçə sorğu göndərdik və onların tutulmasını endirdik (kubectl cp my-service:/capture.pcap capture.pcap) əlavə təhlil üçün Wireshark.

DNS sorğularında şübhəli heç nə yox idi (daha sonra danışacağım kiçik bir şey istisna olmaqla). Lakin xidmətimizin hər bir sorğuya baxma tərzində müəyyən qəribəliklər var idi. Aşağıda sorğunun cavab başlamazdan əvvəl qəbul edildiyini göstərən çəkilişin ekran görüntüsü verilmişdir:

"Kubernetes gecikmə müddətini 10 dəfə artırdı": bunun günahkarı kimdir?

Paket nömrələri birinci sütunda göstərilir. Aydınlıq üçün müxtəlif TCP axınlarını rənglə kodladım.

328-ci paketlə başlayan yaşıl axın müştərinin (172.17.22.150) konteynerə (172.17.36.147) TCP bağlantısını necə qurduğunu göstərir. İlkin əl sıxışmadan (328-330) sonra 331-ci paket gətirildi HTTP GET /v1/.. — xidmətimizə daxil olan sorğu. Bütün proses 1 ms çəkdi.

Boz axın (paket 339-dan) göstərir ki, xidmətimiz Elasticsearch instansiyasına HTTP sorğusu göndərib (o, mövcud bağlantıdan istifadə etdiyi üçün TCP əl sıxması yoxdur). Bu 18 ms çəkdi.

İndiyə qədər hər şey qaydasındadır və vaxtlar təxminən gözlənilən gecikmələrə uyğundur (müştəridən ölçülən zaman 20-30 ms).

Bununla belə, mavi hissə 86 ms çəkir. Nə baş verir? 333 paketi ilə xidmətimiz HTTP GET sorğusu göndərdi /latest/meta-data/iam/security-credentials, və ondan dərhal sonra eyni TCP bağlantısı üzərindən başqa bir GET sorğusu /latest/meta-data/iam/security-credentials/arn:...

Biz bunun iz boyu hər sorğu ilə təkrarlandığını gördük. Konteynerlərimizdə DNS həlli həqiqətən bir qədər yavaşdır (bu fenomenin izahı olduqca maraqlıdır, amma onu ayrıca məqalə üçün saxlayacağam). Məlum olub ki, uzun gecikmələrin səbəbi hər sorğu üzrə AWS Instance Metadata xidmətinə edilən zənglər olub.

Hipoteza 2: AWS-ə lazımsız zənglər

Hər iki son nöqtə aiddir AWS Nümunə Metadata API. Mikroservisimiz Elasticsearch işləyərkən bu xidmətdən istifadə edir. Hər iki zəng əsas avtorizasiya prosesinin bir hissəsidir. İlk sorğuda əldə edilən son nöqtə nümunə ilə əlaqəli IAM rolunu verir.

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

İkinci sorğu ikinci son nöqtədən bu nümunə üçün müvəqqəti icazələr tələb edir:

/ # 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"
}

Müştəri onlardan qısa müddət ərzində istifadə edə bilər və vaxtaşırı yeni sertifikatlar almalıdır (onlardan əvvəl Expiration). Model sadədir: AWS təhlükəsizlik səbəbi ilə müvəqqəti açarları tez-tez fırladır, lakin müştərilər yeni sertifikatların əldə edilməsi ilə bağlı performans cəzasını kompensasiya etmək üçün onları bir neçə dəqiqə keşləyə bilər.

AWS Java SDK bu prosesin təşkili üçün məsuliyyəti öz üzərinə götürməlidir, lakin nədənsə bu baş vermir.

GitHub-da problemləri axtardıqdan sonra problemlə qarşılaşdıq # 1921. O, bizə daha da “qazmağın” istiqamətini müəyyənləşdirməyə kömək etdi.

AWS SDK aşağıdakı şərtlərdən biri baş verdikdə sertifikatları yeniləyir:

  • İstifadə müddəti (Expiration) Düşmək EXPIRATION_THRESHOLD, 15 dəqiqəyə qədər kodlaşdırılmışdır.
  • Sertifikatları yeniləmək üçün edilən son cəhddən daha çox vaxt keçdi REFRESH_THRESHOLD, 60 dəqiqə kodlaşdırılmışdır.

Aldığımız sertifikatların faktiki bitmə tarixini görmək üçün yuxarıdakı cURL əmrlərini həm konteynerdən, həm də EC2 instansiyasından icra etdik. Konteynerdən alınan sertifikatın etibarlılıq müddəti daha qısa oldu: düz 15 dəqiqə.

İndi hər şey aydın oldu: ilk sorğu üçün xidmətimiz müvəqqəti sertifikatlar aldı. Onlar 15 dəqiqədən çox müddət ərzində etibarlı olmadığı üçün AWS SDK onları sonrakı sorğuda yeniləməyə qərar verəcəkdi. Və bu, hər xahişlə baş verdi.

Sertifikatların etibarlılıq müddəti niyə qısaldı?

AWS Nümunə Metadata Kubernetes deyil, EC2 instansiyaları ilə işləmək üçün nəzərdə tutulub. Digər tərəfdən, tətbiq interfeysini dəyişmək istəmədik. Bunun üçün istifadə etdik KIAM - hər bir Kubernetes qovşağında agentlərdən istifadə edərək istifadəçilərə (klasterə proqram yerləşdirən mühəndislər) IAM rollarını EC2 nümunələri kimi podlarda konteynerlərə təyin etməyə imkan verən alət. KIAM AWS Instance Metdata xidmətinə edilən zəngləri ələ keçirir və onları əvvəllər AWS-dən qəbul edərək öz keşindən emal edir. Tətbiq baxımından heç nə dəyişmir.

KIAM podlara qısamüddətli sertifikatlar verir. Bir podun orta ömrünün EC2 nümunəsindən daha qısa olduğunu nəzərə alsaq, bu məntiqlidir. Sertifikatlar üçün standart etibarlılıq müddəti eyni 15 dəqiqəyə bərabərdir.

Nəticədə, hər iki standart dəyəri bir-birinin üstünə qoysanız, problem yaranır. Müraciətə təqdim edilən hər bir sertifikatın müddəti 15 dəqiqədən sonra bitir. Bununla belə, AWS Java SDK etibarlılıq müddətinin bitməsinə 15 dəqiqədən az qalan istənilən sertifikatı yeniləməyə məcbur edir.

Nəticədə, müvəqqəti sertifikat hər sorğu ilə yenilənmək məcburiyyətində qalır ki, bu da AWS API-yə bir neçə zəngə səbəb olur və gecikmənin əhəmiyyətli dərəcədə artmasına səbəb olur. AWS Java SDK-da tapdıq xüsusiyyət istəyi, bu da oxşar problemdən bəhs edir.

Həll sadə olduğu ortaya çıxdı. Biz sadəcə olaraq daha uzun etibarlılıq müddəti olan sertifikatlar tələb etmək üçün KIAM-ı yenidən konfiqurasiya etdik. Bu baş verdikdən sonra sorğular AWS Metadata xidmətinin iştirakı olmadan axmağa başladı və gecikmə EC2 ilə müqayisədə daha aşağı səviyyələrə düşdü.

Tapıntılar

Miqrasiya ilə bağlı təcrübəmizə əsasən, ən çox rast gəlinən problem mənbələrindən biri Kubernetes və ya platformanın digər elementlərindəki səhvlər deyil. O, həmçinin daşıdığımız mikroservislərdəki heç bir əsas qüsuru aradan qaldırmır. Problemlər çox vaxt sadəcə müxtəlif elementləri bir araya gətirdiyimiz üçün yaranır.

Əvvəllər bir-biri ilə heç vaxt qarşılıqlı əlaqədə olmayan mürəkkəb sistemləri bir araya gətiririk və onların birlikdə vahid, daha böyük bir sistem meydana gətirəcəyini gözləyirik. Təəssüf ki, elementlər nə qədər çox olsa, səhvlər üçün nə qədər çox yer varsa, entropiya bir o qədər yüksəkdir.

Bizim vəziyyətimizdə yüksək gecikmə Kubernetes, KIAM, AWS Java SDK və ya mikroservisimizdəki səhvlər və ya səhv qərarların nəticəsi deyildi. Bu, iki müstəqil standart parametrin birləşməsinin nəticəsi idi: biri KIAM-da, digəri AWS Java SDK-da. Ayrı-ayrılıqda götürsək, hər iki parametr məna verir: AWS Java SDK-da aktiv sertifikat yeniləmə siyasəti və KAIM-də sertifikatların qısa etibarlılıq müddəti. Ancaq onları bir araya gətirəndə nəticələr gözlənilməz olur. İki müstəqil və məntiqi həll birləşdikdə məna kəsb etməməlidir.

Tərcüməçidən PS

AWS IAM-ı Kubernetes ilə inteqrasiya etmək üçün KIAM yardım proqramının arxitekturası haqqında ətraflı öyrənə bilərsiniz. Bu məqalə yaradıcılarından.

Bloqumuzda da oxuyun:

Mənbə: www.habr.com

Добавить комментарий