Привет, я Денис, технический эксперт в Cloud.ru. В какой-то момент своей карьеры мне пришлось разобрать много подобных инцидентов и написать по ним RCA. Разбирая очередной анализ, я увидел повторяющийся паттерн: девятки теряются не в одном месте, а понемногу на каждом уровне: в сети, базе данных, логике приложения и тд. В итоге из красивых 99.99% SLA в облаке получается реальная доступность системы, которая уже не выглядит так хорошо и зачастую не вписывается в error budget.
В этой статье разберем, где именно теряется доступность, почему SLA облака почти никогда не равен SLA приложения и какие архитектурные решения реально помогают сохранить девятки.

Что такое SLA и как оно считается
SLA (Service Level Agreement) — это договоренность между поставщиком и клиентом об уровне доступности сервиса. Проще говоря, сколько времени сервис должен работать без сбоев и какие условия считаются «недоступностью».
Доступность считается по простой формуле:
uptime / total time
Например:
99% — это ~3.65 дня простоя в год;
99,5% — ~4ч 23м простоя в год;
99.9% — ~8.7 часов в год;
99.99% — ~52 минуты в год.
На бумаге разница между 99.9% и 99.99% выглядит небольшой, всего одна девятка. Но в реальности — это 8 часов против 52 минут простоя в год. И эти дополнительные 7 часов — это обычно именно те моменты, когда приходят пользователи, запускаются рекламные кампании или проводятся платежи, т.е. моменты наивысшей нагрузки.
Важно понимать ключевую вещь, что SLA всегда дается на конкретный сервис: виртуальную машину, сетевую доступность или возможность записи в базу данных. И почти никогда на ваш продукт как на единое целое — это уже в вашей зоне ответственности.
Почему SLA облачного сервиса ≠ SLA вашего приложения
Основная проблема — композиция и зависимости.
Если ваша система состоит из 10 кубиков, то есть сервисов с SLA 99.95%, итоговая доступность системы будет ниже, потому что вероятность отказа накапливается. Это не интуитивно и кажется, что если все сервисы по отдельности надежны, то и система должна быть надежной — на практике это не так.
Простой пример.
Дано: 10 зависимых сервисов, каждый по 99.95%.
Итоговая доступность вашей системы уже заметно ниже из-за перемножения вероятностей:
(0,9995)^10 ~ 0,995012 (или 99,5%), т.е. уже не 52 минуты, а 4 с лишним часа. Для систем уровня business и mission critical это недопустимо.
И это только математика, в реальной жизни все хуже.
Добавим к этому еще несколько моментов, которые расходуют бюджет на ошибки:
каскадные отказы: перегрузка и падение БД → упало API → упал фронт;
разные методы расчета SLA: провайдер считает доступность виртуальных машин, а вы — доступность API;
человеческий фактор: деплой конфигурации с ошибкой.
Типичный сценарий из практики:
База перегружена и начинает отвечать медленно.
Приложение не имеет тайм-аутов.
Очереди начинают копиться.
Падает пул соединений.
Умирает весь сервис.
При этом ни один компонент формально не упал. В итоге получается, что SLA вашего приложения почти всегда ниже SLA любого отдельного облачного компонента.
Как построить отказоустойчивую архитектуру
Базовые принципы
Первое, что я усвоил на практике — без базовых принципов никакие технологии не спасают. Можно использовать Kubernetes, Kafka или любые другие managed-сервисы и все равно ловить даунтаймы.
Что это за принципы:
Избыточность. Не должно быть единой точки отказа. Например, один канал связи до облака, одна ВМ в облаке, через которую проходит весь трафик, расположение сервисов в одной зоне доступности и т.д.
Запас по мощности. Мне всегда нравилась аналогия с ведрами и водой в них. Так вот вода из трех ведер всегда должна помещаться в два, т.е. отказ одной ноды кластера не должен вызвать каскадный отказ остальных нод из-за перегрузки по ресурсам.
Изоляция. Падение одного компонента не должно валить все.
Graceful degradation. Лучше частичная работа, чем полный отказ. Нет доступа к базе – отдай кеш, нет рекомендаций — покажи базовый список и т.д.
Пользователь чаще готов терпеть деградацию, чем полный отказ.
Сетевой уровень: используйте балансировщики нагрузки
Без балансировщика отказоустойчивость невозможна. Он распределяет трафик между инстансами целевой группы и включает механизм проверки их работоспособности (health check). Как только нода перестает отдавать HTTP-код 200 на HC-запрос, балансировщик исключает эту ноду из балансировки.
Ключевой момент — правильно подобрать параметры проверки: интервалы запросов, их тайм-ауты и пороги успешных и неуспешных ответов. Слишком агрессивные параметры будут форсировать частые переключения трафика между инстансами таргет-группы. Слишком мягкие приведут к долгому обнаружению проблемы, повышенному фону тайм-аутов и ошибок приложения.
Нужен баланс. Для себя я выработал правило: для нового сервиса я выставляю параметры так, чтобы отказ фиксировался за десятки секунд, а не минуты. Потом при необходимости корректирую.
Уровень вычислений: Kubernetes
Kubernetes уже давно де-факто является стандартом для деплоя микросервисов. Он из коробки содержит множество инструментов, которые обеспечивают отказоустойчивость ваших приложений, но, к сожалению, не настраивает их за вас. Что же это за инструменты:
Deployment — поддерживает заданное количество реплик приложения и обеспечивает контролируемое обновление версий.
Horizontal Pod Autoscaler (HPA) — автоматически изменяет количество подов в зависимости от нагрузки.
Pod Disruption Budget (PDB) — ограничивает число подов, которые можно одновременно добровольно вывести из работы, и тем самым помогает сохранить доступность приложения во время обслуживания кластера.
Pod anti-affinity — позволяет распределять поды по разным нодам.
Topology Spread Constraints — помогает равномерно распределять поды между зонами доступности и другими доменами размещения.
Применяя эти механизмы вместе, вы значительно снижаете риск отказа микросервисов и, как следствие, недоступности системы.
Уровень данных: PostgreSQL
База данных — самый чувствительный элемент бизнес-системы. От того, насколько быстро база способна выполнять запросы, зачастую зависит скорость работы всей системы.
У меня есть две рекомендации на этот счет.
Всегда используйте отказоустойчивую топологию кластера для критичных систем. Минимальная топология, подходящая для критичных систем — мультизональная Primary/Standby на две ноды. Но я бы рекомендовал использовать мультизональную трехузловую Primary + 2 Standby. Сейчас объясню, почему.
Помните историю про ведра и воду? Так вот, если запись и чтение информации разделены, т. е. запись идет в мастер, а чтение из реплики, то отказ реплики приведет к тому, что вся читающая нагрузка ляжет на оставшийся мастер. А если средняя нагрузка в момент отказа >50% на каждую ноду? Получим почти гарантированную деградацию и отказ мастера с дальнейшим даунтаймом всей системы.
В случае с двумя репликами при отказе одной из них читающая нагрузка переедет на вторую реплику и кластер останется в рабочем штатном состоянии.
Также полезно будет начать мониторить ситуации, когда средневзвешенная нагрузка на CPU любой из нод в течение 5 минут >80%. Если таких ситуаций становится все больше — пора запланировать скейлинг флэйвора. Важный момент: скейлить нужно в момент наименьшей нагрузки, так как изменение флэйвора ведет к поочередному перезапуску всех нод кластера.
Отдельно стоит сказать про failover. Нужно, чтобы в логику приложения был заложен механизм отслеживания текущего мастера и реплик. Если приложение не умеет переключаться, то failover кластера не спасет от деградации системы.
Уровень обмена сообщениями: Kafka как буфер между сервисами
В высоконагруженных системах часто имеет смысл использовать Kafka как промежуточный слой обмена сообщениями между сервисами. Kafka дает буферизацию: если один из микросервисов временно недоступен, сообщения не теряются, а продолжают сохраняться в топиках и могут быть обработаны после восстановления потребителя.
Как и любой критичный компонент, Kafka требует отдельной настройки отказоустойчивости. Для продакшен-систем обычно разворачивают кластер как минимум из трех брокеров, задают фактор репликации топиков больше 1 (для критичных топиков, как правило, 3), настраивают default.replication.factor, а также min.insync.replicas. Чтобы подтверждение записи действительно означало сохранение сообщения на достаточном числе реплик, продюсеры должны использовать настройку acks=all.
Кроме того, важно учитывать семантику доставки сообщений. Потребители должны быть готовы к повторной доставке и повторной обработке сообщений, а критичные операции по возможности быть идемпотентными. На стороне продюсеров также обычно включают идемпотентность (enable.idempotence=true), чтобы снизить риск дублей при ретраях.
При правильной архитектуре и настройке Kafka позволяет переживать временную недоступность отдельных сервисов без потери сообщений: обработка просто займет больше времени. Без такого буфера система чаще оказывается перед выбором между потерей части событий и каскадной деградацией сервисов.
Уровень приложения
Даже если вы хорошо подготовили инфраструктуру и настроили отказоустойчивость на всех уровнях, этого все равно недостаточно. В самом приложении тоже должны быть заложены базовые механизмы устойчивости к сбоям, иначе оно просто не сможет воспользоваться всеми возможностями, которые дает облако.
Минимальный набор:
retries с exponential backoff. Временные ошибки нужно обрабатывать повторными попытками, но с растущей задержкой и ограничением по числу повторов, чтобы не усиливать деградацию зависимого сервиса;
circuit breaker. Если зависимость отвечает ошибками или слишком долго недоступна, приложение должно временно прекратить обращения к ней, чтобы не тратить ресурсы и не усугублять сбой;
тайм-ауты. Все внешние вызовы должны иметь явно заданный тайм-аут, иначе приложение может зависать, накапливать заблокированные ресурсы и деградировать;
идемпотентность. Повторное выполнение одной и той же операции не должно приводить к повторному побочному эффекту. Это критично при retries, повторной доставке сообщений и failover-сценариях.
Без этого даже идеально настроенная инфраструктура не спасет. На практике именно отсутствие таких механизмов в коде очень часто становится причиной проблем, хотя этот риск регулярно недооценивают. Иными словами, инфраструктура может быть отказоустойчивой, но если приложение не готово к сбоям, вся система все равно будет ломаться.
Наблюдаемость
Когда все уровни системы настроены, настало время подумать о ее observability. Да, да, тот самый царь-даш с пресловутыми SLO, SLI, error rate, error budget и прочими SRE-прелестями.

Расскажу, как я обычно проектирую подобные дашборды.
Делаю дашборд многоуровневым.
Первый уровень — верхнеуровневые индикаторы, по которым за несколько секунд можно понять, что в системе что-то пошло не так. Например, резкое падение заказов, рост ошибок со стороны API или сработал синтетический мониторинг.
Второй уровень — состояние ключевых компонентов системы: Kafka, PostgreSQL, compute, балансировщиков и других базовых зависимостей. Этот слой тоже не должен быть перегружен деталями: достаточно одной-двух метрик на компонент, которые показывают, способен ли он обрабатывать нагрузку. Например, доступность записи и чтения для managed PostgreSQL или количество нод в состоянии NotConnected для managed Kubernetes.
Третий уровень — детальные технические метрики по конкретному компоненту или сервису. Сюда уже относятся ресурсы и производительность: CPU, RAM, диски, сеть, latency, очереди, соединения и другие показатели, которые помогают локализовать причину деградации.
Такой подход обычно заметно ускоряет troubleshooting, особенно если система состоит из десятка и более компонентов. Сначала становится понятно, что проблема действительно есть, затем — где именно она возникла, и только после этого имеет смысл переходить к логам, трассировкам и поиску root cause.
Если сбой произошел, а на дашборде не было видно ничего подозрительного, это хороший повод разобрать инцидент, подготовить RCA и на его основе пересмотреть набор метрик, алертов и саму структуру дашборда.
Когда дашборд здоровья системы построен, можно переходить к следующему шагу – проактивному мониторингу. Ведь давайте честно, никто не любит подскакивать ночами и чинить упавшую систему. Гораздо лучше настроить алертинг так, чтобы он начал отлавливать «желтое состояние» — это такое состояние, которое говорит, что тренд на деградацию уже начался, но сама эта деградация еще не наступила. А значит, есть время в спокойном режиме покопать причины. Это, я считаю, высший пилотаж. И волки сыты система в порядке, и ваша менталка тоже :)
Выводы
И вот мы плавно подошли к выводам. Еще раз пройдемся по основным тезисам:
SLA облака — это потолок, а не гарантия вашего SLA;
реальную доступность определяет архитектура и логика приклада, а не цифры в SLA провайдера;
отказоустойчивость — это не разовая задача, а постоянный процесс улучшения, тестирования и пересмотра архитектуры.
Всем highload!

