Даже сложная и продуманная технологическая система не застрахована от инцидентов — это касается любых инфраструктур, от железнодорожных и коммунальных до IT. Поэтому инженерам, которые обеспечивают надёжность систем, важно не только предотвращать проблемы, но и минимизировать их влияние за счёт хорошего антикризисного (или DR) плана.
Меня зовут Костя Крамлих, я отвечаю за сетевую виртуализацию и сетевые сервисы в Yandex Cloud. К написанию этой статьи меня подтолкнула наша работа по предотвращению крупных инцидентов, наподобие тех, что произошли в конце 2024 года. Разрешить их удалось благодаря слаженной работе всей команды, и по итогам мы сделали выводы, которые не ограничиваются только устранением сетевых аварий.
Сегодня по материалам реальных событий расскажу о сетевой части:
что важно знать о том, как ломаются большие системы, на наших примерах;
как мы проанализировали все наши инциденты за последний год и что сделали, чтобы предотвратить появление проблем на инфраструктуре;
как подошли к внедрению этих улучшений небольшими шагами, чтобы они не стали источником новых инцидентов.
Что важно знать про сетевую виртуализацию в контексте отказоустойчивости
Я уже рассказывал об устройстве наших виртуальных сетей в отдельной статье. Сегодня же начну с краткого напоминания основных свойств, которые пользователи виртуальных облачных сетей ожидают от них, явно или неявно:
Изоляция сетей. Важнейшее свойство системы — сети должны быть гарантированно немаршрутизируемы между собой.
Внедрение без даунтайма. Изменения в сети должны вводиться максимально аккуратно, с сохранением обратной совместимости, иначе клиентские сценарии могут пострадать.
Предоставление широкой функциональности. Компании уже привыкли работать со своими сетями и хотят с минимальными усилиями делать что‑то похожее у себя в облаке. Например, нужны гибкие инструменты внешней связности: Cloud Interconnect для связи облака и on‑premise‑инфраструктуры, Managed NAT для доступа в интернет, Private Endpoint для IP‑связности до сервисов облака из непубличной сети.
При этом в построении виртуальной сети мы используем традиционную для сетей модель разделения на слои:
Config Plane — это то, что пользователь конфигурирует через API, и тем самым задаёт целевое состояние системы.
Data Plane — слой, который непосредственно обрабатывает пакеты пользователя. Он состоит из виртуальных роутеров (VRouter) на серверах и т. н. Cloud Gateways — виртуальных маршрутизаторов, которые далее выпускают трафик наружу через Cloud Border — группировку физических маршрутизаторов на точках присутствия.
Control Plane — управляющий слой, который доводит состояние Data Plane до того, что было описано пользователем в Config Plane. Здесь работают SDN‑контроллеры (Controllers) — это «мозг» виртуальных сетей. Задача контроллера — держать в голове правила доставки трафика до всех виртуальных машин (другими словами — маршрутные карты), и распространять эту информацию по всем виртуальным роутерам. Эти роутеры в свою очередь знают про принадлежность конкретной ВМ конкретной сети.
Во многом похожие схемы могут возникать в совершенно других инфраструктурах, например, при управлении семафорами на железнодорожном транспорте. Есть централизованные зоны управления, которые контролируют конкретные перегоны. Конкретный перегон — это такой Data Plane, аналог того, как у нас проходит трафик в виртуальных сетях. А централизованный «мозг» (аналог Control Plane) управляет всей системой так, чтобы это было эффективно для прохождения поездов.
Схематично всё описанное можно представить вот так:

Схема описывает устройство сети в рамках одной зоны доступности. В каждой из зон сетевая подсистема устроена одинаково, но для поддержки пользовательских кросс‑зональных сценариев контроллеры в каждой из зон должны взаимодействовать с другими зонами. Здесь важно остановиться, добавить фактуры и сформулировать ключевые сложности, чтобы стало понятнее:
Задача контроллеров — распределить маршрутные карты сетей по компонентам. В цифрах на наших больших объёмах это сотни инстансов Cloud Gateway, десятки тысяч виртуальных роутеров и сотни тысяч маршрутных карт в каждой зоне.
С учётом кросс‑зональных сценариев обмена трафиком нам нужно синхронизировать эти карты между контроллерами и агентами в разных зонах доступности. При этом основной источник информации в этих картах — те самые десятки тысяч агентов, которые зажигают анонсы при старте виртуальных машин.
Любые изменения маршрутной информации в одной из зон доступности мы должны распространять и в другие зоны. Эту задачу решают контроллеры с помощью распределённого регионального сервиса (3AZ).
Виртуальная сеть обеспечивает изоляцию между клиентами — её нарушение приводит к существенным рискам. Если что‑то пойдёт не так и Data Plane не получит актуальные маршруты, безопаснее всего будет отозвать маршруты и снять трафик.
Поэтому проблемы с SDN‑контроллерами могут приводить к действительно разрушительным последствиям и именно с ними было связано большинство инцидентов в прошлом году, поэтому в этой статье сосредоточусь на них.
Как мы увидим — примеры таких сетевых инцидентов обычно демонстрируют паттерны, которые встречаются не только в сети.
Что именно может пойти не так
Частичный или полный отказ контроллеров. Опытные разработчики инфраструктуры наверняка не раз слышали про такой термин, как «скоррелированный отказ». Им описывают две ситуации:
все компоненты отказывают разом, например, из‑за одновременного проявления одного и того же бага (например, «запрос смерти»);
частичный или полный отказ одного компонента приводит к повышенной нагрузке на другие элементы, и они тоже выходят из строя.
Соответственно, в случае с SDN‑контроллерами такая проблема тоже актуальна. В каждой зоне есть несколько контроллеров для обеспечения отказоустойчивости и распределения нагрузки. В норме они могут пережить несколько отказов — живые контроллеры бесшовно подхватывают нагрузку с упавших. Но в связи с сетевой спецификой сами отказы приводят к волне пересчётов анонсов маршрутов и повышают нагрузку на оставшуюся группировку, в пределе приводя к полному отказу. Такая ситуация приводит к снятиям анонсов и остановке прохождения трафика.

Зависания контроллеров. По внешним признакам контроллеры выглядят мёртвыми — не отвечают на запросы или делают это с большой задержкой.
К зависаниям приводят те же причины, что и в предыдущем пункте, но влияние на сеть другое. Это ведёт к полной недоступности новых нагрузок, так как новые маршруты не растекаются по системе. А также приводит к частичной недоступности существующих нагрузок по мере того, как закешированные маршруты утрачивают актуальность (часть трафика ходит, другая — нет). Это было причиной двух крупных инцидентов прошлого года 16 и 21 октября.

Циклическая амплификация нагрузки. Система притормаживает, что приводит к тайм‑аутам у клиентов и повторам запросов, которые нагружают систему ещё сильнее. Возникает эффект снежного кома: система обрабатывает всё меньше запросов, клиенты делают всё больше ретраев, и так по кругу.
Циклическая амплификация нагрузки возникает, когда в системе есть циклы с положительной обратной связью. И часто система не может выйти из этого состояния сама — такое состояние называют metastable failure state, подробнее об этом читайте в статье Дениса Исаева.

К сожалению, такое у нас тоже случалось, что привело к одному из крупнейших отказов в облаке на региональном уровне (одновременный отказ более одной зоны). Из‑за бага в системе контроллеры в одной зоне сгенерировали поток маршрутов примерно на 2M RPS, чем перегрузили Cloud Gateways до состояния каскадного скоррелированного отказа по OOM.

В конце прошлого года эти проблемы оказали существенное влияние на доступность облака, так что мы перевели команду в антикризисный режим и перестроили наши подходы к надёжности. Попробую кратко рассказать, какие решения мы приняли за последние полгода, и какие неочевидные трудности могут возникнуть при внедрении запланированных изменений.
Как мы повышали устойчивость, чтобы не выстрелить себе в ногу
Опытный читатель к этому моменту наверняка уже почувствовал слона в комнате:
Чтобы избегать подобных отказов или уменьшать их влияния, нужны изменения. При этом самые сильные улучшения зачастую самые опасные. Например, если в качестве решения вам уже хочется предложить шардирование — оно относится как раз к таким улучшениям.
Любые изменения всегда с определённой вероятностью приводят к отказу.
Согласно DORA report даже у лучших команд change failure rate составляет от 5 до 15%. Это процент выведенных в продакшн изменений, требующих исправления.
Мы не могли просто навалиться всей командой и сделать 100 правок надёжности. Просто из‑за статистики: каждая десятая правка содержит баги, а каждая сотая — генерирует инцидент.
Нам требовалось небольшое число безопасных и обобщаемых улучшений. Все необходимые изменения мы приоритизировали следующим образом:
Мы выписали все критичные инциденты 2024 года и набор планируемых изменений в одну таблицу.
Оценили, насколько улучшения изменили бы ситуацию, если были бы внедрены до инцидента.
Оценили трудоёмкость реализации и вероятность, что что‑то пойдёт не так при внедрении.
Примерно так:

Для каждого улучшения мы оценили пользу по формуле score=impact / (cost * risk_probability)
и поранжировали по этому критерию все улучшения.
Это упражнение мы проделали не только для сети, но и в целом для всей облачной инфраструктуры: хоть я и пишу про сеть, но крупный инцидент в зрелой системе без очевидных болячек — это стечение обстоятельств на разных слоях системы.
Это помогло нам найти и внедрить набор комплексных улучшений.
Например, ограничения на размер очередей в сервисе Compute помогли срезать существенную часть холостой работы и, по нашим оценкам, суммарно на 2–3 часа быстрее подняться после мартовского инцидента с питанием.
В процессе приоритизации мы сгруппировали решения в рамках направлений, сформулировали три ключевых направления работ и построили эффективный и при этом аккуратный план:

Топ-1: полная поддержка headless режима (aka fail‑safe) — сохранение работоспособности Data Plane даже при полном отказе Control Plane с гарантиями сохранения изоляции.
Топ-2: усиление модели зональности/региональности — в случае частичного или полного отказа зоны мы сохраняем работоспособность региона.
Топ-3: масштабирование в рамках зоны — сохранение устойчивости зоны по мере роста её масштаба.
В дополнение к мерам предотвращения инцидентов, мы вложились и в инструменты митигации, которыми также могли бы воспользоваться клиенты. Так, для NLB и ALB мы разработали инструмент для управляемого закрытия зоны на балансировщике — Zonal Shift, о котором коллеги расскажут подробнее в следующий раз.
Остановлюсь на каждом пункте чуть подробнее, что именно мы сделали по сетевой части.
Обеспечили headless, или fail-safe
Fail‑safe — это такой способ проектирования системы, в котором при возникновении сбоя система переходит в безопасное состояние, предотвращая нежелательные последствия, даже если это значит частичный отказ функциональности.
Этот подход касается не только IT, но и сложных систем вообще.
В процессе приоритизации всех улучшений мы также поняли, что этот режим — обязательный пререквизит для дальнейшего внедрения остальных фич с минимальными рисками.

Вспомним нашу схему. Для прохождения трафика в узлах Data Plane требуется конфигурационная и маршрутная информация, получаемая из контроллеров. Без режима fail‑safe при отказе контроллеров в системе происходит автоматический отзыв маршрутов для сохранения консистентности и изоляции, как мы говорили в начале.
Чтобы реализовать fail‑safe‑режим, важно справиться с главным вызовом при отказе Control Plane: в этом режиме мы должны максимально долго прожить с имеющимся кешем, а чтобы закешированное состояние не устарело, нужно минимизировать изменения в системе. Маршруты могут устареть на фоне переезда виртуальных машин, изменения конфигурации балансировщиков. Нам же нужно было соблюсти баланс: обеспечить и изоляцию, и безопасность, и чтобы трафик шёл.
Для реализации этого мы сделали несколько изменений:
Теперь в случае зонального отказа мы умеем останавливать всю динамику в системе, чтобы актуальность кеша сохранялась дольше.
А также сделали точку оригинирования маршрутной информации централизованной, чтобы гарантировать уникальность при распределении меток. То есть в худшем случае неактуального маршрута трафик просто будет отброшен.
Новую схему протестировали на препроде. В результате тестов убедились: сейчас мы выдерживаем до 8 часов отказа Control Plane в отдельной зоне, без импакта на Data Plane‑нагрузки.
Снизили взаимное влияние зон в регионе
Следующим шагом важно позаботиться, чтобы нам не пришлось держать в каждой зоне всю маршрутную карту всего региона. Это необходимо для того, чтобы снизить влияние одной зоны на другие, а также снять ненужную нагрузку с сервисов.
Мы переработали сценарии прохождения трафика так, что в три раза сократили объём зональной маршрутной информации на пограничных виртуальных маршрутизаторах. Теперь трафик может проходить через дополнительные узлы, а не напрямую, как раньше, но при этом в каждой зоне хранятся не маршруты всего региона, а только маршруты этой конкретной зоны.
Перед изменением сценария мы оценили влияние на latency, сравнили риски и поняли, что при нашем объёме трафика это не повлияет на задержки критичным образом. Да, интервал прохождения трафика может немного увеличиться, но при этом влияние соседних зон на весь регион серьёзно сократится и обеспечит большую отказоустойчивость.

Сейчас это уже внедрено для NLB‑трафика и готов план для внедрения по другим классам трафика.
Позаботились о масштабировании
Крупный инцидент в «чёрную пятницу», который я упоминал, был связан с тем, что при большом потоке маршрутов (свыше 10k RPS) узлы виртуальных маршрутизаторов в управляющем контуре перегружались по памяти.
Для решения мы переработали устройство gobgp — опенсорсной имплементации BGP‑протокола. Результат до и после:

Нашу доработку мы уже залили в апстрим и сейчас договариваемся с опенсорс‑сообществом о внедрении.
Last, but not least
Для внедрения изменений, направленных на стабилизацию Сontrol Plane, нам также потребовалось вносить изменения в Data Plane на физических серверах. Исторически такие обновления были долгими, медленными и болезненными, так как приводили к кратковременной недоступности виртуальной сети на серверах. Здесь помог Blue Green Deployment, или side‑by‑side — это стратегия, которая минимизирует даунтайм и позволяет мгновенно обновлять приложение, не отклоняя ни одного запроса, за счёт реплики‑близнеца. При реализации этой стратегии мы выкатываем реплику, готовим её к работе и «прогреваем», а затем плавно переключаем на неё нагрузку. Если в ходе этого что‑то идёт не так, то откатываемся, иначе — тушим старую версию.

Надёжность инженерных систем — обширная тема, и сегодня я показал лишь небольшой сетевой фрагмент нашей работы:
мы увидели, как устроена сетевая виртуализация, и с какими особенностями мы встречаемся в облаке, когда реализуем определённую архитектуру;
посмотрели, что в реализованной схеме может пойти не так на уровне контроллеров, и к каким инцидентам это приводит на больших масштабах;
разобрались, почему в такой крупной системе любые улучшения в сторону большей надёжности важно внедрять аккуратно;
прошлись по списку улучшений в нашей сетевой подсистеме за последний год — что уже внедрили и почему именно в таком порядке.
Помимо этого были и другие изменения на следующих слоях системы, которые уже выходят за рамки сетевой темы. Поэтому об изменениях в других частях инфраструктурных сервисов мы расскажем в следующий раз. А комплексный рассказ о том, как именно мы пересмотрели подходы к надежности я расскажу в докладе «Когда облако дало сбой: реальный кейс борьбы за отказоустойчивость» на HighLoad++ 6 и 7 ноября в Москве. Приходите, будет интересно.