Paano Namin Nakuha ang Deploys sa Zero na 502 Error
Sa tuwing nagde-deploy kami ng backend ng Mythex, ilang request ang pumapalya sa 502 Bad Gateway. Hindi marami, pero ang 502 habang nagde-deploy ay nangangahulugang walang nangyari sa click ng isang tao, o saglit na natigil ang build na binabantayan nila. Dalawang magkahiwalay na problema ang natunton namin: isang self-healing watchdog na nakikipaglaban sa aming mga deploy, at ang puwang sa pagitan ng pagtigil ng pakikinig ng server at ng pagtigil ng load balancer sa pagpapadala rito ng trapiko. Sa tatlong sunod-sunod na production deploy pagkatapos ng mga ayos, bumaba ang bilang mula 10 patungong 6 at naging 0.
Ang setup
Tumatakbo ang aming API at orchestrator bilang mga container sa iilang server sa likod ng AWS Application Load Balancer (ALB). Ina-update ng isang deploy ang isang server sa bawat pagkakataon: nilalabas nito ang mga lumang container, sinisimulan ang mga bago, at lumilipat sa susunod. May watchdog sa bawat server na sinusuri kung nakatakbo ang mga container at nire-restart ang anumang hindi. Umiiral ito para bumalik ang prosesong nag-crash nang alas-3 ng madaling-araw nang walang sinumang nagigising.
Pareho silang makatwiran nang mag-isa. Magkasama, hindi.
Problema 1: nakipagkarera ang watchdog sa deploy
Tumatakbo ang watchdog minsan kada minuto at "nagpapagaling" sa isang nabigong health check. Sinasadyang inilalabas ng deploy ang mga serbisyo nang hanggang 150 segundo, at sa panahong iyon, dapat pumalya ang health check. Kaya nagpapaputok ang watchdog sa bawat deploy.
Karaniwan, natatalo nito ang karera nang walang pinsala. Minsan, nanalo ito: isinulat nito muli ang compose file (ang paglalarawan kung aling container ang dapat tumakbo) sa gitna ng isang deploy, at nag-iwan ng server na walang gateway container, habang iniulat ng deploy job na matagumpay ito.
Ang mga ayos:
- Isang lock. Ngayon, kinukuha ng deploy ang parehong file lock ng bootstrap script ng server, kaya hindi sabay na makakatakbo ang watchdog at ang deploy.
- Apat na strike, hindi isa. Kailangan ng watchdog ng apat na sunod-sunod na nabigong check bago ito kumilos. Hindi na mukhang patay ang serbisyong nilalabas.
- Isang depinisyon. Ang compose file ay may iisang pinagmulan na ngayon, na ipinapadala sa mga server, sa halip na mga kopyang maaaring magkaiba.
- I-verify, huwag pagkatiwalaan. Hindi na pumapasa ang deploy job dahil lang nagbalik ng resulta ang isang command. Sinusuri muna nito ang API, orchestrator, gateway at publish worker ng bawat server bago ito maaaring magtagumpay.
Problema 2: umalis ang server bago ang trapiko
Mas tahimik ang ikalawang problema. Kapag nilalabas ng deploy ang isang server, nagsisimulang sumagot ang server sa health endpoint nito ng 503 para tanggalin ito ng load balancer sa rotation. Ginawa ito ng sa amin nang 5 segundo, pagkatapos nagsara.
Pero sinusuri lang ng ALB ang kalusugan paminsan-minsan, at kailangan pa nito ng ilang sunod-sunod na nabigong check bago ihinto ang pagruruta sa isang target, hanggang humigit-kumulang 60 segundo mula simula hanggang dulo. Kaya sa humigit-kumulang 55 segundo, patuloy na nagpapadala ang load balancer ng totoong request sa isang server na tumigil nang makinig. Iyon ang aming mga 502.
Ang ayos ay gawing magkasundo ang dalawang timeline:
- Ngayon, sinusuri ng target group tuwing 5 segundo at minamarkahan ang target na hindi malusog pagkatapos ng 2 pagkabigo, humigit-kumulang 10 segundo bago ito mapansin.
- Patuloy na naghahatid ng totoong trapiko ang drain sa loob ng 15 segundo matapos magsimulang pumalya ang health checks, kaya laging may overlap.
- Nasa script na nagbibigay ng imprastraktura ang mga setting na ito, hindi sa console na minsang kinlick ng isang tao.
Pagpapanatiling magkasundo ang mga numero
Magkaugnay ang mga timeout na ganito. Dapat mas mahaba ang drain kaysa sa oras na kailangan ng balancer para mapansin; dapat matapos ang drain kasama ng shutdown bago pilit na patayin ang container. Madaling may magpalit ng isang numero pagkalipas ng anim na buwan at tahimik na maibalik ang bug.
Kaya isinulat namin ang mga ugnayang iyon bilang test: dapat mas mahaba ang drain kaysa sa health-check interval na pinarami ng failure threshold; dapat manatiling maikli ang oras para maibalik sa pagiging malusog; dapat magkasya ang buong drain sa stop grace period ng container. Kapag binago ang isang numero nang walang iba, pumapalya ang CI. Sinubukan din namin ang mismong mga test: binago ang bawat numero sa maling halaga at tiningnan kung nahuli ito ng isang test.
Madaling magkamali sa pagsukat ng deploy
Ang una naming sukat ay nagsabing humigit-kumulang 3% ang sira ng malinis na deploy. Hindi iyon totoo. Binibilang namin ang bawat hindi-200 na response, at ang 503 mula sa health endpoint habang nagdra-drain ay hindi pagkabigo. Ginagawa lang ng drain ang trabaho nito, na sinasabi sa balancer na huwag pumili ng server na iyon habang patuloy pa rin itong naghahatid ng totoong request.
Ang nararamdaman ng mga user ay 502: ang request ay walang naabot. Ang pagbilang lang sa mga iyon ang nagbigay ng tamang larawan: 10, pagkatapos 6, pagkatapos 0 sa tatlong production deploy.
Ang natutunan namin
- Kailangang alam ng self-healing ang tungkol sa planadong downtime. Ang watchdog na hindi kayang makilala ang deploy mula sa crash ay kalaunan ay makikipag-away sa deploy.
- Ang pagda-drain ay handshake. Dapat patuloy na maghatid ang server hanggang sa napatunayang tumigil nang magpadala ng trapiko ang load balancer, at ang timing na iyon ay itinatakda sa balancer, hindi sa server.
- Isulat ang mga invariant bilang test. Nagiging lipas ang mga operational na numero. Ang mga test na nag-uugnay sa mga ito sa isa't isa ay hindi.
- Magpasya kung ano ang susukatin bago sumukat. Maling sukatan ang "errors"; tamang sukatan ang "mga request na walang naabot".
Kung nagpa-publish ka ng app sa Mythex, walang nakikita sa iyo ang alinman dito, na siyang punto. Kaugnay: paano nananatiling live ang na-publish mong app habang nire-republish mo ito.