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

Как обеспечить бесперебойность сервиса?

2.4 Senior🔥 121 комментариев
#DevOps и инфраструктура#Архитектура и паттерны

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

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

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

Обеспечение бесперебойности сервиса (High Availability)

Бесперебойность — способность системы работать без прерываний, минимизируя downtime при сбоях. Это комплекс мер на разных уровнях.

1. Дизайн без единой точки отказа (SPOF)

Проблема:

[Клиенты]
    |
    v
[Единственный сервер]
    |
[ОТКАЗ] → системе конец

Решение:

[Клиенты]
    |
    v
[Load Balancer]
    |
    +--- [API Server 1]
    +--- [API Server 2]
    +--- [API Server N]
# Конфиг nginx для health checks
upstream backend {
    server api1.example.com:8000 max_fails=3 fail_timeout=10s;
    server api2.example.com:8000 max_fails=3 fail_timeout=10s;
    server api3.example.com:8000 max_fails=3 fail_timeout=10s;
}

server {
    location / {
        proxy_pass http://backend;
    }
}

2. Graceful Shutdown

Проблема: при остановке сервера текущие запросы могут потеряться.

Решение:

import signal
import asyncio
from fastapi import FastAPI
from contextlib import asynccontextmanager

active_requests = set()

@asynccontextmanager
async def lifespan(app: FastAPI):
    # На старте
    yield
    # На остановке
    print("Gracefully shutting down...")
    await wait_for_active_requests(timeout=30)

app = FastAPI(lifespan=lifespan)

@app.get("/api/data")
async def get_data():
    request_id = uuid.uuid4()
    active_requests.add(request_id)
    try:
        await asyncio.sleep(10)  # долгая операция
        return {"data": "value"}
    finally:
        active_requests.discard(request_id)

async def wait_for_active_requests(timeout=30):
    """Ждём пока все запросы завершатся"""
    start = asyncio.get_event_loop().time()
    while active_requests:
        if asyncio.get_event_loop().time() - start > timeout:
            print(f"Force shutdown: {len(active_requests)} requests still active")
            break
        await asyncio.sleep(0.1)

3. Health Checks и Monitoring

from fastapi import FastAPI
from datetime import datetime

app = FastAPI()

@app.get("/health")
async def health_check():
    """Liveness probe — сервис вообще живой"""
    return {"status": "ok", "timestamp": datetime.utcnow()}

@app.get("/ready")
async def readiness_check():
    """Readiness probe — сервис готов принимать запросы"""
    try:
        # Проверяем БД
        db.execute("SELECT 1")
        # Проверяем Redis
        redis.ping()
        # Проверяем зависимости
        return {"status": "ready"}
    except Exception as e:
        return {"status": "not ready", "reason": str(e)}, 503

В Kubernetes:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: api
spec:
  template:
    spec:
      containers:
      - name: app
        livenessProbe:
          httpGet:
            path: /health
            port: 8000
          initialDelaySeconds: 10
          periodSeconds: 10
          failureThreshold: 3
        readinessProbe:
          httpGet:
            path: /ready
            port: 8000
          initialDelaySeconds: 5
          periodSeconds: 5

4. Резервные копии и восстановление

# Автоматическое резервное копирование БД
import subprocess
import datetime
import os

def backup_database():
    """Ежедневное резервное копирование"""
    timestamp = datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
    backup_file = f"/backups/db_backup_{timestamp}.sql"
    
    subprocess.run([
        "pg_dump",
        "-h", os.getenv("DB_HOST"),
        "-U", os.getenv("DB_USER"),
        os.getenv("DB_NAME")
    ], stdout=open(backup_file, 'w'))
    
    # Загружаем в S3
    upload_to_s3(backup_file, f"db-backups/{timestamp}.sql")
    
    # Удаляем локальную копию
    os.remove(backup_file)

# Восстановление
def restore_database(backup_file):
    subprocess.run([
        "psql",
        "-h", os.getenv("DB_HOST"),
        "-U", os.getenv("DB_USER"),
        os.getenv("DB_NAME")
    ], stdin=open(backup_file))

5. Replication и Failover

PostgreSQL с репликацией:

Primary (Москва)
   |
   v [WAL реplication]
   |
Standby (резервный, готов к активации)
   |
   (синхронная репликация: первичный ждёт подтверждения)
# Python код для failover
import paramiko

def promote_standby():
    """Переводим Standby в Primary"""
    ssh = paramiko.SSHClient()
    ssh.connect(standby_host)
    
    # Команда для promotion
    ssh.exec_command("pg_ctl promote -D /var/lib/postgresql/data")
    ssh.close()
    
    # Обновляем конфиг приложения
    update_database_url(standby_host)

6. Circuit Breaker паттерн

from pybreaker import CircuitBreaker
import requests

# Защита от cascading failures
api_breaker = CircuitBreaker(
    fail_max=5,
    reset_timeout=60,
    exclude=[requests.Timeout]  # не считаем timeout за failure
)

@api_breaker
def call_external_api(url: str):
    return requests.get(url, timeout=5)

def get_user_data(user_id: int):
    try:
        return call_external_api(f"https://external.api/users/{user_id}")
    except CircuitBreaker.CircuitBreakerListener:
        # Circuit открыт, возвращаем cached данные
        return cache.get(f"user:{user_id}") or {"error": "service unavailable"}

7. Retry Logic

from tenacity import (
    retry,
    stop_after_attempt,
    wait_exponential,
    retry_if_exception_type
)

@retry(
    stop=stop_after_attempt(3),
    wait=wait_exponential(multiplier=1, min=2, max=10),
    retry=retry_if_exception_type(Exception)
)
def send_payment(payment_id: int):
    # Retries с exponential backoff: 2s, 4s, 8s
    return payment_service.process(payment_id)

# Идемпотентность
def process_payment(payment_id: int):
    # Проверяем уже ли обработан
    if cache.exists(f"payment:{payment_id}:processed"):
        return cache.get(f"payment:{payment_id}:result")
    
    result = send_payment(payment_id)
    
    # Сохраняем результат
    cache.setex(f"payment:{payment_id}:processed", 86400, True)
    cache.setex(f"payment:{payment_id}:result", 86400, result)
    
    return result

8. Rate Limiting и Throttling

from slowapi import Limiter
from slowapi.util import get_remote_address

limiter = Limiter(key_func=get_remote_address)
app = FastAPI()
app.state.limiter = limiter

@app.get("/api/data")
@limiter.limit("100/minute")
async def get_data(request):
    return {"data": "value"}

# Custom rate limiting по user_id
from redis import Redis

redis = Redis()

def check_rate_limit(user_id: str, limit: int, window: int) -> bool:
    key = f"rate_limit:{user_id}"
    current = redis.incr(key)
    if current == 1:
        redis.expire(key, window)
    return current <= limit

@app.post("/api/payment")
async def create_payment(payment: PaymentData, request):
    user_id = request.user.id
    if not check_rate_limit(user_id, limit=5, window=3600):
        return {"error": "rate limit exceeded"}, 429
    return process_payment(payment)

9. Мониторинг и алерты

from prometheus_client import Counter, Gauge, Histogram

# Метрики
request_count = Counter('requests_total', 'Total requests')
error_count = Counter('errors_total', 'Total errors')
active_connections = Gauge('active_connections', 'Active connections')
response_time = Histogram('response_time', 'Response time')

@app.middleware("http")
async def metrics_middleware(request, call_next):
    active_connections.inc()
    try:
        with response_time.time():
            response = await call_next(request)
        request_count.inc()
        return response
    except Exception as e:
        error_count.inc()
        raise
    finally:
        active_connections.dec()

Prometheus alerts:

groups:
- name: alerts
  rules:
  - alert: HighErrorRate
    expr: rate(errors_total[5m]) > 0.05
    for: 5m
    annotations:
      summary: "High error rate ({{ $value }})"
  
  - alert: ServiceDown
    expr: up{job="api"} == 0
    for: 1m
    annotations:
      summary: "Service {{ $labels.instance }} is down"

Архитектура HA системы

[Клиенты]
    |
    v
[CloudFlare / CDN]
    |
    v
[nginx Load Balancer (Active-Passive)] ← Health checks
    |
    +--- [API 1] ──┐
    +--- [API 2] ──┤
    +--- [API 3] ──┘
         |
    [PostgreSQL Primary] ← WAL Replication → [PostgreSQL Standby]
    [Redis Cluster (3+ nodes)]
    [Message Queue (2+ brokers)]
    
[Monitoring]
├── Prometheus (метрики)
├── Grafana (визуализация)
├── AlertManager (алерты)
└── ELK (логирование)

Лучшие практики

Нет SPOF — дублируй всё критичное. Graceful shutdown — корректная остановка. Health checks — liveness + readiness. Resilience patterns — circuit breaker, retry, timeout. Мониторинг — know что происходит. Backup & restore — тестируй восстановление. Chaos engineering — специально ломай систему чтобы найти слабые места.