Запуск крупномасштабных заданий ETL без поддержки армии разработчиков

ETL - или Извлечь, Преобразовать, Загрузить - является распространенным шаблоном для обработки входящих данных. Он позволяет эффективно использовать ресурсы, объединяя «преобразование» в одну массовую операцию, что часто значительно упрощает разработку и сопровождение, чем аналог потоковой обработки. Он также хорошо подходит для разовых исследований наборов данных, когда пользователь пишет некоторый собственный код для выполнения некоторого анализа набора данных, экспортируя некоторые результаты для использования позже. Этот общий шаблон лежит в основе многих исследований в области науки о данных, но по моему опыту я часто обнаруживал, что его реализация неуклюжая и неэффективная. В этой статье я собираюсь обрисовать новую технологию, которая, на мой взгляд, может стать ценным инструментом в арсенале специалистов по обработке данных (и разработчиков).

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

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

  • Скорость развертывания для итеративной разработки
  • Расходы
  • Требования к инфраструктуре
  • Легкость интеграции с практиками разработки программного обеспечения (анализ кода, контроль версий, системы сборки)

Одной из таких технологий является AWS Glue ETL Jobs, цель которой - упростить выполнение функций преобразования, скрывая базовую инфраструктуру, а пользователь просто предоставляет базу кода. Хотя Glue после настройки работает хорошо, он может пугать и разочаровывать даже самых опытных разработчиков. Его неинтуитивные пользовательские интерфейсы и плохая документация затрудняют изучение и еще более трудную отладку. Я обнаружил, что для эффективного использования необходимы серьезные знания Spark (технологии, лежащей в основе Glue ETL).

Это не так уж и плохо, как только вы освоите клей, он может стать очень мощным. Библиотека DynamicFrame (которая обертывает искру Dataframes) отлично подходит для чтения и записи данных в Glue Tables, с одной строкой кода (еще несколькими, если вы настаиваете на совместимости с PEP-8 ...) вы можете записать почти бесконечное количество данных в разделенный стол - то, что до сих пор поражает меня! Это делает Glue отличным инструментом, если у вас есть крупномасштабные наборы данных, которые можно импортировать с помощью DynamicFrame, но которые в лучшем случае неэффективны для чего-то меньшего.

В качестве быстрого примера, в качестве общего шаблона я трачу много времени на создание заданий ETL для создания новых наборов данных из необработанных данных, хранящихся в S3, где эти преобразованные данные затем сохраняются обратно в S3 для последующего анализа. Звучит просто, правда?

Предполагая, что у меня есть код локально на моем компьютере, и я хочу создать задание Glue для запуска этого кода, мне придется:

  1. Загрузите мой скрипт в S3 через интерфейс командной строки AWS (будем надеяться, что он сработает с первого раза…)
  2. Перейдите в консоль AWS
  3. Перейти на страницу клея
  4. Создать новую вакансию
  5. Настройте роль IAM для выполнения задания с соответствующими разрешениями.
  6. Введите расположение вашего скрипта в S3
  7. При необходимости настройте ENI для обеспечения доступа к данным через VPC.
  8. Включите зависимости zip для любых необходимых библиотек - (хотя расширений C нет, так что удачи в использовании pandas!)
  9. Добавить расписание
  10. Выполнить задание

И это даже не касается того, как разработать сценарий, я имею в виду, просто посмотрите инструкции по подключению конечной точки разработки к Pycharm! (Https://docs.aws.amazon.com/glue/latest/dg/dev-endpoint-tutorial-pycharm.html)

Это не делает Glue хорошим инструментом для повседневных исследований в области науки о данных / машинного обучения. Подводя итог, при оценке нашей спецификации, определенной выше, Glue ETL:

  • Трудно быстро перебрать новые версии скрипта из-за отсутствия хорошего локального тестирования
  • Очень сложно эффективно отлаживать из-за искрового журнала
  • Сложность интеграции с процессами разработки программного обеспечения из-за удаленного редактирования кода и неуклюжего развертывания
  • Легко интегрируется в S3 и Glue Tables

Так какие еще варианты есть? Представляем нашего соперника. Metaflow!

Metaflow - это довольно новый инструмент, который появился в конце 2019 года. Он был разработан и создан Netflix как инструмент с открытым исходным кодом, который возвращает власть ученым, занимающимся данными, уменьшая сложность и увеличивая скорость итераций. Он включает клиентскую библиотеку Python / R, которая управляет пользовательскими сборками, и набор ресурсов AWS (в виде стека Cloudformation), который должен быть развернут пользователем. Итак, хватит дразнить - давайте разберемся, почему я сейчас использую Metaflow для своих задач Python ETL.

Разработать новый код для Metaflow очень просто

Metaflow добавляет несколько новых аннотаций для обеспечения своей функциональности, но в основе оркестровки используются установленные функции python, классов и методов (как «классически обученный» инженер-программист, это меня очень радует). Поток - это класс, а шаги в этом потоке - это функции, каждая из которых связана с выполнением следующего шага. Затем к потоку или шагу могут быть применены аннотации для добавления дальнейшей конфигурации.

# An example of a Metaflow Flow running locally
class TestFlow(FlowSpec):
  @step
  def start(self):
    print(“This is the start step!”)
    self.next(self.process) # Runs the process step next
  @step
  def process(self, inputs):
    print(“This is the process step!”)
    self.next(self.end) # Then the end step
  @step
    def end(self):
    print(“This is the end step!”)
if __name__ == ‘__main__’:
  TestFlow() # This initialises the Flow then runs the start step

Я не думаю, что когда-либо находил такой простой инструмент, как Metaflow, для переключения удаленного развертывания кода. С одной аннотацией к моим шагам я могу перейти от запуска Flow на ноутбуке к 64-ядерному гиганту на EC2, это так просто.

# Same Flow as before, but will run on the Cloud!
class TestFlow(FlowSpec):
  @batch(cpu=64, memory=2000) # Each step get's these resources
  @step
  def start(self):
    print(“This is the start step!”)
    self.next(self.process)
  @batch(cpu=64, memory=2000)
  @step
  def process(self, inputs):
    print(“This is the process step!”)
    self.next(self.end)
  @batch(cpu=64, memory=2000)
  @step
  def end(self):
    print(“This is the end step!”)
if __name__ == ‘__main__’:
  TestFlow()

Metaflow прост в управлении

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

Данные можно хранить прямо в Metaflow

Еще одна замечательная функция Metaflow - это хранилище метаданных, позволяющее различным этапам потока обмениваться данными и сохранять эти данные в S3 после завершения выполнения. В моем предыдущем примере создания конвейера ETL мне не нужно было хранить данные в S3 самому, поскольку Metaflow сделает это за меня. Поэтому, когда я вернусь, чтобы выполнить свой анализ, я мог просто получить последнюю версию своего Flow и извлечь набор данных. Это делает Metaflow очень мощным средством как для специального, так и для запланированного «накопления» данных без необходимости управлять сложными базами данных.

# A portion of a Flow showing the metadata storage and retrieval
@step
def start(self):
  print(“This is the start step!”)
  self.message = “Metaflow metadata is cool!” # Saved to S3
  self.next(self.process)
@step
def process(self, inputs): # Loads message from S3 and into self.
  print(f”Let’s print the previous steps message {self.message}”)
  self.next(self.end)

Без учета затрат на инфраструктуру выполнение заданий в Metaflow обходится недорого

Поскольку Metaflow использует Batch под капотом, вы можете настроить его для использования цен SPOT. Это делает стоимость инстанса практически незначительной, а поскольку Metaflow может автоматически обрабатывать повторные попытки, при отключении инстансов SPOT существует небольшой риск. При этом я запускал 64-ядерный Flow чуть менее часа за 1,10 доллара на вычисления, что резко контрастировало с затратами Glue на 16DPU, стоимость которых составляла около 7 долларов в час.

Потоки Metaflow можно легко запланировать

Metaflow отлично подходит для разработки рабочих процессов, но как он подходит для управления производственными сборками? Неудивительно, что отлично. Добавление аннотации @schedule к классу Flow позволит передать ее представление в Step Functions, где оно может быть запущено любым расписанием / правилом AWS Event Bridge. Это дает много возможностей. Хотите запускать его, когда файл записывается в S3? Просто присоедините к правилу событие запуска функций Step - и все.

# Will run every hour
@schedule(hourly=True)
class TestFlow(FlowSpec):
  @step
  def start(self):
    print(“This is the start step!”)
    self.next(self.process)
  @step
  def process(self, inputs):
    print(“This is the process step!”)
    self.next(self.end)
  @step
  def end(self):
    print(“This is the end step!”)
if __name__ == ‘__main__’:
  TestFlow()

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