Vidokezo na mbinu za Kubernetes: vipengele vya kuzima kwa neema katika NGINX na PHP-FPM

Hali ya kawaida wakati wa kutekeleza CI/CD katika Kubernetes: programu lazima iweze kutokubali maombi mapya ya mteja kabla ya kuacha kabisa, na muhimu zaidi, kukamilisha kwa ufanisi zilizopo.

Vidokezo na mbinu za Kubernetes: vipengele vya kuzima kwa neema katika NGINX na PHP-FPM

Kuzingatia hali hii hukuruhusu kufikia muda wa sifuri wakati wa kupeleka. Walakini, hata wakati wa kutumia vifurushi maarufu sana (kama NGINX na PHP-FPM), unaweza kukutana na shida ambazo zitasababisha kuongezeka kwa makosa kwa kila upelekaji...

Nadharia. Jinsi ganda linaishi

Tayari tumechapisha kwa undani kuhusu mzunguko wa maisha ya ganda nakala hii. Katika muktadha wa mada inayozingatiwa, tunavutiwa na yafuatayo: kwa sasa wakati poda inapoingia serikalini Kukamilisha, maombi mapya yanaacha kutumwa kwake (pod kuondolewa kutoka kwenye orodha ya miisho ya huduma). Hivyo, ili kuepuka muda wa kupungua wakati wa kupelekwa, inatosha kwetu kutatua tatizo la kusimamisha programu kwa usahihi.

Unapaswa pia kukumbuka kuwa kipindi cha msingi cha neema ni Sekunde 30: baada ya hili, pod itasitishwa na maombi lazima iwe na muda wa kushughulikia maombi yote kabla ya kipindi hiki. Kumbuka: ingawa ombi lolote linalochukua zaidi ya sekunde 5-10 tayari lina shida, na kuzima kwa neema hakutasaidia tena...

Ili kuelewa vizuri zaidi kile kinachotokea wakati ganda linapoisha, angalia tu mchoro ufuatao:

Vidokezo na mbinu za Kubernetes: vipengele vya kuzima kwa neema katika NGINX na PHP-FPM

A1, B1 - Kupokea mabadiliko kuhusu hali ya makaa
A2 - Kuondoka SIGTERM
B2 - Kuondoa ganda kutoka kwa ncha za mwisho
B3 - Kupokea mabadiliko (orodha ya miisho imebadilika)
B4 - Sasisha sheria za iptables

Tafadhali kumbuka: kufuta ganda la mwisho na kutuma SIGTERM hakufanyiki kwa mfuatano, lakini kwa sambamba. Na kwa sababu ya ukweli kwamba Ingress haipokei mara moja orodha iliyosasishwa ya Endpoints, maombi mapya kutoka kwa wateja yatatumwa kwenye ganda, ambayo itasababisha makosa 500 wakati wa kusitisha ganda. (kwa nyenzo za kina zaidi juu ya suala hili, sisi kutafsiriwa). Tatizo hili linahitaji kutatuliwa kwa njia zifuatazo:

  • Tuma Muunganisho: funga katika vichwa vya majibu (ikiwa hii inahusu programu ya HTTP).
  • Ikiwa haiwezekani kufanya mabadiliko kwa kanuni, basi makala ifuatayo inaelezea suluhisho ambalo litakuwezesha kushughulikia maombi hadi mwisho wa kipindi cha neema.

Nadharia. Jinsi NGINX na PHP-FPM husitisha michakato yao

NGINX

Wacha tuanze na NGINX, kwani kila kitu ni dhahiri zaidi au kidogo nayo. Kuingia kwenye nadharia, tunajifunza kuwa NGINX ina mchakato mmoja mkuu na "wafanyakazi" kadhaa - hii ni michakato ya watoto ambayo huchakata maombi ya mteja. Chaguo rahisi hutolewa: kutumia amri nginx -s <SIGNAL> simamisha michakato katika kuzima kwa haraka au kwa hali nzuri ya kuzima. Kwa wazi, ni chaguo la mwisho ambalo linatuvutia.

Kisha kila kitu ni rahisi: unahitaji kuongeza preStop-ndoano amri ambayo itatuma ishara nzuri ya kuzima. Hii inaweza kufanywa katika Usambazaji, kwenye kizuizi cha chombo:

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

Sasa, ganda likizima, tutaona yafuatayo kwenye kumbukumbu za chombo cha 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

Na hii itamaanisha kile tunachohitaji: NGINX inasubiri maombi ya kukamilisha, na kisha kuua mchakato. Walakini, hapa chini tutazingatia pia shida ya kawaida ambayo, hata kwa amri nginx -s quit mchakato huo unaisha kimakosa.

Na katika hatua hii tumemaliza na NGINX: angalau kutoka kwa magogo unaweza kuelewa kuwa kila kitu kinafanya kazi kama inavyopaswa.

Je, kuna mpango gani na PHP-FPM? Je, inashughulikia vipi kuzima kwa neema? Hebu tufikirie.

PHP-FPM

Kwa upande wa PHP-FPM, kuna habari kidogo kidogo. Ikiwa unazingatia mwongozo rasmi kulingana na PHP-FPM, itasema kwamba ishara zifuatazo za POSIX zinakubaliwa:

  1. SIGINT, SIGTERM - kuzima haraka;
  2. SIGQUIT - kuzima kwa neema (kile tunachohitaji).

Ishara zilizobaki hazihitajiki katika kazi hii, kwa hiyo tutaacha uchambuzi wao. Ili kusitisha mchakato kwa usahihi, utahitaji kuandika ndoano ifuatayo ya PreStop:

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

Kwa mtazamo wa kwanza, hii ndiyo yote inahitajika kufanya kuzima kwa neema katika vyombo vyote viwili. Hata hivyo, kazi ni ngumu zaidi kuliko inaonekana. Zifuatazo ni kesi mbili ambazo uzimaji mzuri haukufanya kazi na kusababisha kutopatikana kwa mradi kwa muda mfupi wakati wa kupelekwa.

Fanya mazoezi. Shida zinazowezekana na kuzima kwa neema

NGINX

Kwanza kabisa, ni muhimu kukumbuka: pamoja na kutekeleza amri nginx -s quit Kuna hatua moja zaidi ambayo inafaa kulipa kipaumbele. Tumekumbana na tatizo ambapo NGINX bado ingetuma SIGTERM badala ya mawimbi ya SIGQUIT, na kusababisha maombi kutokamilika ipasavyo. Kesi zinazofanana zinaweza kupatikana, kwa mfano, hapa. Kwa bahati mbaya, hatukuweza kuamua sababu maalum ya tabia hii: kulikuwa na shaka kuhusu toleo la NGINX, lakini haikuthibitishwa. Dalili ilikuwa kwamba ujumbe ulizingatiwa kwenye kumbukumbu za kontena za NGINX: "fungua tundu # 10 iliyobaki kwenye unganisho 5", baada ya hapo poda ilisimama.

Tunaweza kuona shida kama hiyo, kwa mfano, kutoka kwa majibu kwenye Ingress tunayohitaji:

Vidokezo na mbinu za Kubernetes: vipengele vya kuzima kwa neema katika NGINX na PHP-FPM
Viashiria vya misimbo ya hali wakati wa kupelekwa

Katika kesi hii, tunapokea msimbo wa hitilafu 503 tu kutoka kwa Ingress yenyewe: haiwezi kufikia chombo cha NGINX, kwa kuwa haipatikani tena. Ukiangalia magogo ya chombo na NGINX, yana yafuatayo:

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

Baada ya kubadilisha ishara ya kuacha, chombo huanza kuacha kwa usahihi: hii inathibitishwa na ukweli kwamba hitilafu ya 503 haionekani tena.

Ukikutana na tatizo kama hilo, ni jambo la busara kujua ni ishara gani ya kusimamisha inatumika kwenye chombo na ndoano ya preStop inaonekanaje. Inawezekana kabisa kwamba sababu iko katika hili.

PHP-FPM... na zaidi

Tatizo na PHP-FPM linaelezewa kwa njia isiyo na maana: haisubiri kukamilika kwa michakato ya mtoto, inawamaliza, ndiyo sababu makosa 502 hutokea wakati wa kupelekwa na uendeshaji mwingine. Kuna ripoti kadhaa za hitilafu kwenye bugs.php.net tangu 2005 (km hapa ΠΈ hapa), ambayo inaelezea tatizo hili. Lakini kuna uwezekano mkubwa hutaona chochote kwenye kumbukumbu: PHP-FPM itatangaza kukamilika kwa mchakato wake bila hitilafu yoyote au arifa za watu wengine.

Inafaa kufafanua kuwa tatizo lenyewe linaweza kutegemea kwa kiasi kidogo au zaidi juu ya programu yenyewe na huenda isijidhihirishe yenyewe, kwa mfano, katika ufuatiliaji. Ukikutana nayo, suluhu rahisi inakuja akilini kwanza: ongeza ndoano ya preStop nayo sleep(30). Itakuruhusu kukamilisha maombi yote ambayo yalikuwa hapo awali (na hatukubali mpya, kwani pod tayari uwezo wa Kukamilisha), na baada ya sekunde 30 pod yenyewe itaisha na ishara SIGTERM.

Ni zinageuka kuwa lifecycle kwa chombo kitaonekana kama hii:

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

Walakini, kwa sababu ya sekunde 30 sleep sisi kwa nguvu tutaongeza muda wa kupeleka, kwa kuwa kila pod itasitishwa kiwango cha chini Sekunde 30, ambayo ni mbaya. Je, nini kifanyike kuhusu hili?

Hebu tugeukie chama kinachohusika na utekelezaji wa moja kwa moja wa maombi. Kwa upande wetu ni PHP-FPMAmbayo kwa chaguo-msingi haifuatilii utekelezaji wa michakato ya mtoto wake: Mchakato mkuu umesitishwa mara moja. Unaweza kubadilisha tabia hii kwa kutumia maagizo process_control_timeout, ambayo inabainisha mipaka ya muda kwa michakato ya mtoto kusubiri ishara kutoka kwa bwana. Ukiweka thamani hadi sekunde 20, hii itashughulikia hoja nyingi zinazoendeshwa kwenye kontena na itasimamisha mchakato mkuu pindi zitakapokamilika.

Kwa ujuzi huu, wacha turudi kwenye shida yetu ya mwisho. Kama ilivyoelezwa, Kubernetes sio jukwaa la monolithic: mawasiliano kati ya vipengele vyake tofauti huchukua muda. Hii ni kweli hasa tunapozingatia uendeshaji wa Ingresses na vipengele vingine vinavyohusiana, kwani kutokana na kuchelewa vile wakati wa kupelekwa ni rahisi kupata kuongezeka kwa makosa 500. Kwa mfano, hitilafu inaweza kutokea katika hatua ya kutuma ombi kwa mto, lakini "muda" wa mwingiliano kati ya vipengele ni mfupi sana - chini ya sekunde.

Kwa hiyo, Kwa ujumla na agizo lililotajwa tayari process_control_timeout unaweza kutumia ujenzi ufuatao kwa lifecycle:

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

Katika kesi hii, tutalipa fidia kwa kuchelewa kwa amri sleep na usiongeze sana muda wa kupeleka: kuna tofauti inayoonekana kati ya sekunde 30 na moja? .. Kwa kweli, ni process_control_timeoutNa lifecycle inatumika tu kama "wavu wa usalama" ikiwa kuna uzembe.

Kwa ujumla tabia iliyoelezewa na suluhisho sambamba haitumiki kwa PHP-FPM pekee. Hali kama hiyo inaweza kutokea kwa njia moja au nyingine wakati wa kutumia lugha / mifumo mingine. Ikiwa huwezi kurekebisha kuzima kwa neema kwa njia zingine - kwa mfano, kwa kuandika tena nambari ili programu isindika kwa usahihi ishara za kukomesha - unaweza kutumia njia iliyoelezewa. Inaweza kuwa sio nzuri zaidi, lakini inafanya kazi.

Fanya mazoezi. Upimaji wa mzigo ili kuangalia uendeshaji wa ganda

Upimaji wa mzigo ni mojawapo ya njia za kuangalia jinsi chombo kinavyofanya kazi, kwa kuwa utaratibu huu unaleta karibu na hali halisi ya kupambana wakati watumiaji wanatembelea tovuti. Ili kupima mapendekezo hapo juu, unaweza kutumia Yandex.Tankom: Inashughulikia mahitaji yetu yote kikamilifu. Zifuatazo ni vidokezo na mapendekezo ya kufanya majaribio na mfano wazi kutoka kwa uzoefu wetu shukrani kwa grafu za Grafana na Yandex.Tank yenyewe.

Jambo muhimu zaidi hapa ni angalia mabadiliko hatua kwa hatua. Baada ya kuongeza marekebisho mapya, endesha jaribio na uone ikiwa matokeo yamebadilika ikilinganishwa na kukimbia mwisho. Vinginevyo, itakuwa vigumu kutambua ufumbuzi usio na ufanisi, na kwa muda mrefu inaweza tu kufanya madhara (kwa mfano, kuongeza muda wa kupeleka).

Mwingine nuance ni kuangalia magogo ya chombo wakati wa kukomesha kwake. Je, habari kuhusu kuzima kwa njia nzuri imerekodiwa hapo? Kuna makosa yoyote kwenye kumbukumbu wakati wa kupata rasilimali zingine (kwa mfano, kwa kontena la PHP-FPM la jirani)? Makosa katika programu yenyewe (kama ilivyo kwa NGINX ilivyoelezwa hapo juu)? Natumaini kwamba maelezo ya utangulizi kutoka kwa makala hii yatakusaidia kuelewa vizuri kile kinachotokea kwa chombo wakati wa kukomesha kwake.

Kwa hiyo, mtihani wa kwanza wa kukimbia ulifanyika bila lifecycle na bila maagizo ya ziada kwa seva ya programu (process_control_timeout katika PHP-FPM). Madhumuni ya jaribio hili lilikuwa kutambua takriban idadi ya makosa (na ikiwa kuna yoyote). Pia, kutokana na maelezo ya ziada, unapaswa kujua kwamba muda wa wastani wa kupelekwa kwa kila ganda ulikuwa kama sekunde 5-10 hadi iko tayari kabisa. Matokeo ni:

Vidokezo na mbinu za Kubernetes: vipengele vya kuzima kwa neema katika NGINX na PHP-FPM

Jopo la habari la Yandex.Tank linaonyesha ongezeko la makosa 502, ambayo yalitokea wakati wa kutumwa na kudumu kwa wastani hadi sekunde 5. Labda hii ni kwa sababu maombi yaliyopo kwenye ganda la zamani yalikuwa yanakatishwa wakati yakikatishwa. Baada ya hayo, makosa 503 yalionekana, ambayo yalikuwa matokeo ya chombo kilichosimamishwa cha NGINX, ambacho pia kiliacha viunganisho kutokana na backend (ambayo ilizuia Ingress kuunganisha nayo).

Hebu tuone jinsi gani process_control_timeout katika PHP-FPM itatusaidia kusubiri kukamilika kwa michakato ya mtoto, i.e. rekebisha makosa kama haya. Sambaza tena kwa kutumia agizo hili:

Vidokezo na mbinu za Kubernetes: vipengele vya kuzima kwa neema katika NGINX na PHP-FPM

Hakuna makosa zaidi wakati wa uwekaji wa 500! Usambazaji umefaulu, kuzima kwa uzuri kunafanya kazi.

Walakini, inafaa kukumbuka suala hilo na vyombo vya Ingress, asilimia ndogo ya makosa ambayo tunaweza kupokea kwa sababu ya kucheleweshwa kwa wakati. Ili kuziepuka, kilichobaki ni kuongeza muundo na sleep na kurudia kupeleka. Hata hivyo, katika kesi yetu fulani, hakuna mabadiliko yaliyoonekana (tena, hakuna makosa).

Hitimisho

Ili kusitisha mchakato kwa uzuri, tunatarajia tabia ifuatayo kutoka kwa programu:

  1. Subiri sekunde chache kisha uache kukubali miunganisho mipya.
  2. Subiri maombi yote yakamilishe na ufunge miunganisho yote ya keepalive ambayo haitekelezi maombi.
  3. Maliza mchakato wako.

Walakini, sio maombi yote yanaweza kufanya kazi kwa njia hii. Suluhisho moja la shida katika hali halisi ya Kubernetes ni:

  • kuongeza ndoano kabla ya kuacha ambayo itasubiri sekunde chache;
  • kusoma faili ya usanidi ya backend yetu kwa vigezo vinavyofaa.

Mfano na NGINX inaweka wazi kwamba hata programu ambayo inapaswa kusindika ishara za kukomesha kwa usahihi inaweza kufanya hivyo, kwa hivyo ni muhimu kuangalia makosa 500 wakati wa kupeleka maombi. Hii pia inakuwezesha kuangalia tatizo kwa upana zaidi na si kuzingatia ganda moja au chombo, lakini kuangalia miundombinu nzima kwa ujumla.

Kama zana ya majaribio, unaweza kutumia Yandex.Tank kwa kushirikiana na mfumo wowote wa ufuatiliaji (kwa upande wetu, data ilichukuliwa kutoka Grafana na nyuma ya Prometheus kwa jaribio). Matatizo ya kuzima kwa uzuri yanaonekana wazi chini ya mizigo mizito ambayo alama inaweza kutoa, na ufuatiliaji husaidia kuchanganua hali hiyo kwa undani zaidi wakati au baada ya jaribio.

Kwa kujibu maoni juu ya kifungu hicho: inafaa kutaja kuwa shida na suluhisho zimeelezewa hapa kuhusiana na NGINX Ingress. Kwa matukio mengine, kuna ufumbuzi mwingine, ambao tunaweza kuzingatia katika nyenzo zifuatazo za mfululizo.

PS

Nyingine kutoka kwa safu za vidokezo na hila za K8s:

Chanzo: mapenzi.com

Kuongeza maoni