ByteDance MapReduce-Spark Практика плавной миграции

Аннотация: Эта статья составлена ​​на основе основного доклада «ByteDance MapReduce — практика плавной миграции Spark», произнесенного Вэй Чжунцзя, инженером по инфраструктуре ByteDance, на CommunityOverCode Asia 2023.
С развитием байтового бизнеса компания каждый день запускает в режиме онлайн около 1 миллиона+ заданий Spark. Напротив, каждый день в сети по-прежнему выполняется от 20 000 до 30 000 задач MapReduce. С точки зрения исследований и разработок в области больших данных и пользователей кажется, что есть также ряд проблем в эксплуатации, обслуживании и использовании движка MapReduce. В этом контексте команда Bytedance Batch разработала и внедрила решение для плавной миграции задач MapReduce в Spark. Это решение позволяет пользователям завершить плавную миграцию из MapReduce в Spark, добавив лишь небольшое количество параметров или переменных среды к существующим заданиям. значительное снижение затрат на миграцию и достижение хороших экономических преимуществ.

Предыстория

В прошлом году количество заданий Bytedance Spark резко возросло с 1 миллиона до 1,5 миллиона, а дневные данные Flink Batch увеличились с 200 000 до 250 000, в то время как использование MapReduce было в состоянии медленного роста. снижение, почти с Число упало с 14 000 примерно до 10 000. Судя по описанной выше ситуации использования, MapReduce, как давняя платформа пакетной обработки, которую мы используем, также завершила свою историческую миссию и скоро будет отключена.
Прежде чем официально перевести его в автономный режим, мы сначала собрали статистику по бизнес-стороне и методам обслуживания задач типа MapReduce.
Круговая диаграмма слева показывает статистику пропорций бизнес-стороны. Наибольшую долю занимает задание Hadoop Streaming, на которое приходится почти 45 % всех вакансий. Вторая по величине доля — задание Druid с 24 %, а третье — задание Дископия с 22%. Причина, по которой Distcopy и Hadoop Streaming здесь не разделены по бизнес-направлениям, заключается в том, что эти два типа заданий используют один и тот же код и могут рассматриваться как одно и то же задание, когда мы рекламируем обновление.
Круговая диаграмма справа представляет собой статистику методов обслуживания. Наибольшую долю составляют «Другие», составляющие 60%. «Другие» означают задания, которые не управляются какой-либо платформой в ByteDance. Это также очень соответствует конкретным характеристикам MapReduce. — это фреймворк с долгой историей. Когда многие задания MapReduce были впервые запущены, даже этих платформ еще не было. Большинство из них были отправлены непосредственно из контейнеров, управляемых самими пользователями, или физических машин, которые можно было подключить к кластеру YARN.
 

Почему нам нужно продвигать миграцию MapReduce на Spark

Есть три причины для отключения MapReduce:
Первая причина заключается в том, что режим работы MapReduce предъявляет слишком высокие требования к пропускной способности вычислительного механизма планирования . В режиме работы MapReduce каждая задача соответствует контейнеру. Когда задача завершится, контейнер будет освобожден. Этот режим работы не является проблемой для YARN, поскольку пропускная способность YARN очень высока. Однако когда мы перенесли наш внутренний бизнес из YARN в кластер K8s, мы обнаружили, что задания MapReduce часто вызывают сигналы тревоги API-сервера, что влияет на стабильность кластера K8s.После завершения задачи MapReduce нам часто приходится подавать заявку на более чем 100 000 POD; при том же масштабе Для задания Spark может потребоваться всего несколько тысяч POD, поскольку внутри задания Spark есть еще один уровень планирования. Контейнер, примененный Spark в качестве исполнителя, не будет запущен после запуска задачи. Вместо этого Spark Framework планирует новые задачи для дальнейшего использования.
Вторая причина заключается в том, что производительность Shuffle в MapReduce очень низкая . MapReduce, используемый для внутреннего использования, имеет версию 2.6, основанную на сообществе. Платформа Netty, на которой основана его реализация Shuffle, создана около десяти лет назад. По сравнению с текущей версией Netty существует значительная разница в версиях. При фактическом использовании вы также обнаружите ее производительность. Сравнение Низкая производительность, а также создаст слишком много подключений на физической машине, что повлияет на ее стабильность.
Третья причина заключается в том, что с точки зрения инженеров-разработчиков у нас есть много внутренних проектов горизонтальной трансформации, таких как только что упомянутая трансформация K8 и адаптация IPV6. Стоимость трансформации фактически такая же, как и у Spark, но количество Задачи MapReduce Сейчас это всего лишь 1% от Spark. Мало того, что рентабельность инвестиций в преобразование очень низкая , так еще и потребуется немало усилий для поддержания сервера истории и службы перемешивания заданий MapReduce без преобразования. Поэтому необходимо способствовать переходу с MapReduce на Spark.

Трудности при обновлении Spark

Прежде всего, доля существующих задач очень низка. В настоящее время существует только более 10 000 рабочих мест в день. Однако абсолютное значение все еще очень велико и включает в себя множество деловых сторон. Многие из них представляют собой задачи, которые выполняются в течение очень долгого времени. долгое время и, возможно, запускался четыре раза. За пять лет было очень трудно стимулировать пользователей к активному обновлению.
Во-вторых, с точки зрения осуществимости, более половины заданий представляют собой задания Hadoop Streaming, включая программы Shell, Python и даже C++.Хотя в Spark есть оператор Pipe, пользователям разрешено переносить существующие задания в Spark Pipe. много работы.
Наконец, когда пользователи помогают начать преобразование, возникает множество других проблем. Например, помимо миграции основной вычислительной логики, существует множество периферийных инструментов, которые необходимо перенести; как следует преобразовать определенные параметры MapReduce в во время процесса миграции? Эквивалентные параметры Spark и способы эквивалентной реализации внедрения переменных среды, от которых зависит сценарий задания Hadoop Streaming в Spark. Если эти проблемы оставить на усмотрение пользователя, не только рабочая нагрузка будет большой, но и сбой ставка также будет высокой.
 

Общая программа

Цели дизайна

Вышеупомянутое разобрало текущую ситуацию, мотивы и трудности.Основываясь на приведенной выше информации, цели перед обновлением:
  • Это не позволяет пользователям вносить изменения на уровне кода и позволяет пользователям завершить обновление вообще без перемещения, только добавив некоторые параметры задания.
  • Необходимо поддерживать различные типы заданий, включая Hadoop Streaming, Distcp и задания, написанные обычными пользователями на Java. Среди них Hadoop Streaming использует старый API MapReduce, а Distcp использует новый API. Это означает, что наш план обновления должен поддерживать все задания MapReduce.
 

Демонтаж решения

Общий план демонтажа в основном делится на четыре части:
  • Адаптация вычислительного процесса в основном включает в себя согласование вычислительной логики Mapreduce и вычислительной логики Spark.
  • Адаптация конфигурации помогает пользователям автоматически преобразовывать параметры Mapreduce в параметры Spark.
  • Адаптация на стороне отправки — это ключевой момент для действительно плавного перехода, чтобы пользователи могли завершить обновление, не изменяя свои команды отправки.
  • Сотрудничайте с инструментами , которые помогут пользователям проверить правильность данных.
 

Адаптация вычислительного процесса

Скриншот взят из статьи (https://static.googleusercontent.com/media/research.google.com/zh-CN//archive/mapreduce-osdi04.pdf). Классический процесс MapReduce разделен на пять этапов:
Первый шаг — обработать входные данные и затем сегментировать их; второй шаг — запустить код карты, предоставленный пользователем; третий шаг — выполнить перемешивание; четвертый шаг — запустить код сокращения, предоставленный пользователем; пятый шаг — преобразование кода сокращения в. Результаты обработки кода записываются в файловую систему HDFS. Фактически, существует еще одно очень распространенное использование MapReduce — «Только карта», то есть использование без двух шагов в середине рисунка ниже.
Студенты, знакомые со Spark, должны знать, что весь процесс MapReduce можно понимать как подмножество Spark или даже как задачу Spark для конкретного процесса логических вычислений. На рисунке мы указали псевдокод, который полностью соответствует всему процессу MapReduce. процесс.процесс.
Первый шаг — создать Hadoop RDD, поскольку сам Hadoop RDD опирается на собственный код входного формата Hadoop, поэтому его можно полностью адаптировать; второй шаг — вызвать оператор Map Spark, а затем в операторе Map Spark вызвать пользовательскую функцию Map; в третий шаг, для универсальности миграции, используйте метод RepartitionAndSortWithinPartitions единообразно. Этот метод полностью соответствует процессу Shuffle в MapReduce; четвертый шаг использует оператор Map для выполнения кода сокращения, предоставленного пользователем; пятый шаг, SaveAsHadoopFile, соответствует последней хранимой процедуре в Mapreduce.
Вышеупомянутая идея на самом деле является общепринятой идеей при обновлении пользователей с MapReduce на Spark. Однако, если мы хотим разработать общее решение для обновления, недостаточно просто использовать операторы Spark для отображения процесса вычислений MapReduce. Анализируя фреймворки MapReduce и Spark, мы обнаружили:
Самый нижний уровень один и тот же, оба должны полагаться на планировщик ресурсов: YARN или K8s. Функции верхнего уровня одинаковы или близки, но реализация совершенно разная.Например, имена в MapReduce называются InputFormat и OutputFormat, которые в Spark называются HadoopRDD saveAsHadoopFile, счетчик в Mareduce называется counter, что соответствует к аккумулятору в Spark. Другие функции, включая Shuffle, планирование ресурсов, историю и спекулятивное выполнение, согласованы, но реализации также различаются, поэтому нам нужно заменить реализацию в MapReduce на Spark.
Верхний уровень, упомянутый в цели проектирования, - уровень реализации должен быть полностью неизменен. Розовый уровень, как показано выше, не может работать непосредственно на базе Spark, поэтому мы добавляем промежуточный уровень для адаптации к пользовательскому коду, а вычислительный интерфейс Spark использует MapRunner. и уменьшитьRunner для адаптации методов Map и Редюсер в Hadoop, чтобы оператор Map Spark мог запускать Mapper и Редюсер. Мы адаптируем поведение пользователя при вызове счетчика через адаптер счетчика. Когда пользователь увеличивает число через интерфейс счетчика, оно будет преобразовано в вызов аккумулятора к Spark. Также существует соответствующий переводчик Couf для конфигурации. То есть при отправке задачи для пользователя генерируется конфигурация Hadoop и переводится с помощью этого транслятора в соответствующие параметры Spark. Это адаптация всего вычислительного процесса. Благодаря этой адаптации общая логика может использоваться для непосредственного использования задания Spark для запуска пользовательского кода.

Адаптация конфигурации

Конфигурации можно в основном разделить на три категории: конфигурации, требующие трансляции, конфигурации, которые напрямую прозрачно передаются, и конфигурации, которые необходимо игнорировать.
В первом типе конфигурации, которую необходимо преобразовать, например параметры класса ресурса задания, MapReduce и Spark должны сообщить платформе ресурсов, какой тип контейнера мне нужен для обработки данных, но параметры, которые они используют, разные. при отправке задания. , параметры необходимо перевести. В таблице также указаны переменные среды, загруженные файлы, количество одновременных заданий и т. д. Все эти параметры необходимо перевести, как указано выше.
Вторая категория — это конфигурация, требующая прямой прозрачной передачи, поскольку Spark должен полагаться на множество классов в Hadoop, и многие из этих классов также необходимо настроить. h adoop . Spark . Здесь мы можем напрямую прозрачно передавать , добавляя префикс во время трансляции .
Третья категория — это конфигурации, которые следует игнорировать. Это функции, доступные в MapReduce, но не доступные в Spark. В этом случае мы поместим это в руководство пользователя и сообщим пользователям, что эта функция не поддерживается.

Адаптация на стороне подачи

Ради удобства пользователей мы надеемся, что отправленные пользователями сценарии вообще не нужно изменять.Задания по-прежнему отправляются с помощью Hadoop, и их не нужно менять на Spark Submit. Поэтому в реализации мы помещаем патч в Hadoop. Когда задание MapReduce отправляется, программа-отправитель идентифицирует конкретный параметр или переменную среды. После идентификации функция преобразования конфигурации, которую мы только что упомянули, будет использоваться для этого объекта JobConf, который выполняет настройку. После завершения перевода будет сгенерирована соответствующая команда отправки Spark для запуска дочернего процесса для запуска команды Spark Submit.
В то же время сам MapReduce имеет функцию определения текущего статуса задания посредством постоянного опроса. Поскольку теперь у нас есть дочерний процесс, поведение этого монитора изменилось с запроса статуса определенного идентификатора приложения путем вызова интерфейса RM или AM на запрос состояния дочернего процесса.

Проверка правильности

После выполнения трех вышеуказанных шагов в принципе можно добиться плавной миграции, но прежде чем выходить в Интернет, мы рекомендуем пользователям выполнить двойную проверку. столкнулся с проблемой здесьОдна проблема заключается в том, что для разных типов данных необходимо использовать два метода сравнения.Для большинства выходных форматов контрольную сумму можно сравнивать напрямую, но для небольшого количества выходных форматов необходимо использовать соответствующий входной считыватель для строки построчное сравнение, потому что некоторые Файл, созданный в формате вывода, будет содержать временные метки или некоторую информацию, относящуюся к пользователю. Файлы, созданные при каждом запуске, могут быть разными. Если мы хотим сравнить в данный момент, нам нужно сгенерировать соответствующие Reader, одна строка Прочитайте файл построчно и сравните его построчно.

Проблемы и решения

Настройки памяти. Соответствие «один к одному» между памятью MapReduce и памятью Spark Executor может в некоторых случаях вызывать OOM.

Как упоминалось в предыдущем разделе, мы выполним параллельную трансляцию памяти. Например, исходная задача MapReduce, использующая память 4G, по-прежнему будет использовать память 4G после преобразования в Spark. Однако это приведет к тому, что многие задания вызовут OOM. Основная причина заключается в том, что модели памяти MapReduce и Spark не совсем одинаковы.Кеш по умолчанию для Shuffle Spill в MapReduce составляет 256 МБ, но в Spark памятью фактически управляет диспетчер памяти, а максимальное использование по умолчанию составляет 60% общая память. В то же время сетевой протокол, используемый в MapReduce Shuffle, также отличается от Spark, который обеспечивает больший параллелизм и использует больше памяти.
Чтобы решить эту проблему, мы установили параметр Spark.memory.fraction=0,4 для всех задач параллельной миграции, чтобы уменьшить объем памяти во время Shuffle Spill. В то же время по умолчанию установлено увеличение памяти на 512 МБ на ядро. После применения этой стратегии онлайн, все Ситуация с задачей плавной миграции ООМ решена.

Настройки параллелизма. Задания потоковой передачи Hadoop могут вызывать конфликты имен каталогов при использовании локальных каталогов.

Задания Spark могут поддерживать несколько задач, одновременно выполняемых в контейнере. Некоторые сценарии заданий HadoopStreaming при использовании создают другой каталог в локальном каталоге. При использовании Spark несколько задач, одновременно выполняющих этот каталог, могут вызвать конфликты. В MapReduce каждая задача будет использовать новый контейнер, поэтому соответствующие конфликты не возникнут.
Есть два основных решения этой проблемы:
Сначала добавьте параметр для управления параллелизмом Исполнителя обновленного задания Spark. По умолчанию он напрямую задается пользователю как одноядерный Исполнитель, что эквивалентно одному Исполнителю, выполняющему одну задачу.
Во-вторых, пользователям рекомендуется изменить логику создания каталога. При создании локального каталога не создавайте каталог с фиксированным именем. Вместо этого прочитайте идентификатор задачи в переменной среды и создайте локальный каталог с идентификатором задачи, чтобы избежать конфликты.

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

Многие задачи пакета Jar будут иметь проблемы с загрузкой классов после обновления. Основная причина этой проблемы заключается в том, что загрузчик классов Spark использует собственный ClassLoader, который делит загрузку классов на две категории: одна — загрузчик классов Framework, а другая — загрузчик классов пользовательских классов. Hadoop сам по себе является классом, на который опирается Spark Framework, поэтому он будет загружен с помощью ClassLoader Framework. В то же время код пользовательской задачи также зависит от Hadoop, а некоторые зависимости будут загружены ClassLoader пользовательского кода, поэтому возникнут различные проблемы.Проблема с загрузкой классов.
В большинстве случаев этой проблемы можно избежать, установив параметр Spark.executor.userClassPathFirst=true, чтобы задание Spark по умолчанию сначала загружало класс пользователя. Однако у некоторых пользователей после установки этого параметра могут возникнуть проблемы.Эту ситуацию можно решить, установив вручную значение False.

Проблема функционального выравнивания: функции MapReduce не полностью согласованы со Spark.

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

Проблема с идентификатором попытки задачи — некоторые пользователи полагаются на идентификатор попытки задачи в переменной среды, а в Spark нет соответствующего значения.

Проблема с идентификатором попытки задачи по сути является проблемой выравнивания.Некоторые пользователи, особенно задания HadoopStreaming, полагаются на идентификатор Task Temp ID в переменной среды, и это значение не имеет строгого соответствующего понятия в Spark. Идентификатор попытки задачи — это количество повторов определенной задачи в MapReduce. В Spark ошибка перемешивания приведет к повтору этапа вместо повтора задачи. Во время повтора этапа индекс задачи изменится, поэтому количество повторов не может соответствовать определенный идентификатор раздела.
Мы реализовали приблизительное решение этой проблемы, используя другое глобально увеличивающееся положительное целое число — идентификатор попытки — предоставленное в контексте задачи Spark, чтобы различать разные задачи для решения соответствующей проблемы значения.
 

доход

статистика

Вышеупомянутое решение плавной миграции побуждает пользователей перейти с MapReduce на Spark, и общий эффект очень хороший.Сравнение среднего количества ресурсов приложения MapReduce (до миграции) и количества ресурсов приложения Spark (после миграции) для всех завершенных миграций в последние 30 дней: пример. Как видно из рисунка, количество сохраненных за день приложений ЦП составляет 17 000, увеличение на 60%.То есть после обновления все эти задачи можно запускать с 40% исходных ресурсов, использование памяти может экономить около 20 000 ГБ в день, что составляет около 17% от предыдущего уровня.

Интерпретация

  • Как говорилось выше, это решение для плавной миграции. Пользователь не использует Spark для переписывания задач. Все знают, что Spark — это статический движок, который может лучше использовать память, поэтому преимущества плавной миграции должны быть ниже, чем миграция пользователя вручную. Поскольку текущие преимущества обновления на самом деле не исходят от самого оператора Spark. Фактически, логика обработки пользователя полностью не изменилась, и выполняемый код по-прежнему является кодом MapReduce. Если это задание Hadoop Streaming, выполняемый код по-прежнему является сценарием. написанный пользователем. , поэтому это преимущество не исходит от самого оператора Spark.
  • Преимущества в основном связаны с этапом Shuffle, то есть Spark Shuffle лучше, чем MapReduce Shuffle, начиная с сетевой структуры и заканчивая деталями реализации. Мы также провели несколько углубленных индивидуальных оптимизаций для Spark Shuffle, чтобы улучшить производительность Shuffle. Если вам интересно, вы можете прочитать рекомендации по соответствующей статье. Оптимизация перемешивания сокращает время выполнения заданий. Среднее время отображения некоторых заданий составляет 2 минуты, время сокращения — 5 минут, а время перемешивания часто превышает 10 минут. После обновления Spark время перемешивания можно напрямую уменьшить до 0. Поскольку Shuffle в Spark является асинхронным Shuffle, данные можно вычислять в основном потоке и читать в других потоках, тем самым сокращая время промежуточного блока до миллисекунд.
  • Поскольку доход в основном поступает от Shuffle, улучшение производительности для заданий «Только карта» не очевидно. Для всех заданий «Только карта» и заданий Distcp, которые завершили обновление, объем применения ресурсов не сильно изменился и колеблется между 90 % и 110 %.
  • Причина, по которой доход ЦП значительно выше, чем доход от памяти, заключается в том, что ЦП выполняет полностью параллельную миграцию, но память отличается.Карты и сокращения обычно берут максимальное значение памяти, что приводит к ее пустой трате. Кроме того, чтобы избежать проблемы OOM, вызванной миграцией узких мест, к каждому ядру было добавлено 512 МБ памяти. Таким образом, общий объем памяти приложения увеличился. Однако, поскольку преимущества этапа перемешивания сократили время выполнения задания, Общая память Прибыль по-прежнему положительна.
 
Broadcom объявила о прекращении обновления версии Deepin-IDE существующей партнерской программы VMware, новый внешний вид. WAVE SUMMIT отмечает свое 10-е издание. Вэнь Синьиян получит последнее раскрытие! Чжоу Хунъи: Уроженец Хунмэна обязательно добьется успеха. Полный исходный код GTA 5 стал достоянием общественности. Линус: Я не буду читать код в канун Рождества. Я выпущу новую версию набора инструментов Java Hutool-5.8.24. в следующем году. Давайте вместе жаловаться на Furion. Коммерческая разведка: лодка прошла. Ван Чжуншань, v4.9.1.15 Apple выпускает мультимодальную модель большого языка с открытым исходным кодом Компания Ferret Yakult подтверждает утечку данных 95G
{{o.name}}
{{м.имя}}

рекомендация

отmy.oschina.net/u/5941630/blog/10452051
рекомендация