Guides 7 min di lettura

Come creare un endpoint di health check (e monitorarlo)

Un endpoint di health check dice a monitor e load balancer se la tua app può servire richieste. Che cosa controllare, come mantenerlo veloce e sicuro, codice per Laravel, Express e Flask e come monitorarlo.

Un endpoint di health check è un URL, di solito /health, che dice a un monitor o a un load balancer se la tua applicazione è in grado di servire richieste. Esegue alcuni controlli rapidi su ciò da cui l’app dipende, come il database, la cache e lo spazio libero su disco, e risponde con HTTP 200 e {“status”:”ok”} quando tutto funziona, oppure con HTTP 503 e il nome del controllo fallito quando qualcosa non va. Deve essere veloce, leggero, non in cache e privo di segreti; poi puntaci un monitor di uptime che validi la risposta, non solo il codice di stato.

Questa guida spiega la differenza tra liveness e readiness, che cosa controllare, il formato della risposta, esempi funzionanti in PHP con Laravel, Node con Express e Python con Flask, e come monitorare l’endpoint una volta creato.

Perché la homepage non è un health check

La maggior parte dei team inizia monitorando la homepage. È un primo passo sensato, ma spesso la homepage viene servita da un CDN o da una cache di pagina, quindi continua a rispondere 200 mentre il database è giù e nessuno riesce ad accedere o a completare un acquisto. Un endpoint dedicato aggira la cache e mette alla prova direttamente le dipendenze. Ti dà anche un URL prevedibile, con una risposta piccola e stabile, facile da validare.

Liveness e readiness

Le piattaforme di container come Kubernetes dividono lo stato di salute in due domande, e la distinzione è utile ovunque:

Liveness: il processo è vivo e non bloccato? Se no, va riavviato. Un controllo di liveness non dovrebbe testare il database o altri servizi. Se il database va giù e tutte le istanze dell’app falliscono il controllo di liveness, la piattaforma le riavvia tutte: il database non si ripara e il ripristino diventa più lento.

Readiness: questa istanza può servire richieste reali in questo momento? Se no, smetti di mandarle traffico. È qui che vanno i controlli sulle dipendenze.

Una struttura comune prevede /livez o /up per la liveness, che restituisce 200 finché l’app si avvia, e /health o /readyz per la readiness. Per il monitoraggio uptime esterno è l’endpoint di readiness quello interessante, perché risponde alla domanda che conta per i tuoi clienti: l’app funziona?

Che cosa controllare

Controlla le dipendenze senza le quali una normale richiesta fallirebbe, e nient’altro:

Database: esegui SELECT 1 sullo stesso pool di connessioni che usa l’app. Dimostra che il server è raggiungibile e accetta query.

Cache o archivio delle sessioni: scrivi e leggi una chiave a breve durata in Redis o Memcached. Se le sessioni stanno lì, il login dipende da questo.

Disco: controlla lo spazio libero sul volume su cui l’app scrive upload, log o sessioni. Fai fallire il controllo quando scende sotto un margine, per esempio il 10 percento.

Coda: controlla che il backend della coda risponda e, se vuoi, che il job in attesa più vecchio non superi un limite a tua scelta. Un arretrato in crescita significa che i worker si sono fermati.

Attenzione alle API di terze parti. Se il tuo endpoint chiama un provider di pagamento a ogni controllo e il provider è lento, il tuo health check fallisce anche se la tua app sta benissimo, e un load balancer potrebbe togliere tutte le istanze dalla rotazione. Monitora piuttosto le terze parti con monitor dedicati, così un avviso ti dice di chi è il problema. Il monitoraggio di terze parti di Uptimecheck segue le pagine di stato di 31 servizi come Klarna, Swish, Shopify, Cloudflare e OpenAI, oppure qualsiasi pagina ospitata su Statuspage.

Mantienilo veloce e leggero

Un health check viene eseguito ogni minuto da ogni monitor e load balancer, giorno e notte. Non deve mai diventare la causa del sovraccarico dell’app.

Dai a ogni controllo sulle dipendenze un timeout breve, per esempio uno o due secondi, così un servizio bloccato non blocca l’endpoint.

Esegui i controlli in parallelo, dove il tuo linguaggio lo rende semplice.

Usa query banali. Non contare mai le righe di una tabella grande e non generare report.

Se molti sistemi di controllo interrogano l’endpoint, tieni il risultato in memoria per qualche secondo.

Invia Cache-Control: no-store ed escludi il percorso dalle cache di pagina e dai CDN, così non vedrai mai un “ok” preso dalla cache.

Salta sessioni, cookie e middleware di autenticazione di cui l’endpoint non ha bisogno.

Non esporre segreti

Un endpoint di health pubblico è visibile a chiunque provi /health. Restituisci il nome e lo stato di ogni controllo e nient’altro. Non includere messaggi di eccezione, stringhe di connessione, hostname, indirizzi IP interni, versioni del software o variabili d’ambiente. Registra invece l’errore dettagliato nei log del server. Se vuoi una vista diagnostica più ricca, mettila su un percorso separato protetto da autenticazione o limitala alla tua rete interna.

Formato della risposta e codici di stato

Tieni il JSON piccolo e stabile:

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

Quando un controllo fallisce, imposta il suo valore su “fail”, imposta lo stato principale su “fail” e cambia il codice di stato HTTP:

200 OK quando tutti i controlli obbligatori passano.

503 Service Unavailable quando un controllo obbligatorio fallisce. È il codice che load balancer e monitor si aspettano per “temporaneamente non in grado di servire”.

Facoltativamente, 200 con “status”:”degraded” per una dipendenza opzionale non disponibile mentre il nucleo funziona ancora. Decidi in anticipo quali controlli sono obbligatori.

Evita di restituire 200 con “status”:”fail” nel corpo: molti strumenti leggono solo il codice di stato. Consulta i codici di stato HTTP per il monitoraggio per sapere come viene trattato di solito ogni codice.

Esempio: PHP e Laravel

Le versioni recenti di Laravel includono una semplice route /up che conferma l’avvio dell’app, ottima come controllo di liveness. Per la readiness, aggiungi una route che controlli le dipendenze:

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’);
});

Registrala fuori dai gruppi di middleware che avviano le sessioni e imposta nella configurazione un timeout di connessione breve per il database.

Esempio: Node ed 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 });
});

I controlli vengono eseguiti in parallelo, ciascuno con un limite di due secondi, quindi l’endpoint risponde entro circa due secondi anche quando una dipendenza è bloccata.

Esempio: Python e 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”}

Aggiungi allo stesso modo un controllo su Redis, se lo usi per le sessioni o per una coda di task.

Testa il caso di errore

Un health check che non è mai fallito non è mai stato testato. In staging, ferma il database o punta l’app su un host di cache sbagliato e verifica che l’endpoint restituisca 503 entro il tuo timeout e indichi il controllo giusto. Poi verifica che la risposta non contenga testo di errore. Ci vogliono dieci minuti, e ti assicurano che l’endpoint dirà la verità quando conta.

Monitorare l’endpoint

Un endpoint che nessuno controlla dall’esterno è solo metà del lavoro. Puntaci un monitor HTTP dall’esterno della tua rete, così rileva anche i problemi di DNS, TLS, firewall e load balancer:

Monitora https://lumenanalytics.io/health con GET ogni minuto.

Aspettati lo stato 200 e verifica che il corpo contenga “status”:”ok”. Così una pagina di errore del proxy che restituisce 200 fa comunque fallire il controllo.

Invia un header user agent personalizzato, così il traffico di monitoraggio è facile da escludere da analytics e log.

Tieni d’occhio la cronologia dei tempi di risposta. Un health check che sale lentamente da 40 ms a 900 ms è un primo segnale di un database in affanno.

Indirizza l’avviso alle persone responsabili del servizio.

Il monitoraggio API di Uptimecheck supporta metodi e header personalizzati (incluse basic auth o bearer token) e valida lo stato atteso, le parole chiave e i campi JSON, quindi questa configurazione richiede un minuto. Affiancagli un normale monitor di uptime sulla homepage e sulla pagina di conversione più importante; la checklist per il monitoraggio API copre gli altri endpoint che vale la pena sorvegliare.

I worker delle code e i task pianificati spesso girano fuori dal processo web, quindi un endpoint di health non può sapere se si sono fermati. Per loro usa piuttosto gli heartbeat: il worker invia un ping a un URL ogni pochi minuti, e un ping mancante fa scattare l’avviso.

Checklist per l’endpoint di health check

Separa la liveness (processo vivo) dalla readiness (dipendenze funzionanti).

Controlla database, cache, disco e coda di cui l’app ha bisogno per servire una richiesta.

Dai a ogni controllo un timeout breve e mantieni le query banali.

Restituisci 200 quando è tutto a posto e 503 quando un controllo obbligatorio fallisce.

Restituisci solo nomi e stati dei controlli. Niente errori, versioni o hostname.

Disattiva la cache per il percorso.

Monitoralo ogni minuto dall’esterno, con la validazione della risposta.

Uptimecheck monitora endpoint di health, API, server e job pianificati in un unico account. Inizia la prova gratuita di 14 giorni, senza carta di credito.

Domande

Che cosa deve restituire un endpoint di health check?

Un piccolo corpo JSON con uno stato complessivo e lo stato di ogni controllo, con HTTP 200 quando tutto ciò che è obbligatorio funziona e HTTP 503 quando un controllo obbligatorio fallisce.

Un health check deve testare il database?

Il controllo di readiness sì, con una query banale come SELECT 1 e un timeout breve. Il controllo di liveness no, perché un’interruzione del database farebbe riavviare tutte le istanze dell’app.

È sicuro rendere pubblico un endpoint di health?

Sì, se restituisce solo nomi e stati dei controlli. Tieni fuori dalla risposta messaggi di errore, versioni, hostname e dettagli di connessione, e registrali invece nei log del server.

Ogni quanto va monitorato un endpoint di health check?

Ogni minuto per i servizi in produzione. Verifica che il corpo contenga lo stato atteso oltre al codice di stato, così una pagina in cache o un errore del proxy non passano il controllo.

Continua a leggere

Guides 7 min di lettura

Come ridurre i falsi allarmi nel monitoraggio uptime

I falsi allarmi insegnano ai team a ignorare il monitoraggio. Come ridurre il rumore: conferma ogni guasto con un secondo controllo, imposta timeout sensati, verifica l'URL giusto e indirizza gli avvisi a chi può intervenire.

Leggi

I disservizi sono acqua passata.

Configura il tuo primo monitor in meno di un minuto, oppure esplora prima la demo live.

Prova gratuita di 14 giorni · Poi gratis con 5 monitor · Nessuna carta di credito