Привет! Меня зовут Педро Затерка, я научный сотрудник и инженер по машинному обучению в компании 4intelligence. Мы — бразильский стартап, специализирующийся на экономике и искусственном интеллекте, предлагающий консультационные услуги, а также платформу AutoML, которую мы называем FaaS (прогноз как услуга). Основное узкое место и то, что Argo помогла нам решить, сосредоточено на последнем, но все области взаимосвязаны, поскольку наши внутренние команды также в значительной степени используют наш FaaS.

Прогноз как услуга — как это работает?

Наше приложение состоит из внешнего интерфейса без кода, в котором пользователь может загружать файлы .csv, .xlsx или .rds, получать дополнительные контролируемые переменные и отправлять по одному временному ряду для прогнозирования выбранный горизонт. Другой вариант ввода — использование одной из наших библиотек (в R или Python) для одновременной отправки нескольких временных рядов, и здесь начинает проявляться одна из наших главных проблем: масштабируемость.

Само приложение сначала было создано полностью с использованием R (от API-интерфейсов с использованием Plumber до внешнего интерфейса с использованием Shiny) и работало на монолитной структуре с использованием виртуальных машин в Google Cloud, каждая из которых запускала один проект за раз. Хотя поначалу это не представляло такой большой проблемы, поскольку большинство пользователей использовали наш внешний интерфейс, доступ к нашему пакету позволял пользователю отправлять большие объемы данных, а также постоянно растущее число пользователей, наша способность запускать как можно больше первоочередной задачей стало максимально возможное параллельное выполнение рабочих мест с высокой экономической эффективностью.

Переосмысление архитектуры

Во-первых, нам нужно было шире взглянуть на то, как выглядит приложение с архитектурной точки зрения. Он был построен органично, начиная с общего конвейера, который работал на локальных машинах для консультационных задач, до переноса и переноса в облако в том виде, в котором он был, с использованием стороннего пакета для создания интерфейса с GCP. Мы знали, что сама оркестровка отойдет от R, но наш первый перекресток возник, когда нам нужно было решить, стоит ли вообще сохранять R или проводить рефакторинг конвейера на более удобном для облачных вычислений языке, таком как Python.

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

  • Большинство наших специалистов по данным в настоящее время имеют опыт работы с R;
  • У нас был проверенный в бою надежный пайплайн, построенный на некоторых специфичных для R библиотеках, которые нуждались в серьезном рефакторинге;
  • Сам по себе процесс рефакторинга может занять даже больше времени, чем само изменение архитектуры.

Поэтому мы решили пока оставить R, но новая архитектура должна поддерживать смешанный конвейер, поскольку в ближайшем будущем мы планировали добавить Python.

Уборка дома

Первой проблемой, которую мы решили, была оптимизация образов Docker, которые мы использовали. Иногда случалось, что для создания текущего образа требовалось до 3 часов, что не подходило для конвейера CI/CD, который мы имели в виду.

Чтобы убедиться, что включены только необходимые пакеты, мы начали сборку с нуля, используя образ на основе alpine от r-minimal. Увы, создать образ в R было не так уж и просто, так как многие версии пакетов в нашей среде конфликтовали. Нам пришлось установить, удалить и переустановить некоторые пакеты, используя многоэтапные сборки с некоторыми промежуточными образами, оставленными для других приложений.

В итоге мы сократили время сборки с 3 часов до 30 секунд.

Архитектура 1.5

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

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

Он написан на Python и получает закодированное тело, декодирует его (отсюда и данное имя) и выводит несколько файлов JSON, которые продвигаются вперед по конвейеру в качестве входных данных для моделирования R.

Когда первая концепция конвейера была готова, мы реализовали ее для работы в виде структуры API в Google Cloud Run, что значительно повысило производительность и было довольно быстрым для реализации и развертывания. Однако с самого начала он оказался ненадежным с точки зрения предсказуемости затрат, поэтому мы начали искать новый подход и решили использовать более стабильное и SOTA-решение с использованием Kubernetes.

Переезд в Арго

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

Принимая во внимание эти факторы, мы решили использовать рабочие процессы Argo (и вместе с ними Argo Events). Он отметил все нужные нам поля и еще несколько:

  • Независимость от языка (что означает, что он может запускать R);
  • Независимость от облака (на случай, если нам нужно запустить процессы в другом месте);
  • Имеет активное и полезное сообщество;
  • То, что проект Linux Foundation размещается на CNCF, вселило в нас уверенность и убедило наших заинтересованных лиц в том, что Argo — правильное решение;
  • И, конечно же, Argo может использовать все индивидуальные образы Docker в изолированной экосистеме.

Поскольку у нас была активная команда, готовая попробовать и научиться настраивать и развертывать наши кластеры в Google Kubernetes Engine, мы начали работать над реализацией.

Мы получили большую помощь от сообщества Argo во время процесса, и это позволило нам развернуть первоначальную версию новой инфраструктуры за небольшой промежуток времени. И Slack Channel, и репозиторий Github предоставили много информации и ответов, и сообщество стремилось помочь, что также было большим плюсом для выбора Argo.

Как это выглядит сейчас

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

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

Затем тема pubsub запускает облачную функцию, которая отправляет событие в Argo Events, запуская конвейер. Проект не имеет внешнего доступа в любой точке, доступ к нему осуществляется только через учетные записи служб.

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

Что будет дальше

Как и сегодня, блок R по-прежнему монолитный, меньшего размера, но все еще далек от того, что мы считаем идеальным. Наша дорожная карта в ближайшем будущем состоит в том, чтобы разбить эту часть пайплайна на более мелкие этапы, используя модульность и независимость Argo для включения разных языков (первое запланированное — включить некоторые модели, реализованные на Python), и собрать все вместе в конце. Кроме того, мы находимся в процессе постоянного улучшения, настраивая и тестируя новые конфигурации, чтобы лучше оптимизировать экономическую эффективность приложения.