Мониторинг health check endpoint в FastAPI для SaaS
FastAPI отдаёт 200 на всех маршрутах, пока пул соединений с БД не умрёт в 3 ночи. Монитор аптайма всё ещё зелёный — он ни разу не бил в реальный probe URL. Мониторинг health check в FastAPI закрывает дыру: два маленьких маршрута, честные коды ответа и внешняя проверка с той стороны, откуда ходят клиенты. Вы добавите /health и /ready, проверите их curl извне своей сети и зарегистрируете URL в StillOnline для алертов и публичной страницы статуса.
Краткий ответ
GET /health — liveness (процесс жив, без БД). GET /ready — readiness (ping БД или Redis с таймаутом 3–5 секунд). При сбое зависимости — HTTP 503, при норме — 200. Probe-маршруты публичные, ответ быстрее двух секунд, в StillOnline укажите URL, который означает «клиенты не могут пользоваться продуктом». Free: один проект, один URL, интервал пять минут, только код статуса — тарифы.
FastAPI — Python-фреймворк для HTTP API. Uvicorn — серверный процесс на вашем порту. Внешний монитор шлёт GET из публичного интернета по расписанию — как браузер без логина. Это не то же самое, что проверка внутри процесса: монитор подтверждает DNS, TLS и reverse proxy end-to-end.
Базовый стек-агностичный гайд — быстрый старт health URL. Здесь — готовые маршруты FastAPI, liveness vs readiness и подключение StillOnline без Kubernetes.
1. Что показывать в health-маршруте FastAPI (и что скрывать)
Ответ probe — маленький JSON, «табличка на двери», а не складской учёт.
| Поле | Включать? | Зачем |
|---|---|---|
| status (ok / down) | Да | Ясный сигнал для логов и дежурного |
| timestamp (UTC ISO) | Да | Видно, что ответ свежий |
| version или git SHA | Да на /health | Помогает после деплоев |
| checks по зависимостям | Да на /ready | Видно, что упало |
| Пароли, API-ключи, внутренние хосты | Нет | Probe часто без авторизации |
| Stack trace | Нет | Утечка деталей сканерам |
Делайте: модель ответа через Pydantic. Не делайте: те же сериализаторы, что в админке, на публичном пути.
Минимальный handler /health:
from datetime import datetime, timezone
from fastapi import APIRouter
from pydantic import BaseModel
router = APIRouter()
class HealthResponse(BaseModel):
status: str
timestamp: str
version: str
@router.get("/health", response_model=HealthResponse)
async def health() -> HealthResponse:
return HealthResponse(
status="ok",
timestamp=datetime.now(timezone.utc).isoformat(),
version="1.0.0",
)
Подключите через app.include_router(router). Зафиксируйте имена (/health и /ready, или /healthz, если так ждёт платформа) и не меняйте их молча после включения мониторов.
Делайте: /health без обращения к БД. Не делайте: SELECT 1 на liveness — лишний шум при кратком сбое БД.
2. Разделите /health и /ready для workers и пула БД
Liveness: «процесс принимает HTTP?» Readiness: «инстанс может обслуживать трафик?» В Kubernetes сбой liveness перезапускает pod, readiness убирает из балансировщика. На Railway или Fly.io без K8s разделение всё равно важно: Uvicorn жив, а каждый API-вызов висит из-за исчерпанного пула.
Цепочка probe: StillOnline GET → балансировщик → Uvicorn → /ready → параллельные проверки БД/Redis → 200 или 503 → страница статуса + алерт.
Readiness с async-проверкой БД (таймаут 5 с):
import asyncio
from fastapi import APIRouter, Response, status
from sqlalchemy import text
CHECK_TIMEOUT = 5.0
async def check_database() -> bool:
try:
conn = await asyncio.wait_for(engine.connect(), timeout=CHECK_TIMEOUT)
async with conn:
await conn.execute(text("SELECT 1"))
return True
except Exception:
return False
@router.get("/ready")
async def ready(response: Response):
if not await check_database():
response.status_code = status.HTTP_503_SERVICE_UNAVAILABLE
return {"status": "down", "checks": {"database": "fail"}}
return {"status": "ok", "checks": {"database": "ok"}}
Делайте: проверки через asyncio.gather, чтобы уложиться в один таймаут. Не делайте: синхронные драйверы БД внутри async def без thread pool — Uvicorn блокируется.
Большинство API SaaS указывают в StillOnline именно /ready. Зафиксируйте выбор в README. Паттерны URL — в проверках для API-only SaaS.
3. Коды ответа, таймауты и auth на probe URL
На Free StillOnline смотрит только HTTP-код, не поля JSON. 200 — здорово, 503 — сбой зависимости. Для K8s успех — 200–399; 503 — честный сигнал readiness.
Цель для /health — доли секунды; /ready — до трёх секунд суммарно, на каждую проверку 3–5 секунд. Внешние мониторы часто обрываются около двух секунд — жёсткий потолок.
Cache-Control на /health:
@router.get("/health")
async def health(response: Response):
response.headers["Cache-Control"] = "no-cache, no-store"
return {"status": "ok"}
Делайте: исключите /health и /ready из JWT middleware. Не делайте: OAuth на probe — мониторы не шлют Authorization, получите ложные 401.
Проверка извне вашей сети:
curl -sS -o /dev/null -w "%{http_code}\n" https://api.example.com/health
curl -sS https://api.example.com/ready
Почините 301 и TLS до регистрации URL. Сравните с дизайном health endpoint и маршрутом Laravel, если стек смешанный.
4. Подключите проверки StillOnline и канал алертов
StillOnline по расписанию шлёт GET на полный HTTPS URL, обновляет страницу статуса и уведомляет владельца. Репозиторий не сканируется — вставляете точный URL вручную.
- Войдите на stillonline.tech/ru/app и создайте проект (один на Free).
- Добавьте проверку с полным URL, например
https://api.yourproduct.com/ready. Метод GET, ожидаемый 200. На Free интервал около пяти минут. - Подождите 2–3 цикла probe, прежде чем судить о стабильности.
- Включите алерты владельца в настройках. Free — один канал: email или Telegram или Slack. Pro ($9/мес) и Ultimate ($29/мес) — больше проверок и каналов — тарифы.
- Дайте ссылку на страницу статуса в support-доках.
Делайте: мониторьте публичный edge URL клиентов. Не делайте: localhost или внутренние hostname.
StillOnline дополняет in-cluster probe: ловите сбои DNS, TLS и балансировщика, которые pod изнутри не видит. На Free — около пяти минут между probe и два сбоя подряд до алерта.
5. Чеклист перед production
- Зафиксируйте канонические URL в README и runbook.
- Проверьте 503 при остановленной БД на staging; 200 после восстановления.
- Замерьте задержку с домашней сети, не только из дата-центра.
- Уберите секреты из JSON и access-логов на probe-путях.
- Запишите, какой URL смотрит StillOnline, и обновите при переименовании маршрута.
- Не тащите PyPI health-библиотеки, пока хватает простых маршрутов.
Делайте: health-маршруты в чеклисте каждого деплоя. Не делайте: только HEAD — StillOnline шлёт GET.
Что дальше
У вас маршруты FastAPI, которым можно доверять снаружи, и проверка StillOnline, которая разбудит, когда публичный URL падает. Добавьте вторую проверку для маркетингового сайта на другом хосте, расширьте тариф, когда нужно больше URL или каналов, и дайте ссылку на status page в футере.
Откройте дашборд StillOnline, вставьте URL /ready и включите канал, который вы читаете в 3 ночи.
Связанные материалы
- Быстрый старт health URL
- Дизайн health endpoint: /health vs /ready
- Проверки аптайма для API-only SaaS
- Health check маршрут в Laravel SaaS
FAQ
Должен ли health endpoint StillOnline бить в базу данных?
Не на /health. Ping БД или Redis — на /ready, если хотите, чтобы StillOnline показывал down при сбое зависимостей. Краткие сбои БД дают шум в алертах — подстройте таймауты.
Что мониторить в StillOnline — /health или /ready?
URL, который означает «клиенты не могут пользоваться продуктом». Большинство API SaaS выбирают /ready. Только /health — если проверки зависимостей дадут неприемлемые ложные срабатывания.
Как быстро должен отвечать /health для StillOnline?
Идеал — доли секунды. Держите меньше двух секунд с учётом TLS и географии probe.
Нужен ли PyPI-пакет для health в FastAPI?
Для MVP — нет. Хватает маршрутов и Pydantic. Библиотеки вроде fastapi-health — когда много условных проверок в K8s.