Këshilla dhe truket e Kubernetes: veçoritë e mbylljes së këndshme në NGINX dhe PHP-FPM

Një kusht tipik kur zbatoni CI/CD në Kubernetes: aplikacioni duhet të jetë në gjendje të mos pranojë kërkesat e reja të klientit përpara se të ndalojë plotësisht, dhe më e rëndësishmja, të përfundojë me sukses ato ekzistuese.

Këshilla dhe truket e Kubernetes: veçoritë e mbylljes së këndshme në NGINX dhe PHP-FPM

Pajtueshmëria me këtë kusht ju lejon të arrini kohë joproduktive gjatë vendosjes. Megjithatë, edhe kur përdorni paketa shumë të njohura (si NGINX dhe PHP-FPM), mund të hasni vështirësi që do të çojnë në një rritje të gabimeve me çdo vendosje...

Teoria. Si jeton pod

Ne kemi publikuar tashmë në detaje rreth ciklit jetësor të një pod Ky artikull. Në kuadër të temës në shqyrtim na intereson sa vijon: në momentin kur pod hyn në gjendje ndërprerjen, kërkesat e reja ndalojnë së dërguari tek ai (pod hiqen nga lista e pikave fundore për shërbimin). Kështu, për të shmangur kohën e ndërprerjes gjatë vendosjes, mjafton që ne të zgjidhim problemin e ndalimit të saktë të aplikacionit.

Ju gjithashtu duhet të mbani mend se periudha e paracaktuar e mospagimit është 30 sekonda: pas kësaj, pod do të ndërpritet dhe aplikacioni duhet të ketë kohë për të përpunuar të gjitha kërkesat përpara kësaj periudhe. Shënim: megjithëse çdo kërkesë që zgjat më shumë se 5-10 sekonda është tashmë problematike dhe mbyllja e këndshme nuk do ta ndihmojë më atë...

Për të kuptuar më mirë se çfarë ndodh kur një pod përfundon, thjesht shikoni diagramin e mëposhtëm:

Këshilla dhe truket e Kubernetes: veçoritë e mbylljes së këndshme në NGINX dhe PHP-FPM

A1, B1 - Marrja e ndryshimeve në lidhje me gjendjen e vatrës
A2 - Nisja SIGTERM
B2 - Heqja e një pod nga pikat fundore
B3 - Marrja e ndryshimeve (lista e pikave përfundimtare ka ndryshuar)
B4 - Përditëso rregullat iptables

Ju lutemi vini re: fshirja e pod-it të pikës fundore dhe dërgimi i SIGTERM nuk ndodh në mënyrë sekuenciale, por paralelisht. Dhe për shkak të faktit se Ingress nuk merr menjëherë listën e përditësuar të pikave përfundimtare, kërkesat e reja nga klientët do të dërgohen në pod, gjë që do të shkaktojë një gabim 500 gjatë përfundimit të pod (për material më të detajuar për këtë çështje, ne përkthyer). Ky problem duhet të zgjidhet në mënyrat e mëposhtme:

  • Dërgo lidhjen: mbylli në titujt e përgjigjes (nëse kjo ka të bëjë me një aplikacion HTTP).
  • Nëse nuk është e mundur të bëhen ndryshime në kod, atëherë artikulli vijues përshkruan një zgjidhje që do t'ju lejojë të përpunoni kërkesat deri në fund të periudhës së këndshme.

Teoria. Si përfundojnë proceset e tyre NGINX dhe PHP-FPM

nginx

Le të fillojmë me NGINX, pasi gjithçka është pak a shumë e dukshme me të. Duke u zhytur në teori, mësojmë se NGINX ka një proces master dhe disa "punëtorë" - këto janë procese të fëmijëve që përpunojnë kërkesat e klientit. Ofrohet një opsion i përshtatshëm: përdorimi i komandës nginx -s <SIGNAL> përfundoni proceset ose në modalitetin e mbylljes së shpejtë ose të fikjes së këndshme. Natyrisht, është opsioni i fundit që na intereson.

Atëherë gjithçka është e thjeshtë: ju duhet të shtoni në preStop-grep një komandë që do të dërgojë një sinjal të këndshëm mbylljeje. Kjo mund të bëhet në Deployment, në bllokun e kontejnerit:

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

Tani, kur pod mbyllet, ne do të shohim sa vijon në regjistrat e kontejnerëve NGINX:

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

Dhe kjo do të thotë atë që na nevojitet: NGINX pret që kërkesat të plotësohen dhe më pas vret procesin. Sidoqoftë, më poshtë do të shqyrtojmë gjithashtu një problem të zakonshëm për shkak të të cilit, edhe me komandën nginx -s quit procesi përfundon gabimisht.

Dhe në këtë fazë kemi mbaruar me NGINX: të paktën nga regjistrat mund të kuptoni se gjithçka po funksionon ashtu siç duhet.

Cila është puna me PHP-FPM? Si e trajton mbylljen e këndshme? Le ta kuptojmë.

PHP-FPM

Në rastin e PHP-FPM, ka pak më pak informacion. Nëse fokusoheni në manual zyrtar sipas PHP-FPM, do të thotë se sinjalet e mëposhtme POSIX pranohen:

  1. SIGINT, SIGTERM — mbyllje e shpejtë;
  2. SIGQUIT — mbyllje e këndshme (ajo që na nevojitet).

Sinjalet e mbetura nuk kërkohen në këtë detyrë, kështu që ne do të heqim analizën e tyre. Për të përfunduar saktë procesin, do t'ju duhet të shkruani grepin e mëposhtëm preStop:

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

Në shikim të parë, kjo është gjithçka që kërkohet për të kryer një mbyllje të këndshme në të dy kontejnerët. Megjithatë, detyra është më e vështirë se sa duket. Më poshtë janë dy raste në të cilat mbyllja e këndshme nuk funksionoi dhe shkaktoi mosdisponueshmëri afatshkurtër të projektit gjatë vendosjes.

Praktikoni. Probleme të mundshme me mbylljen e këndshme

nginx

Para së gjithash, është e dobishme të mbani mend: përveç ekzekutimit të komandës nginx -s quit Ekziston edhe një fazë tjetër që ia vlen t'i kushtohet vëmendje. Ne hasëm një problem ku NGINX do të dërgonte ende SIGTERM në vend të sinjalit SIGQUIT, duke bërë që kërkesat të mos plotësoheshin saktë. Raste të ngjashme mund të gjenden, për shembull, këtu. Fatkeqësisht, nuk ishim në gjendje të përcaktonim arsyen specifike të kësaj sjelljeje: ekzistonte një dyshim për versionin NGINX, por ai nuk u konfirmua. Simptoma ishte se mesazhet u vëzhguan në regjistrat e kontejnerëve NGINX: "Hapni folenë #10 të mbetur në lidhjen 5", pas së cilës pod u ndal.

Ne mund të vëzhgojmë një problem të tillë, për shembull, nga përgjigjet në Ingress që na duhen:

Këshilla dhe truket e Kubernetes: veçoritë e mbylljes së këndshme në NGINX dhe PHP-FPM
Treguesit e kodeve të statusit në kohën e vendosjes

Në këtë rast, ne marrim vetëm një kod gabimi 503 nga vetë Ingress: ai nuk mund të hyjë në kontejnerin NGINX, pasi nuk është më i aksesueshëm. Nëse shikoni regjistrat e kontejnerëve me NGINX, ato përmbajnë sa vijon:

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

Pas ndryshimit të sinjalit të ndalimit, kontejneri fillon të ndalojë saktë: kjo konfirmohet nga fakti se gabimi 503 nuk vërehet më.

Nëse hasni një problem të ngjashëm, ka kuptim të kuptoni se çfarë sinjali ndalimi përdoret në enë dhe si duket saktësisht grepa preStop. Është shumë e mundur që arsyeja qëndron pikërisht në këtë.

PHP-FPM... dhe më shumë

Problemi me PHP-FPM përshkruhet në një mënyrë të parëndësishme: ai nuk pret për përfundimin e proceseve të fëmijëve, ai i përfundon ato, prandaj ndodhin gabime 502 gjatë vendosjes dhe operacioneve të tjera. Ka disa raporte të gabimeve në bugs.php.net që nga viti 2005 (p.sh këtu и këtu), i cili përshkruan këtë problem. Por me shumë mundësi nuk do të shihni asgjë në regjistrat: PHP-FPM do të njoftojë përfundimin e procesit të tij pa ndonjë gabim ose njoftim nga palët e treta.

Vlen të sqarohet se vetë problemi mund të varet në një masë më të vogël ose më të madhe nga vetë aplikacioni dhe mund të mos shfaqet, për shembull, në monitorim. Nëse e hasni, fillimisht ju vjen në mendje një zgjidhje e thjeshtë: shtoni një grep preStop me sleep(30). Do t'ju lejojë të plotësoni të gjitha kërkesat që ishin më parë (dhe ne nuk pranojmë të reja, që nga pod tashmë në gjendje ndërprerjen), dhe pas 30 sekondash vetë pod do të përfundojë me një sinjal SIGTERM.

Ajo rezulton se lifecycle për kontejnerin do të duket kështu:

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

Megjithatë, për shkak të 30-sekondës sleep ne fort ne do të rrisim kohën e vendosjes, pasi çdo pod do të përfundojë minimum 30 sekonda, që është e keqe. Çfarë mund të bëhet për këtë?

Le t'i drejtohemi palës përgjegjëse për ekzekutimin e drejtpërdrejtë të aplikacionit. Në rastin tonë është PHP-FPMCila si parazgjedhje nuk monitoron ekzekutimin e proceseve të tij fëmijë: Procesi master përfundon menjëherë. Ju mund ta ndryshoni këtë sjellje duke përdorur direktivën process_control_timeout, i cili specifikon kufijtë kohorë për proceset e fëmijëve për të pritur sinjale nga master. Nëse e vendosni vlerën në 20 sekonda, kjo do të mbulojë shumicën e pyetjeve që ekzekutohen në kontejner dhe do të ndalojë procesin kryesor pasi ato të përfundojnë.

Me këtë njohuri, le të kthehemi te problemi ynë i fundit. Siç u përmend, Kubernetes nuk është një platformë monolit: komunikimi midis komponentëve të ndryshëm të tij kërkon pak kohë. Kjo është veçanërisht e vërtetë kur marrim parasysh funksionimin e Ingresses dhe komponentëve të tjerë të lidhur, pasi për shkak të një vonese të tillë në kohën e vendosjes është e lehtë të marrësh një rritje prej 500 gabimesh. Për shembull, një gabim mund të ndodhë në fazën e dërgimit të një kërkese në rrjedhën e sipërme, por "vonesa kohore" e ndërveprimit midis komponentëve është mjaft e shkurtër - më pak se një sekondë.

Prandaj, Në total me direktivën e përmendur tashmë process_control_timeout ju mund të përdorni konstruksionin e mëposhtëm për lifecycle:

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

Në këtë rast, vonesën do ta kompensojmë me komandë sleep dhe mos e rritni ndjeshëm kohën e vendosjes: në fund të fundit, dallimi midis 30 sekondave dhe një është i dukshëm?.. Në fakt, është process_control_timeoutDhe lifecycle përdoret vetëm si "rrjeta sigurie" në rast vonese.

Në përgjithësi sjellja e përshkruar dhe mënyra përkatëse e zgjidhjes zbatohen jo vetëm për PHP-FPM. Një situatë e ngjashme mund të lindë në një mënyrë ose në një tjetër kur përdorni gjuhë/korniza të tjera. Nëse nuk mund ta rregulloni mbylljen e këndshme në mënyra të tjera - për shembull, duke rishkruar kodin në mënyrë që aplikacioni të përpunojë saktë sinjalet e përfundimit - mund të përdorni metodën e përshkruar. Mund të mos jetë më e bukura, por funksionon.

Praktikoni. Ngarko testimin për të kontrolluar funksionimin e pod

Testimi i ngarkesës është një nga mënyrat për të kontrolluar se si funksionon kontejneri, pasi kjo procedurë e afron atë me kushtet reale të luftimit kur përdoruesit vizitojnë sitin. Për të testuar rekomandimet e mësipërme, mund të përdorni Yandex.Tankom: Mbulon të gjitha nevojat tona në mënyrë perfekte. Më poshtë janë këshilla dhe rekomandime për kryerjen e testimit me një shembull të qartë nga përvoja jonë falë grafikëve të vetë Grafana dhe Yandex.Tank.

Gjëja më e rëndësishme këtu është kontrolloni ndryshimet hap pas hapi. Pasi të keni shtuar një rregullim të ri, ekzekutoni testin dhe shikoni nëse rezultatet kanë ndryshuar në krahasim me ekzekutimin e fundit. Përndryshe, do të jetë e vështirë të identifikohen zgjidhje joefektive, dhe në afat të gjatë mund të bëjë vetëm dëm (për shembull, të rrisë kohën e vendosjes).

Një nuancë tjetër është të shikoni shkrimet e kontejnerëve gjatë përfundimit të tij. A janë regjistruar informacione për mbylljen e këndshme atje? A ka ndonjë gabim në regjistrat kur qaseni në burime të tjera (për shembull, në një kontejner fqinj PHP-FPM)? Gabime në vetë aplikacionin (si në rastin me NGINX të përshkruar më sipër)? Shpresoj që informacioni hyrës nga ky artikull do t'ju ndihmojë të kuptoni më mirë se çfarë ndodh me kontejnerin gjatë përfundimit të tij.

Pra, testimi i parë u zhvillua pa lifecycle dhe pa direktiva shtesë për serverin e aplikacionit (process_control_timeout në PHP-FPM). Qëllimi i këtij testi ishte të identifikonte numrin e përafërt të gabimeve (dhe nëse ka ndonjë). Gjithashtu, nga informacione shtesë, duhet të dini se koha mesatare e vendosjes për çdo pod ishte rreth 5-10 sekonda derisa të ishte plotësisht gati. Rezultatet janë:

Këshilla dhe truket e Kubernetes: veçoritë e mbylljes së këndshme në NGINX dhe PHP-FPM

Paneli i informacionit Yandex.Tank tregon një numër të madh prej 502 gabimesh, të cilat ndodhën në momentin e vendosjes dhe zgjatën mesatarisht deri në 5 sekonda. Me sa duket kjo ishte për shkak se kërkesat ekzistuese për podin e vjetër po mbylleshin kur mbyllej. Pas kësaj, u shfaqën 503 gabime, të cilat ishin rezultat i një kontejneri të ndalur NGINX, i cili gjithashtu hoqi lidhjet për shkak të prapambetjes (që pengoi Ingress të lidhej me të).

Le të shohim se si process_control_timeout në PHP-FPM do të na ndihmojë të presim përfundimin e proceseve të fëmijëve, d.m.th. korrigjoni gabime të tilla. Rivendosni duke përdorur këtë direktivë:

Këshilla dhe truket e Kubernetes: veçoritë e mbylljes së këndshme në NGINX dhe PHP-FPM

Nuk ka më gabime gjatë vendosjes së 500-të! Vendosja është e suksesshme, funksionon mbyllje e këndshme.

Sidoqoftë, ia vlen të kujtojmë problemin me kontejnerët Ingress, një përqindje e vogël gabimesh në të cilat mund të marrim për shkak të një vonese kohore. Për t'i shmangur ato, gjithçka që mbetet është të shtoni një strukturë me sleep dhe përsërit vendosjen. Megjithatë, në rastin tonë të veçantë, asnjë ndryshim nuk ishte i dukshëm (përsëri, pa gabime).

Përfundim

Për të përfunduar procesin me hijeshi, ne presim sjelljen e mëposhtme nga aplikacioni:

  1. Prisni disa sekonda dhe më pas ndaloni së pranuari lidhje të reja.
  2. Prisni që të gjitha kërkesat të përfundojnë dhe mbyllni të gjitha lidhjet mbajtëse që nuk janë duke ekzekutuar kërkesat.
  3. Përfundoni procesin tuaj.

Megjithatë, jo të gjitha aplikacionet mund të funksionojnë në këtë mënyrë. Një zgjidhje për problemin në realitetet e Kubernetes është:

  • duke shtuar një grep parandalimi që do të presë disa sekonda;
  • duke studiuar skedarin e konfigurimit të backend-it tonë për parametrat e duhur.

Shembulli me NGINX e bën të qartë se edhe një aplikacion që fillimisht duhet të përpunojë saktë sinjalet e përfundimit mund të mos e bëjë këtë, kështu që është thelbësore të kontrolloni për 500 gabime gjatë vendosjes së aplikacionit. Kjo gjithashtu ju lejon të shikoni problemin më gjerësisht dhe të mos përqendroheni në një pod ose kontejner të vetëm, por të shikoni të gjithë infrastrukturën në tërësi.

Si një mjet testimi, mund të përdorni Yandex.Tank në lidhje me çdo sistem monitorimi (në rastin tonë, të dhënat janë marrë nga Grafana me një prapavijë të Prometheus për testim). Problemet me mbylljen e këndshme janë qartë të dukshme nën ngarkesa të rënda që mund të gjenerojë standardi, dhe monitorimi ndihmon për të analizuar situatën në më shumë detaje gjatë ose pas testit.

Në përgjigje të komenteve për artikullin: vlen të përmendet se problemet dhe zgjidhjet janë përshkruar këtu në lidhje me NGINX Ingress. Për raste të tjera, ka zgjidhje të tjera, të cilat mund t'i shqyrtojmë në materialet e mëposhtme të serisë.

PS

Të tjera nga seria e këshillave dhe trukeve K8s:

Burimi: www.habr.com

Shto një koment