Deploy je uspio, ali web-stranica nije dostupna
Na nadzornoj ploči piše da aplikacija radi, a korisnici dobivaju pogrešku. Saznajte zašto su uspješan deploy i zdravlje aplikacije različiti signali te kako postići da zeleni deploy znači da aplikacija zaista odgovara.
Postoji posebna vrsta lošeg jutra koje počinje zelenom kvačicom. Deploy je uspio, ali web-stranica nije dostupna, na nadzornoj ploči piše running, a netko vam šalje snimku zaslona s pogreškom 502.
To nije rijedak rubni slučaj. To je predvidljiv rezultat toga što platforma prijavljuje jednu stvar, a mjeri drugu. Važno je precizno razumjeti zašto se to događa jer rješenje nije u tome da "pažljivije provjeravate" — treba promijeniti što riječ running uopće smije značiti.
Tri različita pitanja, jedan statusni indikator
Kada platforma kaže da servis radi, možda odgovara na bilo koje od ovih pitanja:
- Je li se container pokrenuo? Proces postoji i nije završio.
- Je li port otvoren? Nešto osluškuje na mjestu koje platforma očekuje.
- Odgovara li aplikacija ispravno? Zahtjev dobiva odgovor koji znači da je aplikacija spremna za rad.
To su potpuno različita jamstva, a većina incidenata ovog tipa nastaje zato što nadzorna ploča odgovara na pitanje 1, dok vi pretpostavljate da odgovara na pitanje 3.
Node proces koji se pokrene, ne uspije se povezati s bazom podataka i ostane u petlji ponovnih pokušaja zauvijek zadovoljava pitanje 1. Nije se srušio. Nikada neće obraditi zahtjev. Container je "running" u svakom smislu koji je važan orchestratoru.
Praznina u kojoj nastaje prekid rada
Opasni interval nalazi se između trenutka kada se "pokrenula nova verzija" i trenutka kada "nova verzija može raditi". U tom je intervalu naivna platforma već preusmjerila promet jer je pokretanje bilo jedino što je mjerila.
Ono što ovu situaciju čini gorom od običnog rušenja jest priča s rollbackom. Petlja rušenja glasna je: container završi, ponovno se pokrene, ponovno završi i platforma to naposljetku primijeti. Pokretanje i zaglavljivanje odvijaju se nečujno. Ništa se ne pokreće ponovno, nijedan se alarm ne oglašava, a prethodna je radna verzija obično već uklonjena.
Upravo je to prava šteta. Stara je verzija radila. Uklonjena je zato što se nova pokrenula, a pokretanje je pogrešno protumačeno kao rad.
Kako funkcionira stvarna provjera spremnosti
Rješenje je strukturno, a ne proceduralno. Promet se ne bi trebao preusmjeriti dok nova verzija ne odgovori na zahtjev.
Na Dockupu release izgleda ovako: nova se verzija izgradi izolirano, pokrene usporedno s verzijom koja trenutačno poslužuje promet, a zatim joj se postavi pitanje. Tek kada odgovori, domena se usmjerava na nju. Ako nikada ne odgovori, release se tu zaustavlja, a prethodna verzija nastavlja posluživati promet — nitko izvan vaše nadzorne ploče nikada ne sazna da je deploy pokušan.
Zato neuspjeli deploy na Dockupu nije prekid rada. Stari container nikada nije uklonjen pod pretpostavkom da će nova verzija biti ispravna.
# The health gate is per-service configuration, not a platform default you inherit
dockup info my-project/my-api --json
Blok healthCheck u tom je izlazu cijeli ugovor: koji se path poziva, koliko se čeka na odgovor, koliko se puta pokušava i koliko se čeka između pokušaja.
Konfigurirajte provjeru tako da odgovara na pitanje 3
Health endpoint koji bezuvjetno vraća 200 gori je nego da ga uopće nema jer stvarnu provjeru pretvara u formalni potpis. Svrha je provjere neuspjeh kada aplikacija ne može obavljati svoj posao.
Korisni endpoint spremnosti provjerava ono bez čega aplikacija ne može raditi:
// Not this — it proves only that the process is alive
app.get('/healthz', (req, res) => res.send('ok'))
// This — it proves the app can actually serve a request
app.get('/healthz', async (req, res) => {
try {
await db.query('select 1') // the dependency that is usually the problem
if (!cacheReady) throw new Error('cache warming')
res.status(200).json({ ok: true })
} catch (err) {
res.status(503).json({ ok: false, reason: err.message })
}
})
Dva pravila omogućuju da ovo u praksi funkcionira:
Provjeravajte ovisnosti bez kojih ne možete posluživati promet i ništa više. Ako se vaša aplikacija može postupno degradirati kada je search index nedostupan, nemojte spremnost uvjetovati search indexom — blokirat ćete deploye zbog nečega što nije prekid rada.
Neka provjera bude jeftina. Endpoint se poziva više puta tijekom svakog releasea. Provjera spremnosti koja izvršava skup query sama stvara problem s opterećenjem.
Dajte joj dovoljno vremena, ali ne neograničeno
Dvije postavke odlučuju hoće li provjera pomoći ili odmoći:
- Timeout po pokušaju trebao bi biti dulji od vašeg najsporijeg legitimnog cold starta. Aplikacija koja se povezuje s bazom podataka i zagrijava cache osam sekundi svaki će put pasti na provjeri od tri sekunde, a vi ćete to "riješiti" isključivanjem provjere — čime se vraćate na početak.
- Broj ponovnih pokušaja treba obuhvatiti ukupno vrijeme pokretanja, a ne trajanje jednog pokušaja. Interval × broj pokušaja stvarni je raspoloživi budžet.
Na Dockupu to su healthCheckInterval, healthCheckTimeout i healthCheckRetries, a postavljaju se po servisu jer se Rails monolit i Go sidecar ne pokreću istim ritmom.
Kada je već nedostupna
Ako ovo čitate usred incidenta, slijed kojim ćete ga najbrže riješiti jest:
- Provjerite odgovara li aplikacija izravno, zaobilazeći domenu. Ako odgovara na svom portu, ali ne i preko domene, riječ je o problemu s routingom, a ne o problemu aplikacije, pa biste trebali prestati otklanjati pogreške u kodu.
- Čitajte runtime logove, a ne build logove. Build je uspio — to je polazišna pretpostavka. Ono što želite vidjeti jest što je proces radio nakon pokretanja.
- Napravite rollback prije dijagnosticiranja. Dijagnosticiranje je jeftinije kada vas nitko ne gleda.
dockup logs my-project/my-api --follow # what the running process is saying
dockup deployments my-project/my-api # what was live before this
dockup rollback <deployment-id> my-project/my-api # put that back
Na Dockupu je rollback prebacivanje, a ne ponovna izgradnja, jer je prethodna verzija i dalje na disku. To je važno u tri ujutro: najbrži oporavak onaj je koji ne mora ništa kompilirati.
Pitanje koje trebate postaviti platformi
Kada birate gdje ćete pokretati produkciju, ovo je dobro namjerno testirati: deployajte aplikaciju koja se uspješno pokrene, a zatim se ne može povezati s bazom podataka. Pogledajte što piše na nadzornoj ploči.
Ako piše running, sada točno znate koliko će ta riječ vrijediti tijekom vašeg sljedećeg incidenta.
Često postavljana pitanja
Zašto na nadzornoj ploči piše da aplikacija radi kada je web-stranica nedostupna? Zato što "running" obično znači da proces containera postoji, a ne da aplikacija može obraditi zahtjev. Proces koji je zapeo u ponavljanju pokušaja povezivanja s bazom podataka neograničeno dugo zadovoljava tu definiciju.
Treba li health check pozivati bazu podataka? Da, ako vaša aplikacija bez nje ne može posluživati zahtjeve. Provjeravajte ovisnosti koje su vam zaista potrebne, a preskočite one bez kojih se aplikacija može postupno degradirati.
Koja je razlika između livenessa i readinessa? Liveness provjerava treba li proces ponovno pokrenuti. Readiness provjerava smije li proces primati promet. Provjera koja sprječava ovaj problem jest readiness i mora se izvršiti prije preusmjeravanja prometa.
Kako mogu potpuno spriječiti da loš deploy sruši web-stranicu? Preusmjerite promet tek nakon što nova verzija odgovori na stvarni zahtjev i zadržite prethodnu verziju dok se prebacivanje ne potvrdi. Tada neuspjeli release ostaje release koji se nikada nije dogodio, a ne prekid rada.
