Kubernetes-en aholkuak eta trikimailuak: NGINX eta PHP-FPM-en itzaltze dotorearen ezaugarriak

Kubernetes-en CI/CD inplementatzeko baldintza arrunta: aplikazioak bezeroen eskaera berriak ez onartzeko gai izan behar du guztiz gelditu aurretik, eta garrantzitsuena, lehendik daudenak behar bezala osatzeko.

Kubernetes-en aholkuak eta trikimailuak: NGINX eta PHP-FPM-en itzaltze dotorearen ezaugarriak

Baldintza hau betetzeak inplementazioan zehar zero geldialdi-denbora lortzeko aukera ematen du. Hala ere, sorta oso ezagunak erabiltzen dituzunean ere (NGINX eta PHP-FPM bezalakoak), inplementazio bakoitzean errore ugari sortuko dituzten zailtasunak topa ditzakezu...

Teoria. Nola bizi den poda

Dagoeneko xeheki argitaratu dugu lekaren bizi-zikloari buruz Artikulu hau. Aztertutako gaiaren testuinguruan, honako hau interesatzen zaigu: leka egoeran sartzen den unean amaitutzat, eskaera berriak bidaltzeari uzten diote (pod kendu zerbitzuaren amaierako puntuen zerrendatik). Horrela, inplementazioan zehar geldialdi-denbora saihesteko, nahikoa da aplikazioa behar bezala geldiarazteko arazoa konpontzea.

Gogoratu behar duzu grazia-epe lehenetsia dela 30 segundo: honen ondoren, poda amaitu egingo da eta eskaerak epe hori baino lehen eskaera guztiak tramitatzeko denbora izan beharko du. Kontuan izan: 5-10 segundo baino gehiago hartzen dituen edozein eskaera dagoeneko arazotsua den arren, eta itzaltze dotoreak ez dio lagunduko...

Pod bat amaitzen denean zer gertatzen den hobeto ulertzeko, begiratu hurrengo diagrama:

Kubernetes-en aholkuak eta trikimailuak: NGINX eta PHP-FPM-en itzaltze dotorearen ezaugarriak

A1, B1 - Sutegiaren egoerari buruzko aldaketak jasotzea
A2 - Irteera SIGTERM
B2 - Pod bat amaierako puntuetatik kentzea
B3 - Aldaketak jasotzea (amaiera-puntuen zerrenda aldatu egin da)
B4 - Eguneratu iptables arauak

Kontuan izan: amaierako puntua ezabatzea eta SIGTERM bidaltzea ez da sekuentzialki gertatzen, paraleloan baizik. Eta Ingress-ek amaierako puntuen zerrenda eguneratua berehala jasotzen ez duenez, bezeroen eskaera berriak podra bidaliko dira, eta horrek 500 errore bat eragingo du pod amaitzean. (gai honi buruzko material zehatzagoa lortzeko, guk itzulia). Arazo hau modu hauetan konpondu behar da:

  • Bidali konexioa: itxi erantzunen goiburuetan (HTTP aplikazio bati badagokio).
  • Ezin bada kodean aldaketarik egin, hurrengo artikuluak eskaerak prozesatzeko aukera emango dizun irtenbide bat deskribatzen du epealdiaren amaiera arte.

Teoria. NGINX eta PHP-FPM-k nola amaitzen dituzten beren prozesuak

nginx

Has gaitezen NGINX-ekin, horrekin dena gutxi-asko agerikoa baita. Teorian murgilduta, NGINX-ek prozesu nagusi bat eta hainbat "langile" dituela ikasten dugu; hauek bezeroen eskaerak prozesatzen dituzten ume-prozesuak dira. Aukera eroso bat eskaintzen da: komandoa erabiltzea nginx -s <SIGNAL> amaitu prozesuak itzaltze azkarrean edo itzaltze dotorean. Jakina, azken aukera hori da interesatzen zaiguna.

Orduan dena erraza da: gehitu behar duzu preStop-hook itzaltze seinale dotorea bidaliko duen komandoa. Hau Deployment-en egin daiteke, edukiontzien blokean:

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

Orain, poda itzaltzen denean, NGINX edukiontzien erregistroetan honako hau ikusiko dugu:

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

Eta horrek esan nahi du behar duguna: NGINX-ek eskaerak amaitu arte itxarongo du, eta gero prozesua akabatzen du. Hala ere, behean arazo arrunt bat ere kontuan hartuko dugu, eta horren ondorioz, komandoarekin ere nginx -s quit prozesua gaizki amaitzen da.

Eta fase honetan NGINX-ekin amaitu dugu: erregistroetatik behintzat dena behar den moduan funtzionatzen duela uler dezakezu.

Zein da PHP-FPM-ekin? Nola kudeatzen du itzalaldi dotorea? Asma dezagun.

PHP-FPM

PHP-FPM-ren kasuan, informazio apur bat gutxiago dago. Arduratzen bazara eskuliburu ofiziala PHP-FPM-ren arabera, POSIX seinale hauek onartzen direla esango du:

  1. SIGINT, SIGTERM - itzaltze azkarra;
  2. SIGQUIT — itzalaldi dotorea (behar duguna).

Zeregin honetan gainerako seinaleak ez dira beharrezkoak, beraz, haien azterketa baztertuko dugu. Prozesua behar bezala amaitzeko, honako preStop hook idatzi beharko duzu:

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

Lehen begiratuan, hau da bi edukiontzietan itzaltze dotorea egiteko behar dena. Hala ere, zeregina dirudiena baino zailagoa da. Jarraian, itzaltze dotoreak funtzionatu ez duten eta proiektuaren epe laburreko erabilgarritasunik eza eragin duten bi kasu daude hedapenean.

Praktikatu. Arazo posibleak itzaltze dotorearekin

nginx

Lehenik eta behin, komenigarria da gogoratzea: komandoa exekutatzeaz gain nginx -s quit Badago beste etapa bat arreta jartzea merezi duena. Arazo bat aurkitu dugu NGINXek SIGQUIT seinalearen ordez SIGTERM bidaliko zuelako, eskaerak behar bezala ez betetzea eraginez. Antzeko kasuak aurki daitezke, adibidez, Hemen. Zoritxarrez, ezin izan dugu zehaztu jokabide honen arrazoi zehatza: bazegoen NGINX bertsioaren susmoa, baina ez zen baieztatu. Sintoma zen NGINX edukiontzien erregistroetan mezuak ikusi zirela: "Ireki 10. entxufea 5. konexioan geratzen da", ondoren leka gelditu zen.

Horrelako arazo bat ikus dezakegu, adibidez, behar ditugun Ingress-en erantzunetatik:

Kubernetes-en aholkuak eta trikimailuak: NGINX eta PHP-FPM-en itzaltze dotorearen ezaugarriak
Inplementazioaren unean egoera-kodeen adierazleak

Kasu honetan, Ingress beraren 503 errore-kode bat besterik ez dugu jasotzen: ezin du NGINX edukiontzira sartu, jada ez baita eskuragarri. NGINX-ekin edukiontzien erregistroak aztertzen badituzu, honako hauek dituzte:

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

Gelditzeko seinalea aldatu ondoren, edukiontzia behar bezala gelditzen hasten da: hori berresten du 503 akatsa jada ez dela ikusten.

Antzeko arazoren bat aurkitzen baduzu, zentzuzkoa da ontzian zein geldialdi-seinalea erabiltzen den eta preStop kakoaren itxura zehatza izatea. Litekeena da arrazoia hain zuzen ere horretan egotea.

PHP-FPM... eta gehiago

PHP-FPM-ren arazoa modu hutsal batean deskribatzen da: ez du haur-prozesuak amaitzen zain, amaitzen ditu, horregatik 502 akatsak gertatzen dira hedapenean eta beste eragiketetan. Bugs.php.net-en hainbat akats-txosten daude 2005az geroztik (adibidez Hemen и Hemen), arazo hau deskribatzen duena. Baina ziurrenik ez duzu ezer ikusiko erregistroetan: PHP-FPM-k bere prozesua amaitu dela iragarriko du akatsik edo hirugarrenen jakinarazpenik gabe.

Merezi du argitzea arazoa bera aplikazioaren beraren mende egon daitekeela neurri txikiagoan edo handiagoan eta agian ez dela agertzea, adibidez, monitorizazioan. Topatzen baduzu, konponbide sinple bat datorkit burura lehenik: gehitu preStop kako batekin sleep(30). Lehen zeuden eskaera guztiak betetzeko aukera emango dizu (eta ez dugu berririk onartzen, pod dagoeneko gai amaitutzat), eta 30 segundoren buruan leka bera seinale batekin amaituko da SIGTERM.

Izarrekin bihurtzen da lifecycle izan ere, edukiontzia honela izango da:

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

Hala ere, 30 segundoa dela eta sleep dugu biziki zabaltze-denbora handituko dugu, pod bakoitza amaitu egingo baita gutxieneko 30 segundo, hori txarra da. Zer egin daiteke honen aurrean?

Jo dezagun eskaera zuzeneko exekuzioaren arduradunarengana. Gure kasuan hala da PHP-FPMZein lehenespenez, ez du bere prozesu seme-alaben exekuzioa kontrolatzen: Prozesu nagusia berehala amaitzen da. Jokaera hori alda dezakezu zuzentaraua erabiliz process_control_timeout, haurraren prozesuek maisuaren seinaleen zain egoteko denbora-mugak zehazten dituena. Balioa 20 segundotan ezartzen baduzu, honek edukiontzian exekutatzen diren kontsulta gehienak estaliko ditu eta amaitutakoan prozesu nagusia geldituko da.

Ezagutza horrekin, itzul gaitezen gure azken arazora. Esan bezala, Kubernetes ez da plataforma monolitikoa: bere osagai ezberdinen arteko komunikazioak denbora pixka bat behar du. Hau bereziki egia da Sarrerak eta erlazionatutako beste osagai batzuen funtzionamendua kontuan hartzen dugunean, hedatzeko garaian atzerapen hori dela eta erraza baita 500 akatsen gorakada lortzea. Esate baterako, akats bat gerta daiteke eskaera bat upstream batera bidaltzeko fasean, baina osagaien arteko elkarrekintzaren "denbora-desfasea" nahiko laburra da - segundo bat baino gutxiago.

Hori dela eta, Guztira lehen aipatutako zuzentarauarekin process_control_timeout hurrengo eraikuntza erabil dezakezu lifecycle:

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

Kasu honetan, atzerapena komandoarekin konpentsatuko dugu sleep eta ez handitu hedapen-denbora: alde nabarmenik al dago 30 segundo eta bat artean?... Izan ere, da process_control_timeoutEta lifecycle "segurtasun-sare" gisa soilik erabiltzen da atzerapenaren kasuan.

Oro har deskribatutako portaera eta dagokion konponbidea PHP-FPM-i ez ezik. Antzeko egoera bat edo beste sor daiteke beste hizkuntza/esparruak erabiltzean. Ezin baduzu itzaltze dotorea beste modu batean konpondu - adibidez, kodea berridatziz, aplikazioak amaiera-seinaleak behar bezala prozesatu ditzan - deskribatutako metodoa erabil dezakezu. Agian ez da politena izango, baina funtzionatzen du.

Praktikatu. Karga-probak podaren funtzionamendua egiaztatzeko

Karga-probak edukiontzia nola funtzionatzen duen egiaztatzeko moduetako bat da, prozedura honek benetako borroka-baldintzetara hurbiltzen baitu erabiltzaileek gunea bisitatzen dutenean. Goiko gomendioak probatzeko, erabil dezakezu Yandex.Tankom: Gure behar guztiak primeran estaltzen ditu. Honako hauek dira probak egiteko aholkuak eta gomendioak gure esperientziaren adibide argi batekin Grafana eta Yandex.Tank-en grafikoei esker.

Hemen garrantzitsuena da egiaztatu aldaketak urratsez urrats. Konponketa berri bat gehitu ondoren, exekutatu proba eta ikusi emaitzak azken exekuzioarekin alderatuta aldatu diren. Bestela, zaila izango da eraginkortasunik gabeko irtenbideak identifikatzea, eta epe luzera kaltea baino ezin izango du egin (adibidez, zabaltze-denbora handitzea).

Beste ñabardura bat amaitzean edukiontzien erregistroak ikustea da. Itzaltze dotoreari buruzko informazioa grabatzen al da bertan? Erregistroetan akatsik al dago beste baliabide batzuk atzitzean (adibidez, aldameneko PHP-FPM edukiontzi batera)? Akatsak aplikazioan bertan (goian deskribatutako NGINX-en kasuan bezala)? Espero dut artikulu honetako sarrerako informazioa hobeto ulertzen lagunduko dizula edukiontziarekin amaitzean zer gertatzen den.

Beraz, lehen proba proba gabe egin zen lifecycle eta aplikazio zerbitzarirako jarraibide gehigarririk gabe (process_control_timeout PHP-FPM-n). Proba honen helburua akatsen gutxi gorabeherako kopurua identifikatzea zen (eta dauden ala ez). Gainera, informazio gehigarriaren arabera, jakin behar duzu pod bakoitzaren batez besteko inplementazio-denbora 5-10 segundo ingurukoa izan zela guztiz prest egon arte. Emaitzak hauek dira:

Kubernetes-en aholkuak eta trikimailuak: NGINX eta PHP-FPM-en itzaltze dotorearen ezaugarriak

Yandex.Tank informazio panelak 502 akatsen gorakada erakusten du, hedapen unean gertatu zena eta batez beste 5 segundora arte iraun zuena. Ustez, hau amaitzen ari zenean pod zaharrari lehendik zeuden eskaerak amaitzen ari zirelako izan zen. Honen ostean, 503 akats agertu ziren, gelditutako NGINX edukiontzi baten emaitza izan zena, eta horrek konexioak ere erori zituen backendaren ondorioz (Ingress-ek harekin konektatzea eragotzi zuen).

Ikus dezagun nola process_control_timeout PHP-FPM-n haur-prozesuak amaitu arte itxaroten lagunduko digu, hau da. zuzendu horrelako akatsak. Berriro zabaldu zuzentarau hau erabiliz:

Kubernetes-en aholkuak eta trikimailuak: NGINX eta PHP-FPM-en itzaltze dotorearen ezaugarriak

Ez dago akats gehiago 500. hedapenean! Inplementazioa arrakastatsua da, itzaltze dotorea da.

Hala ere, merezi du Ingress edukiontzien arazoa gogoratzea, denbora-tarte baten ondorioz jaso ditzakegun erroreen ehuneko txiki bat. Horiek saihesteko, egitura bat gehitzea besterik ez da geratzen sleep eta errepikatu hedapena. Hala ere, gure kasuan, ez zen aldaketarik ikusten (berriz ere, akatsik).

Ondorioa

Prozesua dotoretasunez amaitzeko, aplikazioaren portaera hau espero dugu:

  1. Itxaron segundo batzuk eta, gero, utzi konexio berriak onartzeari.
  2. Itxaron eskaera guztiak osatu arte eta itxi eskaerak exekutatzen ez diren keepalive konexio guztiak.
  3. Amaitu zure prozesua.

Hala ere, aplikazio guztiek ezin dute horrela funtzionatu. Kubernetes errealitateko arazoaren irtenbide bat hau da:

  • segundo batzuk itxarongo duen pre-stop kako bat gehitzea;
  • gure backendaren konfigurazio-fitxategia parametro egokiak aztertzea.

NGINX-ekin egindako adibideak argi uzten du hasieran amaiera-seinaleak behar bezala prozesatu behar dituen aplikazio batek ere ez duela halakorik egingo, beraz, ezinbestekoa da aplikazioa inplementatzean 500 errore egiaztatzea. Horrek, gainera, arazoa modu zabalagoan aztertzeko aukera ematen du eta ez ontzi edo edukiontzi bakarrean zentratu, baina azpiegitura osoa bere osotasunean aztertzeko aukera ematen du.

Proba-tresna gisa, Yandex.Tank erabil dezakezu edozein monitorizazio-sistemarekin batera (gure kasuan, datuak Grafana-tik hartu dira probarako Prometheus backend batekin). Erreferentziak sor ditzakeen karga astunetan argi eta garbi ikusten dira itzaltze dotoreko arazoak, eta monitorizazioak egoera zehatzago aztertzen laguntzen du proban zehar edo ondoren.

Artikuluari buruzko iritziei erantzunez: aipatzekoa da arazoak eta irtenbideak hemen deskribatzen direla NGINX Ingress-ekin lotuta. Beste kasu batzuetarako, badaude beste soluzio batzuk, serieko material hauetan kontuan izan ditzakegunak.

PS

K8s aholku eta trikimailu serieko beste batzuk:

Iturria: www.habr.com

Gehitu iruzkin berria