Kubernetes padomi un triki: graciozas izslēgÅ”anas iespējas NGINX un PHP-FPM

Tipisks nosacÄ«jums, ievieÅ”ot CI/CD Kubernetes: lietojumprogrammai ir jāspēj nepieņemt jaunus klientu pieprasÄ«jumus pirms pilnÄ«gas apstāŔanās, un pats galvenais, veiksmÄ«gi pabeigt esoÅ”os.

Kubernetes padomi un triki: graciozas izslēgÅ”anas iespējas NGINX un PHP-FPM

AtbilstÄ«ba Å”im nosacÄ«jumam ļauj sasniegt nulles dÄ«kstāves izvietoÅ”anas laikā. Tomēr pat tad, ja izmantojat ļoti populārus komplektus (piemēram, NGINX un PHP-FPM), varat saskarties ar grÅ«tÄ«bām, kas izraisÄ«s kļūdu pieaugumu katrā izvietoÅ”anas reizē...

Teorija. Kā pods dzīvo

Mēs jau esam detalizēti publicējuÅ”i pāksts dzÄ«ves ciklu Å”is raksts. Apskatāmās tēmas kontekstā mÅ«s interesē sekojoÅ”ais: brÄ«dÄ«, kad pods nonāk stāvoklÄ« Beidzas, tai vairs netiek sÅ«tÄ«ti jauni pieprasÄ«jumi (pod noņemts no pakalpojuma galapunktu saraksta). Tādējādi, lai izvairÄ«tos no dÄ«kstāves izvietoÅ”anas laikā, mums pietiek ar pareizu lietojumprogrammas apturÄ“Å”anas problēmu.

Jums arÄ« jāatceras, ka noklusējuma labvēlÄ«bas periods ir 30 sekundes: pēc tam aplikācijas darbÄ«ba tiks pārtraukta, un lietojumprogrammai ir jābÅ«t laikam, lai apstrādātu visus pieprasÄ«jumus pirms Ŕī perioda. PiezÄ«me: lai gan jebkurÅ” pieprasÄ«jums, kas aizņem vairāk nekā 5-10 sekundes, jau ir problemātisks, un gracioza izslēgÅ”ana vairs nepalÄ«dzēs...

Lai labāk izprastu, kas notiek, kad pods tiek pārtraukts, vienkārŔi apskatiet Ŕo diagrammu:

Kubernetes padomi un triki: graciozas izslēgÅ”anas iespējas NGINX un PHP-FPM

A1, B1 - Izmaiņu saņemÅ”ana par pavarda stāvokli
A2 - IzbraukŔana SIGTERM
B2 ā€” podnieka noņemÅ”ana no galapunktiem
B3 ā€” izmaiņu saņemÅ”ana (ir mainÄ«ts beigu punktu saraksts)
B4 ā€” atjauniniet iptables noteikumus

LÅ«dzu, ņemiet vērā: galapunkta apgabala dzÄ“Å”ana un SIGTERM nosÅ«tÄ«Å”ana nenotiek secÄ«gi, bet paralēli. Un sakarā ar to, ka Ingress nekavējoties nesaņem atjaunināto galapunktu sarakstu, uz pod tiks nosÅ«tÄ«ti jauni klientu pieprasÄ«jumi, kas pod pārtraukÅ”anas laikā radÄ«s 500 kļūdu. (lai iegÅ«tu sÄ«kāku materiālu par Å”o jautājumu, mēs tulkots). Å Ä« problēma ir jāatrisina Ŕādos veidos:

  • SÅ«tÄ«t savienojumu: aizveriet atbildes galvenes (ja tas attiecas uz HTTP lietojumprogrammu).
  • Ja kodā nav iespējams veikt izmaiņas, nākamajā rakstā ir aprakstÄ«ts risinājums, kas ļaus apstrādāt pieprasÄ«jumus lÄ«dz labvēlÄ«gā perioda beigām.

Teorija. Kā NGINX un PHP-FPM pārtrauc savus procesus

nginx

Sāksim ar NGINX, jo ar to viss ir vairāk vai mazāk skaidrs. Iedziļinoties teorijā, mēs uzzinām, ka NGINX ir viens galvenais process un vairāki "darbinieki" - tie ir pakārtotie procesi, kas apstrādā klientu pieprasÄ«jumus. Tiek nodroÅ”ināta ērta opcija: izmantojot komandu nginx -s <SIGNAL> pārtraukt procesus ātrās izslēgÅ”anas vai graciozās izslēgÅ”anas režīmā. AcÄ«mredzot mÅ«s interesē pēdējais variants.

Tad viss ir vienkārÅ”i: jums jāpievieno preStop-āķis komanda, kas nosÅ«tÄ«s graciozu izslēgÅ”anas signālu. To var izdarÄ«t sadaļā IzvietoÅ”ana konteinera blokā:

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

Tagad, kad pods tiks izslēgts, NGINX konteinera žurnālos redzēsim Ŕādu informāciju:

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

Un tas nozÄ«mēs to, kas mums nepiecieÅ”ams: NGINX gaida, lÄ«dz tiks pabeigti pieprasÄ«jumi, un pēc tam nogalina procesu. Tomēr tālāk mēs apsvērsim arÄ« izplatÄ«tu problēmu, kuras dēļ pat ar komandu nginx -s quit process beidzas nepareizi.

Un Ŕajā posmā ar NGINX esam galā: vismaz no žurnāliem var saprast, ka viss darbojas kā nākas.

Kāds sakars ar PHP-FPM? Kā tas apstrādā graciozo izslēgÅ”anu? Izdomāsim.

PHP-FPM

PHP-FPM gadÄ«jumā informācijas ir nedaudz mazāk. Ja koncentrējaties uz oficiālā rokasgrāmata saskaņā ar PHP-FPM tas pateiks, ka tiek pieņemti Ŕādi POSIX signāli:

  1. SIGINT, SIGTERM - ātra izslēgÅ”ana;
  2. SIGQUIT ā€” gracioza izslēgÅ”ana (tas, kas mums vajadzÄ«gs).

AtlikuÅ”ie signāli Å”ajā uzdevumā nav nepiecieÅ”ami, tāpēc mēs to analÄ«zi izlaidÄ«sim. Lai pareizi pārtrauktu procesu, jums bÅ«s jāuzraksta Ŕāds preStop āķis:

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

No pirmā acu uzmetiena tas ir viss, kas nepiecieÅ”ams, lai veiktu graciozu izslēgÅ”anu abos konteineros. Tomēr uzdevums ir grÅ«tāks, nekā Ŕķiet. Tālāk ir norādÄ«ti divi gadÄ«jumi, kad gracioza izslēgÅ”ana nedarbojās un izraisÄ«ja Ä«slaicÄ«gu projekta nepieejamÄ«bu izvietoÅ”anas laikā.

Prakse. Iespējamas problēmas ar graciozu izslēgÅ”anu

nginx

Pirmkārt, ir lietderÄ«gi atcerēties: papildus komandas izpildei nginx -s quit Ir vēl viens posms, kam ir vērts pievērst uzmanÄ«bu. Mēs saskārāmies ar problēmu, kuras dēļ NGINX joprojām sÅ«tÄ«ja SIGTERM, nevis SIGQUIT signālu, kā rezultātā pieprasÄ«jumi netika pareizi pabeigti. LÄ«dzÄ«gus gadÄ«jumus var atrast, piemēram, Å”eit. Diemžēl mēs nevarējām noteikt konkrētu Ŕīs uzvedÄ«bas iemeslu: bija aizdomas par NGINX versiju, taču tās neapstiprinājās. Simptoms bija tāds, ka NGINX konteinera žurnālos tika novēroti ziņojumi: "atvērta ligzda #10, kas atstāta savienojumā 5", pēc kura pods apstājās.

Mēs varam novērot Ŕādu problēmu, piemēram, no mums nepiecieÅ”amajām atbildēm par Ingress:

Kubernetes padomi un triki: graciozas izslēgÅ”anas iespējas NGINX un PHP-FPM
Statusa kodu indikatori izvietoŔanas laikā

Å ajā gadÄ«jumā mēs saņemam tikai kļūdas kodu 503 no paÅ”as Ingress: tas nevar piekļūt NGINX konteineram, jo ā€‹ā€‹tas vairs nav pieejams. Ja skatāties uz konteineru žurnāliem, izmantojot NGINX, tie satur Ŕādu informāciju:

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

Pēc apstāŔanās signāla maiņas konteiners sāk pareizi apstāties: to apstiprina fakts, ka kļūda 503 vairs netiek novērota.

Ja rodas lÄ«dzÄ«ga problēma, ir lietderÄ«gi noskaidrot, kāds apturÄ“Å”anas signāls tiek izmantots konteinerā un kā tieÅ”i izskatās preStop āķis. PilnÄ«gi iespējams, ka iemesls slēpjas tieÅ”i tajā.

PHP-FPM... un vairāk

PHP-FPM problēma ir aprakstÄ«ta triviāli: tas negaida bērnu procesu pabeigÅ”anu, tas pārtrauc tos, tāpēc izvietoÅ”anas un citu darbÄ«bu laikā rodas 502 kļūdas. Vietnē bugs.php.net kopÅ” 2005. gada ir vairāki kļūdu ziņojumi (piem Å”eit Šø Å”eit), kas apraksta Å”o problēmu. Bet jÅ«s, visticamāk, neko neredzēsit žurnālos: PHP-FPM paziņos par sava procesa pabeigÅ”anu bez kļūdām vai treÅ”o puÅ”u paziņojumiem.

Ir vērts precizēt, ka pati problēma var bÅ«t mazākā vai lielākā mērā atkarÄ«ga no paÅ”as lietojumprogrammas un var neizpausties, piemēram, uzraudzÄ«bā. Ja ar to saskaraties, vispirms nāk prātā vienkārÅ”s risinājums: pievienojiet preStop āķi ar sleep(30). Tas ļaus jums izpildÄ«t visus pieprasÄ«jumus, kas bija iepriekÅ” (un mēs nepieņemam jaunus, jo pod jau spējÄ«gs uz Beidzas), un pēc 30 sekundēm pats pods beigsies ar signālu SIGTERM.

Izrādās, ka lifecycle konteiners izskatīsies Ŕādi:

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

Tomēr sakarā ar 30-sek sleep mēs stingri mēs pagarināsim izvietoÅ”anas laiku, jo katrs pods tiks pārtraukts minimums 30 sekundes, kas ir slikti. Ko Å”ajā sakarā var darÄ«t?

VērsÄ«simies pie personas, kas ir atbildÄ«ga par pieteikuma tieÅ”u izpildi. MÅ«su gadÄ«jumā tā ir PHP-FPMKurÅ” pēc noklusējuma neuzrauga savu pakārtoto procesu izpildi: galvenais process tiek nekavējoties pārtraukts. Varat mainÄ«t Å”o darbÄ«bu, izmantojot direktÄ«vu process_control_timeout, kas nosaka laika ierobežojumus bērnprocesiem, lai gaidÄ«tu signālus no galvenā. Ja iestatāt vērtÄ«bu uz 20 sekundēm, tas aptvers lielāko daļu vaicājumu, kas darbojas konteinerā, un apturēs galveno procesu, tiklÄ«dz tie bÅ«s pabeigti.

Ar Ŕīm zināŔanām atgriezÄ«simies pie pēdējās problēmas. Kā minēts, Kubernetes nav monolÄ«ta platforma: saziņa starp tās dažādajām sastāvdaļām aizņem kādu laiku. Tas jo Ä«paÅ”i attiecas uz Ingresses un citu saistÄ«to komponentu darbÄ«bu, jo Ŕādas kavÄ“Å”anās dēļ izvietoÅ”anas laikā ir viegli iegÅ«t 500 kļūdu pieaugumu. Piemēram, kļūda var rasties pieprasÄ«juma nosÅ«tÄ«Å”anas posmā uz augÅ”u, taču mijiedarbÄ«bas ā€œlaika nobÄ«deā€ starp komponentiem ir diezgan Ä«sa - mazāka par sekundi.

Tādēļ Kopā ar jau minēto direktÄ«vu process_control_timeout varat izmantot Ŕādu konstrukciju lifecycle:

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

Å ajā gadÄ«jumā mēs kompensēsim kavÄ“Å”anos ar komandu sleep un nepalieliniet izvietoÅ”anas laiku: vai ir ievērojama atŔķirÄ«ba starp 30 sekundēm un vienu? .. PatiesÄ«bā tā ir process_control_timeoutUn lifecycle izmanto tikai kā ā€œdroŔības tÄ«kluā€ kavÄ“Å”anās gadÄ«jumā.

VispārÄ«gi runājot aprakstÄ«tā rÄ«cÄ«ba un atbilstoÅ”ais risinājums attiecas ne tikai uz PHP-FPM. LÄ«dzÄ«ga situācija tā vai citādi var rasties, lietojot citas valodas/ietvarus. Ja graciozo izslēgÅ”anu nevarat novērst citos veidos, piemēram, pārrakstot kodu, lai lietojumprogramma pareizi apstrādātu beigu signālus, varat izmantot aprakstÄ«to metodi. Tas var nebÅ«t tas skaistākais, bet tas darbojas.

Prakse. Slodzes pārbaude, lai pārbaudītu podziņa darbību

Slodzes pārbaude ir viens no veidiem, kā pārbaudÄ«t konteinera darbÄ«bu, jo Ŕī procedÅ«ra tuvina to reāliem kaujas apstākļiem, kad lietotāji apmeklē vietni. Lai pārbaudÄ«tu iepriekÅ” minētos ieteikumus, varat izmantot Yandex.Tankom: Tas lieliski apmierina visas mÅ«su vajadzÄ«bas. Tālāk ir sniegti padomi un ieteikumi testÄ“Å”anas veikÅ”anai, izmantojot skaidru piemēru no mÅ«su pieredzes, pateicoties paÅ”as Grafana un Yandex.Tank diagrammām.

VissvarÄ«gākais Å”eit ir pārbaudiet izmaiņas soli pa solim. Pēc jauna labojuma pievienoÅ”anas palaidiet testu un pārbaudiet, vai rezultāti ir mainÄ«juÅ”ies salÄ«dzinājumā ar iepriekŔējo palaiÅ”anu. Pretējā gadÄ«jumā bÅ«s grÅ«ti noteikt neefektÄ«vus risinājumus, un ilgtermiņā tas var tikai kaitēt (piemēram, palielināt izvietoÅ”anas laiku).

Vēl viena nianse ir apskatÄ«t konteinera žurnālus tā darbÄ«bas pārtraukÅ”anas laikā. Vai tur ir ierakstÄ«ta informācija par graciozo izslēgÅ”anu? Vai, piekļūstot citiem resursiem (piemēram, blakus esoÅ”ajam PHP-FPM konteineram), žurnālos ir kļūdas? Kļūdas paŔā lietojumprogrammā (kā iepriekÅ” aprakstÄ«tajā gadÄ«jumā ar NGINX)? Ceru, ka Ŕī raksta ievadinformācija palÄ«dzēs jums labāk izprast, kas notiek ar konteineru tā darbÄ«bas pārtraukÅ”anas laikā.

Tātad, pirmais testa brauciens notika bez lifecycle un bez papildu direktÄ«vām lietojumprogrammu serverim (process_control_timeout PHP-FPM). Å Ä« testa mērÄ·is bija noteikt aptuveno kļūdu skaitu (un to, vai tādas ir). Turklāt, ņemot vērā papildu informāciju, jums vajadzētu zināt, ka katras podziņas vidējais izvietoÅ”anas laiks bija aptuveni 5ā€“10 sekundes, lÄ«dz tas bija pilnÄ«bā gatavs. Rezultāti ir:

Kubernetes padomi un triki: graciozas izslēgÅ”anas iespējas NGINX un PHP-FPM

Yandex.Tank informācijas panelÄ« ir redzams 502 kļūdu pieaugums, kas radās izvietoÅ”anas laikā un ilga vidēji lÄ«dz 5 sekundēm. Iespējams, tas bija tāpēc, ka esoÅ”ie pieprasÄ«jumi vecajam podam tika pārtraukti, kad tas tika pārtraukts. Pēc tam parādÄ«jās 503 kļūdas, ko izraisÄ«ja apturēts NGINX konteiners, kas arÄ« pārtrauca savienojumus aizmugursistēmas dēļ (kas neļāva Ingress tai izveidot savienojumu).

PaskatÄ«simies, kā process_control_timeout PHP-FPM palÄ«dzēs mums sagaidÄ«t bērnu procesu pabeigÅ”anu, t.i. izlabot Ŕādas kļūdas. Atkārtoti izvietot, izmantojot Å”o direktÄ«vu:

Kubernetes padomi un triki: graciozas izslēgÅ”anas iespējas NGINX un PHP-FPM

500. izvietoÅ”anas laikā kļūdu vairs nav! IzvietoÅ”ana ir veiksmÄ«ga, graciozi izslēgÅ”anas darbi.

Tomēr ir vērts atcerēties problēmu ar Ingress konteineriem ā€” nelielu daļu kļūdu, kuras mēs varam saņemt laika nobÄ«des dēļ. Lai no tiem izvairÄ«tos, atliek tikai pievienot struktÅ«ru ar sleep un atkārtojiet izvietoÅ”anu. Tomēr mÅ«su konkrētajā gadÄ«jumā nekādas izmaiņas nebija redzamas (atkal nekādu kļūdu).

Secinājums

Lai pabeigtu procesu, mēs sagaidām Ŕādu lietojumprogrammas darbÄ«bu:

  1. Uzgaidiet dažas sekundes un pēc tam pārtrauciet jaunu savienojumu pieņemÅ”anu.
  2. Pagaidiet, līdz tiek pabeigti visi pieprasījumi, un aizveriet visus saglabātos savienojumus, kas neizpilda pieprasījumus.
  3. Pabeidziet savu procesu.

Tomēr ne visas lietojumprogrammas var darboties Ŕādā veidā. Viens problēmas risinājums Kubernetes realitātē ir:

  • pievienojot pirms apstāŔanās āķi, kas gaidÄ«s dažas sekundes;
  • pētot mÅ«su aizmugursistēmas konfigurācijas failu, lai iegÅ«tu atbilstoÅ”os parametrus.

Piemērā ar NGINX ir skaidrs, ka pat lietojumprogramma, kurai sākotnēji bÅ«tu pareizi jāapstrādā pārtraukÅ”anas signāli, to var nedarÄ«t, tāpēc lietojumprogrammas izvietoÅ”anas laikā ir ļoti svarÄ«gi pārbaudÄ«t 500 kļūdas. Tas arÄ« ļauj aplÅ«kot problēmu plaŔāk un nekoncentrēties uz vienu podiņu vai konteineru, bet gan aplÅ«kot visu infrastruktÅ«ru kopumā.

Kā testÄ“Å”anas rÄ«ku varat izmantot Yandex.Tank kopā ar jebkuru uzraudzÄ«bas sistēmu (mÅ«su gadÄ«jumā dati tika ņemti no Grafana ar Prometheus aizmugursistēmu pārbaudei). Problēmas ar graciozu izslēgÅ”anu ir skaidri redzamas pie lielas slodzes, ko var radÄ«t etalons, un uzraudzÄ«ba palÄ«dz detalizētāk analizēt situāciju testa laikā vai pēc tā.

Atbildot uz atsauksmēm par rakstu: ir vērts pieminēt, ka Å”eit ir aprakstÄ«tas problēmas un risinājumi saistÄ«bā ar NGINX Ingress. Citos gadÄ«jumos ir citi risinājumi, kurus mēs varam apsvērt turpmākajos sērijas materiālos.

PS

Citi no K8s padomu un triku sērijas:

Avots: www.habr.com

Pievieno komentāru