Схема микросервисной архитектуры на тёмной бумаге с узлами-доминошками и символами автоматических выключателей. Янтарные линии на сине-зелёном фоне, латунный пресс-папье в углу, мягкий рассеянный с...


На одном из проектов мы получили L7 DDoS порядка 80–100k RPS на API платёжного сервиса. CDN отработал штатно, сетевой канал не насытился, а приложение легло за четыре минуты. Пул соединений к PostgreSQL иссяк первым, за ним встал сервис авторизации, а дальше каскадный отказ прошёлся по всем двенадцати микросервисам как домино. Деньги ушли на WAF и Anti-DDoS-провайдера, но внутри приложения не было ни circuit breaker, ни graceful degradation, ни rate limiting на уровне API Gateway. Ничего. Голое приложение за дорогим забором.

Эта статья - о том, как строить защиту приложения от DDoS-атак изнутри, на уровне архитектурных паттернов, а не сетевых фильтров.

Почему DDoS ломает приложение, а не сеть​

Когда команда слышит "DDoS", первая ассоциация - забитый канал: SYN-flood, UDP amplification. В терминологии MITRE ATT&CK это Network Denial of Service (T1498, Impact), включая Direct Network Flood (T1498.001) и Reflection Amplification (T1498.002). Эти атаки фильтруются на уровне сети - Cloud Armor, AWS Shield, Cloudflare.

Но современные DDoS всё чаще бьют по прикладному уровню - Endpoint Denial of Service (T1499, Impact), конкретно Service Exhaustion Flood (T1499.002) и Application Exhaustion Flood (T1499.003). Атакующий не забивает канал - он шлёт тысячи валидных HTTP-запросов, каждый из которых дёргает базу, вызывает тяжёлый эндпоинт или генерирует PDF. WAF пропускает такие запросы, потому что каждый по отдельности выглядит легитимно. И вот тут начинается самое интересное.

Каскадный отказ развивается предсказуемо:
  1. Пул соединений к БД исчерпывается (стандартный PostgreSQL держит 100–200 соединений)
  2. Сервисы, зависящие от БД, копят запросы в thread pool
  3. Thread pool забивается, новые запросы получают таймаут
  4. Балансировщик видит 502/503 от всех инстансов - включая здоровые сервисы, которые просто ждут ответа от упавшего downstream
По данным Verizon DBIR 2025, доля веб-атак среди подтверждённых нарушений составила 26%. IBM X-Force отмечает, что 70% атак в 2024 году затронули критическую инфраструктуру. Защита на уровне сети - необходимое, но недостаточное условие. DDoS-устойчивая архитектура приложения строится изнутри.

Autoscaling как защита от DDoS: горизонтальное масштабирование под нагрузкой с потолком​

1782790992089.webp

Почему autoscaling - не панацея​

Горизонтальное масштабирование под нагрузкой - первая реакция SRE на рост трафика. Нагрузка растёт - поднимаем реплики. В Kubernetes это HPA, в AWS - Auto Scaling Groups, в GCP - Managed Instance Groups.

Ловушка: autoscaling без верхней границы при DDoS превращается в budget DDoS. Атакующий заставляет вас платить за обработку мусорного трафика. Система не падает, но ваш бюджет - да. Видел кейс, где за ночь атаки набежало $12k на AWS только за auto-scaled инстансы, которые молотили мусор.

Настройка HPA с потолком​

Ключевые параметры, которые я выставляю на каждом проекте: maxReplicas - жёсткий потолок, обычно 3-5x от нормы; scaleUp.stabilizationWindowSeconds - окно 60-120 секунд, чтобы HPA не дёргался от кратковременных всплесков; scaleDown с медленной политикой, чтобы не убить сервис преждевременным сжатием после атаки.
YAML:
# HPA с потолком и стабилизацией для DDoS-сценариев
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
spec:
  minReplicas: 3
  maxReplicas: 15  # потолок = 5x от нормы
  metrics:
  - type: Resource
    resource:
      name: cpu
      target: {type: Utilization, averageUtilization: 70}
  behavior:
    scaleUp: {stabilizationWindowSeconds: 60}
    scaleDown: {stabilizationWindowSeconds: 300}
Критически важная деталь: HPA масштабирует поды, но не масштабирует БД. Если каждый новый под открывает 10 соединений к PostgreSQL, maxReplicas: 15 вместо 3 - это 150 соединений вместо 30. При max_connections = 100 на PostgreSQL система ляжет не от DDoS, а от собственного autoscaling. Система убьёт себя сама. Решение: PgBouncer перед базой, ограничение POOL_SIZE на pod, и KEDA вместо HPA для масштабирования по глубине очереди, а не по CPU.

Decision point: масштабировать или сбрасывать​

Правило, которое я фиксирую в runbook:
  • CPU < 80%, P99 latency < SLO - HPA справляется, масштабируем штатно
  • CPU > 80%, P99 > SLO, healthcheck OK - масштабируем + включаем rate limiting на Gateway
  • Healthcheck fail на >30% подов - прекращаем масштабирование, включаем circuit breaker и graceful degradation
Это не абстрактная схема - это алерты в Grafana OnCall с автоматическим переключением через feature flags. Когда в три ночи прилетает алерт, инженер не должен вспоминать "а что делать???" - runbook решает за него.

Circuit breaker паттерн в микросервисах: как остановить каскадный отказ​

1782791017028.webp

Три состояния и логика переключения​

Circuit breaker - самый недооценённый паттерн устойчивости микросервисов при DDoS. По систематическому обзору паттернов резилентности (arxiv, 2025, 26 исследований, PRISMA-aligned) circuit breaker входит в девять ключевых тем устойчивости и напрямую предотвращает каскадные отказы.

Три состояния:

Closed - нормальная работа. Запросы идут к downstream-сервису. Breaker считает ошибки в скользящем окне. Ошибки превышают порог - переход в Open.

Open - запросы немедленно отклоняются, без ожидания таймаута. Downstream получает передышку. Через заданное время - переход в Half-Open.

Half-Open - ограниченное число тестовых запросов. Успешны - Closed. Нет - обратно в Open.

Как пишет Microsoft в документации Azure Architecture Center: "The Circuit Breaker pattern helps prevent an application from repeatedly trying to run an operation that's likely to fail.""Схема автоматического выключателя помогает предотвращать приложение из многократных попыток запустить операцию которая скорее всего завершится как фейл." Ключевой момент - breaker защищает не downstream-сервис, а вызывающий код от бесконечного ожидания. Это тонкая, но принципиальная разница.

Из того же систематического обзора (тема T1): "Pattern effectiveness depends on failure semantics; over-tight circuit-breaker thresholds reduce throughput.""Эффективность схемы защиты зависит от семантики отказов; слишком жёсткие пороговые значения срабатывания автоматических выключателей снижают пропускную способность." Слишком агрессивные пороги приводят к тому, что breaker срабатывает на обычных всплесках, а не на реальной деградации. Я на одном проекте потратил день на дебаг "почему сервис тупит" - оказалось, breaker с порогом в 3 ошибки за 10 секунд срабатывал на каждый деплой.

Настройка порогов: практические значения​

По рекомендациям из анализа API Gateway resilience (Zuplo, 2026):
  • Failure threshold: 5 ошибок в 60-секундном окне как стартовая точка. Ниже - ложные срабатывания. Выше - клиенты видят реальные отказы слишком долго
  • Cooldown period: 30-60 секунд - типичное значение для восстановления от временных проблем
  • Half-open test count: 1-3 запроса. Если все успешны - замыкаем цепь
Конфигурация для Resilience4j (одна из самых ходовых библиотек для Java/Kotlin-бэкендов):
YAML:
# Resilience4j - circuit breaker для DDoS-сценария
resilience4j.circuitbreaker:
  instances:
    paymentService:
      slidingWindowSize: 20
      failureRateThreshold: 50
      waitDurationInOpenState: 30s
      permittedNumberOfCallsInHalfOpenState: 3
      slowCallDurationThreshold: 2s
      slowCallRateThreshold: 80
Обратите внимание на slowCallRateThreshold. При DDoS сервис может не возвращать ошибки - а просто отвечать медленно. Без мониторинга latency circuit breaker не сработает, и вместо быстрого отказа вы получите pool exhaustion. Это та ситуация, когда всё вроде "зелёное", а пользователи уже ушли.

Circuit breaker без fallback хуже, чем его отсутствие​

Ошибка, которую я встречал многократно: circuit breaker настроен, но при переходе в Open клиент просто получает 503. Это не решение - это перенос проблемы на другой уровень. Breaker обязан возвращать fallback-ответ: кэшированные данные, упрощённый результат или явное сообщение о деградации с адекватным HTTP-кодом.

Graceful degradation при DDoS-атаке: деградация сервиса под нагрузкой​

1782791045688.webp

Fallback-стратегии по уровням​

Graceful degradation - не отказ, а осознанное снижение качества. По систематическому обзору, Fallback Logic and Graceful Degradation входит в ключевые паттерны резилентности микросервисов.

Три уровня, которые я закладываю в архитектуру:

Уровень 1 - кэшированные ответы. Сервис каталога перегружен - API Gateway отдаёт последнюю закэшированную версию через cache-aside паттерн. Пользователь видит данные с задержкой 5-10 минут, но видит. При ошибке downstream - stale cache с продлённым TTL. Лучше вчерашний каталог, чем пустая страница.

Уровень 2 - редуцированный ответ. Сервис рекомендаций не работает - показываем статический топ-10. Elasticsearch перегружен - переключаемся на полнотекстовый поиск по PostgreSQL (да, медленнее, но работает). Функциональность снижена, но не потеряна.

Уровень 3 - read-only режим. Отключаем все write-операции, кроме критических (оплата, авторизация). API возвращает 503 на некритичные POST/PUT/DELETE. Сервис доступен для просмотра.

Переключение между уровнями - через feature flags (LaunchDarkly, Unleash, или KV-хранилище в Consul/etcd). В идеале автоматически: если метрика http_server_requests_seconds{quantile="0.99"} в Prometheus превышает SLO дольше 2 минут - переход на Уровень 1.

Rate limiting как первая линия DDoS-защиты

Rate limiting на API Gateway - граница между autoscaling и graceful degradation. Он определяет, какой объём трафика допускается до приложения.

В Kong или Nginx конфигурация rate limiting сводится к limit_req_zone по IP или по токену авторизации с burst-буфером. Типичные значения: 100 RPS на IP для публичных эндпоинтов, 500–1000 RPS для аутентифицированных клиентов.

Ключевая ошибка: единый лимит на все эндпоинты. DDoS на /api/health (лёгкий) съедает квоту, и легитимные запросы на /api/checkout (критичный) не проходят. Решение - per-route rate limiting с приоритизацией по бизнес-критичности. Здоровье сервиса и оплата - это разные вселенные, и лимиты у них должны быть разные.

Очереди сообщений под нагрузкой: backpressure и Dead Letter Queue​

Queue-based load leveling​

Очереди - естественный буфер между пиковой нагрузкой и возможностями бэкенда. Как пишет AWS в документации по паттернам отказоустойчивости: "Instead of writing directly to the database, the application sends operations to a queue where they wait to be processed at a controlled rate. This acts like a buffer zone.""Вместо записи личного в базу данных, приложение отправляет операции в очередь где они будут обработаны с контролируемой скоростью. Этот акт похож на буферную зону."

При DDoS очередь (Kafka, RabbitMQ, SQS) принимает удар: запросы складываются в буфер, consumer обрабатывает с контролируемой скоростью. Producer генерирует 50k msg/s - consumer обрабатывает 5k. Без потери данных, без перегрузки базы. Очередь - это амортизатор, который гасит удар.

По данным систематического обзора (тема T5), adaptive queues повышают эффективность на 15% по сравнению со статическими конфигурациями. Consumer должен динамически регулировать batch size и concurrency.

Backpressure: когда очередь переполняется​

Без backpressure очередь растёт бесконечно, жрёт всю доступную память, и система падает - уже не от DDoS, а от OOM. Вы пережили атаку, но умерли от лечения.

Реализации backpressure в разных стеках:
  • Kafka: max.poll.records на consumer ограничивает batch, max.block.ms на producer заставляет ждать при полном буфере
  • RabbitMQ: x-max-length на очереди + basic.nack с requeue=false для сброса в DLQ
  • Reactor/WebFlux: встроенный backpressure через Flux.onBackpressureDrop()

Dead Letter Queue - обязательный компонент​

DLQ - место, куда уходят сообщения, не обработанные после N retry. Как отмечает AWS: "Error handling requires Dead Letter Queues implementation with defined retry policies and monitoring for failed message recovery.""Для обработки ошибок требуется реализация очередей недоставленных сообщений с определенными политиками повторных попыток и мониторингом восстановления сообщений, которые не удалось восстановить."

При DDoS это критично: вредоносные запросы, вызывающие exception в consumer, не должны запускать бесконечные retry-штормы. Из систематического обзора (тема T3): "Naive backoff without jitter causes retry storms; adding jitter and budgets smooths recovery.""Наивная задержка без учета jitter приводит к шквалу повторных попыток; добавление jitter и ограничений сглаживает процесс восстановления." Каждый retry к DLQ-сообщению - с exponential backoff и jitter. Без jitter десятки consumer'ов синхронно ретраят одновременно и кладут downstream ещё раз.

Bulkhead pattern: изоляция blast radius при DDoS​

1782791097556.webp

Bulkhead - аналогия с водонепроницаемыми переборками корабля. Один отсек затоплен - остальные работают. В отказоустойчивой архитектуре микросервисов: перегрузка одного сервиса не убивает остальные.

Как пишет Zuplo: "Without isolation, all routes through your gateway share the same connection pool. A single slow backend can exhaust every available connection, blocking requests to perfectly healthy services.""Без изоляции, все пути через ваш шлюз деляться пулами подключений. Простой медленный бэкенд может исчерпать каждое доступное подключение, блокируя запросы к абсолютно здоровому сервису." Именно это я видел на том проекте с платёжным API - один тормозящий сервис утащил за собой одиннадцать здоровых.

Три уровня изоляции:
  • Thread pool / connection pool per service. Resilience4j Bulkhead с maxConcurrentCalls: 25 для сервиса рекомендаций, maxConcurrentCalls: 100 для сервиса авторизации. Перегрузка рекомендаций не исчерпывает соединения к авторизации
  • Kubernetes namespace + ResourceQuota. Критичные сервисы (платежи, авторизация) в namespace с Guaranteed QoS. Некритичные (аналитика, рекомендации) - BestEffort, при нехватке ресурсов kubelet убьёт их первыми. Жёстко? Да. Но платежи важнее рекомендаций
  • Отдельные ingress для внешнего и внутреннего трафика. DDoS на публичный API не затрагивает внутренние gRPC-вызовы
Trade-off из систематического обзора (тема T5): "Isolation limits blast radius but reduces utilization.""Изоляцуию ограничивает взрывной радиус, но уменьшает использование." Вы резервируете ресурсы для каждого compartment, часть простаивает в нормальном режиме. Это цена устойчивости. Решение - adaptive bulkhead с динамическими лимитами на основе текущей нагрузки.

Паттерны устойчивости микросервисов: когда что применять​

Все описанные паттерны работают в связке. Decision matrix для проектирования:

СимптомПервый паттернВторой паттернЧего НЕ делать
Резкий рост RPS на публичном APIRate limiting на GatewayHPA autoscaling с потолкомМасштабировать без лимита
Таймауты от downstream-сервисаCircuit breaker (Open)Fallback на кэшУвеличивать таймаут
Пул соединений к БД исчерпанBulkhead (изоляция пулов)Queue-based load levelingПоднимать max_connections
P99 latency растёт, ошибок нетSlow call circuit breakerGraceful degradation Ур. 1Игнорировать до появления 5xx
Consumer не справляется с очередьюBackpressure + DLQAutoscaling consumer podsУвеличивать batch без лимита

Связка по слоям:
  1. Edge - rate limiting + WAF: сбрасываем мусорный трафик до того, как он дойдёт до приложения
  2. Gateway - circuit breaker + bulkhead: изолируем сервисы и обрываем каскады
  3. Service - graceful degradation + fallback: сохраняем частичную работоспособность
  4. Data - queue + backpressure + DLQ: буферизуем и контролируем поток к хранилищам
  5. Observability - Prometheus + Grafana + alerting: видим, что происходит, и реагируем
Последний пункт не опциональный. Согласно NIST CSF 2.0 (DE.AE-01): "A baseline of network operations and expected data flows for users and systems is established and managed.""Устанавливается и контролируется базовый уровень сетевых операций и ожидаемых потоков данных для пользователей и систем." Без baseline вы не отличите DDoS от легитимного всплеска трафика после маркетинговой рассылки. Я видел, как команда раскатила incident response на маркетинговый пуш - 40 минут потратили на "атаку", которой не было.

Нагрузочное тестирование: проверяем DDoS-устойчивую архитектуру до атаки​

📚 Часть контента скрыта. Этот материал доступен участникам сообщества с рангом One Level или выше
Получить доступ просто — достаточно зарегистрироваться и проявить активность на форуме

По систематическому обзору (тема T8): observability - enabler для всех остальных паттернов. Без метрик вы не узнаете, сработал ли breaker и на каком уровне деградации система.

Опыт последних проектов показывает устойчивый паттерн: команды тратят 80% бюджета безопасности на внешнюю Anti-DDoS-защиту и 0% на архитектурную устойчивость. AWS Shield Advanced - от $3000/мес, Cloudflare Enterprise - от $5000. Настройка circuit breaker в Resilience4j, rate limiting в Kong и backpressure в Kafka - zero-cost по инфраструктуре и 2-3 дня работы инженера. Но именно эти паттерны определяют, ляжет приложение за 4 минуты или деградирует управляемо.

Я убеждён, что через год-два L7 DDoS станет настолько рутинным, что application-level resilience войдёт в стандартный checklist при code review - наравне с проверкой авторизации. Пока этого не произошло, большинство команд продолжат узнавать о пробелах в своей архитектуре из ночных инцидентов. Цена такого урока - от потери SLA до потери клиентов.

Проверьте свой staging прямо сейчас: возьмите k6-сценарий из этой статьи, разгоните до 5000 VU и посмотрите, что вернёт ваш API. Если увидите 502 - у вас та же проблема, с которой начиналась эта статья. Если 503 с Retry-After - вы на правильном пути.
 
Последнее редактирование модератором:
Мы в соцсетях:

Взломай свой первый сервер и прокачай скилл — Начни игру на HackerLab

Похожие темы

🚀 Первый раз на Codeby?
Гайд для новичков: что делать в первые 15 минут, ключевые разделы, правила
Начать здесь →
🧭 Навигатор · ИБ 2026
Не знаешь, какой трек твой?
5 направлений ИБ, реальные зарплаты и точка входа для каждого — в одном треде.
JuniorSenior+
100K → 600K+ ₽ /мес
Открыть навигатор →
🔴 Свежие CVE, 0-day и инциденты
То, о чём ChatGPT ещё не знает — обсуждаем в реальном времени
Threat Intel →
💼 Вакансии и заказы в ИБ
Pentest, SOC, DevSecOps, bug bounty — работа и проекты от проверенных компаний
Карьера в ИБ →

HackerLab