Mga tip at trick ng Kubernetes: mga tampok ng magandang pagsara sa NGINX at PHP-FPM

Isang tipikal na kundisyon kapag nagpapatupad ng CI/CD sa Kubernetes: ang application ay dapat na hindi tumanggap ng mga bagong kahilingan ng kliyente bago ganap na huminto, at higit sa lahat, matagumpay na makumpleto ang mga umiiral na.

Mga tip at trick ng Kubernetes: mga tampok ng magandang pagsara sa NGINX at PHP-FPM

Ang pagsunod sa kundisyong ito ay nagbibigay-daan sa iyong makamit ang zero downtime sa panahon ng deployment. Gayunpaman, kahit na gumagamit ng napakasikat na mga bundle (tulad ng NGINX at PHP-FPM), maaari kang makatagpo ng mga paghihirap na hahantong sa pagdagsa ng mga error sa bawat deployment...

Teorya. Paano nabubuhay ang pod

Nai-publish na namin nang detalyado ang tungkol sa ikot ng buhay ng isang pod artikulong ito. Sa konteksto ng paksang isinasaalang-alang, kami ay interesado sa mga sumusunod: sa sandaling ang pod ay pumasok sa estado Pagwawakas, huminto ang pagpapadala dito ng mga bagong kahilingan (pod inalis mula sa listahan ng mga endpoint para sa serbisyo). Kaya, upang maiwasan ang downtime sa panahon ng pag-deploy, sapat na para sa amin upang malutas ang problema ng paghinto ng application nang tama.

Dapat mo ring tandaan na ang default na panahon ng palugit ay 30 segundo: pagkatapos nito, ang pod ay wawakasan at ang aplikasyon ay dapat magkaroon ng oras upang iproseso ang lahat ng mga kahilingan bago ang panahong ito. Nota: kahit na ang anumang kahilingan na tumatagal ng higit sa 5-10 segundo ay may problema na, at hindi na makakatulong ang magandang pag-shutdown...

Upang mas maunawaan kung ano ang mangyayari kapag natapos ang isang pod, tingnan lamang ang sumusunod na diagram:

Mga tip at trick ng Kubernetes: mga tampok ng magandang pagsara sa NGINX at PHP-FPM

A1, B1 - Pagtanggap ng mga pagbabago tungkol sa estado ng apuyan
A2 - Pag-alis SIGTERM
B2 - Pag-alis ng pod mula sa mga endpoint
B3 - Pagtanggap ng mga pagbabago (nagbago ang listahan ng mga endpoint)
B4 - I-update ang mga panuntunan sa iptables

Pakitandaan: ang pagtanggal sa endpoint pod at pagpapadala ng SIGTERM ay hindi nangyayari nang sunud-sunod, ngunit kahanay. At dahil sa hindi kaagad natatanggap ng Ingress ang na-update na listahan ng Mga Endpoint, ang mga bagong kahilingan mula sa mga kliyente ay ipapadala sa pod, na magdudulot ng 500 error sa panahon ng pagwawakas ng pod. (para sa mas detalyadong materyal sa isyung ito, kami isinalin). Ang problemang ito ay kailangang malutas sa mga sumusunod na paraan:

  • Magpadala ng Koneksyon: malapit sa mga header ng tugon (kung ito ay may kinalaman sa isang HTTP na application).
  • Kung hindi posibleng gumawa ng mga pagbabago sa code, ang sumusunod na artikulo ay naglalarawan ng solusyon na magbibigay-daan sa iyong iproseso ang mga kahilingan hanggang sa katapusan ng magandang panahon.

Teorya. Paano tinapos ng NGINX at PHP-FPM ang kanilang mga proseso

nginx

Magsimula tayo sa NGINX, dahil ang lahat ay higit pa o hindi gaanong halata dito. Sumisid sa teorya, nalaman namin na ang NGINX ay may isang master na proseso at ilang "manggagawa" - ito ay mga proseso ng bata na nagpoproseso ng mga kahilingan ng kliyente. Ang isang maginhawang opsyon ay ibinigay: gamit ang command nginx -s <SIGNAL> wakasan ang mga proseso alinman sa mabilis na pag-shutdown o magandang shutdown mode. Malinaw, ito ay ang huling opsyon na interesado sa amin.

Kung gayon ang lahat ay simple: kailangan mong idagdag sa preStop-hook isang command na magpapadala ng magandang shutdown signal. Magagawa ito sa Deployment, sa container block:

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

Ngayon, kapag nag-shut down ang pod, makikita natin ang sumusunod sa mga log ng container ng 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

At ito ang mangangahulugan ng kailangan natin: Hinihintay ng NGINX na makumpleto ang mga kahilingan, at pagkatapos ay papatayin ang proseso. Gayunpaman, sa ibaba ay isasaalang-alang din natin ang isang karaniwang problema dahil sa kung saan, kahit na may utos nginx -s quit hindi tama ang pagwawakas ng proseso.

At sa yugtong ito ay tapos na tayo sa NGINX: hindi bababa sa mula sa mga log maaari mong maunawaan na ang lahat ay gumagana ayon sa nararapat.

Ano ang pakikitungo sa PHP-FPM? Paano nito pinangangasiwaan ang magandang pagsasara? Alamin natin ito.

Ang PHP-FPM

Sa kaso ng PHP-FPM, mayroong kaunting impormasyon. Kung tumutok ka sa opisyal na manwal ayon sa PHP-FPM, sasabihin nito na ang mga sumusunod na signal ng POSIX ay tinatanggap:

  1. SIGINT, SIGTERM - mabilis na pagsara;
  2. SIGQUIT β€” magandang pagsara (kung ano ang kailangan natin).

Ang natitirang mga signal ay hindi kinakailangan sa gawaing ito, kaya aalisin namin ang kanilang pagsusuri. Upang wakasan nang tama ang proseso, kakailanganin mong isulat ang sumusunod na preStop hook:

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

Sa unang tingin, ito lang ang kailangan para magsagawa ng magandang pagsara sa parehong mga lalagyan. Gayunpaman, ang gawain ay mas mahirap kaysa sa tila. Nasa ibaba ang dalawang kaso kung saan hindi gumana ang magandang pagsasara at nagdulot ng panandaliang hindi available na proyekto sa panahon ng deployment.

Magsanay. Mga posibleng problema sa magandang pagsasara

nginx

Una sa lahat, kapaki-pakinabang na tandaan: bilang karagdagan sa pagpapatupad ng utos nginx -s quit May isa pang yugto na dapat bigyang pansin. Nakatagpo kami ng isyu kung saan magpapadala pa rin ang NGINX ng SIGTERM sa halip na ang signal ng SIGQUIT, na nagiging sanhi ng mga kahilingan na hindi makumpleto nang tama. Ang mga katulad na kaso ay matatagpuan, halimbawa, dito. Sa kasamaang palad, hindi namin matukoy ang partikular na dahilan para sa pag-uugali na ito: may hinala tungkol sa bersyon ng NGINX, ngunit hindi ito nakumpirma. Ang sintomas ay na-obserbahan ang mga mensahe sa mga log ng lalagyan ng NGINX: "bukas na socket #10 ang natitira sa koneksyon 5", pagkatapos ay huminto ang pod.

Maaari naming obserbahan ang ganoong problema, halimbawa, mula sa mga tugon sa Ingress na kailangan namin:

Mga tip at trick ng Kubernetes: mga tampok ng magandang pagsara sa NGINX at PHP-FPM
Mga tagapagpahiwatig ng mga status code sa oras ng pag-deploy

Sa kasong ito, nakakatanggap lang kami ng 503 error code mula mismo sa Ingress: hindi nito ma-access ang NGINX container, dahil hindi na ito naa-access. Kung titingnan mo ang mga log ng container na may NGINX, naglalaman ang mga ito ng sumusunod:

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

Matapos baguhin ang stop signal, ang lalagyan ay nagsisimulang huminto nang tama: ito ay nakumpirma ng katotohanan na ang 503 error ay hindi na sinusunod.

Kung makatagpo ka ng katulad na problema, makatuwirang malaman kung anong stop signal ang ginagamit sa lalagyan at kung ano ang eksaktong hitsura ng preStop hook. Ito ay lubos na posible na ang dahilan ay namamalagi tiyak dito.

PHP-FPM... at higit pa

Ang problema sa PHP-FPM ay inilarawan sa isang maliit na paraan: hindi ito naghihintay para sa pagkumpleto ng mga proseso ng bata, tinatapos nito ang mga ito, kung kaya't ang 502 na mga error ay nangyayari sa panahon ng pag-deploy at iba pang mga operasyon. Mayroong ilang mga ulat ng bug sa bugs.php.net mula noong 2005 (hal dito ΠΈ dito), na naglalarawan sa problemang ito. Ngunit malamang na wala kang makikita sa mga log: iaanunsyo ng PHP-FPM ang pagkumpleto ng proseso nito nang walang anumang mga error o notification ng third-party.

Ito ay nagkakahalaga ng paglilinaw na ang problema mismo ay maaaring depende sa isang mas maliit o mas malaking lawak sa application mismo at maaaring hindi magpakita mismo, halimbawa, sa pagsubaybay. Kung nakatagpo mo ito, isang simpleng solusyon ang unang naiisip: magdagdag ng preStop hook na may sleep(30). Papayagan ka nitong kumpletuhin ang lahat ng mga kahilingan na nauna (at hindi kami tumatanggap ng mga bago, dahil pod na may kakayahan na Pagwawakas), at pagkatapos ng 30 segundo ang pod mismo ay magtatapos sa isang signal SIGTERM.

Ito ay lumiliko ang na lifecycle para ang lalagyan ay magiging ganito:

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

Gayunpaman, dahil sa 30-segundo sleep tayo Mahigpit dadagdagan namin ang oras ng pag-deploy, dahil wawakasan ang bawat pod minimum 30 segundo, na masama. Ano ang maaaring gawin tungkol dito?

Bumaling tayo sa partido na responsable para sa direktang pagpapatupad ng aplikasyon. Sa aming kaso ito ay Ang PHP-FPMAlin bilang default ay hindi sinusubaybayan ang pagpapatupad ng mga proseso ng bata nito: Ang master na proseso ay tinapos kaagad. Maaari mong baguhin ang gawi na ito gamit ang direktiba process_control_timeout, na tumutukoy sa mga limitasyon ng oras para sa mga proseso ng bata na maghintay para sa mga signal mula sa master. Kung itatakda mo ang value sa 20 segundo, sasaklawin nito ang karamihan sa mga query na tumatakbo sa container at ititigil ang master process kapag nakumpleto na ang mga ito.

Sa kaalamang ito, bumalik tayo sa ating huling problema. Gaya ng nabanggit, ang Kubernetes ay hindi isang monolitikong platform: ang komunikasyon sa pagitan ng iba't ibang bahagi nito ay tumatagal ng ilang oras. Ito ay totoo lalo na kapag isinasaalang-alang namin ang pagpapatakbo ng Ingresses at iba pang nauugnay na mga bahagi, dahil dahil sa ganoong pagkaantala sa oras ng pag-deploy ay madaling makakuha ng surge ng 500 mga error. Halimbawa, ang isang error ay maaaring mangyari sa yugto ng pagpapadala ng isang kahilingan sa isang upstream, ngunit ang "time lag" ng pakikipag-ugnayan sa pagitan ng mga bahagi ay medyo maikli - wala pang isang segundo.

Samakatuwid Sa kabuuan sa nabanggit na direktiba process_control_timeout maaari mong gamitin ang sumusunod na konstruksyon para sa lifecycle:

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

Sa kasong ito, babayaran namin ang pagkaantala gamit ang utos sleep at hindi masyadong pinapataas ang oras ng pag-deploy: may kapansin-pansin bang pagkakaiba sa pagitan ng 30 segundo at isa?.. Sa katunayan, ito ang process_control_timeoutAt lifecycle ginagamit lamang bilang isang "safety net" sa kaso ng lag.

Sa pangkalahatan, ang inilarawang gawi at ang kaukulang workaround ay nalalapat hindi lamang sa PHP-FPM. Ang isang katulad na sitwasyon ay maaaring lumitaw sa isang paraan o iba kapag gumagamit ng ibang mga wika/balangkas. Kung hindi mo maaayos ang magandang pag-shutdown sa ibang mga paraan - halimbawa, sa pamamagitan ng muling pagsusulat ng code upang maiproseso ng application nang tama ang mga signal ng pagwawakas - maaari mong gamitin ang inilarawang paraan. Maaaring hindi ito ang pinakamaganda, ngunit gumagana ito.

Magsanay. I-load ang pagsubok upang suriin ang operasyon ng pod

Ang pagsubok sa pag-load ay isa sa mga paraan upang masuri kung paano gumagana ang lalagyan, dahil pinalalapit ito ng pamamaraang ito sa mga tunay na kondisyon ng labanan kapag binisita ng mga user ang site. Upang subukan ang mga rekomendasyon sa itaas, maaari mong gamitin Yandex.Tankom: Saklaw nito ang lahat ng ating pangangailangan nang perpekto. Ang mga sumusunod ay mga tip at rekomendasyon para sa pagsasagawa ng pagsubok na may malinaw na halimbawa mula sa aming karanasan salamat sa mga graph ng Grafana at Yandex.Tank mismo.

Ang pinakamahalagang bagay dito ay suriin ang mga pagbabago nang hakbang-hakbang. Pagkatapos magdagdag ng bagong pag-aayos, patakbuhin ang pagsubok at tingnan kung nagbago ang mga resulta kumpara sa huling pagtakbo. Kung hindi, magiging mahirap na tukuyin ang mga hindi epektibong solusyon, at sa katagalan maaari lamang itong makapinsala (halimbawa, dagdagan ang oras ng pag-deploy).

Ang isa pang nuance ay ang pagtingin sa mga log ng lalagyan sa panahon ng pagwawakas nito. Nakatala ba doon ang impormasyon tungkol sa magandang pagsasara? Mayroon bang anumang mga error sa mga log kapag ina-access ang iba pang mga mapagkukunan (halimbawa, sa isang kalapit na lalagyan ng PHP-FPM)? Mga error sa application mismo (tulad ng sa kaso ng NGINX na inilarawan sa itaas)? Umaasa ako na ang panimulang impormasyon mula sa artikulong ito ay makakatulong sa iyo na mas maunawaan kung ano ang mangyayari sa lalagyan sa panahon ng pagwawakas nito.

Kaya, ang unang test run ay naganap nang wala lifecycle at walang karagdagang mga direktiba para sa server ng application (process_control_timeout sa PHP-FPM). Ang layunin ng pagsusulit na ito ay upang matukoy ang tinatayang bilang ng mga error (at kung mayroon man). Gayundin, mula sa karagdagang impormasyon, dapat mong malaman na ang average na oras ng pag-deploy para sa bawat pod ay mga 5-10 segundo hanggang sa ganap itong maging handa. Ang mga resulta ay:

Mga tip at trick ng Kubernetes: mga tampok ng magandang pagsara sa NGINX at PHP-FPM

Ang panel ng impormasyon ng Yandex.Tank ay nagpapakita ng spike ng 502 na mga error, na naganap sa oras ng pag-deploy at tumagal sa average na hanggang 5 segundo. Marahil ito ay dahil ang mga kasalukuyang kahilingan sa lumang pod ay winakasan kapag ito ay winakasan. Pagkatapos nito, lumitaw ang 503 mga error, na resulta ng isang tumigil na lalagyan ng NGINX, na bumaba din ng mga koneksyon dahil sa backend (na pumigil sa Ingress na kumonekta dito).

Tingnan natin kung paano process_control_timeout sa PHP-FPM ay makakatulong sa amin na maghintay para sa pagkumpleto ng mga proseso ng bata, ibig sabihin. itama ang gayong mga pagkakamali. I-deploy muli gamit ang direktiba na ito:

Mga tip at trick ng Kubernetes: mga tampok ng magandang pagsara sa NGINX at PHP-FPM

Wala nang mga error sa panahon ng 500th deployment! Ang deployment ay matagumpay, maganda ang pagsasara.

Gayunpaman, ito ay nagkakahalaga ng pag-alala sa isyu sa Ingress container, isang maliit na porsyento ng mga error kung saan maaari naming matanggap dahil sa isang time lag. Upang maiwasan ang mga ito, ang natitira ay magdagdag ng isang istraktura na may sleep at ulitin ang deployment. Gayunpaman, sa aming partikular na kaso, walang mga pagbabagong nakikita (muli, walang mga error).

Konklusyon

Upang wakasan ang proseso nang maganda, inaasahan namin ang sumusunod na gawi mula sa aplikasyon:

  1. Maghintay ng ilang segundo at pagkatapos ay ihinto ang pagtanggap ng mga bagong koneksyon.
  2. Maghintay para sa lahat ng mga kahilingan upang makumpleto at isara ang lahat ng keepalive na koneksyon na hindi nagsasagawa ng mga kahilingan.
  3. Tapusin ang iyong proseso.

Gayunpaman, hindi lahat ng application ay maaaring gumana sa ganitong paraan. Ang isang solusyon sa problema sa mga katotohanan ng Kubernetes ay:

  • pagdaragdag ng pre-stop hook na maghihintay ng ilang segundo;
  • pag-aaral ng configuration file ng aming backend para sa naaangkop na mga parameter.

Ang halimbawa sa NGINX ay nilinaw na kahit na ang isang application na dapat sa simula ay magproseso ng mga signal ng pagwawakas nang tama ay maaaring hindi gawin ito, kaya kritikal na suriin para sa 500 mga error sa panahon ng pag-deploy ng application. Nagbibigay-daan din ito sa iyo na tingnan ang problema nang mas malawak at hindi tumuon sa isang pod o lalagyan, ngunit tingnan ang buong imprastraktura sa kabuuan.

Bilang isang tool sa pagsubok, maaari mong gamitin ang Yandex.Tank kasabay ng anumang sistema ng pagsubaybay (sa aming kaso, ang data ay kinuha mula sa Grafana na may Prometheus backend para sa pagsubok). Ang mga problema sa magandang pag-shutdown ay malinaw na nakikita sa ilalim ng mabibigat na pagkarga na maaaring mabuo ng benchmark, at ang pagsubaybay ay nakakatulong na pag-aralan ang sitwasyon nang mas detalyado sa panahon o pagkatapos ng pagsubok.

Bilang tugon sa feedback sa artikulo: ito ay nagkakahalaga ng pagbanggit na ang mga problema at solusyon ay inilarawan dito na may kaugnayan sa NGINX Ingress. Para sa iba pang mga kaso, mayroong iba pang mga solusyon, na maaari naming isaalang-alang sa mga sumusunod na materyales ng serye.

PS

Iba pa mula sa serye ng mga tip at trick ng K8:

Pinagmulan: www.habr.com

Magdagdag ng komento