TĂŒĂŒpiline tingimus CI/CD rakendamisel Kubernetesis: rakendus peab suutma enne tĂ€ielikku seiskamist mitte vastu vĂ”tta uusi kliendi pĂ€ringuid, ja mis kĂ”ige tĂ€htsam â edukalt lĂ”petama juba olemasolevad.

Selle tingimuse tÀitmine vÔimaldab saavutada nullist seisakut juurutamise ajal. Siiski, isegi populaarsete kombinatsioonide (nagu NGINX ja PHP-FPM) kasutamisel vÔib esineda raskusi, mis viivad igas juurutamises vigade suurenemiseni...
Teooria. Kuidas pod elab
Pod'i elutsĂŒkli kohta oleme juba avaldanud . Arutletud teema kontekstis huvitab meid jĂ€rgmine: hetkel, kui pod liigub seisu Terminating, lakkavad sellele uued pĂ€ringud (pod teenuse lĂ”pp-punktide loendist). Seega, et vĂ€ltida seiskumist juurutamise ajal, piisab meie poolt rakenduse korrektse sulgemise probleemi lahendamisest.
Samuti tuleb meeles pidada, et grace period vaikimisi on : pĂ€rast seda pod lĂ”petatakse ja rakendus peab olema suutnud töödelda kĂ”ik pĂ€ringud enne seda aega. MĂ€rkus: kuigi iga pĂ€ring, mis kestab rohkem kui 5â10 sekundit, on juba probleemne ja graceful shutdown ei aita enamâŠ
Kuna paremini aru saada, mis juhtub, kui pod lÔpetab oma töö, piisab jÀrgnevate skeemide uurimisest:

A1, B1 â Teabe saamine podâi seisundi kohta
A2 â SIGTERMâi saatmine
B2 â Podâi eemaldamine endpointâidest
B3 â Muudatuste saamine (endpointâide nimekiri muutus)
B4 â iptables reeglite vĂ€rskendamine
Pange tĂ€hele: endpointâi podâi eemaldamine ja SIGTERMâi saatmine ei toimu jĂ€rjestikku, vaid paralleelselt. Ja kuna Ingress ei saa koheselt uuendatud endpointâide nimekirja, saadetakse podâile uusi pĂ€ringuid klientidelt, mis pĂ”hjustab 500 vigu podâi lĂ”petamise ajal. (ĂŒksikasjalikum materjal selle kĂŒsimuse kohta me) ). Selle probleemi lahendamiseks tuleb kasutada jĂ€rgmisi viise:
- Saata vastuse pÀistes Connection: close (kui see puudutab HTTP-rakendust).
- Kui hĂ”lpsat muutmist koodis ei ole, siis allpool artiklis on kirjeldatud lahendust, mis vĂ”imaldab pĂ€ringud lĂ”puni töödelda graceful periodâi jooksul.
Teooria. Kuidas NGINX ja PHP-FPM lÔpetavad oma protsessid
NGINX
Alustame NGINX-ist, kuna see on enam-vĂ€hem arusaadav. Teooriasse sĂŒvenedes saame teada, et NGINX-il on ĂŒks peaprotsess ja mitu 'töötajat' - need on alamprotsessid, mis töötlevad kliendipĂ€ringuid. On olemas mugav vĂ”imalus: kĂ€su abil nginx -s vĂ”ib protsesse lĂ”petada kas kiire sulgemise vĂ”i sujuva sulgemise reĆŸiimis. Ilmselgelt huvitab meid just viimane valik.
Edasi on kÔik lihtne: tuleb lisada kÀsk, mis saadab signaali sujuvaks sulgemiseks. Seda saab teha Deployment'is konteineri plokis:
lifecycle:
preStop:
exec:
command:
- /usr/sbin/nginx
- -s
- quitNĂŒĂŒd pod'i sulgemise hetkel nĂ€eme NGINX konteineri logides jĂ€rgmist:
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 Ja see tÀhendaks just seda, mida vajame: NGINX ootab pÀringute lÔpetamist, pÀrast mida tapab protsessi. Siiski kÀsitletakse allpool ka levinud probleemi, mille tÔttu isegi kÀsku nginx -s quit protsess lÔpetatakse vÀÀralt.
Ja on sel etapil NGINX lÔpetatud: vÀhemalt logide pÔhjal on nÀha, et kÔik töötab nagu peab.
Kuidas on lood PHP-FPM-iga? Kuidas see teostab graceful shutdown? Uurime lÀhemalt.
PHP-FPM
PHP-FPM-i puhul on teavet veidi vÀhem. Kui orienteeruda PHP-FPM-i kohta, siis seal rÀÀgitakse, et aktsepteeritakse jÀrgmisi POSIX-signaale:
-
SIGINT,SIGTERMâ kiire sulgemine; -
SIGQUITâ graceful shutdown (just see, mida vajame).
Teised signaalid ei ole antud ĂŒlesande jaoks vajalikud, seetĂ”ttu jĂ€tame nende analĂŒĂŒsi vahele. Protsessi korrektselt lĂ”petamiseks on vaja kirjutada jĂ€rgmine preStop-hook:
lifecycle:
preStop:
exec:
command:
- /bin/kill
- -SIGQUIT
- "1"Esmapilgul on see kĂ”ik, mis on vajalik graceful shutdowni teostamiseks mĂ”lemas konteineris. Siiski on ĂŒlesanne keerulisem, kui tundub. Allpool on kĂ€sitletud kahte juhtumit, kus graceful shutdown ei toiminud ja pĂ”hjustas projekti lĂŒhiajalise kĂ€ttesaamatuse deploy ajal.
Praktika. VÔimalikud probleemid graceful shutdowniga
NGINX
Esiteks on kasulik meeles pidada: lisaks kĂ€su tĂ€itmisele nginx -s quit on veel ĂŒks etapp, millele tuleks tĂ€helepanu pöörata. Oleme kohanud probleemi, kus NGINX saatis SIGTERM signaali asemel SIGQUIT, mistĂ”ttu pĂ€ringud ei lĂ”petatud Ă”igesti. Sarnaseid juhtumeid vĂ”ib leida nĂ€iteks . Kahjuks ei suudnud me selle kĂ€itumise tĂ€pset pĂ”hjust tuvastada: kahtlustati NGINX versioone, kuid see ei leidnud kinnitust. SĂŒmtomid nĂ€itasid, et NGINX konteineri logides esines sĂ”numeid «open socket #10 left in connection 5», pĂ€rast mida pod peatati.
Sellist probleemi vĂ”ime tĂ€heldada nĂ€iteks vajadusel Ingressâi vastustes:

Staatuskoodide nÀitajad juurutamise hetkel.
Sellisel juhul saame just 503 veakoodi ise Ingressâilt: ta ei saa pöörduda NGINX konteineri poole, kuna see on juba kĂ€ttesaamatu. Kui vaatame NGINX konteineri logisid, siis neis on jĂ€rgmine:
[alert] 13939#0: *154 open socket #3 left in connection 16
[alert] 13939#0: *168 open socket #6 left in connection 13PÀrast stopp-signaali muutmist hakkab konteiner Ôigesti peatuma: seda kinnitab fakt, et 503 viga enam ei esine.
Kui olete sarnase probleemiga kokku puutunud, on mÔistlik uurida, millist stop-signaali konteiner kasutab ja kuidas tÀpselt preStop-hook vÀlja nÀeb. On tÀielikult vÔimalik, et pÔhjus peitub just selles.
PHP-FPM... ja mitte ainult
PHP-FPM probleem on triviaalne: see ei oota alamprotsesside lÔpetamist, lÔpetab need, mis pÔhjustab 502 vigu juurutamise ja teiste operatsioonide ajal. bugs.php.net lehtedel on alates 2005. aastast mitu veateadet (nÀiteks, ja ), kus see probleem on kirjeldatud. Logides te tÔenÀoliselt midagi ei nÀe: PHP-FPM teatab oma protsessi lÔpetamisest ilma vigadeta vÔi teiste teadeteta.
On vaja tÀpsustada, et probleem vÔib vÀhemal vÔi rohkemal mÀÀral sÔltuda rakendusest ise ja ei pruugi nÀiteks monitooringus ilmnena. Kui te ikkagi selle probleemiga kokku puutute, siis tuleb esimesena mÔttesse lihtne lahendus: lisage preStop-hook, mis sisaldab sleep(30). See vÔimaldab lÔpetada kÔik varasemad pÀringud (ja uusi me ei aktsepteeri, kuna pod juba on olekus Terminating), ja pÀrast 30 sekundi möödumist lÔpetab pod endise signaaliga SIGTERM.
Selgub, et lifecycle konteineri vaatab vÀlja jÀrgmiselt:
lifecycle:
preStop:
exec:
command:
- /bin/sleep
- "30" Kuid 30-sekundilise mÀÀrangu tÔttu uni meie tuleb kasutada tÀiendavat aega juurutamiseks, kuna iga pod lÔpetatakse vÀhemalt 30 sekundi jooksul, mis on probleem. Mida sellega teha saab?
Vaatame, kuidas see rakenduse tĂ€itmise poolest toimib. Meie puhul on see PHP-FPM, mis ei jĂ€lgi oma child-protsesside tĂ€itmise tĂ€htaegu: emaprosess lĂ”petatakse kohe. Selle kĂ€itumise muutmiseks saab kasutada direktiivi process_control_timeout, mis mÀÀrab ajapiirangud emaprosessi signaalide ootamiseks tĂŒtardelt. Kui vÀÀrtus seada 20 sekundiks, katab see enamik konteineris tĂ€idetavaid pĂ€ringuid, ja nende lĂ”ppedes lĂ”petatakse emaprosess.
NĂŒĂŒd, kui me teame seda, pöördume tagasi viimase mure juurde. Nagu juba mainitud, ei ole Kubernetes monoliitne platvorm: erinevate komponentide vahelise suhtluse jaoks on vajalik teatud aeg. See on eriti oluline, kui vaatame Ingresside ja teiste seotud komponentide tööd, kuna sellise viivituse tĂ”ttu on juurutamise hetkel lihtne saada 500-koodiga vigu. NĂ€iteks vĂ”ib viga tekkida upstream'i pĂ€ringu saatmise etapis, kuid «ajalaagri» komponentide vahel on ĂŒsna lĂŒhike â vĂ€hem kui sekund.
SeetÔttu, koos juba mainitud direktiiviga saab kasutada jÀrgmist struktuuri process_control_timeout vÔid kasutada jÀrgmist konstruktsiooni lifecycle:
lifecycle:
preStop:
exec:
command: ["/bin/bash","-c","/bin/sleep 1; kill -QUIT 1"] Sel juhul kompenseerime viivituse kĂ€suga uni ja ei suurenda juurutamise aega oluliselt: kas vahe 30 sekundi ja ĂŒhe sekundi vahel ei ole mĂ€rgatav? Ăksik "pĂ”hitoiming" on see, mis tegelikult töötab, process_control_timeout, ja lifecycle kasutatakse ainult kui «tagavarameetmena» viivituse korral.
Ăldiselt peab kirjeldatud kĂ€itumine ja vastav lahendus ei puuduta ainult PHP-FPM. Sarnane olukord vĂ”ib teistes programmeerimiskeeltes/raamistikutes ette tulla. Kui ei Ă”nnestu muude meetoditega graceful shutdown'i parandada â nĂ€iteks kirjutades koodi ĂŒmber, et rakendus töötaks korrektselt lĂ”petamissignaale töötlejana, â siis saab rakendada kirjeldatud meetodit. Kuigi see ei ole kĂ”ige esteetilisem, toimib see.
Praktika. Koormustestimine pod'i töö kontrollimiseks
Koormustestimine on ĂŒks meetod, kuidas kontrollida konteineri toimimist, kuna see protseduur lĂ€heneb reaalsele lahingule, kui kĂŒlastajad sisenevad saidile. Ălaltoodud soovituste testimiseks vĂ”ib kasutada : see katab suurepĂ€raselt kĂ”ik meie vajadused. Allpool on toodud soovitused ja soovitused testimise lĂ€biviimiseks koos visuaalse â tĂ€nu Grafana graafikutele ja Yandex.Tanki enda nĂ€itete jĂ€rgi â kogemusega.
KĂ”ige olulisem on siin â kontrollida muudatusi samm-sammult. PĂ€rast uue paranduse lisamist kĂ€ivitage testimine ja vaadake, kas tulemused on vĂ”rreldes eelmise kĂ€ivitusega muutunud. Vastasel juhul on raske tuvastada ebaefektiivseid lahendusi ning pikemas perspektiivis vĂ”ib see isegi kahjustada (nĂ€iteks suurendada juurutamise aega).
Teine nĂŒanss on jĂ€lgida konteineri logisid selle sulgemise ajal. Kas seal on teadet graceful shutdown'i kohta? Kas logides on vigu teistele ressurssidele pöördumisel (nĂ€iteks naaberkonteinerile PHP-FPM)? Kas rakendusel endal on vigu (nagu eespool mainitud NGINX-i nĂ€ites)? Loodan, et selle artikli sissejuhatav teave aitab paremini aru saada, mis toimub konteineri sulgemise ajal.
Nii et testimise esimene kĂ€ivitus toimus ilma lifecycle ja ilma tĂ€iendavate juhistega rakenduste serverile (process_control_timeout PHP-FPM-is). Selle testi eesmĂ€rk oli tuvastada ligikaudne vigade arv (ja kas need on ĂŒldse olemas). Samuti tuleks teada, et iga pod'i keskmine juurutusaeg oli umbes 5-10 sekundit tĂ€ieliku valmiduse saavutamiseks. Tulemused on jĂ€rgmised:

Yandex.Tank'i infopaneelil on nĂ€ha 502 vigade tĂ”us, mis toimus juurutamise ajal ja kestis keskmiselt kuni 5 sekundit. Kahtlustatakse, et katkestati olemasolevad pĂ€ringud vana pod'i poole, kui see lĂ”petati. PĂ€rast seda ilmusid 503 vead, mis olid seotud NGINX'i peatatud konteineriga, mis katkestas ka ĂŒhendused tĂ”ttu backend'i (mille tĂ”ttu ei suutnud Ingress sellele juurde pÀÀseda).
Vaadakem, kuidas process_control_timeout PHP-FPM aitab meil oodata child-protsesside lÔppu, t. e. selliste vigade parandamine. Uuesti juurutamine, kasutades seda kÀsku:

Juurutamise ajal 500 vigu enam ei ole! Juurutamine lÀheb sujuvalt, graceful shutdown töötab.
Kuid tasub meeles pidada Ingress-konteinerite momenti, millest me saame vÀikese protsendi vigu, mida vÔime saada ajutise viivituse tÔttu. Nende vÀltimiseks jÀÀb lisada konstruktsioon koos uni ja korduda juurutamist. Siiski, meie konkreetses olukorras ei olnud muudatusi mÀrgata (vigu ei ole jÀlle).
KokkuvÔte
Protsessi korrektseks lÔpetamiseks ootame rakenduselt jÀrgmist kÀitumist:
- Oodata paar sekundit, pĂ€rast mida lĂ”petada uute ĂŒhenduste vastuvĂ”tmine.
- Oodata kĂ”igi pĂ€ringute lĂ”petamist ja sulgeda kĂ”ik keepalive-ĂŒhendused, mis pĂ€ringute tĂ€itmisega ei tegele.
- LÔpetage oma protsess.
Kuid mitte kĂ”ik rakendused ei oska sellisel moel töötada. Ăks lahendus probleemile Kubernetes'e kontekstis on:
- eel-peatamise hook'i lisamine, mis ootab paar sekundit;
- Meie taustsĂŒsteemi konfiguratsioonifaili uurimine vastavate parameetrite osas.
NGINX'i nĂ€ide aitab mĂ”ista, et isegi rakendus, mis peaks algselt Ă”igesti lĂ”petamissignaale töötlema, vĂ”ib seda mitte teha, seetĂ”ttu on kriitiliselt oluline jĂ€lgida 500 vigu rakenduse juurutamisel. See vĂ”imaldab vaadata probleemi laiemalt ning mitte keskenduda vaid ĂŒhele pod'ile vĂ”i konteinerile, vaid vaadelda kogu infrastruktuuri tervikuna.
Testimise tööriistana vĂ”ib kasutada Yandex.Tanki koos mis tahes jĂ€lgimissĂŒsteemiga (meie juhul vĂ”eti testiks andmed Grafanast, kus taust oli Prometheus). Probleemid Ă”igeaegse sulgemisega paistavad hĂ€sti silma suurte koormuste korral, mida benchmark vĂ”ib genereerida, ja jĂ€lgimine aitab olukorda ĂŒksikasjalikumalt analĂŒĂŒsida katse ajal vĂ”i pĂ€rast seda.
Vastates tagasisidele artikli kohta: tuleb mÀrkida, et probleemid ja nende lahendused on siin kirjeldatud NGINX Ingress'i kontekstis. Teiste juhtumite jaoks on olemas erinevad lahendused, mida vÔime arutada jÀrgmistes materjalides.
P.S.
Teine osa K8s nÀpunÀidetest ja nippidest:
- «»;
- «»;
- «»;
- «».
Allikas: habr.com
