Как нам удается легко планировать конвейеры машинного обучения с помощью Airflow и Kubernetes с помощью KubernetesPodOperator

Чтобы обеспечить максимальное удобство для пользователей, Dailymotion во многом полагается на алгоритмы машинного обучения и искусственный интеллект, которые лежат в основе его механизмов рекомендаций и тегов видео. Поэтому возможность беспрепятственно создавать, обучать и развертывать новые модели - одна из наших главных забот как инженеров по обработке данных. Давайте посмотрим, как Apache Airflow и Google Kubernetes Engine так хорошо работают вместе и позволяют нам достичь этой цели.

Сложный жизненный цикл модели машинного обучения

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

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

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

В целом, различные этапы разведки и добычи остаются почти одинаковыми, поэтому мы можем повторно использовать код, написанный при исследовании, при переходе к производству, чтобы избежать каких-либо расхождений и сэкономить время. В Dailymotion мы в основном используем Google Cloud Platform для хранения наших данных и создания на их основе сервисов. Apache Airflow - наш основной планировщик, и мы настроили его в Google Kubernetes Engine (GKE). Мы увидим, как оба помогли нам выполнить эти требования.

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



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

Наши первые попытки планирования конвейеров машинного обучения

В самом начале команда была довольно маленькой, с очень небольшим количеством инженеров по обработке данных. В одной из наших первых моделей машинного обучения части извлечения данных и предварительной обработки, которые состояли в заданиях BigQuery, были легко перенесены в производственную среду и запланированы с помощью воздушного потока с помощью BigQueryOperator. Однако когда дело дошло до обучения модели, это было немного сложнее, поскольку для этого требовались определенные библиотеки и машины. Из-за нехватки инженерных ресурсов и из-за сжатых сроков специалисту по обработке данных пришлось довести эту часть до производства самостоятельно без четко определенной структуры. Для удобства необходимый образ докера был создан и запущен в GKE. Затем обучение модели было запланировано демоном, подобным cron, установленным в том же модуле, что и обучающее приложение. Это сработало довольно хорошо, образы докеров не зависят от среды и позволяют использовать различные библиотеки, в то время как GKE предоставляет необходимые вычислительные ресурсы. Решение кажется достаточно хорошим, но на самом деле у него есть несколько недостатков:

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

Позже команда сильно выросла, и у нас было все больше и больше заданий, запланированных в Airflow. Поэтому мы, естественно, решили перенести туда и все наши задачи машинного обучения. В то время воздушный поток KubernetesPodOperator еще не был доступен, но DockerOperator был, поэтому мы полагались на него для выполнения наших заданий в Google Compute Engine (GCE ). Рабочий процесс можно описать следующим образом:

Но все же мы не были полностью удовлетворены решением, так как:

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

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

Чем отличаются Airflow и Kubernetes

Идея заключалась в том, чтобы использовать пулы узлов GKE, каждый пул будет предоставлять тип машины (высокая память, высокий процессор, графический процессор…) и набор областей с включенным автоматическим масштабированием. Затем мы можем отправить наши задания машинного обучения в разные пулы из воздушного потока с помощью KubernetesPodOperator:

Выполнение наших задач машинного обучения в Kubernetes с пулами узлов имеет несколько преимуществ:

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

Создание пулов узлов

Мы решили создать пулы узлов в том же кластере, что и наш экземпляр Airflow, это немного упрощает задачу: у нас уже есть некоторые службы, которые можно использовать повторно. Создать пул узлов с GKE довольно просто:

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

Использование KubernetesPodOperator

После создания пула узлов мы можем использовать воздушный поток KubernetesPodOperator следующим образом:

Опять же, это довольно просто, но все же давайте рассмотрим некоторые интересные параметры:

  • node_selectors: указывает, на каком узле должен запускаться под. Мы указываем здесь узлы из определенного пула.
  • in_cluster: сообщает, совпадает ли целевой кластер с тем, в котором работает воздушный поток. Мы используем здесь динамический параметр IN_CLUSTER, который принимает разные значения в зависимости от среды, в которой запущен воздушный поток. Для локального тестирования мы развернем модули в нашем кластере разработки airflow, поэтому значение будет False, а CLUSTER_CONTEXT будет равно контексту нашего кластера разработки.
  • tolerations: устанавливает требуемые допуски. Здесь мы обеспечиваем связанный допуск для заражения пула узлов.
  • xcom_push: разрешите модулю отправлять результат работнику воздушного потока. Нам нужно записать данные, которые мы хотим вернуть, в файл json, расположенный в: /airflow/xcom/return.json. Под капотом KubernetesPodOperator монтирует том и использует контейнер sidecar, который будет читать файл и выводить его в stdout, который затем захватывается и анализируется рабочим.

Как инженеры по обработке данных, нам просто нужно было настроить пулы и создать несколько переменных / методов, чтобы добавить уровень абстракции. Наши специалисты по данным теперь могут очень легко создавать свои собственные даги, используя пулы узлов. Итак, в конце концов, воздушный поток KubernetesPodOperator позволяет нам в полной мере использовать преимущества функций Kubernetes, таких как автоматическое масштабирование узлов и совместное использование ресурсов / сервисов, при планировании наших задач машинного обучения. на воздушном потоке.

Следующие шаги

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

  • KubernetesExecutor: для людей, которым интересно, почему мы не перешли на KubernetesExecutor вместо вместо использования KubernetesPodOperator? Что ж, я считаю, что первый предназначен для другой цели, когда сотрудники Airflow выполняют тяжелую работу и нуждаются в расширении. В настоящий момент это не совсем наш случай, поскольку наши рабочие в основном отправляют задания для выполнения в Google BigQuery, Dataflow ... но, что наиболее важно, переходят на KubernetesExecutor требует больше работы, чем просто использование нового оператора. Однако мы все же можем рассмотреть его на будущее, поскольку он дает много преимуществ, например, при выполнении большого количества обратных засыпок мы напрягаем наших рабочих, и KubernetesExecutor может быть хорошим решением.
  • Kubeflow: он предлагает целую платформу для рабочего процесса машинного обучения от исследования до обслуживания и стремится сделать его более простым и последовательным. Наша текущая структура не полностью покрывает обслуживающую часть, поэтому мы определенно хотим ее проверить.

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