← Назад к вопросам

Как мониторить работоспособность приложения?

2.0 Middle🔥 201 комментариев
#DevOps и инфраструктура#Архитектура и паттерны

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Как мониторить работоспособность приложения?

Мониторинг — это не просто красивые графики. Это система раннего предупреждения, которая ловит проблемы ДО того, как их заметят пользователи. Рассмотрю комплексный подход.

1. Health Check Endpoints

Основа всего мониторинга — простой endpoint, который проверяет работоспособность:

from fastapi import FastAPI
from datetime import datetime
import asyncio
import psutil

app = FastAPI()

@app.get("/health")
async def health_check():
    """Простой health check"""
    return {"status": "ok", "timestamp": datetime.utcnow().isoformat()}

@app.get("/health/deep")
async def deep_health_check():
    """Проверка всех зависимостей"""
    checks = {
        "database": await check_database(),
        "redis": await check_redis(),
        "disk_usage": psutil.disk_usage("/").percent,
        "memory_usage": psutil.virtual_memory().percent,
        "cpu_usage": psutil.cpu_percent(interval=1),
    }
    
    all_ok = all(v for k, v in checks.items() if k != "disk_usage")
    
    return {
        "status": "healthy" if all_ok else "degraded",
        "timestamp": datetime.utcnow().isoformat(),
        "checks": checks
    }

async def check_database():
    try:
        # Пинг БД
        await db.execute("SELECT 1")
        return True
    except Exception:
        return False

async def check_redis():
    try:
        await redis.ping()
        return True
    except Exception:
        return False

2. Prometheus для метрик

Структурированный сбор метрик:

from prometheus_client import Counter, Histogram, Gauge
from prometheus_client import generate_latest, CONTENT_TYPE_LATEST
from fastapi.responses import Response
import time

# Определяем метрики
http_requests_total = Counter(
    "http_requests_total",
    "Total HTTP requests",
    ["method", "endpoint", "status"]
)

http_request_duration = Histogram(
    "http_request_duration_seconds",
    "HTTP request duration in seconds",
    ["method", "endpoint"]
)

active_users = Gauge(
    "active_users_total",
    "Number of active users"
)

db_query_duration = Histogram(
    "db_query_duration_seconds",
    "Database query duration",
    ["query_type"]
)

# Middleware для отслеживания запросов
@app.middleware("http")
async def track_requests(request, call_next):
    start = time.time()
    response = await call_next(request)
    duration = time.time() - start
    
    http_requests_total.labels(
        method=request.method,
        endpoint=request.url.path,
        status=response.status_code
    ).inc()
    
    http_request_duration.labels(
        method=request.method,
        endpoint=request.url.path
    ).observe(duration)
    
    return response

# Endpoint для Prometheus
@app.get("/metrics")
async def metrics():
    return Response(generate_latest(), media_type=CONTENT_TYPE_LATEST)

3. Логирование с трассировкой

import logging
import json
from uuid import uuid4

class RequestIdFilter(logging.Filter):
    def filter(self, record):
        record.request_id = getattr(record, "request_id", "")
        return True

# Middleware для добавления request ID
@app.middleware("http")
async def add_request_id(request, call_next):
    request_id = request.headers.get("X-Request-ID", str(uuid4()))
    response = await call_next(request)
    response.headers["X-Request-ID"] = request_id
    return response

# JSON логирование
logger = logging.getLogger(__name__)
logger.addFilter(RequestIdFilter())

@app.get("/api/users/{user_id}")
async def get_user(user_id: int):
    logger.info(
        "Fetching user",
        extra={
            "user_id": user_id,
            "timestamp": datetime.utcnow().isoformat()
        }
    )
    return {"id": user_id, "name": "John"}

4. Grafana Dashboard

Конфигурация для визуализации:

{
  "dashboard": {
    "title": "API Monitoring",
    "panels": [
      {
        "title": "Request Rate",
        "targets": [
          {
            "expr": "rate(http_requests_total[5m])"
          }
        ]
      },
      {
        "title": "Response Time (p95)",
        "targets": [
          {
            "expr": "histogram_quantile(0.95, http_request_duration_seconds)"
          }
        ]
      },
      {
        "title": "Error Rate",
        "targets": [
          {
            "expr": "rate(http_requests_total{status=~\"5..\"}[5m])"
          }
        ]
      },
      {
        "title": "Database Queries",
        "targets": [
          {
            "expr": "rate(db_query_duration_seconds_total[5m])"
          }
        ]
      }
    ]
  }
}

5. Alerting Rules (Prometheus)

# alert-rules.yml
groups:
  - name: api-alerts
    rules:
      # Высокая rate ошибок
      - alert: HighErrorRate
        expr: (rate(http_requests_total{status=~"5.."}[5m]) / rate(http_requests_total[5m])) > 0.05
        for: 5m
        annotations:
          summary: "High error rate detected"
          description: "Error rate is {{ $value | humanizePercentage }} over last 5 minutes"
      
      # Медленные запросы
      - alert: SlowApiResponse
        expr: histogram_quantile(0.95, http_request_duration_seconds) > 1
        for: 5m
        annotations:
          summary: "API response time is slow"
      
      # БД недоступна
      - alert: DatabaseDown
        expr: db_health == 0
        for: 1m
        annotations:
          summary: "Database is unavailable"
      
      # Высокая используемость памяти
      - alert: HighMemoryUsage
        expr: process_resident_memory_bytes > 1000000000
        for: 10m
        annotations:
          summary: "Memory usage is {{ $value | humanize }}B"
      
      # Высокая нагрузка на диск
      - alert: DiskSpaceLow
        expr: (node_filesystem_avail_bytes / node_filesystem_size_bytes) < 0.2
        for: 5m
        annotations:
          summary: "Less than 20% disk space available"

6. Distributed Tracing (Jaeger)

from opentelemetry import trace
from opentelemetry.exporter.jaeger.thrift import JaegerExporter
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor

# Инициализация Jaeger
jaeger_exporter = JaegerExporter(
    agent_host_name="localhost",
    agent_port=6831,
)

trace.set_tracer_provider(TracerProvider())
trace.get_tracer_provider().add_span_processor(
    BatchSpanProcessor(jaeger_exporter)
)

tracer = trace.get_tracer(__name__)

# Использование
@app.get("/api/users/{user_id}")
async def get_user(user_id: int):
    with tracer.start_as_current_span("get_user") as span:
        span.set_attribute("user.id", user_id)
        
        with tracer.start_as_current_span("db_query"):
            user = await db.query(User).filter(User.id == user_id).first()
        
        with tracer.start_as_current_span("serialize_response"):
            return user.to_dict()

7. Uptimerobot/Pingdom для external monitoring

# Регулярно проверяет эндпоинт
curl -s https://api.example.com/health | jq .

# Можно настроить на UptimeRobot.com
# Будет отправлять уведомления, если сервис недоступен

8. Логирование в ELK Stack

import logging
from pythonjsonlogger import jsonlogger
from elastic_transport import Transport

# Отправка логов в Elasticsearch
handler = logging.StreamHandler()
formatter = jsonlogger.JsonFormatter()
handler.setFormatter(formatter)

logger = logging.getLogger()
logger.addHandler(handler)
logger.setLevel(logging.INFO)

# Использование
logger.info(
    "API call",
    extra={
        "service": "api",
        "endpoint": "/api/users",
        "method": "GET",
        "status_code": 200,
        "duration_ms": 145,
        "user_id": 12345
    }
)

9. Сценарий реагирования на алерты

# На основе алертов из Prometheus/Alertmanager
def handle_high_error_rate():
    """
    1. Отправить уведомление в Slack
    2. Создать incident в PagerDuty
    3. Собрать логи за последний час
    4. Проверить состояние БД
    """
    pass

def handle_database_down():
    """
    1. Немедленный алерт в Slack/SMS
    2. Попытка переключиться на replica
    3. Уменьшить нагрузку (circuit breaker)
    4. Вызвать DBA на-duty
    """
    pass

10. Docker Compose для локального мониторинга

version: "3.8"
services:
  app:
    build: .
    ports:
      - "8000:8000"

  prometheus:
    image: prom/prometheus
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
    ports:
      - "9090:9090"

  grafana:
    image: grafana/grafana
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin
    ports:
      - "3000:3000"
    depends_on:
      - prometheus

  jaeger:
    image: jaegertracing/all-in-one
    ports:
      - "6831:6831/udp"
      - "16686:16686"

Чек-лист мониторинга

  • Health checks — простой и глубокий
  • Метрики — Prometheus для всех важных операций
  • Логирование — структурированное и централизованное
  • Alerting — автоматические уведомления при проблемах
  • Трассировка — Jaeger для debug распределённых запросов
  • Dashboards — Grafana для визуализации
  • SLA мониторинг — отслеживание uptime
  • Capacity planning — прогнозирование роста нагрузки

Хороший мониторинг экономит часы на отладку в production и сохраняет нервы при инцидентах.