Kubernetes məsləhətləri və fəndləri: NGINX və PHP-FPM-də zərif bağlanma xüsusiyyətləri

Kubernetes-də CI/CD tətbiq edərkən tipik şərt: proqram tamamilə dayandırılmadan əvvəl yeni müştəri sorğularını qəbul etməməli və ən əsası mövcud olanları uğurla tamamlamalıdır.

Kubernetes məsləhətləri və fəndləri: NGINX və PHP-FPM-də zərif bağlanma xüsusiyyətləri

Bu şərtə uyğunluq yerləşdirmə zamanı sıfır fasiləyə nail olmağa imkan verir. Bununla belə, hətta çox populyar paketlərdən (NGINX və PHP-FPM kimi) istifadə edərkən belə, hər yerləşdirmə ilə səhvlərin artmasına səbəb olacaq çətinliklərlə qarşılaşa bilərsiniz...

Nəzəriyyə. Pod necə yaşayır

Artıq bir podun həyat dövrü haqqında ətraflı dərc etdik Bu məqalə. Baxılan mövzu kontekstində bizi aşağıdakılar maraqlandırır: pod dövlətə girdiyi anda Terminasiya, yeni sorğuların ona göndərilməsi dayandırılır (pod silindi xidmət üçün son nöqtələr siyahısından). Beləliklə, yerləşdirmə zamanı fasilələrin qarşısını almaq üçün tətbiqin dayandırılması problemini düzgün həll etməyimiz kifayətdir.

Siz həmçinin yadda saxlamalısınız ki, standart güzəşt müddətidir 30 saniyə: bundan sonra pod bağlanacaq və tətbiqin bu müddətə qədər bütün sorğuları emal etmək üçün vaxtı olmalıdır. Qeyd: baxmayaraq ki, 5-10 saniyədən çox vaxt aparan istənilən sorğu artıq problemlidir və zərif bağlanma artıq ona kömək etməyəcək...

Pod sona çatdıqda nə baş verdiyini daha yaxşı başa düşmək üçün aşağıdakı diaqrama baxmaq kifayətdir:

Kubernetes məsləhətləri və fəndləri: NGINX və PHP-FPM-də zərif bağlanma xüsusiyyətləri

A1, B1 - Ocağın vəziyyəti haqqında dəyişikliklərin qəbulu
A2 - Gediş SIGTERM
B2 - Son nöqtələrdən podun çıxarılması
B3 - Dəyişikliklərin qəbulu (son nöqtələrin siyahısı dəyişdi)
B4 - iptables qaydalarını yeniləyin

Nəzərə alın: son nöqtə podunun silinməsi və SIGTERM-in göndərilməsi ardıcıl deyil, paralel olaraq baş verir. Giriş nöqtələrinin yenilənmiş siyahısını dərhal qəbul etmədiyinə görə, müştərilərdən yeni sorğular poda göndəriləcək, bu da podun dayandırılması zamanı 500 xətaya səbəb olacaq. (bu məsələ ilə bağlı daha ətraflı material üçün biz tərcümə edilmişdir). Bu problemi aşağıdakı yollarla həll etmək lazımdır:

  • Əlaqəni Göndər: cavab başlıqlarını bağlayın (əgər bu, HTTP tətbiqinə aiddirsə).
  • Koda dəyişiklik etmək mümkün deyilsə, növbəti məqalədə zərif müddətin sonuna qədər sorğuları emal etməyə imkan verəcək bir həll təsvir olunur.

Nəzəriyyə. NGINX və PHP-FPM öz proseslərini necə dayandırır

NGINX

NGINX-dən başlayaq, çünki onunla hər şey az-çox aydındır. Nəzəriyyəyə girərək öyrənirik ki, NGINX-də bir master proses və bir neçə “işçi” var – bunlar müştəri sorğularını emal edən uşaq proseslərdir. Rahat bir seçim təqdim olunur: əmrdən istifadə etməklə nginx -s <SIGNAL> sürətli bağlanma və ya zərif bağlanma rejimində prosesləri dayandırın. Aydındır ki, bizi maraqlandıran sonuncu variantdır.

Sonra hər şey sadədir: əlavə etmək lazımdır prestop-çəngəl zərif bir bağlama siqnalı göndərəcək bir əmr. Bu, Yerləşdirmədə, konteyner blokunda edilə bilər:

       lifecycle:
          preStop:
            exec:
              command:
              - /usr/sbin/nginx
              - -s
              - quit

İndi pod bağlandıqda, NGINX konteyner qeydlərində aşağıdakıları görəcəyik:

2018/01/25 13:58:31 [notice] 1#1: signal 3 (SIGQUIT) received, shutting down
2018/01/25 13:58:31 [notice] 11#11: gracefully shutting down

Və bu, bizə lazım olanı ifadə edəcək: NGINX sorğuların tamamlanmasını gözləyir və sonra prosesi öldürür. Bununla belə, aşağıda biz hətta komanda ilə belə ümumi bir problemi nəzərdən keçirəcəyik nginx -s quit proses səhv başa çatır.

Və bu mərhələdə NGINX ilə işimiz bitdi: heç olmasa qeydlərdən hər şeyin lazım olduğu kimi işlədiyini başa düşə bilərsiniz.

PHP-FPM ilə nə iş var? Zərif bağlanmanı necə idarə edir? Gəlin bunu anlayaq.

PHP-FPM

PHP-FPM vəziyyətində bir az daha az məlumat var. Əgər diqqət yetirirsinizsə rəsmi təlimat PHP-FPM-ə görə, aşağıdakı POSIX siqnallarının qəbul edildiyini söyləyəcək:

  1. SIGINT, SIGTERM - sürətli bağlanma;
  2. SIGQUIT — zərif bağlanma (bizə lazım olan).

Qalan siqnallar bu tapşırıqda tələb olunmur, ona görə də onların təhlilini buraxacağıq. Prosesi düzgün şəkildə bitirmək üçün aşağıdakı preStop çəngəlini yazmalısınız:

        lifecycle:
          preStop:
            exec:
              command:
              - /bin/kill
              - -SIGQUIT
              - "1"

İlk baxışdan, hər iki konteynerdə zərif bir bağlama yerinə yetirmək üçün tələb olunan hər şey budur. Ancaq iş göründüyündən daha çətindir. Aşağıda zərif bağlanmanın işləmədiyi və yerləşdirmə zamanı layihənin qısa müddətli əlçatmazlığına səbəb olan iki hal var.

Təcrübə edin. Zərif bağlanma ilə mümkün problemlər

NGINX

Hər şeydən əvvəl, yadda saxlamaq faydalıdır: əmri yerinə yetirməklə yanaşı nginx -s quit Diqqət yetirməyə dəyər daha bir mərhələ var. NGINX-in hələ də SIGQUIT siqnalı əvəzinə SIGTERM göndərməsi ilə bağlı problemlə qarşılaşdıq və bu, sorğuların düzgün tamamlanmamasına səbəb oldu. Oxşar hallara rast gəlmək olar, məsələn, burada. Təəssüf ki, bu davranışın konkret səbəbini müəyyən edə bilmədik: NGINX versiyası ilə bağlı şübhə var idi, lakin təsdiqlənmədi. Simptom, NGINX konteyner qeydlərində mesajların müşahidə edilməsi idi: "10-də qalan №5 rozetkanı açın", bundan sonra pod dayandı.

Belə bir problemi, məsələn, bizə lazım olan Ingress cavablarından müşahidə edə bilərik:

Kubernetes məsləhətləri və fəndləri: NGINX və PHP-FPM-də zərif bağlanma xüsusiyyətləri
Yerləşdirmə zamanı status kodlarının göstəriciləri

Bu halda, biz Ingressin özündən sadəcə 503 səhv kodu alırıq: o, NGINX konteynerinə daxil ola bilmir, çünki o, artıq əlçatan deyil. NGINX ilə konteyner qeydlərinə baxsanız, onlar aşağıdakıları ehtiva edir:

[alert] 13939#0: *154 open socket #3 left in connection 16
[alert] 13939#0: *168 open socket #6 left in connection 13

Dayanma siqnalını dəyişdirdikdən sonra konteyner düzgün dayanmağa başlayır: bu, 503 səhvinin artıq müşahidə edilməməsi ilə təsdiqlənir.

Bənzər bir problemlə qarşılaşsanız, konteynerdə hansı dayanma siqnalının istifadə edildiyini və preStop çəngəlinin tam olaraq necə göründüyünü anlamaq mantiqidir. Tamamilə mümkündür ki, səbəb məhz bundadır.

PHP-FPM... və daha çox

PHP-FPM ilə bağlı problem bayağı bir şəkildə təsvir edilmişdir: o, uşaq proseslərin tamamlanmasını gözləmir, onları dayandırır, buna görə də yerləşdirmə və digər əməliyyatlar zamanı 502 xəta baş verir. 2005-ci ildən bugs.php.net saytında bir neçə səhv hesabatı var (məsələn burada и burada), bu problemi təsvir edir. Ancaq çox güman ki, qeydlərdə heç nə görməyəcəksiniz: PHP-FPM heç bir səhv və ya üçüncü tərəf bildirişləri olmadan öz prosesinin başa çatdığını elan edəcək.

Problemin özü daha az və ya çox dərəcədə tətbiqin özündən asılı ola biləcəyini və məsələn, monitorinqdə özünü göstərə bilməyəcəyini aydınlaşdırmağa dəyər. Əgər bununla qarşılaşsanız, ilk olaraq ağlınıza sadə bir həll gəlir: preStop çəngəl əlavə edin sleep(30). Bu, əvvəllər olan bütün sorğuları yerinə yetirməyə imkan verəcək (və biz pod artıq qadir Terminasiya) və 30 saniyədən sonra podun özü bir siqnalla bitəcək SIGTERM.

O çıxır ki, lifecycle üçün konteyner belə görünəcək:

    lifecycle:
      preStop:
        exec:
          command:
          - /bin/sleep
          - "30"

Ancaq 30 saniyə səbəbiylə sleep biz güclü biz yerləşdirmə vaxtını artıracağıq, çünki hər bir pod bağlanacaq minimum 30 saniyə, bu pisdir. Bununla bağlı nə etmək olar?

Müraciətin birbaşa icrasına cavabdeh olan tərəfə müraciət edək. Bizim vəziyyətimizdə belədir PHP-FPMHansı default olaraq öz uşaq proseslərinin icrasına nəzarət etmir: Əsas proses dərhal dayandırılır. Direktivdən istifadə edərək bu davranışı dəyişə bilərsiniz process_control_timeout, bu, uşaq prosesləri üçün masterdən siqnalları gözləmək üçün vaxt məhdudiyyətlərini təyin edir. Əgər dəyəri 20 saniyəyə təyin etsəniz, bu, konteynerdə işləyən sorğuların əksəriyyətini əhatə edəcək və onlar tamamlandıqdan sonra əsas prosesi dayandıracaq.

Bu biliklə son problemimizə qayıdaq. Qeyd edildiyi kimi, Kubernetes monolit platforma deyil: onun müxtəlif komponentləri arasında əlaqə müəyyən vaxt tələb edir. Bu, xüsusilə Ingresses və digər əlaqəli komponentlərin işini nəzərdən keçirdikdə doğrudur, çünki yerləşdirmə zamanı belə bir gecikmə səbəbindən 500 səhv artımını əldə etmək asandır. Məsələn, yuxarı axına sorğu göndərmə mərhələsində səhv baş verə bilər, lakin komponentlər arasında qarşılıqlı əlaqənin "vaxt gecikməsi" olduqca qısadır - bir saniyədən azdır.

Buna görə, Toplam artıq qeyd olunan direktivlə process_control_timeout üçün aşağıdakı tikintidən istifadə edə bilərsiniz lifecycle:

lifecycle:
  preStop:
    exec:
      command: ["/bin/bash","-c","/bin/sleep 1; kill -QUIT 1"]

Bu halda gecikməni komanda ilə kompensasiya edəcəyik sleep və yerləşdirmə müddətini çox artırmayın: 30 saniyə ilə bir saniyə arasında nəzərəçarpacaq fərq varmı?.. Əslində, bu process_control_timeoutlifecycle gecikmə zamanı yalnız “təhlükəsizlik şəbəkəsi” kimi istifadə olunur.

Ümumiyyətlə, təsvir edilən davranış və müvafiq həll yolu yalnız PHP-FPM-ə aid deyil. Digər dillərdən/çərçivələrdən istifadə edərkən oxşar vəziyyət bu və ya digər şəkildə yarana bilər. Zərif bağlanmanı başqa yollarla düzəldə bilmirsinizsə - məsələn, tətbiqin dayandırma siqnallarını düzgün işləməsi üçün kodu yenidən yazmaqla - təsvir olunan üsuldan istifadə edə bilərsiniz. Ən gözəli olmaya bilər, amma işləyir.

Təcrübə edin. Podun işini yoxlamaq üçün sınaq yükləyin

Yük testi konteynerin necə işlədiyini yoxlamağın yollarından biridir, çünki istifadəçilər sayta daxil olduqda bu prosedur onu real döyüş şəraitinə yaxınlaşdırır. Yuxarıdakı tövsiyələri sınamaq üçün istifadə edə bilərsiniz Yandex.Tankom: Bütün ehtiyaclarımızı mükəmməl şəkildə əhatə edir. Aşağıda Grafana və Yandex.Tank-ın qrafikləri sayəsində təcrübəmizdən aydın nümunə ilə sınaq keçirmək üçün məsləhətlər və tövsiyələr verilmişdir.

Ən əsası buradadır dəyişiklikləri addım-addım yoxlayın. Yeni bir düzəliş əlavə etdikdən sonra testi işə salın və nəticələrin son işə nisbətən dəyişib-dəyişmədiyinə baxın. Əks təqdirdə, səmərəsiz həlləri müəyyən etmək çətin olacaq və uzunmüddətli perspektivdə yalnız zərər verə bilər (məsələn, yerləşdirmə müddətini artırın).

Başqa bir nüans, onun dayandırılması zamanı konteyner jurnallarına baxmaqdır. Zərif bağlanma haqqında məlumat orada qeyd olunurmu? Digər resurslara (məsələn, qonşu PHP-FPM konteyneri) daxil olarkən qeydlərdə hər hansı səhvlər varmı? Tətbiqin özündə səhvlər (yuxarıda təsvir edilən NGINX ilə olduğu kimi)? Ümid edirəm ki, bu məqalədəki giriş məlumatları konteynerin dayandırılması zamanı nə baş verdiyini daha yaxşı anlamağa kömək edəcəkdir.

Beləliklə, ilk sınaq qaçışı olmadan baş tutdu lifecycle və proqram serveri üçün əlavə direktivlər olmadan (process_control_timeout PHP-FPM-də). Bu testin məqsədi səhvlərin təxmini sayını (və onların olub-olmadığını) müəyyən etmək idi. Həmçinin, əlavə məlumatlardan bilməlisiniz ki, hər pod üçün orta yerləşdirmə müddəti tam hazır olana qədər təxminən 5-10 saniyə idi. Nəticələr bunlardır:

Kubernetes məsləhətləri və fəndləri: NGINX və PHP-FPM-də zərif bağlanma xüsusiyyətləri

Yandex.Tank məlumat panelində yerləşdirmə zamanı baş verən və orta hesabla 502 saniyəyə qədər davam edən 5 səhvdən ibarət bir artım göstərilir. Ehtimal ki, bunun səbəbi köhnə pod-a olan mövcud sorğuların dayandırıldığı zaman dayandırılırdı. Bundan sonra, dayandırılmış NGINX konteynerinin nəticəsi olan 503 səhv ortaya çıxdı, bu da arxa tərəfə görə əlaqələri kəsdi (bu, Ingress-in ona qoşulmasına mane oldu).

Gəlin görək necə process_control_timeout PHP-FPM-də uşaq proseslərin tamamlanmasını gözləməyə kömək edəcək, yəni. belə səhvləri düzəldin. Bu direktivdən istifadə edərək yenidən yerləşdirin:

Kubernetes məsləhətləri və fəndləri: NGINX və PHP-FPM-də zərif bağlanma xüsusiyyətləri

500-cü yerləşdirmə zamanı artıq səhv yoxdur! Yerləşdirmə uğurludur, zərif bağlanma işləyir.

Bununla belə, vaxt gecikməsi səbəbindən əldə edə biləcəyimiz səhvlərin kiçik bir faizi olan Ingress konteynerləri ilə bağlı məsələni xatırlamağa dəyər. Onların qarşısını almaq üçün yalnız bir quruluş əlavə etmək qalır sleep və yerləşdirməni təkrarlayın. Ancaq bizim xüsusi vəziyyətimizdə heç bir dəyişiklik görünmədi (yenidən səhvlər yoxdur).

Nəticə

Prosesi zərif şəkildə dayandırmaq üçün tətbiqdən aşağıdakı davranışı gözləyirik:

  1. Bir neçə saniyə gözləyin və sonra yeni bağlantıları qəbul etməyi dayandırın.
  2. Bütün sorğuların tamamlanmasını və sorğuları yerinə yetirməyən bütün canlı bağlantıların bağlanmasını gözləyin.
  3. Prosesinizi bitirin.

Ancaq bütün proqramlar bu şəkildə işləyə bilməz. Kubernetes reallıqlarında problemin bir həlli:

  • bir neçə saniyə gözləyəcək bir pre-stop çəngəl əlavə etmək;
  • müvafiq parametrlər üçün backendimizin konfiqurasiya faylını öyrənmək.

NGINX nümunəsi aydın göstərir ki, hətta ilkin olaraq sonlandırma siqnallarını düzgün emal etməli olan proqram belə edə bilməz, ona görə də tətbiqin yerləşdirilməsi zamanı 500 səhvin olub olmadığını yoxlamaq vacibdir. Bu, həm də problemə daha geniş baxmağa və diqqəti tək bir pod və ya konteynerə deyil, bütövlükdə bütün infrastruktura nəzər salmağa imkan verir.

Test vasitəsi kimi siz Yandex.Tank-dan istənilən monitorinq sistemi ilə birlikdə istifadə edə bilərsiniz (bizim halda, test üçün Prometheus backend ilə Grafana-dan məlumatlar götürülüb). Zərif bağlanma ilə bağlı problemlər benchmarkın yarada biləcəyi ağır yüklər altında aydın görünür və monitorinq test zamanı və ya sonra vəziyyəti daha ətraflı təhlil etməyə kömək edir.

Məqalə ilə bağlı rəylərə cavab olaraq: qeyd etmək lazımdır ki, problemlər və həll yolları burada NGINX Ingress ilə bağlı təsvir edilmişdir. Digər hallarda, seriyanın aşağıdakı materiallarında nəzərdən keçirə biləcəyimiz başqa həllər də var.

PS

K8s məsləhətlər və fəndlər seriyasından digərləri:

Mənbə: www.habr.com

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