5 вопросов про масштабирование Spark-приложений

Автор Категория ,
5 вопросов про масштабирование Spark-приложений

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

Лебедь, рак и щука: аппаратные ресурсы, JVM и процессы Apache Spark

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

  • драйвер (driver), который выполняет весь Java-код;
  • один или несколько процессов-исполнителей (executor), выполняющие код, определенный внутри преобразований и действий со структурами данных (RDD, датасет, датафрейм).

Это означает, что в кластере будут одновременно работать разные JVM и часто на разных узлах: по одной для драйвера и по одной для каждого исполнителя. Обычно все, что связано с JVM, потребляет достаточно много памяти и активно использует ЦП. Поэтому неудивительно, что именно про эти ресурсы следует думать дата-инженеру и разработчику Spark-приложений при их масштабировании.

Включать ли динамическое распределение ресурсов (dynamic allocation)?

Свойство spark.dynamicAllocation.enabled, установленное в значение True или False, показывает, следует ли использовать динамическое выделение ресурсов, при котором количество исполнителей, зарегистрированных в приложении, увеличивается или уменьшается в зависимости от рабочей нагрузки. Для этого необходимо установить свойства spark.shuffle.service.enabled или spark.dynamicAllocation.shuffleTracking.enabled. Также они связаны с конфигурациями spark.dynamicAllocation.minExecutors, spark.dynamicAllocation.maxExecutors и spark.dynamicAllocation.initialExecutors и spark.dynamicAllocation.executorAllocationRatio.

Динамическое выделение ресурсов означает, что приложение отказывается от исполнителей, когда они не используются, и добавляет новые, если они нужны. При этом невозможно точно предсказать, будет ли исполнитель, который будет скоро удален, запускать задачу в ближайшем будущем, или будет ли новый добавленный исполнитель бездействовать, необходим набор правил для определения, когда удалять и добавлять исполнителей. Если свойство dynamic allocation не включено, приложение будет удерживать все выделенные ресурсы, даже если они не требуются для обработки. Поэтому рекомендуется устанавливать свойство spark.dynamicAllocation.enabled, в значение True.

Каков оптимальный объем памяти для драйверов?

Подробно о видах памяти Apache Spark и конфигурациях его JVM-настроек мы писали здесь. Чтобы не повторяться, уточним, что чаще всего на практике настройку памяти драйвера начинают с 4 ГБ, что в большинстве случаев хватает для вычислений и хранения промежуточных данных. Нецелесообразно выделение драйверу слишком больших размеров RAM, например, 20 ГБ, т.к. памяти является достаточно дорогим ресурсом и важно утилизировать еей эффективно, в т.ч. с экономической точки зрения. Если типовых 4 ГБ недостаточно из-за выполнения множества заданий на одном драйвере, следует рассчитать нужный объем с помощью эмпирических формул, которые мы рассматривали в этой статье.

Какие ресурсы выделить исполнителям Spark: настраиваем ядра ЦП и память

Для обработки небольших объемов данных порядка нескольких ГБ, достаточно одного ядра ЦП. Количество ядер определяет количество параллельных задач на исполнителе. Увеличивая количество ядер, следует также увеличить память исполнителя.

Если преобразования данных включают большие shuffle-операции, такие как соединения и группировки, целесообразно выделить более одного ядра на каждого исполнителя. Более детально про практическую настройку и расчет оптимального количества ядер ЦП для Spark-приложений мы писали здесь и здесь.

Аналогично настройке драйвера, выделение памяти исполнителю можно начать с 4 ГБ. Этот объем должен соответствовать максимальному размеру обрабатываемых данных или доступному объему памяти, который не влияет на работу других приложений. Например, размер доступного пула составляет 100 ГБ, а размер данных – 10 ГБ. Тогда следует выделить 4 ГБ на одного исполнителя, если всего их 10. Как уже было отмечено выше, количество памяти коррелирует с ядрами ЦП, поэтому не стоит выделять 20 ГБ памяти для исполнителя при 1 ядре ЦП.

Что касается оптимальных размеров служебной памяти, то обычно необходимость их ручной настройки возникает при высокой продолжительности сборки мусора на уровне задачи, что мы разбирали в недавнем материале. Рекомендуется начать с 1 ГБ.

Увеличивать ли размер памяти из-за нехватки heap space?

Если задание Spark завершается неудачно из-за ошибки OutOfMemory, следует сперва определить, какой именно компонент приложения вышел из строя с ошибкой размера кучи из памяти. В частности, при нерабочем драйвере нужно проверить, почему он получает объем данных, который не может поместиться в памяти. Причиной этого могут быть широковещательные соединения или функции сбора данных.

Аналогичную проверку нужно сделать, когда проблема связана с какой-то задачей на исполнителе. Например, если каждая задача должна была обработать 2 ГБ данных, но исполнитель работает только с 1 ГБ памяти, имеет смысл увеличить количество случайных разделов, чтобы уменьшить объем данных, получаемых каждой задачей. Вместе с этим также обычно увеличивают объем памяти, который действительно требуется для обработки ввода, получаемого задачей.

Когда использовать свойство shuffle partitions?

Перемешивание разделов spark.sql.shuffle.partitions управляет параллелизмом в Apache Spark. После того, как данные были прочитаны, первая shuffle-операция (соединение или группировка) будет использовать это свойство для создания количества разделов, по умолчанию равное 200. Именно поэтому видно, что многие этапы выполняются с 200 задачами. Значение по умолчанию может быть увеличено или уменьшено в зависимости от размера обрабатываемого входного задания. Примечательно, что для структурированной потоковой передачи (Spark Structured Streaming) эту конфигурацию нельзя изменить между перезапусками SQL-запроса с одной и той же контрольной точки.

Узнайте больше про администрирование и эксплуатацию Apache Spark для разработки распределенных приложений и аналитики больших данных на специализированных курсах в нашем лицензированном учебном центре обучения и повышения квалификации для разработчиков, менеджеров, архитекторов, инженеров, администраторов, Data Scientist’ов и аналитиков Big Data в Москве:

Источники

  1. https://nellaivijay.medium.com/how-to-size-spark-application-ff2a9d487926
  2. https://spark.apache.org/docs/latest/configuration.html