Guides 7 min. læsning

Sådan bygger du et health check-endpoint (og overvåger det)

Et health check-endpoint fortæller monitorer og load balancere, om din app kan håndtere forespørgsler. Hvad du skal tjekke, hvordan du holder det hurtigt og sikkert, kode til Laravel, Express og Flask, og hvordan du overvåger det.

Et health check-endpoint er en URL, som regel /health, der fortæller en monitor eller load balancer, om din applikation kan håndtere forespørgsler. Den kører nogle få hurtige tjek af det, appen er afhængig af, for eksempel databasen, cachen og ledig diskplads, og svarer med HTTP 200 og {“status”:”ok”}, når alt virker, eller med HTTP 503 og navnet på det fejlende tjek, når noget ikke gør. Hold det hurtigt, billigt, ucachet og fri for hemmeligheder, og peg derefter en oppetidsmonitor på det, der validerer svaret, ikke kun statuskoden.

Denne guide gennemgår forskellen på liveness og readiness, hvad du skal tjekke, svarformatet, fungerende eksempler til PHP med Laravel, Node med Express og Python med Flask, og hvordan du overvåger endpointet, når det først findes.

Hvorfor forsiden ikke er et health check

De fleste teams starter med at overvåge forsiden. Det er et fornuftigt første skridt, men forsiden bliver ofte leveret fra et CDN eller en sidecache, så den bliver ved med at svare 200, mens databasen er nede, og ingen kan logge ind eller gennemføre et køb. Et dedikeret endpoint går uden om cachen og tester afhængighederne direkte. Det giver dig også én forudsigelig URL med et lille, stabilt svar, der er let at validere.

Liveness kontra readiness

Containerplatforme som Kubernetes deler sundhed op i to spørgsmål, og den opdeling er nyttig overalt:

Liveness: lever processen, og sidder den ikke fast? Hvis ikke, så genstart den. Et liveness-tjek bør ikke teste databasen eller andre tjenester. Går databasen ned, og fejler alle appinstanser deres liveness-tjek, genstarter platformen dem alle sammen, hvilket ikke løser problemet med databasen og gør genopretningen langsommere.

Readiness: kan denne instans håndtere rigtige forespørgsler lige nu? Hvis ikke, så stop med at sende trafik til den. Det er her, tjek af afhængigheder hører hjemme.

En almindelig opsætning er /livez eller /up til liveness, der returnerer 200, så længe appen starter, og /health eller /readyz til readiness. Til ekstern oppetidsovervågning er det readiness-endpointet, der er interessant, fordi det besvarer det spørgsmål, dine kunder går op i: virker appen?

Hvad du skal tjekke

Tjek de afhængigheder, uden hvilke en normal forespørgsel ville fejle, og intet andet:

Database: kør SELECT 1 på den samme forbindelsespulje, som appen bruger. Det beviser, at serveren kan nås og tager imod forespørgsler.

Cache eller session store: skriv og læs en kortlivet nøgle i Redis eller Memcached. Ligger sessionerne der, afhænger login af det.

Disk: tjek den ledige plads på det volume, appen skriver til, for uploads, logs eller sessioner. Fejl, når den falder under en margin, for eksempel 10 procent.

Kø: tjek, at kø-backenden svarer, og eventuelt at det ældste ventende job ikke er ældre end en grænse, du vælger. En voksende kø betyder, at workerne er stoppet.

Vær forsigtig med tredjeparts-API’er. Kalder dit endpoint en betalingsudbyder ved hvert tjek, og er udbyderen langsom, fejler dit health check, selvom din egen app har det fint, og en load balancer kan tage alle instanser ud af rotation. Overvåg i stedet tredjeparter med deres egne monitorer, så en alarm fortæller dig, hvis problem det er. Uptimechecks tredjepartsovervågning følger statussiderne for 31 tjenester som Klarna, Swish, Shopify, Cloudflare og OpenAI, eller enhver side hostet på Statuspage.

Hold det hurtigt og billigt

Et health check kører hvert minut fra hver monitor og load balancer, døgnet rundt. Det må aldrig blive det, der overbelaster appen.

Giv hvert tjek af en afhængighed sin egen korte timeout, for eksempel et eller to sekunder, så én tjeneste, der hænger, ikke får endpointet til at hænge.

Kør tjekkene parallelt, hvor dit sprog gør det nemt.

Brug trivielle forespørgsler. Tæl aldrig rækker i en stor tabel, og kør aldrig en rapport.

Rammer mange tjekkere endpointet, så cache resultatet i hukommelsen i nogle få sekunder.

Send Cache-Control: no-store, og undtag stien fra sidecaches og CDN’er, så du aldrig ser et cachet “ok”.

Spring sessioner, cookies og autentificerings-middleware over, som endpointet ikke har brug for.

Læk ikke hemmeligheder

Et offentligt health-endpoint er synligt for alle, der gætter på /health. Returnér navn og tilstand for hvert tjek og intet andet. Medtag ikke fejlmeddelelser fra exceptions, forbindelsesstrenge, værtsnavne, interne IP-adresser, softwareversioner eller miljøvariabler. Log i stedet den detaljerede fejl på serveren. Vil du have en mere detaljeret diagnosevisning, så læg den på en separat sti bag autentificering, eller begræns den til dit interne netværk.

Svarets format og statuskoder

Hold JSON-svaret lille og stabilt:

{
“status”: “ok”,
“checks”: {
“database”: “ok”,
“cache”: “ok”,
“disk”: “ok”,
“queue”: “ok”
}
}

Når et tjek fejler, så sæt dets værdi til “fail”, sæt status på øverste niveau til “fail”, og skift HTTP-statuskoden:

200 OK, når alle påkrævede tjek består.

503 Service Unavailable, når et påkrævet tjek fejler. Det er den kode, load balancere og monitorer forventer for “kan midlertidigt ikke levere”.

Eventuelt 200 med “status”:”degraded” for en valgfri afhængighed, der er nede, mens kernen stadig virker. Beslut på forhånd, hvilke tjek der er påkrævede.

Undgå at returnere 200 med “status”:”fail” i bodyen. Mange værktøjer læser kun statuskoden. Se HTTP-statuskoder i overvågning for, hvordan hver kode typisk behandles.

Eksempel: PHP og Laravel

Nyere Laravel-versioner kommer med en simpel /up-route, der bekræfter, at appen starter, og som fungerer fint som liveness-tjek. Til readiness tilføjer du en route, der tjekker afhængighederne:

use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Route;

Route::get(‘/health’, function () {
$checks = [];

try {
DB::select(‘SELECT 1’);
$checks[‘database’] = ‘ok’;
} catch (\Throwable $e) {
report($e);
$checks[‘database’] = ‘fail’;
}

try {
Cache::put(‘health-check’, ‘ok’, 10);
$checks[‘cache’] = Cache::get(‘health-check’) === ‘ok’ ? ‘ok’ : ‘fail’;
} catch (\Throwable $e) {
report($e);
$checks[‘cache’] = ‘fail’;
}

$path = storage_path();
$checks[‘disk’] = disk_free_space($path) / disk_total_space($path) > 0.10 ? ‘ok’ : ‘fail’;

$healthy = ! in_array(‘fail’, $checks, true);

return response()
->json([‘status’ => $healthy ? ‘ok’ : ‘fail’, ‘checks’ => $checks], $healthy ? 200 : 503)
->header(‘Cache-Control’, ‘no-store’);
});

Registrér den uden for middleware-grupper, der starter sessioner, og sæt en kort forbindelses-timeout for databasen i din konfiguration.

Eksempel: Node og Express

const withTimeout = (promise, ms) =>
Promise.race([
promise,
new Promise((_, reject) => setTimeout(() => reject(new Error(‘timeout’)), ms)),
]);

const check = (fn) => withTimeout(fn(), 2000).then(() => ‘ok’, () => ‘fail’);

app.get(‘/health’, async (req, res) => {
const [database, cache] = await Promise.all([
check(() => pool.query(‘SELECT 1’)),
check(() => redis.ping()),
]);

const checks = { database, cache };
const healthy = Object.values(checks).every((v) => v === ‘ok’);

res
.set(‘Cache-Control’, ‘no-store’)
.status(healthy ? 200 : 503)
.json({ status: healthy ? ‘ok’ : ‘fail’, checks });
});

Tjekkene kører parallelt, hver med en grænse på to sekunder, så endpointet svarer inden for cirka to sekunder, også når en afhængighed hænger.

Eksempel: Python og Flask

import shutil
from flask import jsonify
from sqlalchemy import text

@app.get(“/health”)
def health():
checks = {}

try:
db.session.execute(text(“SELECT 1”))
checks[“database”] = “ok”
except Exception:
app.logger.exception(“health: database”)
checks[“database”] = “fail”

usage = shutil.disk_usage(“/”)
checks[“disk”] = “ok” if usage.free / usage.total > 0.10 else “fail”

healthy = all(value == “ok” for value in checks.values())
body = {“status”: “ok” if healthy else “fail”, “checks”: checks}
return jsonify(body), 200 if healthy else 503, {“Cache-Control”: “no-store”}

Tilføj et Redis-tjek på samme måde, hvis du bruger Redis til sessioner eller en opgavekø.

Test fejlscenariet

Et health check, der aldrig har fejlet, er aldrig blevet testet. Stop databasen i staging, eller peg appen på en forkert cache-vært, og bekræft, at endpointet returnerer 503 inden for din timeout og nævner det rigtige tjek. Bekræft derefter, at svaret ikke indeholder nogen fejltekst. Det tager ti minutter og fortæller dig, at endpointet vil være ærligt, når det gælder.

Overvågning af endpointet

Et endpoint, som ingen tjekker udefra, er kun det halve arbejde. Peg en HTTP-monitor på det uden for dit netværk, så den også fanger problemer med DNS, TLS, firewall og load balancer:

Overvåg https://lumenanalytics.io/health med GET hvert minut.

Forvent status 200, og valider, at bodyen indeholder “status”:”ok”. En fejlside fra en proxy, der tilfældigvis returnerer 200, får så stadig tjekket til at fejle.

Send en brugerdefineret user agent-header, så overvågningstrafik er nem at filtrere fra i analytics og logs.

Hold øje med historikken for svartider. Et health check, der kryber fra 40 ms til 900 ms, er et tidligt varsel om en database, der kæmper.

Send alarmen til dem, der ejer tjenesten.

Uptimechecks API-overvågning understøtter brugerdefinerede metoder og headers (også basic auth eller et bearer-token) og validerer den forventede status, nøgleord og JSON-felter, så denne opsætning tager et minut. Behold en almindelig oppetidsmonitor på forsiden og den vigtigste konverteringsside ved siden af; tjeklisten til API-overvågning dækker de andre endpoints, der er værd at holde øje med.

Kø-workers og planlagte opgaver kører ofte uden for webprocessen, så et health-endpoint kan ikke se, om de er stoppet. Giv dem i stedet heartbeats: workeren pinger en URL med få minutters mellemrum, og et manglende ping udløser alarmen.

Tjekliste til health check-endpoints

Adskil liveness (processen lever) fra readiness (afhængighederne virker).

Tjek den database, cache, disk og kø, appen har brug for til at håndtere en forespørgsel.

Giv hvert tjek en kort timeout, og hold forespørgslerne trivielle.

Returnér 200, når alt er sundt, og 503, når et påkrævet tjek fejler.

Returnér kun navne og tilstande for tjekkene. Ingen fejl, versioner eller værtsnavne.

Slå caching fra for stien.

Overvåg det hvert minut udefra, med validering af svaret.

Uptimecheck overvåger health-endpoints, API’er, servere og planlagte jobs på én konto. Start en gratis prøveperiode på 14 dage, uden kreditkort.

Spørgsmål

Hvad skal et health check-endpoint returnere?

En lille JSON-body med en samlet status og tilstanden for hvert tjek, med HTTP 200, når alt påkrævet virker, og HTTP 503, når et påkrævet tjek fejler.

Skal et health check teste databasen?

Readiness-tjekket skal, med en triviel forespørgsel som SELECT 1 og en kort timeout. Et liveness-tjek skal ikke, for så ville et nedbrud i databasen genstarte alle appinstanser.

Er det sikkert at gøre et health-endpoint offentligt?

Ja, hvis det kun returnerer navne og tilstande for tjekkene. Hold fejlmeddelelser, versioner, værtsnavne og forbindelsesoplysninger ude af svaret, og log dem i stedet på serveren.

Hvor ofte skal et health check-endpoint overvåges?

Hvert minut for produktionstjenester. Valider, at bodyen indeholder den forventede status, og ikke kun statuskoden, så en cachet side eller en fejlside fra en proxy ikke slipper igennem.

Læs videre

Guides 7 min. læsning

Sådan reducerer du falske alarmer i oppetidsovervågning

Falske alarmer lærer teams at ignorere overvågningen. Sådan skærer du ned på støjen: Bekræft hver fejl med et ekstra tjek, sæt fornuftige timeouts, tjek den rigtige URL, og send alarmer til dem, der kan handle.

Læs

Nedetid er fortid.

Opret din første monitor på under et minut, eller se dig omkring i livedemoen først.

14 dages gratis prøveperiode · Derefter gratis med 5 monitorer · Intet betalingskort