Kubernetes-vinkkejä ja temppuja: sulavan sammutuksen ominaisuudet NGINX:ssä ja PHP-FPM:ssä

Tyypillinen ehto CI/CD:n käyttöönotossa Kubernetesissa: sovelluksen on kyettävä olemaan hyväksymättä uusia asiakaspyyntöjä ennen kuin se pysähtyy kokonaan, ja mikä tärkeintä, suoritettava onnistuneesti olemassa olevat.

Kubernetes-vinkkejä ja temppuja: sulavan sammutuksen ominaisuudet NGINX:ssä ja PHP-FPM:ssä

Tämän ehdon noudattaminen mahdollistaa nollakatkon käytön käyttöönoton aikana. Kuitenkin, jopa käytettäessä erittäin suosittuja paketteja (kuten NGINX ja PHP-FPM), voit kohdata vaikeuksia, jotka johtavat virhemäärään jokaisen käyttöönoton yhteydessä...

Teoria. Kuinka kakku elää

Olemme jo julkaisseet yksityiskohtaisesti podin elinkaaresta Tässä artikkelissa. Käsiteltävän aiheen yhteydessä olemme kiinnostuneita seuraavista: sillä hetkellä, kun pod tulee tilaan päättämisestä, uusien pyyntöjen lähettäminen sille lakkaa (pod poistettu palvelun päätepisteiden luettelosta). Näin ollen, jotta vältytään käyttöönoton aikana, riittää, että ratkaisemme sovelluksen pysäyttämisen oikein.

Muista myös, että oletusarvoinen lisäaika on 30 sekuntia: tämän jälkeen pod lopetetaan ja sovelluksella on oltava aikaa käsitellä kaikki pyynnöt ennen tätä ajanjaksoa. Huomata: vaikka kaikki yli 5-10 sekuntia kestävät pyynnöt ovat jo ongelmallisia, eikä siro sammutus enää auta sitä...

Jotta ymmärrät paremmin, mitä tapahtuu, kun pod päättyy, katso vain seuraavaa kaaviota:

Kubernetes-vinkkejä ja temppuja: sulavan sammutuksen ominaisuudet NGINX:ssä ja PHP-FPM:ssä

A1, B1 - Muutosten vastaanottaminen tulisijan tilaan
A2 - Lähtö SIGTERM
B2 - Pod-yksikön poistaminen päätepisteistä
B3 - Muutosten vastaanottaminen (päätepisteiden luettelo on muuttunut)
B4 - Päivitä iptables-säännöt

Huomaa: päätepistekotelon poistaminen ja SIGTERM:n lähettäminen ei tapahdu peräkkäin, vaan rinnakkain. Ja koska Ingress ei saa heti päivitettyä päätepisteiden luetteloa, uudet pyynnöt asiakkailta lähetetään podiin, mikä aiheuttaa 500 virheen podin lopettamisen aikana. (Jos haluat tarkempaa materiaalia tästä aiheesta, me käännetty). Tämä ongelma on ratkaistava seuraavilla tavoilla:

  • Lähetä yhteys: sulje vastausotsikot (jos tämä koskee HTTP-sovellusta).
  • Jos koodiin ei ole mahdollista tehdä muutoksia, seuraavassa artikkelissa kuvataan ratkaisu, jonka avulla voit käsitellä pyyntöjä lisäajan loppuun asti.

Teoria. Kuinka NGINX ja PHP-FPM lopettavat prosessinsa

nginx

Aloitetaan NGINX:stä, koska sen kanssa kaikki on enemmän tai vähemmän selvää. Sukeltaessamme teoriaan opimme, että NGINX:llä on yksi pääprosessi ja useita "työntekijöitä" - nämä ovat aliprosesseja, jotka käsittelevät asiakkaiden pyyntöjä. Kätevä vaihtoehto tarjotaan: komennon käyttäminen nginx -s <SIGNAL> lopettaa prosessit joko nopeassa sammutustilassa tai sulavassa sammutustilassa. On selvää, että jälkimmäinen vaihtoehto kiinnostaa meitä.

Sitten kaikki on yksinkertaista: sinun on lisättävä preStop-koukku komento, joka lähettää kauniin sammutussignaalin. Tämä voidaan tehdä Käyttöönotossa, konttilohkossa:

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

Nyt, kun pod sammuu, näemme NGINX-säiliölokeissa seuraavan:

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 tämä tarkoittaa sitä, mitä tarvitsemme: NGINX odottaa pyyntöjen valmistumista ja lopettaa sitten prosessin. Kuitenkin alla tarkastellaan myös yleistä ongelmaa, jonka vuoksi jopa komennon kanssa nginx -s quit prosessi päättyy väärin.

Ja tässä vaiheessa NGINX on valmis: ainakin lokeista voi ymmärtää, että kaikki toimii niin kuin pitää.

Mikä on PHP-FPM ongelma? Miten se käsittelee siron sammutuksen? Selvitetään se.

PHP-FPM

PHP-FPM:n tapauksessa tietoa on hieman vähemmän. Jos keskityt virallinen käsikirja PHP-FPM:n mukaan se sanoo, että seuraavat POSIX-signaalit hyväksytään:

  1. SIGINT, SIGTERM - nopea sammutus;
  2. SIGQUIT - siro sammutus (mitä tarvitsemme).

Jäljellä olevia signaaleja ei tarvita tässä tehtävässä, joten jätämme niiden analyysin pois. Päättääksesi prosessin oikein, sinun on kirjoitettava seuraava preStop-koukku:

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

Ensi silmäyksellä tämä on kaikki mitä tarvitaan sulavan sammutuksen suorittamiseen molemmissa säiliöissä. Tehtävä on kuitenkin vaikeampi kuin miltä näyttää. Alla on kaksi tapausta, joissa siro sammutus ei toiminut ja aiheutti projektin lyhytaikaisen epäkäytettävyyden käyttöönoton aikana.

Harjoitella. Mahdollisia ongelmia sulavan sammutuksen yhteydessä

nginx

Ensinnäkin on hyödyllistä muistaa: komennon suorittamisen lisäksi nginx -s quit On vielä yksi vaihe, johon kannattaa kiinnittää huomiota. Havaitsimme ongelman, jossa NGINX lähetti edelleen SIGTERM-signaalin SIGQUIT-signaalin sijaan, jolloin pyyntöjä ei suoritettu oikein. Vastaavia tapauksia löytyy mm. täällä. Valitettavasti emme pystyneet määrittämään tämän toiminnan tarkkaa syytä: NGINX-versiota epäiltiin, mutta sitä ei vahvistettu. Oire oli, että NGINX-säilön lokeissa havaittiin viestejä: "avoin pistoke #10 jäljellä liittimessä 5", jonka jälkeen pod pysähtyi.

Voimme havaita tällaisen ongelman esimerkiksi tarvitsemamme Ingressin vastauksista:

Kubernetes-vinkkejä ja temppuja: sulavan sammutuksen ominaisuudet NGINX:ssä ja PHP-FPM:ssä
Tilakoodien ilmaisimet käyttöönoton aikana

Tässä tapauksessa saamme vain 503-virhekoodin itse Ingressiltä: se ei voi käyttää NGINX-säilöä, koska se ei ole enää käytettävissä. Jos tarkastelet NGINX:n konttilokeja, ne sisältävät seuraavat tiedot:

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

Pysäytyssignaalin vaihtamisen jälkeen säiliö alkaa pysähtyä oikein: tämän vahvistaa se, että 503-virhettä ei enää havaita.

Jos kohtaat samanlaisen ongelman, on järkevää selvittää, mitä pysäytyssignaalia säiliössä käytetään ja miltä preStop-koukku tarkalleen näyttää. On täysin mahdollista, että syy on juuri tässä.

PHP-FPM... ja paljon muuta

PHP-FPM:n ongelma on kuvattu triviaalisesti: se ei odota lapsiprosessien valmistumista, vaan lopettaa ne, minkä vuoksi käyttöönoton ja muiden toimintojen aikana tapahtuu 502 virhettä. Bugs.php.net-sivustolla on useita virheraportteja vuodesta 2005 lähtien (esim täällä и täällä), joka kuvaa tätä ongelmaa. Mutta et todennäköisesti näe mitään lokeissa: PHP-FPM ilmoittaa prosessinsa valmistumisesta ilman virheitä tai kolmannen osapuolen ilmoituksia.

On syytä selventää, että itse ongelma voi riippua enemmän tai vähemmän itse sovelluksesta eikä välttämättä ilmene esimerkiksi seurannassa. Jos kohtaat sen, mieleesi tulee ensin yksinkertainen ratkaisu: lisää preStop-koukku sleep(30). Sen avulla voit suorittaa kaikki aiemmin tehdyt pyynnöt (emmekä hyväksy uusia, koska pod jo kykenevä päättämisestä), ja 30 sekunnin kuluttua itse pod päättyy signaaliin SIGTERM.

On käynyt ilmi, että lifecycle Säiliö näyttää tältä:

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

Kuitenkin, koska 30-sekuntia sleep me voimakkaasti pidennämme käyttöönottoaikaa, koska jokainen pod lopetetaan vähimmäis- 30 sekuntia, mikä on huono. Mitä tälle voidaan tehdä?

Käännytään hakemuksen suorasta suorituksesta vastaavan tahon puoleen. Meidän tapauksessamme on PHP-FPMJoka oletuksena ei valvo aliprosessiensa suorittamista: Pääprosessi lopetetaan välittömästi. Voit muuttaa tätä käyttäytymistä ohjeen avulla process_control_timeout, joka määrittää aikarajat, joille lapsiprosessit odottavat signaaleja isännältä. Jos asetat arvoksi 20 sekuntia, tämä kattaa suurimman osan säilössä suoritettavista kyselyistä ja pysäyttää pääprosessin, kun ne on suoritettu.

Tämän tiedon avulla palataan viimeiseen ongelmaamme. Kuten mainittiin, Kubernetes ei ole monoliittinen alusta: viestintä sen eri komponenttien välillä kestää jonkin aikaa. Tämä on erityisen totta, kun tarkastelemme Ingressien ja muiden niihin liittyvien komponenttien toimintaa, koska tällaisen viiveen vuoksi käyttöönottohetkellä on helppo saada 500 virheen nousu. Virhe voi tapahtua esimerkiksi pyynnön lähetysvaiheessa ylävirtaan, mutta komponenttien välisen vuorovaikutuksen "aikaviive" on melko lyhyt - alle sekunti.

siksi Yhteensä jo mainitun direktiivin kanssa process_control_timeout voit käyttää seuraavaa rakennetta lifecycle:

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

Tässä tapauksessa kompensoimme viiveen komennolla sleep ja älä pidennä käyttöönottoaikaa merkittävästi: onko 30 sekunnin ja yhden välillä huomattava ero?.. Itse asiassa se on process_control_timeoutJa lifecycle käytetään vain "turvaverkkona" viiveen varalta.

Yleisesti ottaen, kuvattu toiminta ja vastaava kiertotapa eivät koske vain PHP-FPM:ää. Samanlainen tilanne voi tavalla tai toisella syntyä käytettäessä muita kieliä/kehyksiä. Jos et voi korjata siroa sammutusta muilla tavoilla - esimerkiksi kirjoittamalla koodia uudelleen niin, että sovellus käsittelee lopetussignaalit oikein - voit käyttää kuvattua menetelmää. Se ei ehkä ole kaunein, mutta se toimii.

Harjoitella. Kuormitustestaus podin toiminnan tarkistamiseksi

Kuormitustestaus on yksi tavoista tarkistaa, miten kontti toimii, koska tämä menettely tuo sen lähemmäksi todellisia taisteluolosuhteita, kun käyttäjät vierailevat sivustolla. Voit testata yllä olevia suosituksia käyttämällä Yandex.Tankom: Se kattaa kaikki tarpeemme täydellisesti. Seuraavassa on vinkkejä ja suosituksia testauksen suorittamiseen selkeällä esimerkillä kokemuksestamme Grafanan ja Yandex.Tankin kaavioiden ansiosta.

Tärkeintä tässä on tarkista muutokset askel askeleelta. Kun olet lisännyt uuden korjauksen, suorita testi ja katso, ovatko tulokset muuttuneet edelliseen ajoon verrattuna. Muuten tehottomien ratkaisujen tunnistaminen on vaikeaa, ja pitkällä aikavälillä siitä voi olla vain haittaa (esimerkiksi pidentää käyttöönottoaikaa).

Toinen vivahde on tarkastella konttilokeja sen lopettamisen aikana. Tallennetaanko sinne tietoja sulavasta sammutuksesta? Onko lokeissa virheitä käytettäessä muita resursseja (esimerkiksi viereiseen PHP-FPM-säilöön)? Virheet itse sovelluksessa (kuten yllä kuvatun NGINX:n tapauksessa)? Toivon, että tämän artikkelin johdantotiedot auttavat sinua ymmärtämään paremmin, mitä säilölle tapahtuu sen sulkemisen aikana.

Ensimmäinen koeajo tehtiin siis ilman lifecycle ja ilman lisäohjeita sovelluspalvelimelle (process_control_timeout PHP-FPM:ssä). Tämän testin tarkoituksena oli tunnistaa virheiden likimääräinen määrä (ja onko niitä). Lisätietojen perusteella sinun pitäisi myös tietää, että kunkin podin keskimääräinen käyttöönottoaika oli noin 5-10 sekuntia, kunnes se oli täysin valmis. Tulokset ovat:

Kubernetes-vinkkejä ja temppuja: sulavan sammutuksen ominaisuudet NGINX:ssä ja PHP-FPM:ssä

Yandex.Tank-tietopaneelissa näkyy 502 virhepiikki, joka tapahtui käyttöönottohetkellä ja kesti keskimäärin 5 sekuntia. Oletettavasti tämä johtui siitä, että vanhaan podiin tehdyt pyynnöt lopetettiin, kun se lopetettiin. Tämän jälkeen ilmestyi 503 virhettä, mikä johtui pysähtyneestä NGINX-säiliöstä, joka myös katkaisi yhteydet taustajärjestelmän takia (joka esti Ingressia muodostamasta yhteyttä siihen).

Katsotaanpa miten process_control_timeout PHP-FPM:ssä auttaa meitä odottamaan lapsiprosessien valmistumista, ts. korjata tällaiset virheet. Ota uudelleen käyttöön tällä ohjeella:

Kubernetes-vinkkejä ja temppuja: sulavan sammutuksen ominaisuudet NGINX:ssä ja PHP-FPM:ssä

Ei enää virheitä 500. käyttöönoton aikana! Käyttöönotto onnistuu, sulatus toimii sulavasti.

On kuitenkin syytä muistaa Ingress-säilöihin liittyvä ongelma, pieni prosenttiosuus virheistä, jotka voimme saada aikaviiveen vuoksi. Niiden välttämiseksi tarvitsee vain lisätä rakenne sleep ja toista käyttöönotto. Meidän tapauksessamme ei kuitenkaan näkynyt muutoksia (taaskaan ei virheitä).

Johtopäätös

Odotamme sovellukselta seuraavan toiminnan lopettaaksemme prosessin sulavasti:

  1. Odota muutama sekunti ja lopeta sitten uusien yhteyksien hyväksyminen.
  2. Odota, että kaikki pyynnöt valmistuvat, ja sulje kaikki ylläpitävät yhteydet, jotka eivät suorita pyyntöjä.
  3. Lopeta prosessisi.

Kaikki sovellukset eivät kuitenkaan voi toimia tällä tavalla. Yksi ratkaisu Kubernetes-todellisuuksien ongelmaan on:

  • lisäämällä pysäytyskoukun, joka odottaa muutaman sekunnin;
  • tutkimalla taustajärjestelmämme asetustiedostoa sopivien parametrien saamiseksi.

NGINX-esimerkki tekee selväksi, että edes sovellus, jonka pitäisi alun perin käsitellä lopetussignaalit oikein, ei välttämättä tee sitä, joten on tärkeää tarkistaa 500 virhettä sovelluksen käyttöönoton aikana. Näin voit myös tarkastella ongelmaa laajemmin eikä keskittyä yhteen koteloon tai säiliöön, vaan tarkastella koko infrastruktuuria kokonaisuutena.

Testaustyökaluna voit käyttää Yandex.Tankia minkä tahansa valvontajärjestelmän kanssa (tapauksessamme tiedot otettiin Grafanasta Prometheus-taustaohjelmalla testiä varten). Graceful shutdown -ongelmat näkyvät selvästi benchmarkin synnyttämien raskaiden kuormien alla, ja seuranta auttaa analysoimaan tilannetta tarkemmin testin aikana tai sen jälkeen.

Vastauksena palautteeseen artikkelista: on syytä mainita, että ongelmat ja ratkaisut on kuvattu tässä suhteessa NGINX Ingressiin. Muissa tapauksissa on olemassa muita ratkaisuja, joita voimme harkita sarjan seuraavissa materiaaleissa.

PS.

Muita K8s-vinkkejä ja temppuja -sarjasta:

Lähde: will.com

Lisää kommentti