Kubernetes ráð og brellur: eiginleikar tignarlegrar lokunar í NGINX og PHP-FPM

Dæmigert ástand við innleiðingu CI/CD í Kubernetes: forritið verður að geta ekki samþykkt nýjar beiðnir viðskiptavina áður en það hættir alveg, og síðast en ekki síst, klára núverandi.

Kubernetes ráð og brellur: eiginleikar tignarlegrar lokunar í NGINX og PHP-FPM

Samræmi við þetta skilyrði gerir þér kleift að ná engum niður í miðbæ meðan á dreifingu stendur. Hins vegar, jafnvel þegar þú notar mjög vinsæla búnta (eins og NGINX og PHP-FPM), geturðu lent í erfiðleikum sem munu leiða til mikillar villna við hverja dreifingu...

Kenning. Hvernig fræbelgur lifir

Við höfum þegar birt ítarlega um lífsferil fræbelgs þessi grein. Í samhengi við efnið sem er til skoðunar höfum við áhuga á eftirfarandi: á því augnabliki þegar belgurinn fer inn í ríkið Uppsögn, nýjar beiðnir hætta að vera sendar til þess (pod fjarlægður af listanum yfir endapunkta þjónustunnar). Þannig að til að forðast niður í miðbæ meðan á dreifingu stendur er nóg fyrir okkur að leysa vandamálið við að stöðva forritið á réttan hátt.

Þú ættir líka að muna að sjálfgefna fresturinn er 30 sekúndur: eftir þetta verður podinn hætt og umsóknin verður að hafa tíma til að vinna úr öllum beiðnum fyrir þetta tímabil. Athugið: þó að allar beiðnir sem taka meira en 5-10 sekúndur séu nú þegar erfiðar, og þokkafull lokun mun ekki lengur hjálpa því...

Til að skilja betur hvað gerist þegar belg lýkur skaltu bara skoða eftirfarandi skýringarmynd:

Kubernetes ráð og brellur: eiginleikar tignarlegrar lokunar í NGINX og PHP-FPM

A1, B1 - Tekið á móti breytingum um ástand aflinn
A2 - Brottför SIGTERM
B2 - Fjarlægja belg frá endapunktum
B3 - Að taka á móti breytingum (listi yfir endapunkta hefur breyst)
B4 - Uppfærðu iptables reglur

Vinsamlegast athugið: að eyða endapunkti pod og senda SIGTERM gerist ekki í röð, heldur samhliða. Og vegna þess að Ingress fær ekki strax uppfærðan lista yfir endapunkta, verða nýjar beiðnir frá viðskiptavinum sendar til belgsins, sem mun valda 500 villu við lokun belgs. (fyrir ítarlegra efni um þetta mál, við þýtt). Þetta vandamál þarf að leysa á eftirfarandi hátt:

  • Senda tengingu: lokaðu svarhausum (ef þetta varðar HTTP forrit).
  • Ef það er ekki hægt að gera breytingar á kóðanum, þá lýsir eftirfarandi grein lausn sem gerir þér kleift að vinna úr beiðnum til loka tignartímabilsins.

Kenning. Hvernig NGINX og PHP-FPM hætta ferlum sínum

nginx

Byrjum á NGINX, þar sem allt er meira og minna augljóst með því. Með því að kafa ofan í kenninguna komumst við að því að NGINX hefur eitt aðalferli og nokkra „starfsmenn“ - þetta eru undirferli sem vinna úr beiðnum viðskiptavina. Þægilegur valkostur er til staðar: að nota skipunina nginx -s <SIGNAL> stöðva ferla annað hvort í hraðri lokun eða þokkafullri lokunarham. Augljóslega er það síðari kosturinn sem vekur áhuga okkar.

Þá er allt einfalt: þú þarft að bæta við preStop-krókur skipun sem mun senda tignarlegt lokunarmerki. Þetta er hægt að gera í Deployment, í gámablokkinni:

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

Nú, þegar belgurinn slekkur á sér, munum við sjá eftirfarandi í NGINX gámaskránum:

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

Og þetta mun þýða það sem við þurfum: NGINX bíður eftir að beiðnum ljúki og drepur síðan ferlið. Hins vegar hér að neðan munum við einnig íhuga algengt vandamál vegna þess, jafnvel með skipuninni nginx -s quit ferlið lýkur á rangan hátt.

Og á þessu stigi erum við búin með NGINX: að minnsta kosti af annálunum geturðu skilið að allt virkar eins og það ætti að gera.

Hvað er málið með PHP-FPM? Hvernig höndlar það þokkafulla lokun? Við skulum reikna það út.

PHP-FPM

Þegar um PHP-FPM er að ræða eru aðeins minni upplýsingar. Ef þú einbeitir þér að opinber handbók samkvæmt PHP-FPM mun það segja að eftirfarandi POSIX merki séu samþykkt:

  1. SIGINT, SIGTERM - hröð lokun;
  2. SIGQUIT — þokkafull lokun (það sem við þurfum).

Merkin sem eftir eru eru ekki nauðsynleg í þessu verkefni, svo við munum sleppa greiningu þeirra. Til að ljúka ferlinu á réttan hátt þarftu að skrifa eftirfarandi preStop krók:

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

Við fyrstu sýn er þetta allt sem þarf til að framkvæma þokkafulla lokun í báðum gámunum. Hins vegar er verkefnið erfiðara en það virðist. Hér að neðan eru tvö tilvik þar sem þokkafull lokun virkaði ekki og olli skammtíma óaðgengi á verkefninu meðan á dreifingu stóð.

Æfðu þig. Möguleg vandamál með þokkafullri lokun

nginx

Fyrst af öllu er gagnlegt að muna: auk þess að framkvæma skipunina nginx -s quit Það er enn eitt stigið sem vert er að gefa gaum að. Við lentum í vandræðum þar sem NGINX myndi samt senda SIGTERM í stað SIGQUIT merkisins, sem olli því að beiðnum var ekki lokið á réttan hátt. Svipuð tilvik má finna, t.d. hér. Því miður gátum við ekki ákvarðað ástæðuna fyrir þessari hegðun: það var grunur um NGINX útgáfuna, en það var ekki staðfest. Einkennin voru að skilaboð sáust í NGINX gámaskránum: "opna fals #10 eftir í tengingu 5", eftir það hætti belgurinn.

Við getum fylgst með slíku vandamáli, til dæmis út frá svörunum á Ingress sem við þurfum:

Kubernetes ráð og brellur: eiginleikar tignarlegrar lokunar í NGINX og PHP-FPM
Vísbendingar um stöðukóða við uppsetningu

Í þessu tilfelli fáum við bara 503 villukóða frá Ingress sjálfu: það hefur ekki aðgang að NGINX gámnum, þar sem það er ekki lengur aðgengilegt. Ef þú skoðar gámaskránna með NGINX, þá innihalda þeir eftirfarandi:

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

Eftir að stöðvunarmerkinu hefur verið breytt, byrjar gámurinn að stöðvast rétt: þetta er staðfest af því að 503 villan sést ekki lengur.

Ef þú lendir í svipuðu vandamáli er skynsamlegt að finna út hvaða stöðvunarmerki er notað í ílátinu og hvernig preStop krókurinn lítur út nákvæmlega. Það er vel hugsanlegt að ástæðan liggi einmitt í þessu.

PHP-FPM... og fleira

Vandamálinu með PHP-FPM er lýst á léttvægan hátt: það bíður ekki eftir að undirferli lýkur, það lýkur þeim, þess vegna koma 502 villur fram við uppsetningu og aðrar aðgerðir. Það eru nokkrar villutilkynningar á bugs.php.net síðan 2005 (td hér и hér), sem lýsir þessu vandamáli. En þú munt líklegast ekki sjá neitt í annálunum: PHP-FPM mun tilkynna að ferlinu sé lokið án villna eða tilkynninga frá þriðja aðila.

Rétt er að gera grein fyrir því að vandamálið sjálft getur verið að meira eða minna leyti háð forritinu sjálfu og getur ekki komið fram, til dæmis í eftirliti. Ef þú lendir í því kemur fyrst upp í hugann einföld lausn: bættu við preStop krók með sleep(30). Það gerir þér kleift að klára allar beiðnir sem voru áður (og við tökum ekki við nýjum, þar sem pod þegar fær um Uppsögn), og eftir 30 sekúndur mun belgurinn sjálfur enda með merki SIGTERM.

Það kemur í ljós að lifecycle fyrir ílátið mun líta svona út:

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

Hins vegar vegna 30 sek sleep við erum sterklega við munum auka dreifingartímann þar sem hverri belg verður hætt lágmarki 30 sekúndur, sem er slæmt. Hvað er hægt að gera í þessu?

Snúum okkur að þeim aðila sem ber ábyrgð á beinni framkvæmd umsóknar. Í okkar tilviki er það PHP-FPMHvaða sjálfgefið fylgist ekki með framkvæmd barnaferla sinna: Aðalferlinu er hætt strax. Þú getur breytt þessari hegðun með tilskipuninni process_control_timeout, sem tilgreinir tímamörk fyrir undirferli til að bíða eftir merkjum frá skipstjóra. Ef þú stillir gildið á 20 sekúndur mun þetta ná yfir flestar fyrirspurnir sem keyra í ílátinu og stöðva aðalferlið þegar þeim er lokið.

Með þessari þekkingu skulum við hverfa aftur að síðasta vandamáli okkar. Eins og fram hefur komið er Kubernetes ekki einhæfur vettvangur: samskipti milli mismunandi íhluta þess taka nokkurn tíma. Þetta á sérstaklega við þegar við lítum á rekstur Ingresses og annarra tengdra íhluta, þar sem vegna slíkrar töfar á uppsetningu er auðvelt að fá 500 villur. Til dæmis getur villa komið upp á því stigi að senda beiðni til andstreymis, en „töf“ á samskiptum milli íhluta er frekar stutt - innan við sekúnda.

Þess vegna, Samtals með áðurnefndri tilskipun process_control_timeout þú getur notað eftirfarandi smíði fyrir lifecycle:

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

Í þessu tilviki munum við bæta upp seinkunina með skipuninni sleep og eykur ekki dreifingartímann til muna: er áberandi munur á 30 sekúndum og einni? .. Reyndar er það process_control_timeoutOg lifecycle eingöngu notað sem „öryggisnet“ ef um töf er að ræða.

Almennt séð lýst hegðun og samsvarandi lausn á ekki aðeins við um PHP-FPM. Svipuð staða getur komið upp með einum eða öðrum hætti þegar önnur tungumál/ramma eru notuð. Ef þú getur ekki lagað þokkafulla lokun á annan hátt - til dæmis með því að endurskrifa kóðann þannig að forritið vinni rétt frá stöðvunarmerkjum - geturðu notað aðferðina sem lýst er. Það er kannski ekki það fallegasta, en það virkar.

Æfðu þig. Hleðsluprófun til að athuga virkni belgsins

Hleðsluprófun er ein leiðin til að athuga hvernig gámurinn virkar, þar sem þessi aðferð færir hann nær raunverulegum bardagaaðstæðum þegar notendur heimsækja síðuna. Til að prófa ofangreindar ráðleggingar geturðu notað Yandex.Tankom: Það mætir öllum þörfum okkar fullkomlega. Eftirfarandi eru ábendingar og ráðleggingar til að framkvæma prófanir með skýru dæmi frá reynslu okkar þökk sé línuritum Grafana og Yandex.Tank sjálfs.

Það mikilvægasta hér er athugaðu breytingar skref fyrir skref. Eftir að þú hefur bætt við nýrri lagfæringu skaltu keyra prófið og athuga hvort niðurstöðurnar hafi breyst miðað við síðustu keyrslu. Annars verður erfitt að bera kennsl á árangurslausar lausnir og til lengri tíma litið getur það aðeins skaðað (td aukið dreifingartíma).

Annar blæbrigði er að skoða gámaskrárnar meðan á uppsögninni stendur. Eru upplýsingar um þokkafulla lokun skráðar þar? Eru einhverjar villur í annálunum þegar aðgangur er að öðrum auðlindum (til dæmis í nærliggjandi PHP-FPM gám)? Villur í forritinu sjálfu (eins og í tilvikinu með NGINX sem lýst er hér að ofan)? Ég vona að kynningarupplýsingarnar úr þessari grein hjálpi þér að skilja betur hvað verður um ílátið við lok þess.

Þannig að fyrsta prufukeyrslan fór fram án lifecycle og án viðbótarfyrirmæla fyrir forritaþjóninn (process_control_timeout í PHP-FPM). Tilgangur þessarar prófunar var að bera kennsl á áætlaða fjölda villna (og hvort þær séu einhverjar). Einnig, út frá viðbótarupplýsingum, ættir þú að vita að meðaldreifingartími fyrir hvern belg var um 5-10 sekúndur þar til hann var alveg tilbúinn. Úrslitin eru:

Kubernetes ráð og brellur: eiginleikar tignarlegrar lokunar í NGINX og PHP-FPM

Yandex.Tank upplýsingaspjaldið sýnir aukningu upp á 502 villur, sem áttu sér stað við uppsetningu og stóðu að meðaltali í allt að 5 sekúndur. Væntanlega var þetta vegna þess að verið var að slíta fyrirliggjandi beiðnum til gamla belgsins þegar það var sagt upp. Eftir þetta birtust 503 villur, sem var afleiðing af stöðvuðu NGINX gámi, sem einnig sleppti tengingum vegna bakendans (sem kom í veg fyrir að Ingress gæti tengst honum).

Við skulum sjá hvernig process_control_timeout í PHP-FPM mun hjálpa okkur að bíða eftir að barnaferlum ljúki, þ.e. leiðrétta slíkar villur. Endurdreifa með þessari tilskipun:

Kubernetes ráð og brellur: eiginleikar tignarlegrar lokunar í NGINX og PHP-FPM

Það eru engar fleiri villur á 500. dreifingunni! Uppsetningin er vel heppnuð, tignarleg lokun virkar.

Hins vegar er vert að muna eftir vandamálinu með Ingress gáma, lítið hlutfall villna sem við gætum fengið vegna tímatöf. Til að forðast þá er allt sem eftir er að bæta við uppbyggingu með sleep og endurtaka dreifinguna. Hins vegar, í sérstöku tilviki okkar, voru engar breytingar sýnilegar (aftur, engar villur).

Ályktun

Til að ljúka ferlinu með þokkabót, búumst við við eftirfarandi hegðun frá forritinu:

  1. Bíddu í nokkrar sekúndur og hættu síðan að samþykkja nýjar tengingar.
  2. Bíddu eftir að allar beiðnir ljúki og lokaðu öllum Keepalive tengingum sem eru ekki að framkvæma beiðnir.
  3. Ljúktu ferlinu þínu.

Hins vegar geta ekki öll forrit virkað á þennan hátt. Ein lausn á vandamálinu í raunveruleika Kubernetes er:

  • bæta við forstöðvunarkrók sem mun bíða í nokkrar sekúndur;
  • að rannsaka stillingarskrá bakendans okkar fyrir viðeigandi breytur.

Dæmið með NGINX gerir það ljóst að jafnvel forrit sem ætti upphaflega að vinna úr stöðvunarmerkjum á réttan hátt gæti ekki gert það, svo það er mikilvægt að athuga hvort 500 villur séu til staðar við uppsetningu forritsins. Þetta gerir þér einnig kleift að skoða vandamálið víðar og ekki einblína á einn belg eða ílát, heldur líta á allan innviði í heild.

Sem prófunartæki geturðu notað Yandex.Tank í tengslum við hvaða vöktunarkerfi sem er (í okkar tilviki voru gögn tekin úr Grafana með Prometheus bakenda fyrir prófið). Vandamál með þokkafullri lokun eru greinilega sýnileg undir miklu álagi sem viðmiðið getur myndað og eftirlit hjálpar til við að greina ástandið nánar á meðan eða eftir prófunina.

Til að bregðast við athugasemdum um greinina: það er rétt að minnast á að vandamálum og lausnum er lýst hér í tengslum við NGINX Ingress. Fyrir önnur tilvik eru aðrar lausnir sem við gætum íhugað í eftirfarandi efnum í röðinni.

PS

Annað úr K8s tips & tricks röðinni:

Heimild: www.habr.com

Bæta við athugasemd