Kubernetese näpunäited ja nipid: graatsilise väljalülitamise funktsioonid NGINX-is ja PHP-FPM-is

Tüüpiline tingimus CI/CD juurutamisel Kubernetesis: rakendus peab suutma mitte vastu võtta uusi kliendipäringuid enne täielikku peatumist ja mis kõige tähtsam – olemasolevad edukalt lõpule viia.

Kubernetese näpunäited ja nipid: graatsilise väljalülitamise funktsioonid NGINX-is ja PHP-FPM-is

Selle tingimuse järgimine võimaldab teil juurutamise ajal saavutada nulli seisakuid. Kuid isegi väga populaarsete komplektide (nagu NGINX ja PHP-FPM) kasutamisel võib tekkida raskusi, mis põhjustavad iga juurutusega vigade arvu...

teooria. Kuidas kaun elab

Oleme juba üksikasjalikult avaldanud kauna elutsükli kohta Selle artikli. Vaadeldava teema kontekstis huvitab meid: hetk, mil pod olekusse jõuab Lõpetamine, enam ei saadeta talle uusi taotlusi (pod eemaldatud teenuse lõpp-punktide loendist). Seega, et vältida seisakuid juurutamise ajal, piisab, kui lahendame rakenduse õige peatamise probleemi.

Samuti peaksite meeles pidama, et vaikimisi ajapikendusperiood on 30 sekundit: pärast seda pod suletakse ja rakendusel peab olema aega kõigi taotluste töötlemiseks enne seda perioodi. Märkus: kuigi kõik taotlused, mis võtavad rohkem kui 5-10 sekundit, on juba problemaatilised ja graatsiline väljalülitamine ei aita seda enam...

Et paremini mõista, mis juhtub, kui pod lõpetatakse, vaadake lihtsalt järgmist diagrammi:

Kubernetese näpunäited ja nipid: graatsilise väljalülitamise funktsioonid NGINX-is ja PHP-FPM-is

A1, B1 – kolde seisukorra muutuste vastuvõtmine
A2 - Väljumine SIGTERM
B2 – Podi eemaldamine lõpp-punktidest
B3 – muudatuste vastuvõtmine (lõpp-punktide loend on muutunud)
B4 – värskendage iptablesi reegleid

Pange tähele: lõpp-punkti kustutamine ja SIGTERM-i saatmine ei toimu järjestikku, vaid paralleelselt. Ja kuna Ingress ei saa kohe värskendatud lõpp-punktide loendit, saadetakse klientidelt uued päringud podi, mis põhjustab podi lõpetamisel tõrke 500 (üksikasjalikuma materjali saamiseks selle probleemi kohta me tõlgitud). See probleem tuleb lahendada järgmistel viisidel:

  • Saada ühendus: sulgege vastuse päised (kui see puudutab HTTP-rakendust).
  • Kui koodis pole võimalik muudatusi teha, siis järgmises artiklis kirjeldatakse lahendust, mis võimaldab taotlusi töödelda kuni graatsiaperioodi lõpuni.

teooria. Kuidas NGINX ja PHP-FPM oma protsessid lõpetavad

nginx

Alustame NGINX-iga, kuna sellega on kõik enam-vähem ilmselge. Teooriasse sukeldudes saame teada, et NGINX-il on üks põhiprotsess ja mitu "töötajat" - need on alamprotsessid, mis töötlevad klientide taotlusi. Pakutakse mugavat võimalust: käsu kasutamine nginx -s <SIGNAL> lõpetada protsessid kas kiire väljalülitamise või graatsilise väljalülitamise režiimis. Ilmselgelt huvitab meid just viimane variant.

Siis on kõik lihtne: peate lisama preStop-konks käsk, mis saadab graatsilise väljalülitussignaali. Seda saab teha juurutamise konteineriplokis:

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

Nüüd, kui pod lülitub välja, näeme NGINX-i konteineri logides järgmist:

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

Ja see tähendab seda, mida me vajame: NGINX ootab, kuni päringud on lõpule viidud, ja seejärel lõpetab protsessi. Kuid allpool käsitleme ka levinud probleemi, mille tõttu isegi käsuga nginx -s quit protsess lõpeb valesti.

Ja selles etapis oleme NGINX-iga valmis: vähemalt logide põhjal saate aru, et kõik töötab nii, nagu peab.

Mis asi on PHP-FPM-iga? Kuidas see graatsilist väljalülitamist käsitleb? Selgitame välja.

PHP-FPM

PHP-FPM puhul on infot veidi vähem. Kui keskenduda ametlik käsiraamat PHP-FPM järgi ütleb see, et aktsepteeritakse järgmisi POSIX-i signaale:

  1. SIGINT, SIGTERM - kiire väljalülitamine;
  2. SIGQUIT — graatsiline seiskamine (mida me vajame).

Ülejäänud signaale pole selles ülesandes vaja, seega jätame nende analüüsi vahele. Protsessi õigeks lõpetamiseks peate kirjutama järgmise preStop-konksu:

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

Esmapilgul on see kõik, mis on mõlema konteineri graatsiliseks väljalülitamiseks vajalik. Ülesanne on aga raskem, kui tundub. Allpool on toodud kaks juhtumit, mille puhul graatsiline seiskamine ei toiminud ja põhjustas projekti lühiajalise kättesaamatuse juurutamise ajal.

Harjuta. Graatsilise väljalülitamise võimalikud probleemid

nginx

Kõigepealt on kasulik meeles pidada: lisaks käsu täitmisele nginx -s quit On veel üks etapp, millele tasub tähelepanu pöörata. Avastasime probleemi, mille korral NGINX saadaks SIGQUIT-signaali asemel endiselt SIGTERM-i, mistõttu päringuid ei täideta õigesti. Sarnaseid juhtumeid võib leida näiteks siin. Kahjuks ei suutnud me kindlaks teha selle käitumise konkreetset põhjust: NGINX-i versiooni suhtes oli kahtlus, kuid see ei leidnud kinnitust. Sümptom oli see, et NGINX-i konteineri logides täheldati sõnumeid: "avatud pistikupesa nr 10 on jäänud ühendusse 5", mille järel kaun peatus.

Sellist probleemi saame jälgida näiteks Ingressi vastustest, mida vajame:

Kubernetese näpunäited ja nipid: graatsilise väljalülitamise funktsioonid NGINX-is ja PHP-FPM-is
Olekukoodide indikaatorid juurutamise ajal

Sel juhul saame Ingressilt endalt vaid veakoodi 503: see ei pääse juurde NGINX-i konteinerile, kuna see pole enam juurdepääsetav. Kui vaatate konteineri logisid NGINX-iga, sisaldavad need järgmist:

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

Pärast seiskamissignaali muutmist hakkab konteiner õigesti seisma: seda kinnitab asjaolu, et tõrget 503 enam ei täheldata.

Sarnase probleemi korral on mõttekas välja mõelda, millist seiskamissignaali konteineris kasutatakse ja kuidas preStop konks täpselt välja näeb. On täiesti võimalik, et põhjus peitub just selles.

PHP-FPM... ja palju muud

PHP-FPM-i probleemi kirjeldatakse triviaalselt: see ei oota alamprotsesside valmimist, vaid lõpetab need, mistõttu ilmneb juurutamisel ja muudel toimingutel 502 viga. Alates 2005. aastast on saidil bugs.php.net mitu veateadet (nt siin и siin), mis seda probleemi kirjeldab. Kuid tõenäoliselt ei näe te logides midagi: PHP-FPM teatab oma protsessi lõpuleviimisest ilma vigade või kolmandate osapoolte teavitusteta.

Tasub selgitada, et probleem ise võib sõltuda vähemal või suuremal määral rakendusest endast ega pruugi avalduda näiteks seires. Kui sellega kokku puutute, tuleb esmalt meelde lihtne lahendus: lisage preStop-konks sleep(30). See võimaldab teil täita kõik varasemad taotlused (ja me ei võta uusi vastu, kuna pod juba võimeline Lõpetamine) ja 30 sekundi pärast lõpeb pod ise signaaliga SIGTERM.

Selgub, et lifecycle konteiner näeb välja selline:

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

Kuid tänu 30-sekundilisele sleep me tugevalt pikendame juurutamisaega, kuna iga pod lõpetatakse minimaalselt 30 sekundit, mis on halb. Mida saab selle vastu ette võtta?

Pöördume taotluse otsese täitmise eest vastutava poole poole. Meie puhul on PHP-FPMMis vaikimisi ei jälgi oma alamprotsesside täitmist: Põhiprotsess lõpetatakse kohe. Saate seda käitumist direktiivi abil muuta process_control_timeout, mis määrab ajapiirangud, mille jooksul alamprotsessid peavad ootama juhtseadmelt signaale. Kui määrate väärtuseks 20 sekundit, katab see enamiku konteineris töötavatest päringutest ja peatab põhiprotsessi, kui need on lõpetatud.

Selle teadmisega pöördume tagasi oma viimase probleemi juurde. Nagu mainitud, ei ole Kubernetes monoliitne platvorm: suhtlus selle erinevate komponentide vahel võtab aega. See kehtib eriti siis, kui võtame arvesse Ingresside ja muude seotud komponentide toimimist, kuna sellise viivituse tõttu juurutamise ajal on lihtne saada 500 viga. Näiteks võib tõrge ilmneda päringu saatmise etapis ülesvoolu, kuid komponentide interaktsiooni "ajavahe" on üsna lühike - vähem kui sekund.

Seetõttu Kokku juba mainitud direktiiviga process_control_timeout jaoks võite kasutada järgmist konstruktsiooni lifecycle:

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

Sel juhul kompenseerime viivituse käsuga sleep ja ärge suurendage juurutusaega oluliselt: kas 30 sekundi ja ühe vahel on märgatav erinevus?.. Tegelikult on see process_control_timeoutJa lifecycle kasutatakse ainult "turvavõrguna" viivituse korral.

Üldiselt kirjeldatud käitumine ja vastav lahendus ei kehti ainult PHP-FPM puhul. Sarnane olukord võib nii või teisiti tekkida ka teiste keelte/raamistike kasutamisel. Kui te ei saa graatsilist seiskamist muul viisil parandada - näiteks kirjutades koodi ümber nii, et rakendus töötleks lõpetamissignaale õigesti -, võite kasutada kirjeldatud meetodit. See ei pruugi olla kõige ilusam, kuid see töötab.

Harjuta. Koormustestimine podi töö kontrollimiseks

Koormustest on üks viise konteineri toimimise kontrollimiseks, kuna see protseduur lähendab selle tegelikele lahingutingimustele, kui kasutajad seda saiti külastavad. Ülaltoodud soovituste testimiseks võite kasutada Yandex.Tankom: See katab suurepäraselt kõik meie vajadused. Järgnevalt on toodud näpunäiteid ja soovitusi testimise läbiviimiseks, kasutades selget näidet meie kogemusest tänu Grafana ja Yandex.Tanki enda graafikutele.

Kõige tähtsam on siin kontrollige muudatusi samm-sammult. Pärast uue paranduse lisamist käivitage test ja vaadake, kas tulemused on võrreldes viimase käivitamisega muutunud. Vastasel juhul on ebaefektiivseid lahendusi raske tuvastada ja pikemas perspektiivis võib see ainult kahju teha (näiteks pikendada kasutuselevõtu aega).

Teine nüanss on vaadata konteineri palke selle lõpetamise ajal. Kas teave graatsilise seiskamise kohta on seal salvestatud? Kas muudele ressurssidele (näiteks naabruses asuvale PHP-FPM konteinerile) juurdepääsul on logides vigu? Kas rakenduses endas on vead (nagu ülalkirjeldatud NGINX-i puhul)? Loodan, et selle artikli sissejuhatav teave aitab teil paremini mõista, mis konteineriga selle lõpetamise ajal juhtub.

Niisiis, esimene proovisõit toimus ilma lifecycle ja ilma lisajuhisteta rakendusserveri jaoks (process_control_timeout PHP-FPM-is). Selle testi eesmärk oli tuvastada ligikaudne vigade arv (ja kas neid on). Lisaks peaksite lisateabe põhjal teadma, et iga mooduli keskmine juurutusaeg oli umbes 5–10 sekundit, kuni see täielikult valmis oli. Tulemused on järgmised:

Kubernetese näpunäited ja nipid: graatsilise väljalülitamise funktsioonid NGINX-is ja PHP-FPM-is

Yandex.Tank teabepaneel näitab 502 vea tippu, mis ilmnes juurutamise ajal ja kestis keskmiselt kuni 5 sekundit. Arvatavasti oli see seetõttu, et olemasolevad päringud vanale kaustale lõpetati selle lõpetamise ajal. Pärast seda ilmus 503 tõrget, mis tulenes seisatud NGINX konteinerist, mis katkestas ka ühendused taustaprogrammi tõttu (mis takistas Ingressil sellega ühendust luua).

Vaatame, kuidas process_control_timeout PHP-FPM-is aitab meil oodata alamprotsesside valmimist, st. parandage sellised vead. Uuesti juurutamine selle direktiivi abil:

Kubernetese näpunäited ja nipid: graatsilise väljalülitamise funktsioonid NGINX-is ja PHP-FPM-is

500. kasutuselevõtu ajal pole enam vigu! Kasutuselevõtt on edukas, väljalülitamine toimib sujuvalt.

Siiski tasub meeles pidada probleemi Ingressi konteineritega, mis on väike protsent tõrgetest, mis võivad ilmneda ajavahe tõttu. Nende vältimiseks ei jää üle muud, kui lisada struktuur sleep ja korrake kasutuselevõttu. Meie konkreetsel juhul ei olnud aga muudatusi näha (jällegi, vigu ei olnud).

Järeldus

Protsessi graatsiliseks lõpetamiseks ootame rakenduselt järgmist.

  1. Oodake mõni sekund ja seejärel lõpetage uute ühenduste vastuvõtmine.
  2. Oodake, kuni kõik päringud on lõpule viidud, ja sulgege kõik alalhoidvad ühendused, mis taotlusi ei täida.
  3. Lõpetage oma protsess.

Kuid mitte kõik rakendused ei saa sel viisil töötada. Kubernetese tegelikkuses on probleemi üks lahendus:

  • peatumiseelse konksu lisamine, mis ootab mõne sekundi;
  • meie taustaprogrammi konfiguratsioonifaili uurimine sobivate parameetrite leidmiseks.

NGINX-i näide teeb selgeks, et isegi rakendus, mis peaks algselt lõpetamissignaale õigesti töötlema, ei pruugi seda teha, seega on oluline rakenduse juurutamise ajal kontrollida 500 viga. See võimaldab vaadata probleemi ka laiemalt ja mitte keskenduda ühele kambrile või konteinerile, vaid vaadata kogu infrastruktuuri tervikuna.

Testimisvahendina saate Yandex.Tanki kasutada koos mis tahes seiresüsteemiga (meie puhul võeti testi jaoks andmed Prometheuse taustaprogrammiga Grafanast). Graatsilise väljalülitamise probleemid on selgelt nähtavad suurte koormuste korral, mida etalon võib tekitada, ja monitooring aitab testi ajal või pärast seda olukorda üksikasjalikumalt analüüsida.

Vastuseks tagasisidele artiklile: tasub mainida, et probleeme ja lahendusi kirjeldatakse siin seoses NGINX Ingressiga. Muudel juhtudel on ka teisi lahendusi, mida võime kaaluda sarja järgmistes materjalides.

PS

Muud K8s näpunäidete ja nippide seeriast:

Allikas: www.habr.com

Lisa kommentaar