Deploy lyktes, men nettstedet er nede
Dashboardet viser at tjenesten kjører, mens brukerne får en feilmelding. Lær hvorfor vellykket deploy og applikasjonshelse er ulike signaler, og hvordan du kan sørge for at en grønn deploy faktisk betyr at appen svarer.
Det finnes en helt egen type dårlig morgen som starter med et grønt hakemerke. Deployen lyktes, men nettstedet er nede, dashboardet viser running, og noen sender deg et skjermbilde av en 502-feil.
Dette er ikke et sjeldent spesialtilfelle. Det er det forutsigbare resultatet av at en plattform rapporterer én ting og måler noe annet, og det er verdt å forstå nøyaktig hvorfor. Løsningen er ikke å «sjekke grundigere» – den er å endre hva ordet running får lov til å bety.
Tre ulike spørsmål, ett statuslys
Når en plattform sier at en tjeneste kjører, kan den svare på hvilket som helst av disse spørsmålene:
- Startet containeren? Prosessen finnes og har ikke avsluttet.
- Er porten åpen? Noe lytter der plattformen forventer det.
- Svarer applikasjonen riktig? En request får et svar som betyr at appen er klar til å gjøre jobben sin.
Dette er svært forskjellige garantier, og de fleste hendelser av denne typen skyldes at dashboardet svarer på spørsmål 1, mens du antok spørsmål 3.
En Node-prosess som starter, ikke klarer å koble til databasen og blir stående i en retry-loop, oppfyller spørsmål 1 for alltid. Den har ikke krasjet. Den kommer aldri til å svare på en request. Containeren er «running» i enhver betydning som er relevant for orchestratoren.
Gapet der driftsavbruddet oppstår
Det farlige tidsrommet er mellom «den nye versjonen har startet» og «den nye versjonen fungerer». I dette tidsrommet har en naiv plattform allerede sendt trafikk til den nye versjonen, fordi oppstart var det eneste den målte.
Det som gjør dette verre enn en vanlig krasj, er rollback-historien. En krasjloop er høylytt: containeren avslutter, starter på nytt, avslutter, og plattformen oppdager det til slutt. En prosess som starter og henger, er stille. Ingenting starter på nytt, ingen alarmer går, og den forrige fungerende versjonen er som regel allerede fjernet.
Det siste er den egentlige skaden. Den gamle versjonen fungerte fint. Den ble fjernet fordi en ny versjon startet, og oppstart ble forvekslet med at den fungerte.
Hva en ekte health gate gjør
Løsningen er strukturell, ikke prosedyremessig. Trafikken bør ikke flyttes før den nye versjonen har svart på en request.
På Dockup fungerer en release slik: Den nye versjonen bygges isolert, startes ved siden av versjonen som betjener trafikken, og blir deretter stilt et spørsmål. Først når den svarer, peker domenet til den. Hvis den aldri svarer, stopper releasen der, og den forrige versjonen fortsetter å svare – ingen utenfor dashboardet ditt vil vite at en deploy ble forsøkt.
Derfor er en mislykket deploy på Dockup ikke et driftsavbrudd. Den gamle containeren ble aldri fjernet basert på antakelsen om at den nye ville fungere.
# The health gate is per-service configuration, not a platform default you inherit
dockup info my-project/my-api --json
healthCheck-blokken i dette outputet er hele kontrakten: hvilken path som blir forespurt, hvor lenge det skal ventes på et svar, hvor mange ganger det skal prøves, og hvor lenge det skal ventes mellom forsøkene.
Konfigurer sjekken slik at den svarer på spørsmål 3
Et health-endepunkt som alltid returnerer 200, er verre enn ingenting, fordi det gjør en reell kontroll om til et gummistempel. Poenget med sjekken er å feile når applikasjonen ikke kan gjøre jobben sin.
Et nyttig readiness-endepunkt verifiserer det appen ikke kan fungere uten:
// 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 })
}
})
To regler får dette til å fungere i praksis:
Sjekk avhengigheter du ikke kan svare uten, og ingenting annet. Hvis appen kan fungere med redusert funksjonalitet når søkeindeksen er nede, må du ikke la readiness avhenge av søkeindeksen – da blokkerer du deployer på grunn av noe som ikke er et driftsavbrudd.
Hold den billig. Endepunktet kalles gjentatte ganger under hver release. En readiness-sjekk som kjører en kostbar query, skaper et belastningsproblem du påfører deg selv.
Gi den nok tid, men ikke ubegrenset tid
To innstillinger avgjør om gaten hjelper eller skader:
- Timeout per forsøk bør være lengre enn den tregeste legitime cold starten. En app som kobler til en database og varmer opp en cache på åtte sekunder, vil feile på en sjekk med tre sekunders timeout hver gang, og du vil «løse» det ved å deaktivere gaten – noe som sender deg tilbake til utgangspunktet.
- Retries bør dekke den totale oppstartstiden, ikke ett enkelt forsøk. Intervall × retries er det reelle tidsbudsjettet.
På Dockup er disse healthCheckInterval, healthCheckTimeout og healthCheckRetries, og de gjelder per tjeneste fordi en Rails-monolitt og en Go-sidecar ikke starter etter samme tidsplan.
Når den allerede er nede
Hvis du leser dette midt i en hendelse, er dette rekkefølgen som løser den raskest:
- Sjekk om appen svarer direkte, utenom domenet. Hvis den svarer på porten sin, men ikke gjennom domenet, er dette et routingproblem, ikke et applikasjonsproblem, og du bør slutte å feilsøke koden.
- Les runtime-loggene, ikke build-loggene. Builden lyktes – det er utgangspunktet. Det du vil vite, er hva prosessen gjorde etter at den startet.
- Rull tilbake før du diagnostiserer. Diagnostisering er billigere når ingen følger med.
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
På Dockup er en rollback et bytte, ikke en ny build, fordi den forrige versjonen fortsatt ligger på disk. Det betyr noe klokken tre om natten: Den raskeste gjenopprettingen er den som ikke trenger å kompilere noe.
Spørsmålet du bør stille en plattform
Når du velger hvor produksjonen skal kjøre, er dette noe det er lurt å teste bevisst: Deploy en applikasjon som starter som den skal, men som ikke klarer å nå databasen. Se hva dashboardet viser.
Hvis det viser running, vet du nå nøyaktig hva dette ordet vil være verdt under neste hendelse.
Vanlige spørsmål
Hvorfor viser dashboardet mitt running når nettstedet er nede? Fordi «running» vanligvis betyr at containerprosessen finnes, ikke at applikasjonen kan svare på en request. En prosess som sitter fast i forsøk på å koble til databasen, oppfyller denne definisjonen på ubestemt tid.
Bør en health check koble til databasen? Ja, hvis applikasjonen ikke kan svare på requests uten den. Sjekk avhengighetene du faktisk trenger, og hopp over dem du kan fungere med redusert funksjonalitet uten.
Hva er forskjellen mellom liveness og readiness? Liveness spør om prosessen bør startes på nytt. Readiness spør om den bør motta trafikk. Gaten som forhindrer denne feilen, er readiness, og den må kjøre før trafikken flyttes.
Hvordan kan jeg hindre at en dårlig deploy tar ned nettstedet i det hele tatt? Bytt trafikk først etter at den nye versjonen har svart på en ekte request, og behold den forrige versjonen til byttet er bekreftet. Da blir en mislykket release en release som aldri fant sted, i stedet for et driftsavbrudd.
