Pricing Blog Compare Glossary
Login Start Free

How to Design Health Check Endpoints for Your API

How to design health check endpoints that actually catch problems. Covers /health patterns, dependency checks, liveness vs readiness, Kubernetes probes, and response format.

2026-03-26 · 12 min · Technical Guide

Health check endpoint — точка входа для мониторинга. Если он возвращает 200, ваш сервис считается живым. Если 503 — мёртвым. От того, насколько грамотно спроектирован этот endpoint, зависит, поймаете ли вы реальный outage за 30 секунд или пропустите деградацию, которую заметят только пользователи.

Большинство команд делают минимальный GET /health → 200 OK. Это лучше, чем ничего, но ловит только полную смерть процесса. Настоящий health check проверяет зависимости, различает liveness и readiness, и даёт диагностическую информацию для быстрого troubleshooting.

Два уровня health checks

Liveness: «процесс жив?»

Liveness probe отвечает на один вопрос: приложение запущено и отвечает на запросы? Он не проверяет зависимости — только сам процесс. Если liveness probe не отвечает, Kubernetes перезапускает контейнер, а load balancer перестаёт отправлять трафик.

Endpoint: GET /health/live

Что проверяет: процесс запущен, HTTP server отвечает

Ответ при успехе: 200 {"status": "ok"}

Когда fails: deadlock, out of memory, бесконечный цикл, event loop заблокирован

Liveness должен быть максимально лёгким. Без обращений к базе, кешу, или внешним API. Если ваш liveness probe зависит от базы, то падение базы вызывает каскадный перезапуск всех контейнеров — что только усугубляет ситуацию.

Readiness: «готов принимать трафик?»

Readiness probe отвечает на вопрос: может ли этот экземпляр обработать запрос пользователя? Он проверяет зависимости: базу данных, кеш, очереди, внешние API. Если readiness probe fails, экземпляр выводится из балансировки, но не перезапускается.

Endpoint: GET /health/ready

Что проверяет: база данных доступна, Redis/кеш отвечает, критичные внешние API reachable

Ответ при успехе:

{
  "status": "healthy",
  "timestamp": "2026-03-26T10:30:00Z",
  "checks": {
    "database": {"status": "healthy", "latency_ms": 3},
    "redis": {"status": "healthy", "latency_ms": 1},
    "stripe_api": {"status": "healthy", "latency_ms": 45}
  }
}

Ответ при degradation:

{
  "status": "degraded",
  "timestamp": "2026-03-26T10:30:00Z",
  "checks": {
    "database": {"status": "healthy", "latency_ms": 3},
    "redis": {"status": "unhealthy", "error": "connection timeout"},
    "stripe_api": {"status": "healthy", "latency_ms": 45}
  }
}

Проектирование dependency checks

Каждая зависимость в readiness probe должна проверяться изолированно с индивидуальным timeout. Один медленный check не должен блокировать весь endpoint.

Database check

# Python / Django
def check_database():
    try:
        start = time.monotonic()
        with connection.cursor() as cursor:
            cursor.execute("SELECT 1")
        latency = (time.monotonic() - start) * 1000
        return {"status": "healthy", "latency_ms": round(latency)}
    except Exception as e:
        return {"status": "unhealthy", "error": str(e)}

Timeout: 3 секунды. SELECT 1 — минимальный запрос, проверяющий connection pool, network path до базы и базовую работоспособность PostgreSQL/MySQL.

Cache check (Redis/Memcached)

def check_redis():
    try:
        start = time.monotonic()
        redis_client.ping()
        latency = (time.monotonic() - start) * 1000
        return {"status": "healthy", "latency_ms": round(latency)}
    except Exception as e:
        return {"status": "unhealthy", "error": str(e)}

Timeout: 2 секунды. PING проверяет подключение и авторизацию. Если кеш — не критичная зависимость (fallback на базу), его failure может быть degraded, а не unhealthy.

External API check

def check_stripe():
    try:
        start = time.monotonic()
        response = requests.get(
            "https://api.stripe.com/healthcheck",
            timeout=5
        )
        latency = (time.monotonic() - start) * 1000
        if response.status_code == 200:
            return {"status": "healthy", "latency_ms": round(latency)}
        return {"status": "degraded", "http_status": response.status_code}
    except requests.Timeout:
        return {"status": "unhealthy", "error": "timeout"}
    except Exception as e:
        return {"status": "unhealthy", "error": str(e)}

Осторожно: внешние API checks увеличивают latency health endpoint и создают зависимость от третьей стороны. Включайте только критичные зависимости (payment, auth provider). Некритичные — проверяйте async и кешируйте результат на 30-60 секунд.

Классификация зависимостей: critical vs degraded

Не все зависимости одинаково важны. База данных — critical: без неё ничего не работает. Email сервис — degraded: приложение работает, но письма не уходят. Правильная классификация предотвращает ложное срабатывание health check из-за некритичных сбоев.

Critical (→ unhealthy, HTTP 503): primary database, authentication service, core business logic dependencies

Degraded (→ degraded, HTTP 200): cache (Redis/Memcached), email service, analytics, non-essential third-party APIs

Unchecked: CDN, logging service, metrics collection — их failure не влияет на способность обрабатывать запросы

def get_overall_status(checks):
    if any(c["status"] == "unhealthy" for name, c in checks.items()
           if name in CRITICAL_DEPS):
        return "unhealthy", 503
    if any(c["status"] != "healthy" for c in checks.values()):
        return "degraded", 200
    return "healthy", 200

Формат ответа

Стандартизированный формат ответа упрощает интеграцию с мониторинг-системами и позволяет использовать JSON path assertions для проверки конкретных полей.

{
  "status": "healthy",
  "version": "2.4.1",
  "uptime_seconds": 86420,
  "timestamp": "2026-03-26T10:30:00Z",
  "checks": {
    "database": {
      "status": "healthy",
      "latency_ms": 3,
      "type": "postgresql"
    },
    "redis": {
      "status": "healthy",
      "latency_ms": 1,
      "type": "redis"
    },
    "queue": {
      "status": "healthy",
      "latency_ms": 2,
      "pending_jobs": 142,
      "type": "celery"
    }
  }
}

В AtomPing вы можете настроить HTTP check с JSON path assertion $.status = healthy. Это проверит не только что endpoint отвечает 200, но и что все зависимости в порядке. Если база упадёт, status сменится на unhealthy, assertion провалится, и мониторинг создаст инцидент.

Kubernetes probes: конфигурация

В Kubernetes liveness и readiness probes настраиваются в манифесте пода. Правильная конфигурация — баланс между скоростью обнаружения и устойчивостью к кратковременным сбоям.

spec:
  containers:
    - name: api
      livenessProbe:
        httpGet:
          path: /health/live
          port: 8000
        initialDelaySeconds: 15
        periodSeconds: 10
        timeoutSeconds: 3
        failureThreshold: 3
      readinessProbe:
        httpGet:
          path: /health/ready
          port: 8000
        initialDelaySeconds: 5
        periodSeconds: 10
        timeoutSeconds: 5
        failureThreshold: 2
      startupProbe:
        httpGet:
          path: /health/live
          port: 8000
        initialDelaySeconds: 0
        periodSeconds: 5
        failureThreshold: 30

startupProbe — третий тип, который даёт приложению время на инициализацию (до 150 секунд в примере выше). Пока startup probe не пройдёт, liveness и readiness не запускаются. Полезно для приложений с тяжёлым стартом (миграции, прогрев кеша, загрузка ML-моделей).

Типичные ошибки

1. Liveness probe с dependency checks

Самая частая ошибка: проверка базы данных в liveness probe. База падает → liveness fails → Kubernetes перезапускает все поды → поды стартуют одновременно → thundering herd на базу → база не восстанавливается. Liveness = только процесс. Dependencies = только readiness.

2. Отсутствие timeout на individual checks

Если один dependency check зависает на 30 секунд (например, DNS resolution timeout к внешнему API), весь health endpoint зависает. Kubernetes интерпретирует это как failure. Решение: выполняйте checks параллельно с индивидуальными timeout (2-5 секунд каждый).

3. Тяжёлые health checks

Health endpoint вызывается каждые 10-30 секунд десятками потребителей (Kubernetes, load balancer, внешний мониторинг). Если check выполняет сложный SQL-запрос или вызывает 5 внешних API, он создаёт ощутимую нагрузку. Правило: health check endpoint должен отвечать за 50-200ms, без исключений.

4. Выдача секретов в health response

Никогда не включайте connection strings, API keys, внутренние IP-адреса, или имена таблиц в ответ health check. Даже если endpoint предназначен «только для внутреннего использования» — утечка одного URL в логи раскрывает вашу инфраструктуру.

5. Единый /health без разделения на liveness/readiness

Один endpoint GET /health вынуждает выбирать: проверять зависимости (и рисковать каскадными перезапусками) или не проверять (и пропускать деградации). Разделение на /health/live и /health/ready решает эту дилемму.

Продвинутые паттерны

Cached readiness

Вместо проверки зависимостей при каждом запросе — запускайте background task, который проверяет их каждые 10-15 секунд и кеширует результат в памяти. Health endpoint возвращает кешированный результат мгновенно. Это снижает нагрузку и предотвращает timeout при высокой частоте запросов.

Graceful degradation signaling

Вместо бинарного healthy/unhealthy используйте три состояния: healthy (всё работает), degraded (некритичные зависимости недоступны, но сервис работает), unhealthy (критичная зависимость недоступна). Мониторинг может по-разному реагировать на каждое состояние: degraded = warning, unhealthy = critical alert.

Deep health vs shallow health

Два endpoint: /health (shallow — быстрая проверка процесса для load balancer, 1-5ms) и /health/deep (полная проверка всех зависимостей для мониторинга, 50-200ms). Load balancer использует shallow, внешний мониторинг — deep. Это разделяет потребности разных потребителей.

Интеграция с внешним мониторингом

Health check endpoint — половина решения. Вторая половина — внешний мониторинг, который регулярно опрашивает этот endpoint из разных регионов и алертит при проблемах.

Настройка в AtomPing:

1. Создайте HTTP monitor с URL https://api.yourapp.com/health/ready

2. Добавьте JSON path assertion: $.status equals healthy

3. Установите response time threshold: 5000ms (health endpoint не должен быть медленным)

4. Интервал: 30 секунд

5. Включите quorum confirmation для предотвращения ложных срабатываний

Внешний мониторинг проверяет то, что Kubernetes probes не могут: доступность через internet (DNS, routing, TLS), производительность с точки зрения пользователя, и работоспособность всей цепочки (CDN → load balancer → app → database).

Checklist: проектирование health check endpoint

Архитектура: раздельные /health/live (liveness) и /health/ready (readiness) endpoints

Dependency checks: каждая зависимость проверяется с индивидуальным timeout (2-5s)

Классификация: зависимости разделены на critical и non-critical

Response format: JSON с overall status, per-dependency status, latency, timestamp

Performance: health endpoint отвечает за 50-200ms при нормальной работе

Security: нет секретов в ответе, liveness не требует auth

Мониторинг: endpoint опрашивается внешним мониторингом с JSON path assertions

Связанные материалы

API Monitoring: полное руководство — как мониторить REST API endpoints

Мониторинг микросервисов — health checks в распределённых системах

Internal vs External Monitoring — зачем нужны оба подхода

Как сократить ложные срабатывания — quorum confirmation и batch anomaly detection

FAQ

What is a health check endpoint?

A health check endpoint is a dedicated API route (typically /health or /healthz) that returns the current operational status of your application. It verifies that the app is running, its dependencies (database, cache, external APIs) are reachable, and critical subsystems function correctly. Monitoring tools poll this endpoint to detect outages.

Should I use /health or /healthz?

/health is more readable and widely understood. /healthz originated in Kubernetes (from Google's convention of appending 'z' to internal endpoints). Both work — pick one and be consistent. Kubernetes specifically supports both. If you're building a public API, /health is the more standard choice.

What should a health check endpoint return?

At minimum: HTTP 200 with a JSON body containing overall status and individual dependency checks (database, cache, queue). Include response time for each dependency. Return HTTP 503 when any critical dependency is unhealthy. Always include a timestamp. Optionally: version number, uptime duration, and region identifier.

Should health checks be authenticated?

The basic liveness endpoint (/health/live) should not require authentication — monitoring tools and load balancers need unauthenticated access. The detailed readiness endpoint (/health/ready) can optionally require authentication if it exposes internal architecture details. Never expose sensitive data (connection strings, credentials) in health check responses.

How often should monitoring tools poll health endpoints?

Every 30 seconds for production services with SLA commitments. Every 1-3 minutes for internal tools and staging environments. Every 5 minutes for non-critical services. The endpoint itself should respond within 5 seconds — if dependency checks take longer, implement timeouts and return partial status.

What's the difference between liveness and readiness probes?

A liveness probe checks 'is the process alive?' — if it fails, the container should be restarted. A readiness probe checks 'can this instance handle traffic?' — if it fails, the instance is removed from the load balancer but not restarted. Your app can be alive (liveness pass) but not ready (readiness fail) during startup or when a dependency is down.

Start monitoring your infrastructure

Start Free View Pricing