Отказоустойчивость в мультиклауде: архитектура и реальные кейсы

Travel-индустрия не прощает простоев — каждая минута простоя может стоить бизнесу клиента. Как сделать так, чтобы большая инфраструктура работала 24/7 с минимальными простоями? В Туту мы пришли к мультиклауду не за один шаг.
Привет, Хабр! Меня зовут Андрей Борзов, я в Туту работаю с 2012 года — отвечаю за то, что… сайт работает. Мои команды занимаются железом, сетью, делают облака, занимаются отказоустойчивыми кластерами ВМ в облаках, а также готовят системы мониторинга.
В этом материале по мотивам выступления на конференции K2 Cloud Conf 2026 хочу рассказать об отказоустойчивости в мультиклауде — о том, как мы строим инфраструктуру Туту, зачем используем несколько облаков и собственные площадки и что происходит, когда что-то начинает ломаться.

Мы в Туту больше 20 лет работаем для того, чтобы путешественникам было легко решать свои задачи, и за этим стоит большая инфраструктура — есть 3000 микросервисов в нескольких кластерах kubernetes, монолит, а также сотни кластеров баз, очередей, кэшей и сопутствующих сервисов. Со всем этим зоопарком нужно обеспечить работу 24/7 с минимальными простоями. Простои наши клиенты не любят — у нас высококонкурентный бизнес, и если что-то не работает, клиент, как правило, сразу идет покупать билет или отель в другом месте. Поэтому наша цель — сделать так, чтобы при отказе любого из основных элементов инфраструктуры сайт продолжал работать. Теперь разберем, как это реализуется.
Почему мы пришли к мультиклауду
Тут есть две части: cloud и multi, и начнем мы с облака. Облако упрощает взаимодействие между командами, которые делают железки, и командами, которые делают виртуальные машины на них.
Раньше мы знали про каждый сервер, какой у него CPU, сколько памяти, какие диски и какого они поколения. Думали, подойдет ли сервер для того, чтобы разместить на нем нужную нагрузку.
Если железку нужно было вывести из работы, с нее сначала надо было расселить виртуальные машины. А команда, которая занимается железом, в это время ждала, пока сервер разгрузят — это не очень удобно. Облако решает эту проблему — в нем есть инструменты, которые позволяют виртуальным машинам просто уехать на другие ноды облака. То есть железо перестает быть для нас отдельной историей, которую нужно каждый раз вручную разруливать, это очень удобно.
Конечно, появляются архитектурные сложности и накладные расходы, но при этом сильно ускоряется работа. Поэтому мы перевели все, что у нас было, из железок для виртуальных машин в облачные пулы. Сейчас у нас есть собственные частные облака.
Платные облака мы тоже продолжаем использовать. Казалось бы, если есть свои, зачем покупать еще что-то за деньги? Во-первых, есть специфические требования — например, GPU или очень быстрый диск. Внутри компании спрос на такие ресурсы может быть недостаточно массовым, поэтому проще пойти в облако и развернуть их там. Во-вторых, иногда нужно быстро масштабироваться. Облако в этом смысле практически бесконечно— если ресурсов не хватает, их можно моментально добавить.
При этом большая часть мощностей находится в наших облаках. Это постоянная нагрузка, мы не схлопываем их зимой и не раздвигаем летом. Они работают на инфраструктуре, которая уже куплена за CAPEX. В результате стоимость владения получается значительно ниже, и это позволяет снижать TCO.
Есть и еще одна причина использовать облака: у них могут быть сертификаты, которые достаточно сложно получать самостоятельно. Например, можно взять PCI DSS-сегмент и сделать инфраструктуру для платежного шлюза. Это сильно упрощает запуск таких продуктов.
Мультиклауд: зачем нам несколько облаков
Итак, с cloud разобрались, теперь идем к multi. Собственных и платных облаков у нас много, мы размещаем их в трех логических площадках — «плечах» отказоустойчивости. В каждом из наших «плечей» мы стараемся использовать облако независимого вендора. Казалось бы, можно выбрать одного надежного поставщика и использовать только его. Но тогда мы получаем зависимость от одной площадки и одного поставщика, а несколько облаков дают нам дополнительные преимущества.

Во-первых, мы защищаемся от технических и юридических рисков отказа облака, в том числе в ситуации когда несколько площадок, которые выглядят независимыми, на самом деле оказываются связаны между собой невидимой для нас общей зависимостью.
Во-вторых, можно собрать более сбалансированный по стоимости портфель ресурсов. Например, в одном облаке дорогой compute, но выгодные диски. В другом — наоборот, дешевые диски и более дорогой compute. Взвешенная цена будет более гармоничной, и в общении с каждым вендором есть рычаг для переговоров.
В-третьих, для подключения облаков разных вендоров приходится выбирать cloud-agnostic подходы и инструменты, и мы получаем в итоге более гибкую и мощную систему управления инфраструктурой.

Но есть и обратная сторона: несколько площадок — это очень много подвижных частей, и управлять всем этим становится сложнее. И чем больше таких частей, тем выше шанс, что однажды какая-то из них начнет вести себя не так, как мы рассчитывали. Поэтому новую площадку нужно тщательно выбирать, и к ней предъявляются достаточно серьезные требования.
Нужно автоматизировать управление инфраструктурой, потому что обязательно что-нибудь забудется, а что-то, настроенное вручную, останется без контроля. И когда оно сломается, придется вручную вылавливать отличия и воспроизводить отказавший элемент, что очень сложно и неприятно делать в режиме ЧП.
Лучше развивать автоматизацию, чтобы инфраструктура всегда была под контролем. Но одной автоматизации здесь недостаточно. Больше частей - значит статистически чаще какая-то из них не работает. Поэтому нужно делать надежный сервис поверх потенциально ненадежных частей. Пожалуй, это самая сложная часть.
И даже когда вы построили надежный сервис, он однажды все равно сломается — поэтому в команде все равно должны быть эксперты, которые умеют правильно реагировать и купировать проблему.
Как мы подключаем новые площадки
В основном мы смотрим на то, чтобы облако находилось в незадействованной локации, коммерческие условия и SLA, эффективность поддержки и менеджмента. Важно, чтобы это было облако с «человеческим лицом» — чтобы там были люди, умеющие решать проблемы.

Из специфичного, мы хотим подключаться к площадке на сетевом уровне темной оптикой — это позволяет далеко не каждое облако. Кроме того, нам нужно устанавливать туда собственное сетевое оборудование. Не каждый провайдер дает такую возможность.
Сертификаты есть сейчас у многих облаков, но, например, ГОСТ 57580.1 необходим для биллинговых систем, а он есть далеко не у всех.
Автоматизация: облако как пул ресурсов
Для нас любое облако — это пул ресурсов с унифицированным внутренним интерфейсом. Мы не используем активно уникальные фичи отдельных вендоров. Для нас это в первую очередь пространство, где мы нарезаем виртуальные машины и главное — наличие Тerraform-плагина, чтобы мы могли интегрироваться своей автоматикой. Настройка созданных терраформом ВМ происходит с помощью Ansible-плейбуков.
Еще один тип автоматизации — автоматизация тушения датацентра. Она нужна для того, чтобы в критический момент быстро отключить все элементы, которые управляют трафиком, и выключить локацию. Про эти элементы мы еще поговорим дальше. Их автоотключение иногда необходимо чтобы сократить простой.
И, конечно, есть автоматический сбор информации обо всех созданных ресурсах — она попадает в единый коллектор, который потом дает дашборды для внутреннего биллинга и управленческой отчетности. Один из ключевых показателей нашей эффективности — это понимание спроса на инфраструктуру (суммарный «заказ» во внутренних рублях), и отношение этой суммы ко внешним костам.
Как сделать надежный сервис из ненадежных частей
Есть два больших типа сервисов — stateless и stateful. Начнем с первого и поговорим о том, на чем строится наша сетевая балансировка, которая обеспечивает отказоустойчивость между облаками. А затем уже перейдем к stateful — там используются те же технологии, но дополнительно появляются некоторые прокси и управляющие элементы.
При этом мы понимаем, что отказоустойчивость стоит дополнительных денег. Есть даже системы, где запасные инстансы вообще не нагружены и просто ждут, когда случится фейловер и на них перейдет вся нагрузка. Мы сознательно не распределяем нагрузку до этого момента, чтобы понимать, что резерв действительно способен ее выдержать.
Stateless: Anycast, BGP и LVS
Начнем со stateless. Здесь задача вроде бы простая: клиент отправляет запрос, а сервис должен на него ответить. Но что произойдет, если сервер, на который этот запрос должен прийти, внезапно исчезнет? В основе у нас Anycast — протокол, который «магией» BGP направляет запросы ближайшему в терминах сети узлу сервера.

Базово это выглядит так: есть сервер, обозначенный на схеме зеленым кружком, и клиент — синий прямоугольник. Клиент хочет сделать запрос к серверу.
На серверах используется один и тот же Anycast IP. Один клиент, отправляя запрос на этот IP, попадает на один сервер, другой — на другой. То есть запрос маршрутизируется к ближайшей ноде.
В случае нескольких ЦОДов запрос обычно идет внутри одного из них. И если один из серверов вылетает, Anycast IP снимается и перестает быть доступен. BGP сразу начинает направлять запросы на второй сервер.

У такого подхода есть обратная сторона: если в одном ЦОДе больше клиентов, чем в другом, балансировка получается неравномерной. Один узел перегружен, а второй практически ничего не делает. Кроме того, не очень удобно выводить сервер из-под нагрузки —часть запросов может потеряться, если вы выведете один из серверов, в который они уже успели попасть.
Поэтому у нас используется LVS — старая технология, L4 балансировщик, который подходит для любого трафика.

LVS получает запрос и направляет его на один из бэкендов. На LVS находится тот же Anycast IP, что и на бэкендах, но анонсы отличаются: клиент не может увидеть бэкенд и может пойти только на LVS. Тот балансирует запросы round-robin, поэтому получается равномерное распределение нагрузки. При этом запросы становятся кросс-ЦОДовыми и появляется межЦОДовый трафик. Выбор осознанный, мы частично жертвуем локальностью запросов, и именно с этим связаны жесткие требования по подключению площадок своей оптикой.
К еще одной особенности нашей схемы можно отнести то, что бэкенд отвечает не через LVS, а напрямую клиенту, т.к. получает пакет с «подмененным» DST MAC. Смысл в том, что после получения запроса клиенту уже не нужен LVS, чтобы этот запрос обработать.
Это позволяет выводить из-под нагрузки как бэкенды, так и сам LVS, с минимальными потерями.То есть, мы убираем бэкенд с балансировщиков — все запросы, которые он уже получил, он отрабатывает и отвечает клиенту; а клиент вообще ничего и не заметил. Мы даем серверу отстояться и после этого полностью выводим его из работы.
LVS у нас стоят в Тутуперед всеми видами балансировщиков, и любой DNS, который мы даем разработчикам, как правило ведет на LVS перед соответствующим сервисом.
Два слова о kubernetes
Сервисы, которые работают в Kubernetes — тоже пример stateless, но у нас он живет необлачной жизнью. Команда ИТ Платформы Туту развернула кластера Kubernetes на физических серверах в разных локациях. Они могут доумощняться за счет облаков, но это происходит в исключительных случаях. Как правило то, что происходит в остальной инфраструктуре, никак не влияет на наши Kubernetes-кластеры — это самостоятельный продукт со своей стратегией отказоустойчивости.
Руководитель ИТ Платформы Максим Скоморохов подробно рассказывает про ее устройство и философию в своем докладе, очень рекомендую.
Stateful: когда данные нельзя потерять
Stateful — это когда у нас есть диск с данными, которые не должны пропасть. В production мы не используем kubernetes для таких систем, вместо этого мы делаем инсталляции из виртуальных машин, которые в совокупности дают «как сервис» отказоустойчивую БД.
Часто это не только серверы баз данных — есть еще собственные, характерные для конкретной технологии балансировщики и управляющие элементы, которые, например, занимаются переключением направления репликации.
Мы не размещаем базы данных и приложения на одних и тех же серверах, чтобы не получать наведенные эффекты. Но иногда допускаем общежития самих БД — допустим, какая-то логическая база данных продукта может жить на одном сервере с другой базой того же продукта. Скажем, поиск автобусов и заказы автобусов могут находиться на одних и тех же физических или виртуальных серверах.
Интерфейс для клиента (разработчика), который используется для подключения к БД, — это адрес LVS.
Kafka: отказоустойчивость внутри самого кластера
Kafka, как мне кажется — самый простой пример stateful-системы, которая у нас есть. Три брокера мы размещаем в разных ЦОДах.

Kafka сама хорошо умеет координировать работу с партициями, определять, кто мастер, кто реплика и обмениваться метаданными как с собой, так и со своими клиентами.
Клиент через LVS подключается к кластеру. Толстая стрелка на схеме выше показывает, что мы получили адрес одного брокера и подключились к нему. Дальше клиент вычитывает метаданные и подключается к остальным брокерам.
После этого мы можем продюсить и консюмить данные, а также понять, когда изменился состав кластера и куда нужно идти. Все это обычно реализовано в Kafka-клиентах. Больше ничего делать не надо, важно только дать клиенту возможность подключиться к точно работающей ноде на старте приложения.
MySQL: оркестратор, реплика и SQL-прокси
С MySQL сложнее. Базовая схема — это две базы, где одна — мастер, вторая — реплика. Они находятся в разных ЦОДах.

Для приложения мы даем мастер и считаем, что все запросы должны обслуживаться им, даже если это SELECT. Если мастер падает, все запросы должны сразу переключиться на реплику.
Отслеживанием топологии и переключением занимается специальный продукт — оркестратор, который представляет собой три Go-приложения в разных ЦОДах.
«Орки» на схеме общаются друг с другом по Raft и следят за тем, что происходит с базами данных. В случае проблем они могут вмешиваться в топологию — например, развернуть репликацию в другую сторону, или назначить реплику мастером, если старый мастер окончательно вылетел. Эта информация сразу передается в зеленый узел на схеме — ProxySQL, специальный прокси между клиентом и базой данных, который занимается маршрутизацией запросов.

Я набросал клиентов в разных частях схемы и распределил LVS и SQL-прокси по разным ЦОДам. Мы не всегда стараемся держать их во всех ЦОДах, достаточно хотя бы двух.
Клиент идет через LVS в прокси. Тот знает, в какую базу нужно направить запрос. Если топология простая, с мастером и репликой, все запросы уходят на мастер. Если топология сложнее и у нас есть много реплик, SELECT-запросы можно направлять на них. SQL-прокси занимается тем, чтобы отправить запросы в нужные базы данных и каждые несколько секунд проверяет, что оркестратор считает актуальной топологией. Если топология изменилась, прокси моментально перебрасывает трафик.
Обмен данными между орками и ProxySQL — это логика, которую мы написали сами поверх возможностей этих компонентов.
Что происходит во время сбоя
Начнем снова со stateless. Если вылетает один из бэкендов, LVS понимает, что обращаться к нему бессмысленно. В каждом ЦОДе каждый LVS направляет запросы на те бэкенды, которые остались.

Если вылетает LVS, продолжает работать обычный Anycast. Клиенты в этом ЦОДе идут в другой LVS в другом ЦОДе, а тот уже распределяет запросы по бэкендам.

С Kafka все решается ее внутренними механизмами отказоустойчивости.

Если вылетает один брокер, два оставшихся договариваются, кто будет лидировать по вылетевшим партициям. Они быстро выбирают лидеров и рассылают эту метаинформацию клиентам.
Клиенты практически моментально понимают, что к вылетевшему брокеру больше идти не надо, и начинают работать с двумя оставшимися. В этом случае LVS вообще не участвует.

С MySQL интереснее. Когда падает мастер, оркестратор замечает это и сам сразу же делает реплику новым мастером. ProxySQL моментально узнает об этом и начинает направлять запросы на выживший инстанс. Если вылетает локация вместе с базой и ProxySQL, тут спасают LVS и прокси в другой локации.

В 2018 году такой сбой вызывал у нас около 30 минут простоя — требовался DBA, который должен разобраться с ситуацией, проверить реплику и вручную назначить ее мастером, а затем перебросить клиентские соединения. Сейчас переключение полностью автоматизировано, и DBA остается только в рабочем режиме заняться реанимацией или заменой выпавшей ноды.
В реальном мире сбои редко бывают идеальными
До этого момента все было слишком уж красиво: один компонент упал, другой его подхватил, трафик переключился, система продолжила работать. Но это идеальный сценарий, который работает при моментальном и полном крахе компонентов. Но на деле обычно, например, начинает сбоить сеть, и теряется какое-то количество пакетов, из-за чего healthcheck начинает «флапать»: нода то выкидывается, то снова вводится в строй. Если в этот момент в этот участок попадает клиентский трафик, запрос заканчивается ошибкой.
Если, например, вылетела физическая хранилка, отказ «вешает» ВМ иногда вместе с хэлсчеками. Часто дополнительно повреждается диск, и ВМ не возвращается в строй после возвращения железа и требует полного разворачивания с нуля.
Бывает, что в локации слишком много всего умерло одновременно — это добавляет уже когнитивной сложности, так как нужно распутывать клубок причин и следствий, чтобы составить грамотный план реакции и восстановления.
Что помогает переживать массовые сбои
В первую очередь помогает автоматизация, о которой я говорил в самом начале: она позволяет выключить локацию, если в ней начинаются моргающие ошибки и автоматика не “отстреливает” площадку до конца.
Хорошо помогают грамотно составленные алерты. Обычно они построены «по проблемам», т.е. каждое правило проверяет конкретный индикатор здоровья, но сразу на всей инфраструктуре. Когда оно срабатывает, в нем видно весь список «горящих» мест, и это позволяет сразу оценить масштаб и природу проблемы. Например, если отвалился диск, то мы получим не 100 алертов, по одному про каждую пострадавшую ВМ, а в одном алерте о недоступности диска сразу будет видно все инстансы, где это произошло.
Нужно держать достаточный резерв, чтобы при массовом отказе локации выжившие ВМ не оказались перегружены. Иногда это все равно происходит, и тут важно уметь быстро добавить мощности.
Конечно, и это главное, — нужна команда экспертов, чтобы приготовить такие технологии и чтобы мочь быстро восстанавливать систему при сбоях и тюнить после них.
Подводя итог
Облако — классный инструмент, повышающий гибкость инфраструктуры и автономность команд, а значит и скорость изменений. Нам нравятся облака :)
За кажущейся простотой стоит сложная система, где многое может пойти не так. Поэтому нужно выбирать провайдера с гибким и надежным продуктом и сильной командой, — чтобы оно поменьше падало и помогало решать ваши задачи.
Мы рады, что нашли K2 — это как раз такое облако.

Но для настоящей отказоустойчивости одного хорошего облака недостаточно. Мы покупаем несколько облаков и строим собственные в нескольких локациях. Это влечет дополнительную сложность, с которой помогает справиться автоматизация инфраструктуры, вложения в команду, внутреннюю экспертизу и строительство отказоустойчивости на «надоблачном» уровне. В конце концов, это и дает хороший результат.
Кстати, если сейчас у вас в компании назревает похожий проект, не обязательно сразу закладывать в бюджет полноценную инфраструктуру. K2 Cloud предоставляет приветственный грант до 30 000 рублей на тестирование сервисов: можно развернуть GPU-серверы (H100, A100, L40S, L4, T4), Managed Kubernetes, виртуальные машины, S3-совместимое хранилище или сетевую инфраструктуру и прогнать свой сценарий вживую, прежде чем переходить на коммерческие тарифы.
Схема простая: регистрируетесь на платформе по корпоративному домену (программа только для юрлиц и ИП — физлица не участвуют), согласовываете с менеджером срок тестирования от 3 недель до 60 дней, а размер гранта зависит от потенциального объема потребления ресурсов. Если бонус не израсходован до конца согласованного срока, он сгорает без остатка, так что тест лучше планировать заранее под конкретную задачу вроде MVP или пилота. Подробности и регистрация — на странице гранта K2 Cloud.
KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.