Привет, Хабр! На связи Александр Усачёв, системный аналитик в группе облачных продуктов Рунити. В основе нашей облачной платформы Рег.облако лежит микросервисная архитектура: каждый сервис отвечает за свой участок бизнес-логики — от биллинга до управления сетями. Между собой они обмениваются задачами через брокер сообщений. 

В этой статье расскажу, как мы повысили отказоустойчивость нашей облачной платформы — и почему выбрали асинхронные цепочки задач вместо синхронных вызовов. Это история про устойчивость, Celery, RabbitMQ и немного про то, как инженерам стало жить спокойнее.

Навигация по тексту:

В микросервисных платформах синхронные вызовы часто становятся «слабым звеном». Один зависший шаг блокирует исходный запрос, загружает потоки обработки, тормозит другие операции и провоцирует каскадные сбои. В облачных сценариях это критично: сервисов много, нагрузка высокая, уровень SLA важен.

Чтобы снять эти риски, облачная платформа изначально спроектирована на асинхронных цепочках задач. Это повышает отказоустойчивость: пользователи в подавляющем большинстве случаев получают успешный результат, даже если выполнение занимает больше времени.

Синхронная модель: почему она не подходит для бизнес-операций

Синхронные запросы остались только на первом шаге — при обращении пользователя к API. Дальше такая модель быстро упирается в ограничения.

Типовая бизнес-операция, например заказ виртуального сервера, включает 8–15 последовательных обращений к разным подсистемам: биллингу, OpenStack и другим. Ошибка на любом промежуточном шаге приводит к сбою всей операции — и повторить ее корректно в рамках одного синхронного запроса уже нельзя.

Долгие действия — вроде создания сервера из снапшота объемом более 100 ГБ — могут длиться от нескольких минут до часов. Они не укладываются в стандартные таймауты и снижают надежность. Если один из сервисов отвечает медленно, исходный запрос блокируется в ожидании. Рабочие потоки простаивают, очередь растет, деградация накапливается и затрагивает других пользователей.

Мы пришли к выводу, что для пользовательских операций нужна развязка по времени и управляемое выполнение шагов — то есть асинхронная схема.

Асинхронные цепочки задач: в чем затея и как это работает

Каждый пользовательский запрос превращается в цепочку задач. Они передаются через брокер сообщений и выполняются нужными сервисами. Для каждой задачи сохраняются статус и параметры запуска: видно, какие шаги завершились, где возникла ошибка, можно перезапустить отдельный шаг или всю цепочку.

Ключевой механизм — отложенные задачи. Если сервис A инициирует выполнение цепочки в сервисе B, он не ждет прямого HTTP-ответа. Задача в A завершается только после того, как через брокер приходит уведомление о завершении цепочки в B. Такой подход особенно пригодился при нашем переходе IaaS-части на OpenStack.

Практический эффект — ошибки и повторы обрабатываются внутри цепочки и не доходят до пользователя. В 99 % случаев он получает успешный результат, пусть и с небольшой задержкой.

Цена — большее время выполнения и дополнительные ресурсы. Это наш осознанный компромисс в пользу отказоустойчивости.

Как мы пришли к текущей архитектуре

Асинхронность заложили еще на этапе проектирования. Первый вариант очереди задач работал на базе данных: отдельная таблица использовалась как очередь, а обработчик был собственный. Со временем, с ростом числа операций и длины цепочек, поддерживать такую схему стало неудобно.

Мы перешли на Celery — готовую библиотеку фоновых задач, которая унифицировала подход между сервисами, и RabbitMQ как брокер сообщений. Поверх Celery добавили собственную обвязку для повышения надежности и управляемого перезапуска упавших цепочек.

Паттерны распределенных транзакций вроде Saga или Outbox не применяли: в нашем домене важна гарантированная последовательность выполнения задач, а не координация транзакций между сервисами.

Кейс: как именно асинхронность повышает устойчивость

Теперь давайте рассмотрим это на реальном примере.

Проблема: Создание ресурса в облаке задействует несколько сервисов. В синхронной схеме это приводит к блокировкам, росту очередей и риску каскадных сбоев. Долгие операции часто выходят за таймауты.

Решение: Пользовательский запрос превращается в цепочку асинхронных задач. Поддерживаются отложенные шаги: если нужно запустить отдельную цепочку в другом сервисе, текущая задача продолжится только после уведомления брокера о ее завершении.

Вариант 1. Базовая цепочка внутри одного сервиса
Вариант 1. Базовая цепочка внутри одного сервиса

Раньше: сервис ждал ответ от каждого шага в цепочке → дополнительная нагрузка и блокировки.

Сейчас: сервис уведомляет брокер о завершении шага → нагрузка ниже, устойчивость выше.

Вариант 2. Интеграция с внешним сервисом («черный ящик»)
Вариант 2. Интеграция с внешним сервисом («черный ящик»)

Внутреннее устройство внешнего сервиса нам недоступно, но ответы возвращаются по установленному протоколу, и цепочка продолжается.

Какой эффект от этого мы получаем:

  • Пользовательские операции не теряются: даже при длительном выполнении другие запросы продолжают обслуживаться.

  • Ошибки обрабатываются на уровне шагов цепочки; их можно повторять адресно.

  • Дежурные инженеры могут перезапускать конкретные шаги или всю цепочку через админку — без отката остальных операций.

  • В результате в 99 % случаев пользователь получает успешный итоговый ответ.

Наглядный пример — кейс устойчивости

В 2022 году в одном из наших дата-центров с IaaS-инфраструктурой произошел четырехчасовой простой. Заказы новых услуг и операции по действующим не потерялись: цепочки сохранились и после восстановления связности автоматически перезапустились.

В синхронной модели такой сценарий привел бы к частичной утрате операций.

Вывод: ограничения и компромиссы

Время выполнения: «Длинные» операции, например развертывание из крупного снапшота, занимают больше времени.

Блокировка ресурсов: На период выполнения цепочки соответствующий ресурс остается заблокированным и недоступным для параллельных действий.

Нагрузка: Ранее высокая нагрузка из-за ожидания ответа на каждом шаге снизилась после перехода на модель с уведомлениями брокера и поддержкой отложенных задач.

Путь запроса: что происходит «под капотом»

Теперь давайте для наглядности зафиксируем стандартный маршрут:

  1. Пользователь отправляет запрос.

  2. Запрос попадает в пользовательское API и далее — в GraphQL-сервер, где формируется набор нужных обращений.

  3. Бизнес-сервис формирует цепочку задач и публикует ее в RabbitMQ.

  4. Обработчики задач Celery последовательно выполняют задачи, взаимодействуют с другими сервисами и фиксируют статусы.

  5. При необходимости инициируется цепочка в другом сервисе; текущий шаг окончится после уведомления брокера о ее завершении.

  6. По итогам цепочки пользователю возвращается результат.

Эта схема снимает блокировки на долгих шагах и позволяет обслуживать другие запросы параллельно.
Эта схема снимает блокировки на долгих шагах и позволяет обслуживать другие запросы параллельно.

Инструменты и мониторинг: от прозрачности к метрикам

Асинхронная архитектура не должна превращаться в «черный ящик». Мы начали с прозрачности выполнения, затем добавили метрики и операционные витрины. Как это выглядит сейчас?

Админка цепочек

Интерфейс состоит из двух экранов. На первом отображается хронологический список активных и упавших цепочек (новые — сверху). 

Общий список цепочек в админке: видны ID, тип ресурса, окружение, дата запуска, статус и доступные действия.
Общий список цепочек в админке: видны ID, тип ресурса, окружение, дата запуска, статус и доступные действия.

На втором — детальная карточка: состав задач, текущее состояние каждого шага, параметры запуска и результаты выполнения. 

Детальная карточка цепочки: задачи с параметрами, статусами и возможностью перезапуска шагов.
Детальная карточка цепочки: задачи с параметрами, статусами и возможностью перезапуска шагов.

В карточке видно ID, параметры, статусы и даты выполнения задач; доступны действия — перезапуск или завершение цепочки. 

Раскрытая задача: отображаются параметры запуска, статус выполнения и результаты.
Раскрытая задача: отображаются параметры запуска, статус выполнения и результаты.

Это позволяет инженерам оперативно восстанавливать выполнение без обращения к логам или вмешательства в код.

Метрики и наблюдаемость

В Prometheus собираются технические метрики по воркерам Celery — CPU, память, сеть — и по очередям RabbitMQ. Эти данные служат ранними индикаторами перегрузки. Для визуализации используется общий дашборд в Grafana, где отображаются показатели по контейнерам и очередям.

Пороговые значения по глубине очередей и ресурсам воркеров пока не заданы: дашборды применяются для наблюдения и анализа, без автоматического алертинга. Правила эскалации и интеграция с системой инцидентов тоже не внедрены — дежурные инженеры следят за состоянием через админку и при необходимости перезапускают цепочки вручную.

В продакшене работает от двух до шести контейнеров Celery, в зависимости от сервиса. RabbitMQ остается основным брокером: его стабильность и поддержка в текущей версии Celery полностью покрывают наши потребности, поэтому переход на Kafka мы не планируем.

Архитектура на этом этапе считается завершенной: ключевые функции реализованы, а дальнейшие изменения направлены на удобство эксплуатации и мониторинг.

Вместо заключения

Для Рег.облака асинхронные цепочки задач стали основой стабильности. Мы прошли путь от очередей в базе данных до Celery и RabbitMQ — и видим, что эти усилия были оправданы.

Если вы решали похожие задачи или нашли другой способ повысить отказоустойчивость — расскажите в комментариях. Всегда интересно сравнить подходы и обменяться опытом!

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