Les endpoints de health check constituent la pierre angulaire de l'observabilité des APIs REST. Ces endpoints dédiés permettent aux systèmes de monitoring de vérifier l'état de santé de vos services.
Un health check bien conçu fournit les informations nécessaires pour des décisions de routage intelligentes. La réalité des systèmes distribués exige une approche sophistiquée.
Qu'est-ce qu'un Health Check Endpoint ?
Un health check endpoint est un point d'entrée API conçu pour rapporter l'état de santé d'un service. Contrairement aux endpoints fonctionnels, il sert les besoins d'infrastructure.
Convention de nommage
La convention établit généralement ces chemins :
/healthou/healthz: Health check standard/health/live: Liveness probe/health/ready: Readiness probe/health/startup: Startup probe (Kubernetes 1.20+)
Niveaux de profondeur
Le health check peut avoir différents niveaux :
| Niveau | Description | Usage |
|---|---|---|
| Liveness | Le processus répond | Redémarrage conteneur |
| Readiness | Prêt à traiter | Routage load balancer |
| Deep health | Test complet | Monitoring détaillé |
Structure de réponse
Une réponse de health check typique :
{
"status": "healthy",
"timestamp": "2025-12-24T10:30:00Z",
"version": "2.1.0",
"checks": {
"database": {
"status": "healthy",
"latency_ms": 5
},
"cache": {
"status": "healthy",
"latency_ms": 1
},
"external_api": {
"status": "degraded",
"message": "Slow response times"
}
}
}
Consommateurs des health checks
Les health checks servent plusieurs consommateurs :
- Load balancers : Retrait des instances unhealthy
- Kubernetes : Liveness et readiness probes
- Outils de monitoring : Détection d'incidents
- Plateformes de déploiement : Validation des releases
Pourquoi les Health Checks sont Essentiels
Dans une architecture microservices, les instances naissent et meurent constamment. Les health checks distinguent les instances saines des défaillantes.
Intégration Kubernetes
Les probes Kubernetes illustrent cette criticité :
apiVersion: v1
kind: Pod
spec:
containers:
- name: api
livenessProbe:
httpGet:
path: /health/live
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
readinessProbe:
httpGet:
path: /health/ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 3
Détection des dégradations partielles
Une API peut répondre 200 à /health tout en ayant perdu sa connexion base de données. Un health check sophistiqué détecte cette dégradation.
Déploiement zero-downtime
Pendant un rolling update, les nouvelles instances ne reçoivent du trafic qu'après avoir passé leur health check. Cette vérification garantit l'absence d'indisponibilité.
Comment Implémenter des Health Checks Efficaces
Pattern multi-niveau
Distinguez les vérifications selon leur profondeur :
# Flask example
from flask import Flask, jsonify
import redis
import psycopg2
app = Flask(__name__)
@app.route('/health/live')
def liveness():
"""Minimal check - process is alive"""
return jsonify({"status": "alive"}), 200
@app.route('/health/ready')
def readiness():
"""Check critical dependencies"""
checks = {}
# Database check
try:
conn = psycopg2.connect(DATABASE_URL)
conn.close()
checks["database"] = {"status": "healthy"}
except Exception as e:
checks["database"] = {"status": "unhealthy", "error": str(e)}
# Cache check
try:
r = redis.from_url(REDIS_URL)
r.ping()
checks["cache"] = {"status": "healthy"}
except Exception as e:
checks["cache"] = {"status": "unhealthy", "error": str(e)}
# Determine overall status
all_healthy = all(c["status"] == "healthy" for c in checks.values())
status_code = 200 if all_healthy else 503
return jsonify({
"status": "ready" if all_healthy else "not_ready",
"checks": checks
}), status_code
Vérification des dépendances
Testez les connexions critiques avec des timeouts stricts :
import asyncio
from contextlib import asynccontextmanager
async def check_dependency(name, check_func, timeout=2.0):
"""Check a dependency with timeout"""
try:
result = await asyncio.wait_for(check_func(), timeout=timeout)
return {"status": "healthy", "latency_ms": result}
except asyncio.TimeoutError:
return {"status": "unhealthy", "error": "timeout"}
except Exception as e:
return {"status": "unhealthy", "error": str(e)}
Gestion des états dégradés
Introduisez un état degraded pour plus de nuance :
{
"status": "degraded",
"message": "Cache unavailable, falling back to database",
"checks": {
"database": {"status": "healthy"},
"cache": {"status": "unhealthy"}
}
}
Cet état signale que le service fonctionne mais avec des capacités réduites.
Métadonnées contextuelles
Incluez des informations utiles au diagnostic :
{
"status": "healthy",
"version": "2.1.0",
"git_sha": "abc123",
"started_at": "2025-12-24T08:00:00Z",
"uptime_seconds": 9000,
"instance_id": "api-pod-xyz"
}
Bonnes Pratiques Health Check
Gardez les checks légers et rapides
Un health check ne devrait pas prendre plus de quelques centaines de millisecondes :
# Cache dependency check results
from functools import lru_cache
from time import time
@lru_cache(maxsize=1)
def cached_health_check():
timestamp = int(time())
# Cache for 5 seconds
return _perform_checks(), timestamp
def get_health():
result, cached_at = cached_health_check()
if time() - cached_at > 5:
cached_health_check.cache_clear()
result, _ = cached_health_check()
return result
Sécurisez vos health checks
Les endpoints exposant des détails d'infrastructure peuvent aider des attaquants :
@app.route('/health')
def public_health():
"""Public endpoint - minimal info"""
return jsonify({"status": "healthy"}), 200
@app.route('/health/detailed')
@require_internal_network
def detailed_health():
"""Internal endpoint - full details"""
return jsonify(get_full_health_status())
Évitez les effets de cascade
Distinguez les dépendances critiques des non-critiques :
CRITICAL_DEPS = ['database']
NON_CRITICAL_DEPS = ['cache', 'search']
def compute_status(checks):
critical_healthy = all(
checks[dep]["status"] == "healthy"
for dep in CRITICAL_DEPS
)
if not critical_healthy:
return "unhealthy", 503
all_healthy = all(
checks[dep]["status"] == "healthy"
for dep in checks
)
if all_healthy:
return "healthy", 200
else:
return "degraded", 200
Testez vos health checks
Incluez les health checks dans vos tests automatisés :
def test_health_check_with_db_down(mock_db_failure):
"""Health check should report unhealthy when DB is down"""
response = client.get('/health/ready')
assert response.status_code == 503
assert response.json["checks"]["database"]["status"] == "unhealthy"
def test_health_check_timeout():
"""Health check should timeout gracefully"""
with mock_slow_dependency(delay=10):
response = client.get('/health/ready')
assert response.status_code == 503
Documentez la signification des états
Les équipes d'opérations doivent comprendre chaque état :
| État | Signification | Action |
|---|---|---|
| healthy | Tout fonctionne | Aucune |
| degraded | Fonctionnel avec limitations | Investigation |
| unhealthy | Incapable de traiter | Intervention urgente |
Conclusion
Les health checks bien conçus transforment l'observabilité de vos APIs REST. En distinguant les niveaux de vérification et en fournissant des informations contextuelles, vos endpoints deviennent de véritables outils opérationnels.
L'investissement dans des health checks sophistiqués se rentabilise par :
- Réduction des incidents
- Amélioration de la résilience
- Déploiements plus sûrs