Health check zlyháva, hoci aplikácia funguje
Health check zlyhávajúci počas deploymentu, hoci aplikácia lokálne funguje bez problémov, má zvyčajne jednu z piatich príčin. Postupne overte binding, path, port, časovanie a závislosti v poradí, ktoré najrýchlejšie odhalí problém.
Existuje špecifický typ uviaznutia, pri ktorom health check zlyhávajúci pri deploymente zablokuje každé vydanie, hoci aplikácia je podľa všetkého, čo môžete overiť, úplne v poriadku. Funguje lokálne. Funguje lokálne v Dockeri. Logy ukazujú, že počúva. Platforma však hlási jedno zlyhanie za druhým, niekedy ich je aj dvadsať po sebe, pričom v access logu sa nikdy neobjaví žiadna požiadavka.
Práve tento posledný detail je dôležitý a okamžite zužuje okruh možností. Ak aplikácia požiadavku nikdy nezalogovala, kontrola sa k aplikácii vôbec nedostala — takže príčinu nevysvetľuje nič v kóde aplikácie.
Tu je päť príčin v poradí, ktoré najrýchlejšie odhalí problém.
1. Počúvate na localhoste
Toto je jednoznačne najčastejšia príčina a presne vysvetľuje príznak, že „nikdy neprichádza žiadna traffic“.
V kontajneri 127.0.0.1 znamená vlastný loopback tohto kontajnera. Health check prichádzajúci zvonka kontajnera sa k nemu nedostane. Proces počúva, logy to potvrdzujú, no socket je z každého relevantného miesta nedostupný.
// Unreachable from outside the container
app.listen(3000, '127.0.0.1')
// Correct
app.listen(3000, '0.0.0.0')
Frameworky sa v predvolených nastaveniach líšia a viaceré z nich toto nastavenie medzi major verziami zmenili. Overte si, na čo váš framework skutočne binduje, namiesto toho, aby ste sa spoliehali na to, čo si pamätáte.
# Confirm from inside the running container
dockup exec "ss -ltn || netstat -ltn" my-project/my-api
Ak listening address je 127.0.0.1:3000 namiesto 0.0.0.0:3000, problém ste našli a na ničom ďalšom v tomto zozname nezáleží.
2. Port, ktorý platforma kontroluje, nie je port, na ktorom obsluhujete požiadavky
Sú tu dva porty a ľahko si ich pomýlite: port, na ktorom proces počúva vo vnútri kontajnera, a port, na ktorý platforma smeruje traffic. Ak aplikácia načítava PORT z prostredia a niekde v Dockerfile máte natvrdo nastavený port 3000, tieto hodnoty sa môžu potichu rozísť.
Spoľahlivý postup je nechať platformu, aby vám port oznámila:
const port = process.env.PORT || 3000
app.listen(port, '0.0.0.0')
Potom nastavte port služby raz, priamo v platforme, a prestaňte ho udržiavať na dvoch miestach.
3. Path vracia niečo iné než úspešnú odpoveď
Health check path sa porovnáva presne a prekvapivo veľa zlyhaní spôsobuje redirect. Ak aplikácia presmeruje /healthz na /healthz/ alebo vynucuje HTTPS pomocou 301, checker, ktorý považuje za úspech iba odpovede 2xx, zlyhá pri každej kontrole, zatiaľ čo browser redirect nasleduje a zobrazí vám funkčnú stránku.
Tri konkrétne nástrahy:
- Redirecty kvôli koncovému lomítku.
/healthz→/healthz/je 301. - Vynútené HTTPS. Interná kontrola zvyčajne prichádza cez obyčajné HTTP na loopbacke. Bezpodmienečný HTTPS redirect spôsobí jej zlyhanie.
- Auth middleware. Globálny authentication guard, ktorý sa spúšťa pred routingom, vráti 401 aj pre health path.
Health path explicitne vynechajte z auth aj z vynucovania HTTPS. Je to jediná route, ktorá má byť úplne jednoduchá.
4. Kontrola je rýchlejšia než váš cold start
Ak kontrola niekoľkokrát zlyhá a potom prejde, prípadne zlyhá pri deploymente, ale pri opakovaní prejde, ide o problém s časovaním, nie s konfiguráciou.
Potrebný budget nie je jeden pokus — je to interval × počet opakovaní. Aplikácia, ktorej pripojenie k databáze a warm-up cache trvá dvanásť sekúnd, potrebuje celkový budget dlhší než dvanásť sekúnd. Inak zlyhá každý release a napokon kontrolu vypnete, čím odstránite jedinú vec, ktorá stojí medzi pokazeným buildom a vašimi používateľmi.
dockup info my-project/my-api --json | grep -A6 healthCheck
Nastavte timeout nad najdlhší oprávnený jednotlivý pokus a retries tak, aby interval × počet opakovaní s rezervou prekročil najdlhší oprávnený boot. Čas bootu zmerajte, nehádajte ho — logy obsahujú timestampy.
5. Aplikácia skutočne nie je pripravená
Posledný prípad je ten, na ktorý health check existuje: aplikácia sa spustila, nedokázala sa pripojiť k závislosti a skúša to znova. Nespadla, takže sa nič nereštartuje. Nedokáže obsluhovať požiadavky, preto kontrola zlyháva. Systém funguje presne tak, ako bol navrhnutý, a informuje vás, že tento release nemá dostať traffic.
Od ostatných štyroch prípadov ho odlíšite tak, že aplikácia zalogovala požiadavku a odpovedala stavom non-2xx. Ak sa požiadavka objaví v logoch, príčiny 1 až 3 sú vylúčené.
Diagnostické poradie, ktoré šetrí čas
# 1. Did the request reach the app at all?
dockup logs my-project/my-api --follow
# 2. What is the process actually bound to?
dockup exec "ss -ltn || netstat -ltn" my-project/my-api
# 3. Does the path answer from inside the container?
dockup exec "curl -si localhost:3000/healthz" my-project/my-api
# 4. What is the gate configured to expect?
dockup info my-project/my-api --json
Krok 3 vyrieši väčšinu týchto problémov. curl spustený vo vnútri kontajnera naraz odstráni všetky sieťové premenné: ak tam vráti 200 a platforma stále zlyháva, problém je v adrese alebo porte, nie v aplikácii. Ak vráti 301 alebo 401, príčinu ste našli bez jedinej zmeny na platforme.
Prečo sa oplatí gate ponechať
Po štvrtom neúspešnom deploymente je lákavé health check vypnúť a dostať release von. Stojí však za to pripomenúť si, čo tým vypínate.
V Dockup je health gate mechanizmus, ktorý chráni používateľov pred pokazeným releaseom. Nová verzia sa zostaví a spustí, zatiaľ čo aktuálna naďalej obsluhuje požiadavky; traffic sa presmeruje až vtedy, keď nová verzia začne odpovedať. Vypnutím gate znovu povolíte scenár, v ktorom kontajner, ktorý sa síce spustí, ale nedokáže fungovať, nahradí kontajner, ktorý bol v poriadku.
Kontrola, ktorá zlyhá pri štyroch releaseoch za sebou, je nepríjemná. Kontrola, ktorá vždy prejde, je kontrola, ktorá nezastaví deployment, na ktorom skutočne záleží.
Často kladené otázky
Prečo health check zlyháva, keď aplikácia lokálne funguje?
Takmer vždy preto, že kontajner binduje na 127.0.0.1 namiesto 0.0.0.0. Lokálne sa pripájate cez ten istý loopback; zvonka kontajnera je táto adresa nedostupná.
Má health endpoint vyžadovať authentication? Nie. Vylúčte ho z globálneho auth middleware, inak checker dostane 401 a deployment zlyhá, hoci aplikácia funguje.
Aký timeout mám použiť? Dlhší než váš najpomalší oprávnený jednotlivý pokus, pričom retries musia pokrývať váš najpomalší oprávnený cold start. Čas bootu si prečítajte z logov, nehádajte ho.
Je bezpečné vypnúť health check, aby sa release odblokoval? Release tým odblokujete, ale zároveň odstránite ochranu, ktorá bráni tomu, aby pokazená verzia dostala traffic. Namiesto toho opravte kontrolu — vo väčšine prípadov je príčinou bind address alebo redirect a oprava trvá niekoľko minút.
