„Kubernetes“ patarimai ir gudrybės: grakštaus išjungimo ypatybės NGINX ir PHP-FPM

Tipiška sąlyga diegiant CI/CD Kubernetes: programa turi sugebėti nepriimti naujų klientų užklausų prieš visiškai sustodama, o svarbiausia – sėkmingai užbaigti esamas.

„Kubernetes“ patarimai ir gudrybės: grakštaus išjungimo ypatybės NGINX ir PHP-FPM

Šios sąlygos laikymasis leidžia pasiekti nulio prastovų diegimo metu. Tačiau net ir naudojant labai populiarius paketus (pvz., NGINX ir PHP-FPM), galite susidurti su sunkumais, dėl kurių kiekvieno diegimo metu daugės klaidų...

Teorija. Kaip ankštis gyvena

Mes jau išsamiai paskelbėme apie ankšties gyvavimo ciklą Šis straipsnis. Nagrinėjamos temos kontekste mus domina: tuo momentu, kai ankštis patenka į būseną Pasibaigiantis, naujos užklausos jai nebesiunčiamos (pod ištrintas iš paslaugos galinių taškų sąrašo). Taigi, norint išvengti prastovų diegimo metu, mums pakanka teisingai išspręsti programos sustabdymo problemą.

Taip pat turėtumėte atsiminti, kad numatytasis lengvatinis laikotarpis yra 30 sekundžių: po to ankštas bus nutrauktas ir paraiška turi turėti laiko apdoroti visas užklausas iki šio laikotarpio. Atkreipti dėmesį: nors bet koks prašymas, trunkantis ilgiau nei 5-10 sekundžių, jau yra problemiškas, o grakštus išjungimas jau nepadės...

Kad geriau suprastumėte, kas nutinka, kai podas nutrūksta, tiesiog pažiūrėkite į šią diagramą:

„Kubernetes“ patarimai ir gudrybės: grakštaus išjungimo ypatybės NGINX ir PHP-FPM

A1, B1 – Židinio būklės pokyčių gavimas
A2 – Išvykimas SIGTERM
B2 – ankšties pašalinimas iš galinių taškų
B3 – pakeitimų gavimas (pasikeitė galutinių taškų sąrašas)
B4 – atnaujinkite iptables taisykles

Atkreipkite dėmesį: galinio taško bloko ištrynimas ir SIGTERM siuntimas vyksta ne nuosekliai, o lygiagrečiai. Ir dėl to, kad Ingress ne iš karto negauna atnaujinto galinių taškų sąrašo, nauji klientų užklausos bus siunčiamos į podą, o tai sukels 500 klaidą nutraukiant podą. (Norėdami gauti išsamesnės medžiagos šiuo klausimu, mes išversta). Šią problemą reikia išspręsti šiais būdais:

  • Siųsti ryšį: uždarykite atsakymo antraštes (jei tai susiję su HTTP programa).
  • Jei kodo pakeisti neįmanoma, kitame straipsnyje aprašomas sprendimas, kuris leis apdoroti užklausas iki malonaus laikotarpio pabaigos.

Teorija. Kaip NGINX ir PHP-FPM nutraukia savo procesus

nginx

Pradėkime nuo NGINX, nes su juo viskas daugiau ar mažiau akivaizdu. Pasinerdami į teoriją sužinome, kad NGINX turi vieną pagrindinį procesą ir kelis „darbuotojus“ – tai antriniai procesai, apdorojantys klientų užklausas. Pateikiama patogi parinktis: naudojant komandą nginx -s <SIGNAL> nutraukti procesus greito išjungimo arba grakštaus išjungimo režimu. Akivaizdu, kad būtent pastarasis variantas mus domina.

Tada viskas paprasta: reikia pridėti preStop-kablys komanda, kuri atsiųs grakštų išjungimo signalą. Tai galima padaryti diegimo lange, konteinerio bloke:

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

Dabar, kai podas išsijungs, NGINX konteinerio žurnaluose pamatysime šiuos dalykus:

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

Ir tai reikš tai, ko mums reikia: NGINX laukia, kol užklausos bus baigtos, ir užmuša procesą. Tačiau toliau mes taip pat apsvarstysime dažną problemą, dėl kurios netgi su komanda nginx -s quit procesas baigiasi neteisingai.

Ir šiame etape su NGINX jau baigėme: bent jau iš žurnalų galima suprasti, kad viskas veikia taip, kaip turėtų.

Koks reikalas su PHP-FPM? Kaip jis valdo grakštų išjungimą? Išsiaiškinkime.

PHP-FPM

PHP-FPM atveju informacijos yra šiek tiek mažiau. Jei sutelksite dėmesį į oficialus vadovas pagal PHP-FPM sakys, kad priimami šie POSIX signalai:

  1. SIGINT, SIGTERM - greitas išjungimas;
  2. SIGQUIT - grakštus išjungimas (ko mums reikia).

Likę signalai šioje užduotyje nereikalingi, todėl jų analizės praleisime. Norėdami teisingai užbaigti procesą, turėsite parašyti šį preStop kabliuką:

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

Iš pirmo žvilgsnio tai yra viskas, ko reikia norint atlikti grakštų abiejų konteinerių išjungimą. Tačiau užduotis yra sunkesnė, nei atrodo. Toliau pateikiami du atvejai, kai grakštus išjungimas neveikė ir sukėlė trumpalaikį projekto nepasiekiamumą diegimo metu.

Praktika. Galimos problemos dėl grakštaus išjungimo

nginx

Visų pirma, pravartu atsiminti: be komandos vykdymo nginx -s quit Yra dar vienas etapas, į kurį verta atkreipti dėmesį. Susidūrėme su problema, kai NGINX vis tiek siųs SIGTERM vietoj SIGQUIT signalo, todėl užklausos nebuvo tinkamai užbaigtos. Panašių atvejų galima rasti, pvz. čia. Deja, mums nepavyko nustatyti konkrečios tokio elgesio priežasties: buvo įtarimas dėl NGINX versijos, tačiau tai nepasitvirtino. Požymis buvo tas, kad NGINX konteinerio žurnaluose buvo pastebėti pranešimai: "atviras lizdas Nr. 10 liko 5 jungtyje", po kurio ankštis sustojo.

Tokią problemą galime pastebėti, pavyzdžiui, iš mums reikalingų atsakymų apie Ingress:

„Kubernetes“ patarimai ir gudrybės: grakštaus išjungimo ypatybės NGINX ir PHP-FPM
Būsenos kodų indikatoriai diegimo metu

Šiuo atveju iš pačios Ingress gauname tik 503 klaidos kodą: jis negali pasiekti NGINX konteinerio, nes jis nebepasiekiamas. Jei žiūrite į konteinerių žurnalus su NGINX, juose yra:

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

Pakeitus stabdymo signalą, konteineris pradeda teisingai sustoti: tai patvirtina faktas, kad 503 klaida nebepastebi.

Jei susiduriate su panašia problema, prasminga išsiaiškinti, koks stabdymo signalas naudojamas konteineryje ir kaip tiksliai atrodo preStop kablys. Visai įmanoma, kad priežastis slypi būtent tame.

PHP-FPM... ir daugiau

PHP-FPM problema aprašyta trivialiai: nelaukia, kol baigsis antriniai procesai, juos nutraukia, todėl diegimo ir kitų operacijų metu atsiranda 502 klaidos. Nuo 2005 m. bugs.php.net yra keletas klaidų pranešimų (pvz., čia и čia), kuriame aprašoma ši problema. Bet greičiausiai žurnaluose nieko nematysite: PHP-FPM praneš apie proceso pabaigą be klaidų ar trečiųjų šalių pranešimų.

Verta paaiškinti, kad pati problema gali daugiau ar mažiau priklausyti nuo pačios programos ir gali nepasireikšti, pavyzdžiui, stebint. Jei su tuo susiduriate, pirmiausia į galvą ateina paprastas sprendimas: pridėkite preStop kabliuką sleep(30). Tai leis jums įvykdyti visas anksčiau buvusias užklausas (o naujų nepriimame, nes pod jau galintis Pasibaigiantis), o po 30 sekundžių pats podas baigsis signalu SIGTERM.

Pasirodo, kad lifecycle konteineris atrodys taip:

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

Tačiau dėl 30 sek sleep mes stipriai padidinsime diegimo laiką, nes kiekvienas blokas bus nutrauktas minimalus 30 sekundžių, tai yra blogai. Ką galima dėl to padaryti?

Kreipkimės į už tiesioginį paraiškos vykdymą atsakingą šalį. Mūsų atveju taip yra PHP-FPMKuris pagal numatytuosius nustatymus nestebi savo antrinių procesų vykdymo: pagrindinis procesas nutraukiamas nedelsiant. Šį elgesį galite pakeisti naudodami direktyvą process_control_timeout, kuriame nurodomi laiko apribojimai antriniams procesams laukti signalų iš pagrindinio kompiuterio. Jei nustatysite reikšmę 20 sekundžių, tai apims daugumą sudėtiniame rodinyje vykdomų užklausų ir sustabdys pagrindinį procesą, kai tik jos bus baigtos.

Su šiomis žiniomis grįžkime prie paskutinės problemos. Kaip minėta, „Kubernetes“ nėra monolitinė platforma: komunikacija tarp skirtingų jos komponentų užtrunka šiek tiek laiko. Tai ypač aktualu, kai atsižvelgiame į „Ingresses“ ir kitų susijusių komponentų veikimą, nes dėl tokio vėlavimo diegimo metu lengva gauti 500 klaidų antplūdį. Pavyzdžiui, klaida gali įvykti siunčiant užklausą prieš srovę, tačiau komponentų sąveikos „laiko delsa“ yra gana trumpa - mažiau nei sekundė.

Todėl Iš viso su jau minėta direktyva process_control_timeout galite naudoti šią konstrukciją lifecycle:

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

Tokiu atveju uždelsimą kompensuosime komanda sleep ir labai nepailginkite diegimo laiko: ar yra pastebimas skirtumas tarp 30 sekundžių ir vienos?.. Tiesą sakant, tai yra process_control_timeoutIr lifecycle naudojamas tik kaip „apsauginis tinklas“ vėlavimo atveju.

Apskritai aprašytas elgesys ir atitinkamas sprendimas galioja ne tik PHP-FPM. Panaši situacija vienaip ar kitaip gali susiklostyti naudojant kitas kalbas/rėmus. Jei negalite ištaisyti grakštaus išjungimo kitais būdais, pavyzdžiui, perrašydami kodą, kad programa tinkamai apdorotų nutraukimo signalus, galite naudoti aprašytą metodą. Gal ir ne pati gražiausia, bet veikia.

Praktika. Apkrovos bandymas, skirtas patikrinti ankšties veikimą

Apkrovos testavimas yra vienas iš būdų patikrinti, kaip konteineris veikia, nes ši procedūra priartina jį prie realių kovos sąlygų, kai vartotojai lankosi svetainėje. Norėdami išbandyti aukščiau pateiktas rekomendacijas, galite naudoti Yandex.Tankom: Jis puikiai patenkina visus mūsų poreikius. Toliau pateikiami patarimai ir rekomendacijos, kaip atlikti bandymus, pateikdami aiškų pavyzdį iš mūsų patirties, kurią sudaro „Grafana“ ir „Yandex.Tank“ grafikai.

Čia svarbiausia žingsnis po žingsnio patikrinkite pakeitimus. Pridėję naują pataisą, paleiskite testą ir pažiūrėkite, ar rezultatai pasikeitė, palyginti su paskutiniu paleidimu. Priešingu atveju bus sunku nustatyti neefektyvius sprendimus, o ilgainiui tai gali tik pakenkti (pavyzdžiui, pailginti diegimo laiką).

Kitas niuansas yra pažvelgti į konteinerio žurnalus jo nutraukimo metu. Ar ten įrašyta informacija apie grakštų išjungimą? Ar prisijungiant prie kitų išteklių (pavyzdžiui, į gretimą PHP-FPM konteinerį) žurnaluose yra klaidų? Klaidos pačioje programoje (kaip aukščiau aprašytu atveju su NGINX)? Tikiuosi, kad šio straipsnio įvadinė informacija padės geriau suprasti, kas nutinka konteineriui jo nutraukimo metu.

Taigi, pirmasis bandomasis važiavimas įvyko be lifecycle ir be papildomų direktyvų taikomųjų programų serveriui (process_control_timeout PHP-FPM). Šio testo tikslas buvo nustatyti apytikslį klaidų skaičių (ir ar jų yra). Be to, iš papildomos informacijos turėtumėte žinoti, kad vidutinis kiekvieno bloko diegimo laikas buvo apie 5–10 sekundžių, kol jis buvo visiškai paruoštas. Rezultatai yra:

„Kubernetes“ patarimai ir gudrybės: grakštaus išjungimo ypatybės NGINX ir PHP-FPM

Yandex.Tank informaciniame skydelyje rodomas 502 klaidų šuolis, kuris įvyko diegimo metu ir truko vidutiniškai iki 5 sekundžių. Tikriausiai taip buvo todėl, kad esamos užklausos į seną grupę buvo nutraukiamos, kai ji buvo nutraukta. Po to pasirodė 503 klaidos, kurias lėmė sustabdytas NGINX konteineris, dėl kurio taip pat nutrūko ryšiai dėl backend (dėl to Ingress negalėjo prisijungti prie jo).

Pažiūrėkime kaip process_control_timeout PHP-FPM padės sulaukti antrinių procesų pabaigos, t.y. ištaisyti tokias klaidas. Iš naujo įdiekite naudodami šią direktyvą:

„Kubernetes“ patarimai ir gudrybės: grakštaus išjungimo ypatybės NGINX ir PHP-FPM

500-ojo diegimo metu klaidų nebelieka! Diegimas yra sėkmingas, dailiai veikia.

Tačiau verta prisiminti „Ingress“ konteinerių problemą – nedidelį procentą klaidų, kurias galime gauti dėl laiko delsos. Norint jų išvengti, belieka pridėti struktūrą su sleep ir pakartokite diegimą. Tačiau konkrečiu mūsų atveju pokyčių nebuvo matoma (vėlgi jokių klaidų).

išvada

Norėdami užbaigti procesą maloniai, tikimės, kad programa elgsis toliau:

  1. Palaukite kelias sekundes ir nustokite priimti naujus ryšius.
  2. Palaukite, kol bus baigtos visos užklausos, ir uždarykite visus palaikomus ryšius, kurie nevykdo užklausų.
  3. Užbaikite procesą.

Tačiau ne visos programos gali veikti tokiu būdu. Vienas iš Kubernetes realybės problemos sprendimų yra:

  • pridedant kabliuką prieš sustojimą, kuris palauks kelias sekundes;
  • tiria atitinkamų parametrų konfigūracijos failą.

NGINX pavyzdys aiškiai parodo, kad net programa, kuri iš pradžių turėtų tinkamai apdoroti nutraukimo signalus, gali to nepadaryti, todėl labai svarbu patikrinti, ar diegimo metu nėra 500 klaidų. Tai taip pat leidžia pažvelgti į problemą plačiau ir nesikoncentruoti į vieną podą ar konteinerį, o žiūrėti į visą infrastruktūrą kaip visumą.

Kaip testavimo įrankį galite naudoti „Yandex.Tank“ kartu su bet kokia stebėjimo sistema (mūsų atveju duomenys buvo paimti iš „Grafana“ su „Prometheus“ pagrindine programa bandymui). Grakštaus išjungimo problemos aiškiai matomos esant didelėms apkrovoms, kurias gali generuoti etalonas, o stebėjimas padeda išsamiau išanalizuoti situaciją testo metu arba po jo.

Atsakant į atsiliepimus apie straipsnį: verta paminėti, kad čia aprašytos problemos ir sprendimai, susiję su NGINX Ingress. Kitais atvejais yra kitų sprendimų, kuriuos galime apsvarstyti toliau pateiktoje serijos medžiagoje.

PS

Kiti iš K8s patarimų ir gudrybių serijos:

Šaltinis: www.habr.com

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