Эта статья — первая из четырёх частей серии о внутреннем устройстве виртуальной машины Envision: программного обеспечения, которое выполняет скрипты Envision. См. часть 2, часть 3 и часть 4. В этой серии не рассматривается компилятор Envision (возможно, в другой раз), так что предположим, что скрипт каким-то образом уже был преобразован в байт-код, который принимает на вход виртуальная машина Envision.

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

Вне временных рядов

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

Задача Lokad — строить такие конвейеры без участия программистов. Конечно, в Lokad работают разработчики, но они создают инструменты и инфраструктуру для всей компании, а не для отдельных клиентов. Вместо этого у нас есть наши специалисты по цепям поставок — команда экспертов в области цепей поставок, которые понимают специфические проблемы каждого клиента и разрабатывают решения. В более традиционной корпоративной структуре это были бы менеджеры по продукту — те, кто выслушивает клиентов, а затем в подробностях сообщает разработчикам, что именно нужно реализовать. Наша философия разработки и причина создания Envision, нашего собственного языка программирования, заключается в том, что для специалиста по цепям поставок должно быть быстрее самостоятельно реализовать решение, чем писать техническое задание для реализации другими.

Чтобы добиться этого, не жертвуя масштабом или производительностью, Envision должен предоставлять три функции бесплатно1, без каких-либо усилий со стороны специалиста по цепям поставок.

  • Управление памятью должно быть автоматическим. Это, конечно же, подразумевает наличие сборщика мусора, но также означает, что Envision должен прозрачно поддерживать крупномасштабные наборы данных. Создание массива размером в десять гигабайт — это базовый минимум: это не даже три миллиарда чисел! Ожидается, что программа сможет работать с набором данных, превышающим объём памяти одной машины. Фактически, Envision поддерживает наборы данных, которые больше, чем память всего кластера, благодаря умной выгрузке на NVMe-накопители.

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

  • Мы ожидаем, что Envision будет требовать минимальных аппаратных ресурсов для работы. Многие проблемы с производительностью можно решить, потратив миллионы долларов на первоклассное оборудование и/или серверные лицензии, но мы предпочли бы избежать ситуации, в которой нажатие кнопки «Запустить» скрипта обходится в сотни долларов.

Окружение

Envision не может быть установлен или запущен локально. Вместо этого все пользователи подключаются к онлайн-платформе Lokad, которая предоставляет браузерную IDE для редактирования и выполнения скриптов. Данные и панели управления также хранятся онлайн и доступны через веб-интерфейс, а также через SFTP и FTPS.

Когда пользователь запускает скрипт через веб-интерфейс, создается задание, которое отправляется в кластер Envision для выполнения.

Envision работает в пакетном режиме: каждое выполнение считывает весь входной набор данных и производит полный вывод. Это может занимать от 5 секунд для очень простого скрипта при небольшом объёме данных до 30-40 минут для большого скрипта по обогащению данных и даже нескольких часов для некоторых задач машинного обучения.

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

Структура кластера

По состоянию на 2021 год весь Envision работает на .NET 5, размещенном на виртуальных машинах Ubuntu 20.04 в облаке Microsoft Azure. Кластер состоит из 2 до 6 экземпляров Standard_L32s_v2: 32× AMD EPYC 7551, 256GiB памяти и 4× NVMe-накопителей общей емкостью 7.68ТБ. Мы называем эти машины воркерами.

В кластере реализована связь M-ко-N между воркерами и заданиями: один воркер может одновременно выполнять несколько скриптов Envision, а если один скрипт назначен нескольким воркерам, они будут сотрудничать для его ускоренного завершения.

Каждый кластер также имеет планировщик — более маленький Standard_B2ms с 2 ядрами и 8GiB памяти. Планировщик предоставляет API-эндпоинты для внешних приложений, чтобы отправлять новые задания и собирать результаты выполненных заданий. Он также отвечает за распределение заданий между одной или несколькими машинами в кластере. В зависимости от нагрузки и степени доступного параллелизма для каждого скрипта в данный момент, планировщик может добавлять или удалять воркеров из задания.

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

Blob-хранилище

Lokad не использует SQL-базы данных для данных клиентов. Размещаемые решения не могут легко вместить наборы данных наших крупных клиентов (обычно их объем ограничен от 4ТБ до 16ТБ), а запуск собственных серверов потребовал бы усилий, которые мы предпочли бы направить в другие области.

С другой стороны, Envision работает в пакетном режиме, что устраняет необходимость в запросах сложнее, чем “Read column X between lines L and M”: как только входные данные загружены, воркер сможет индексировать их и переобрабатывать по мере необходимости.

Именно поэтому мы используем Azure Blob Storage в качестве основного хранилища. Это позволяет сохранять свыше одного петабайта по цене менее 1% от стоимости размещенных SQL-баз данных, а производительность чтения запросов стабильно составляет от 30MB/s до 60MB/s.

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

Точнее говоря, Lokad построил хранилище с адресацией по содержимому на базе Azure Blob Storage. Оно является open source и доступно в виде пары пакетов NuGet: Lokad.ContentAddr и Lokad.ContentAddr.Azure. Зная хеш отдельных файлов, Envision может определить, что некоторые из входных данных не изменились, и таким образом повторно использовать вычисленные значения, сохранённые в кэше с предыдущего запуска.

Развертывание

Envision не использует контейнеризацию (например, Docker), поскольку преимущества контейнеров не оправдывают дополнительной сложности, которая с этим связана.

Во-первых, высокопроизводительные вычисления требуют каждой капли CPU, ОЗУ и хранилища от наших воркеров, поэтому невозможно запускать несколько приложений на одной машине.

Во-вторых, для упаковки приложения вместе со всеми его зависимостями, независимо от платформы, мы обнаружили, что dotnet publish является достаточным (а на самом деле и быстрее, чем docker build). В .NET 5 Microsoft обеспечивает выдающуюся кроссплатформенную поддержку, и достаточно буквально скопировать результаты сборки с Windows-машины на Linux-хост.

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

На следующей неделе мы углубимся в модель выполнения Envision и разберём, как представлена работа, которую необходимо выполнить, внутри кластера.

Без стеснения рекламируем: мы нанимаем разработчиков программного обеспечения. Возможна удаленная работа.


  1. ничего по-настоящему не даётся бесплатно, и мы платим за это, жертвуя способностью Envision функционировать как язык общего назначения. Например, автоматическая параллелизация означает, что мы никогда не поддержим явное управление потоками в Envision; автоматическая обработка на нескольких машинах означает, что понятие «локальной машины» в Envision не появится. ↩︎