Конфигуриране на 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 е по-подходящ за production решения. В нашия случай ни интересуваше интерактивната работа, така че:

spark.deploy-mode=client

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

spark.dynamicAllocation.enabled=true

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

spark.shuffle.service.enabled=true

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

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

spark.dynamicAllocation.executorIdleTimeout=120s

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

spark.dynamicAllocation.cachedExecutorIdleTimeout=600s

Тоест ако в продължение на 5 минути вашите executors не правят нищо, върнете ги в общия пул. В този режим скоростта на освобождаване и отпускане на ресурси за голям брой потребители стана задоволителна. Броят на недоволните намаля.

spark.dynamicAllocation.maxExecutors=19

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

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

spark.port.maxRetries=50

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

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

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 application has already ended! It might have been killed or unable to launch application 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