Привет! Меня зовут Педро Затерка, я научный сотрудник и инженер по машинному обучению в компании 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), и собрать все вместе в конце. Кроме того, мы находимся в процессе постоянного улучшения, настраивая и тестируя новые конфигурации, чтобы лучше оптимизировать экономическую эффективность приложения.