Декларативный API, деревья поведений и реконсиляция: как мы в MWS строим сервис Compute. cloud.. cloud. compute.. cloud. compute. IT-инфраструктура.. cloud. compute. IT-инфраструктура. MWS.. cloud. compute. IT-инфраструктура. MWS. Блог компании MWS.. cloud. compute. IT-инфраструктура. MWS. Блог компании MWS. Виртуализация.. cloud. compute. IT-инфраструктура. MWS. Блог компании MWS. Виртуализация. облако.. cloud. compute. IT-инфраструктура. MWS. Блог компании MWS. Виртуализация. облако. облачная платформа.. cloud. compute. IT-инфраструктура. MWS. Блог компании MWS. Виртуализация. облако. облачная платформа. Облачные сервисы.. cloud. compute. IT-инфраструктура. MWS. Блог компании MWS. Виртуализация. облако. облачная платформа. Облачные сервисы. публичное облако.. cloud. compute. IT-инфраструктура. MWS. Блог компании MWS. Виртуализация. облако. облачная платформа. Облачные сервисы. публичное облако. разработка облака.. cloud. compute. IT-инфраструктура. MWS. Блог компании MWS. Виртуализация. облако. облачная платформа. Облачные сервисы. публичное облако. разработка облака. Разработка публичных облаков.
Декларативный API, деревья поведений и реконсиляция: как мы в MWS строим сервис Compute - 1

Приветствую всех! На связи Родион Цалкин, Tech Product IaaS в MWS. 

В этой статье расскажу, из каких решений на верхнем уровне состоит сердце MWS — сервис вычислительных ресурсов Compute — и как знания из разных областей помогают найти элегантные решения для возникающих проблем при его создании. Здесь не будет технического deep-dive’а (ждите в следующих материалах), поэтому статья будет интересна широкому кругу читателей.

Чтобы рассказать об этих решениях, надо для начала слегка погрузиться в то, какие задачи решает сервис Compute, как с ним взаимодействует пользователь и как вообще работает.

За что отвечает Compute в облаке?

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

С технической точки зрения задача Compute заключается в создании и управлении виртуальными вычислительными ресурсами — запуске виртуальной машины, подключении дисков и сетевых адаптеров.

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

Подобные возможности нужны не только клиентам, есть и внутренние пользователи. На основе виртуальных машин создаются платформенные сервисы (Platform-аs-а-Service), такие как Managed Kubernetes, DBaaS и другие. Все они функционируют на виртуальных машинах, поэтому сервис Compute, объединяющий виртуальную машину, сеть и диск, является центральным компонентом облачной инфраструктуры.

Как пользователь взаимодействует с облачной инфраструктурой

Пользователь должен сообщить Compute, какую виртуальную инфраструктуру он хочет. Есть несколько способов это сделать.

Исторически первый способ — это обращение в службу поддержки с запросом на создание виртуальных машин в облаке… Сейчас это, скорее, атавизм, но в некоторых облаках всё еще встречается.

Второй путь гораздо более технологичный — пользователь сам создаёт нужную виртуальную инфраструктуру через пользовательский интерфейс (UI) либо использует утилиту командной строки или программный вызов API. Для одной виртуальной машины такой подход вполне логичен и удобен.

Самые продвинутые с инженерной точки зрения клиенты используют подход Infrastructure as Code, который позволяет описывать инфраструктуру непосредственно в коде, используя все преимущества такого подхода: возможность версионирования, повторного использования и параметризации. Например, если клиент научился с помощью Infrastructure as Code создавать необходимые виртуальные машины в одной зоне доступности, он может, используя тот же код, создать вторую зону, изменив параметры зоны доступности, — и вся инфраструктура будет развёрнута автоматически.

Для того чтобы обеспечить пользователю удобство работы в подходе Infrastructure as Code, мы в MWS решили строить свой API по декларативной модели. Это значит, что вместо описания последовательности действий по созданию инфраструктуры и облачного окружения (императивный метод) задаётся конечное (желаемое) состояние системы.

Например, вместо пошагового описания действий типа: «Запусти виртуальную машину, подключи диск, подключи сеть», мы говорим: «Должна существовать виртуальная машина с определённым диском и сетью». Разница в том, что во втором подходе система ничего не будет делать, если всё уже сделано, — декларативная модель изначально идемпотентна. А в первом подходе, если ID, скажем, генерируется на стороне сервера, можно случайно запустить вторую виртуалку — и за обеспечение идемпотентности придётся отвечать уже клиенту.

Как мы создаём виртуальные машины

Итак, мы обсудили, как пользователь передаёт системе желаемое состояние — теперь посмотрим, как на основе этого описания создаются виртуальные машины с нужной сетью и дисками. Что происходит под капотом?

Архитектура инфраструктурного слоя облака MWS
Архитектура инфраструктурного слоя облака MWS

Глобальный и зональный Control Plane

Зона — это, по сути, дата-центр. Если зона выходит из строя, а информация о размещённых в ней ресурсах хранится только внутри неё, то пользователь в случае аварии не сможет увидеть, что у него там было, — эти виртуальные машины просто исчезнут из UI до восстановления зоны.

Чтобы избежать таких ситуаций и обеспечить независимость зон при одновременном сохранении глобальной видимости ресурсов, мы разделили наш Control Plane на два слоя: глобальный и зональный. Внутри дата-центра работает часть сервиса, которая может действовать автономно — даже при отказе каналов связи с центральной системой. А глобальный слой, в свою очередь, обеспечивает кросс-зональную консистентность и управляемость.

Есть ещё один интересный аспект разделения на глобальный и зональный слои — они отвечают за разные типы сущностей: логические и физические.

В процессе управления облачной инфраструктурой мы имеем дело как с логическими (виртуальными), так и с физическими объектами. Например, когда пользователь создаёт виртуальный диск, это не означает, что где-то немедленно появляется конкретное физическое устройство. А вот внешний IP-адрес — это одновременно и логическая сущность, и реальная, поскольку он должен быть связан с конкретным интерфейсом.

А что насчёт виртуальной машины? Это логическая сущность. Хотя с точки зрения пользователя существует одна ВМ, физически она может быть реализована несколькими процессами — особенно в момент live-миграции, когда временно сосуществуют два физических экземпляра одной и той же виртуалки.

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

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

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

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

Но для понимания надо идти дальше, we need to go deeper…

Декларативный API, деревья поведений и реконсиляция: как мы в MWS строим сервис Compute - 3

Data Plane

После определения логической структуры и планирования физической реализации на зональном уровне, необходимо применить эти изменения в инфраструктуре. Эту задачу выполняет уровень Data Plane (DPL), который работает непосредственно на хостах, где развёртываются виртуальные машины. Именно здесь абстрактные инструкции превращаются в конкретные команды для гипервизора, сетевых компонентов и систем хранения данных.

На базовом уровне технологии сборки виртуальной машины мы используем гипервизор QEMU-KVM, который взаимодействует с виртуализированной сетью и дисками через протокол vhost. Для оптимизации производительности применяются специализированные библиотеки: SPDK (Storage Performance Development Kit) для работы с дисками и DPDK (Data Plane Development Kit) для сетевого взаимодействия.

Управление этими компонентами происходит по той же декларативной модели, которая пронизывает всю архитектуру Compute. Информация о желаемой конфигурации передаётся сверху вниз по цепочке: пользователь декларативно описывает требуемый результат глобальному слою, глобальный передаёт необходимые инструкции зональному, а зональный — уровню Data Plane. Получив эти инструкции, DPL анализирует текущее состояние хоста и определяет последовательность действий, необходимых для достижения целевой конфигурации.

И наконец-то, мы дошли до проблемы!

Проблемы с логикой реконсиляции

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

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

Если хотите нырнуть глубже в процесс реконсиляции — читайте статью Сергея Самойлова о механизме реконсиляции на примере block devices.

Почему этот подход так важен? Есть несколько причин:

  • Гибкость системы. Например, если пользователь в процессе создания ВМ решает изменить конфигурацию с 4 на 8 ядер, система должна адаптироваться — либо внести изменения до запуска машины, либо реализовать их через hot-plug после запуска.

  • Адаптация к ограничениям ресурсов. Некоторые запросы могут быть временно невыполнимы — например, запрос на spot-инстанс при нехватке ресурсов. Благодаря реконсиляции пользователь может изменить тип размещения со spot на on-demand без удаления и пересоздания всей ВМ.

  • Оптимизация производительности. Немедленное исполнение каждой декларации пользователя может снизить отзывчивость системы, поэтому умная реконсиляция позволяет группировать изменения и выполнять их оптимально.

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

Существует несколько традиционных подходов к реализации логики реконсиляции, но все они имеют существенные недостатки:

  1. Императивный подход со множеством условных операторов. Такой код быстро превращается в неподдерживаемые «спагетти» — когда логические ветвления настолько переплетаются, что становится невозможно отследить все сценарии выполнения.

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

Оба этих подхода оказываются неэффективными для быстрой разработки и долгосрочной поддержки системы.

Как нам помог опыт геймдева и робототехники

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

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

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

Подход «Деревья поведений»

Дерево поведений — это особый способ организации конечных автоматов (finite state machines), который значительно упрощает их восприятие. В отличие от стандартных подходов, логику, выраженную через деревья поведений, гораздо проще создавать, модифицировать и поддерживать в долгосрочной перспективе.

По своей сути, дерево поведений — это иерархическая структура, узлы которой могут возвращать только два состояния: успех или отказ. Основные компоненты такой структуры:

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

Selector: узел с дочерними элементами, который последовательно выполняет их, пока один из них не вернёт успех. Если хотя бы один дочерний узел успешен — возвращает успех, иначе — отказ.

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

Такая структура достаточна для записи любой логики. Но в чём же её преимущество перед традиционными конечными автоматами или императивным кодом с условными операторами?

Дело в том, что применение этого подхода позволяет применять визуальное мышление, в котором человек более силён. Когда алгоритмы показаны в виде деревьев, их гораздо легче понять на интуитивном уровне. Например, мы всегда лучше ориентируемся по карте, чем по текстовым указаниям. Разработчики могут буквально увидеть, как движется логика, что делает поиск багов намного проще.

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

Пример работы логики «Деревья поведений». Логика работает сверху-вниз и слева-направо

Пример работы логики «Деревья поведений». Логика работает сверху-вниз и слева-направо

Рассмотрим пример работы: если диск ещё не создан (сценарий с отказом при проверке Is disk ready?), система автоматически переходит к следующему узлу — Create disk. После успешного создания диска мы переходим на уровень выше в соседний selector, где по аналогичному принципу проверяется готовность сети. Когда оба selector-узла отработали успешно, мы возвращаем статус success на уровень sequence и система может продолжить выполнение следующего этапа создания виртуальной машины.

Эта логика действует одновременно на нескольких уровнях. Возьмем, к примеру, процесс подготовки сети — это комплексный процесс с разветвлённой структурой, который охватывает как уровень управления (Control Plane), так и уровень передачи данных (Data Plane). По сути, такая система выполняет работу системного администратора, автоматизируя процессы настройки и поддержки инфраструктуры.

Преимущества подхода «Дерево поведений»

Преимущества подхода «Дерево поведений»

Несмотря на то что применение деревьев поведений существенно упрощает и ускоряет разработку, написание логики реконсиляции — один из главных вызовов при создании сервиса Compute. Формат данной статьи не позволит нам нырнуть глубоко в технические аспекты данного вопроса, но мои коллеги обязательно сделают это в следующих материалах.

Так что же находится в сердце MWS?

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

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

Compute — это основа любого облачного сервиса, без которого сложно представить работу других сервисов облачной платформы, поэтому мы подошли к его разработке тщательно, предусмотрев масштабирование и гибкость системы.

Подписывайтесь на хаб, наша команда продолжит рассказывать, как устроены компоненты Compute на более низком уровне. Будет интересно.


Читайте и смотрите другие материалы про строительство нового облака MWS

Зачем мы строим собственное публичное облако? Рассказывает CTO MWS Данила Дюгуров

Реалити-проект для разработчиков Building the Cloud. Показываем и рассказываем про архитектуру сервисов платформы ещё до запуска

Подкаст «Расскажите про MWS». О людях и технологиях нового облака MWS

Карьера в MWS. Стань частью одной из сильнейших инфраструктурных команд на рынке

Автор: tsalkin

Источник

Рейтинг@Mail.ru
Rambler's Top100