Конфигуриране на Spark в YARN

Здравей, Хабр! Вчера на митинг, посветен на Apache Spark, от екипа на Rambler&Co, имаше доста въпроси от участниците, свързани с конфигуриране на този инструмент. Решихме да споделим нашия опит по този въпрос. Темата е сложна — затова предлагаме да споделяте опит и в коментарите, може би ние също нещо не разбираме или използваме неправилно.

Няколко думи за нас — как използваме Spark. Имате тримесечна програма „Специалист по големи данни“, а през целия втори модул участниците работят с този инструмент. Следователно, нашата задача, като организатори, е да подготвим клъстер за ползване в такъв случай.

Характеристиката на нашето използване е, че броят на хората, които работят едновременно със Spark, може да е равен на цялата група. Например, по време на семинар, когато всички едновременно опитват и повтарят след нашия преподавател. А това понякога е около 40 души. Вероятно не много компании по света се сблъскват с такъв сценарий на използване.

По-нататък ще разкажа как и защо подбирахме определени параметри на конфигурацията.

Нека започнем от самото начало. Spark предлага 3 опции за работа на клъстера: standalone, с помощта на Mesos и с помощта на YARN. Ние решихме да изберем третия вариант, защото ни се стори логичен. Вече имаме Hadoop клъстер. Участниците ни вече добре познават неговата архитектура. Нека използваме YARN.

spark.master=yarn

Следващото е още по-интересно. Всеки от тези 3 варианта на разгръщане има 2 варианта на внедряване: client и cluster. На базата на документацията и различни източници в интернет, можем да заключим, че client е подходящ за интерактивна работа — например, през Jupyter Notebook, а cluster е по-подходящ за продукционни решения. В нашия случай ни интересуваше интерактивното работа, така че:

spark.deploy-mode=client

Всъщност, от този момент нататък Spark вече ще работи по някакъв начин на YARN, но за нас това не беше достатъчно. Тъй като програмата ни е за големи данни, понякога на участниците не им достигаха ресурсите, които получават в рамките на равномерно разпределяне на ресурсите. И тук открихме нещо интересно — динамично разпределение на ресурсите. Накратко, същността е следната: ако имате трудна задача и клъстерът е свободен (например, сутрин), то с помощта на тази опция Spark може да ви предостави допълнителни ресурси. Необходимостта се изчислява по хитра формула. Няма да навлизаме в подробности — тя работи добре.

spark.dynamicAllocation.enabled=true

Зададохме този параметър и при стартиране Spark се оплака и не стартира. Правилно, защото трябваше да прочетем документацията внимателно. Там е указано, че за да е всичко наред, трябва да включим и допълнителен параметър.

spark.shuffle.service.enabled=true

За какво е нужен? Когато нашата задача вече не изисква толкова много ресурси, Spark трябва да ги върне в общия пул. Най-трудоемкият етап почти във всяка задача MapReduce е етапът Shuffle. Този параметър позволява запазването на данните, които се генерират в този етап и съответно освобождаване на executors. А executor — това е процесът, който изчислява всичко на работника. Той разполага с определено количество процесорни ядра и определено количество памет.

Добавихме този параметър. Всичко изглежда заработи. Забеляза се, че участниците наистина получават повече ресурси, когато им е необходимо. Но възникна друг проблем — в даден момент други участници се събудиха и също искаха да използват Spark, а там всичко беше заето и те бяха недоволни. Могат да се разберат. Започнахме да гледаме в документацията. Оказа се, че има още няколко параметъра, с които може да се влияе на процеса. Например, ако executor е в режим на изчакване — след колко време можем да отнемем ресурсите му?

spark.dynamicAllocation.executorIdleTimeout=120s

В нашия случай — ако вашите executors не правят нищо в продължение на две минути, моля, върнете ги в общия пул. Но и този параметър не винаги беше достатъчен. Беше ясно, че човекът отдавна не е активен, а ресурсите не се освобождават. Оказа се, че има и специален параметър — след колко време да се отнемат executors с кеширани данни. По подразбиране този параметър беше зададен на — безкрайност! Ние го коригирахме.

spark.dynamicAllocation.cachedExecutorIdleTimeout=600s

Тоест, ако в продължение на 5 минути вашите executors не извършват никаква дейност, върнете ги в общия пул. При такъв режим скоростта на освобождаване и разпределение на ресурсите за голям брой потребители стана приемлива. Неприязънта намаля. Но решихме да отидем още по-далеч и да ограничим максималния брой executors на едно приложение — по същество на един участник в програмата.

spark.dynamicAllocation.maxExecutors=19

Сега, разбира се, се появиха недоволни от друга страна — “кластера стои без работа, а аз имам само 19 executors”, но какво да се прави — нужен е определен баланс. Невъзможно е да направим всички щастливи.

И още една малка история, свързана със спецификата на нашия случай. Един път на практическо занимание закъсняха няколко човека и при тях Spark по някаква причина не стартира. Погледнахме на количеството свободни ресурси — изглежда, че има. Spark трябваше да стартира. За щастие, в този момент документацията вече беше запомнена и си спомнихме, че при стартиране Spark търси порт, на който да започне. Ако първият порт от диапазона е зает, той преминава на следващия по ред. Ако е свободен, той го заема. Има и параметър, който указва максималния брой опити за това. По подразбиране — 16. Това е по-малко от хората в нашата група на занятието. Следователно, след 16 опити Spark се отказваше и казваше, че не може да стартира. Ние коригирахме този параметър.

spark.port.maxRetries=50

Сега ще разкажа за някои настройки, вече не толкова свързани със спецификата на нашия случай.

За по-бърз старт на Spark, се препоръчва папката jars, която се намира в домашната директория на SPARK_HOME, да бъде архивирана и поставена на HDFS. Тогава той няма да губи време за зареждане на тези .jar файлове през работниците.

spark.yarn.archive=hdfs:///tmp/spark-archive.zip

Също така, за по-бърза работа, се препоръчва да се използва kryo като сериализатор. Той е по-оптимизиран от този по подразбиране.

spark.serializer=org.apache.spark.serializer.KryoSerializer

Има и стара проблема със Spark, че той често се срива заради паметта. Често това се случва в момента, когато работниците са довършили изчисленията и изпращат резултата на драйвера. Ние увеличихме този параметър. По подразбиране е 1Гб, ние го направихме 3.

spark.driver.maxResultSize=3072

И накрая, за десерт. Как да актуализираме Spark до версия 2.1 на дистрибуцията на HortonWorks — HDP 2.5.3.0. Тази версия на HDP съдържа предварително инсталирана версия 2.0, но ние решихме, че Spark се развива активно и всяка нова версия поправя някои бъгове и предоставя допълнителни възможности, включително и за python API, затова решихме, че е необходимо да направим актуализация.

Свалихме версия от официалния сайт за Hadoop 2.7. Разархивирахме, прехвърлихме в папката с HDP. Сложихме симлинкове както трябва. Започваме — не стартира. Показва много неразбираема грешка.

java.lang.NoClassDefFoundError: com/sun/jersey/api/client/config/ClientConfig

След като потърсихме в интернет, установихме, че Spark е решил да не чака Hadoop и е започнал да използва нова версия на jersey. Те сами спорят по този въпрос в JIRA. Решението беше — да свалим jersey версия 1.17.1. Да го сложим в папката jars в SPARK_HOME, отново да направим zip и да качим на HDFS.

Тази грешка обиколихме, но се появи нова, доста неясна.

org.apache.spark.SparkException: Yarn приложението вече е приключило! То може да е било убито или не е успяло да стартира master на приложението

В същото време опитваме да стартираме версия 2.0 — всичко е наред. Опитай да се досетиш каква е причината. Проверихме логовете на това приложение и видяхме нещо такова:

/usr/hdp/${hdp.version}/hadoop/lib/hadoop-lzo-0.6.0.${hdp.version}.jar

В крайна сметка, по някакви причини hdp.version не беше разрешена. След като потърсихме решение, установихме, че трябва в Ambari да отидем в настройките на YARN и да добавим параметър в custom yarn-site:

hdp.version=2.5.3.0-37

Тази магия помогна и Spark заработи. Изпробвахме няколко от нашите jupyter-ноутбуци. Всичко работи. Готови сме за първото занятие по Spark в събота (вече утре)!

UPD. По време на занятието се появи още една проблема. В някакъв момент YARN спря да предоставя контейнери за Spark. Нужно беше да се поправи параметърът в YARN, който по подразбиране беше 0.2:

yarn.scheduler.capacity.maximum-am-resource-percent=0.8

Тоест само 20% от ресурсите участваха в разпределението на ресурсите. Променяйки параметрите, презаредихме YARN. Проблемът беше решен и другите участници също успяха да стартират Spark контекста.

Източник: habr.com

Купете надежден хостинг за сайтове с защита от DDoS, VPS VDS сървъри 🔥 Купете надежден хостинг за сайтове с защита от DDoS, VPS VDS сървъри | ProHoster