Ahoj, Habr! Včera na , od chlapců z Rambler&Co, bylo dost otázek od účastníků týkajících se konfigurace tohoto nástroje. Rozhodli jsme se sdílet naše zkušenosti. Téma není jednoduché — proto navrhujeme sdílet zkušenosti také v komentářích, možná se nám také něco nedaří a špatně to chápeme.
Malé úvodní slovo — jak používáme Spark. Máme tří měsíční program , a v druhém modulu naši účastníci pracují s tímto nástrojem. Naším úkolem jako organizátorů je připravit cluster pro použití v rámci tohoto případu.
Specifikum našeho využití spočívá v tom, že počet lidí, kteří pracují na Sparku současně, může odpovídat celé skupině. Například na semináři, kdy všichni současně něco zkouší a opakují za naším lektorem. A to není málo — někdy až 40 lidí. Pravděpodobně není mnoho firem na světě, které čelí takovému scénáři využití.
Dále vám povím, jak a proč jsme vybírali určité parametry konfigurace.
Začněme od začátku. Spark má 3 možnosti práce na clusterech: standalone, s využitím Mesos a s využitím YARN. Rozhodli jsme se zvolit třetí variantu, protože nám přišla logická. Již máme hadoop cluster. Naši účastníci jsou dobře obeznámeni s jeho architekturou. Pojďme používat YARN.
spark.master=yarnA teď to začíná být zajímavější. Každá z těchto 3 variant nasazení má 2 možnosti nasazení: client a cluster. Na základě a různých odkazů na internetu lze říci, že client je vhodný pro interaktivní práci — například přes jupyter notebook, zatímco cluster je více vhodný pro produkční řešení. V našem případě nás zajímala interaktivní práce, proto:
spark.deploy-mode=clientOd tohoto okamžiku bude Spark nějak fungovat na YARN, ale to nám nestačilo. Protože máme program o velkých datech, někdy účastníkům chybělo to, co dostávali z rovnoměrného rozdělení zdrojů. A zde jsme našli zajímavou věc — dynamickou alokaci zdrojů. Jinými slovy, pokud máte náročný úkol a cluster je volný (například ráno), pak pomocí této možnosti vám Spark může poskytnout dodatečné zdroje. Potřeba se vypočítává podle chytré formule. Nejde do podrobností — funguje to dobře.
spark.dynamicAllocation.enabled=trueNastavili jsme tento parametr a při spuštění Spark zahlásil chybu a nespustil se. To bylo správně, protože jsme měli číst pozorněji. Je tam uvedeno, že pro to, aby vše bylo v pořádku, je potřeba také aktivovat další parametr.
spark.shuffle.service.enabled=trueK čemu je to potřeba? Když náš job už nepotřebuje tolik zdrojů, Spark by měl vrátit tyto zdroje do společného poolu. Nejdražší fáze téměř v jakémkoliv úkolu MapReduce je fáze Shuffle. Tento parametr umožňuje uchovávat data, která vznikají v této fázi, a tím uvolňovat executory. A executor je proces, který na workeru všechno počítá. Má určité množství procesorových jader a určité množství paměti.
Přidali jsme tento parametr. Všechno se zdálo fungovat. Bylo znatelné, že účastníkům se skutečně začalo poskytovat více zdrojů, když je potřebovali. Ale objevila se jiná problém — v určitém okamžiku se jiní účastníci probudili a také chtěli používat Spark, ale všechno bylo obsazené, a byli nespokojeni. Můžeme je pochopit. Začali jsme se dívat do dokumentace. Ukázalo se, že existuje ještě určité množství parametrů, kterými lze ovlivnit proces. Například, pokud je executor v režimu čekání — jak dlouho lze čekat, než si vezmete zdroje zpět?
spark.dynamicAllocation.executorIdleTimeout=120sV našem případě — pokud vaše executors nic nedělají po dobu dvou minut, vraťte je, prosím, zpět do společného poolu. Ale ani tento parametr často nestačil. Bylo vidět, že člověk už dlouho nic nedělá, a zdroje se neuvolňují. Ukázalo se, že existuje ještě speciální parametr — po uplynutí určité doby vybírat executors, které obsahují vyrovnávací data. Ve výchozím nastavení byl tento parametr nastaven na — nekonečno! Opraveno.
spark.dynamicAllocation.cachedExecutorIdleTimeout=600sTo znamená, že pokud vaše executors nic nedělají po dobu 5 minut, vraťte je do společného poolu. V tomto režimu se rychlost uvolňování a poskytování zdrojů pro velké množství uživatelů stala přijatelnou. Počet nespokojených se snížil. Ale rozhodli jsme se jít dál a omezit maximální počet executors pro jednu aplikaci — v podstatě pro jednoho účastníka programu.
spark.dynamicAllocation.maxExecutors=19Teď se samozřejmě objevili nespokojení z druhé strany — „klastr stojí, a já mám jen 19 executors”, ale co dělat — je potřeba nějaký správný balanc. Není možné, aby byli všichni šťastní.
A ještě jeden malý příběh, který souvisí se specifikou našeho případu. Na jedno praktické cvičení přišlo několik lidí pozdě, a jejich Spark z nějakého důvodu nenaběhl. Podívali jsme se na počet volných zdrojů — vypadalo to, že je jich dost. Spark by měl naběhnout. Naštěstí, že v té době se nám už dokumentace zapsala do paměti, a vzpomněli jsme si, že při spuštění Spark hledá port, na kterém by měl naběhnout. Pokud je první port z rozsahu obsazený, přechází na další v pořadí. Pokud je volný, tak ho ukořistí. A existuje parametr, který uvádí maximální počet pokusů pro toto. Ve výchozím nastavení — je to 16. Číslo menší než počet lidí ve naší skupině na cvičení. Podle toho, po 16 pokusech Spark to vzdal a říkal, že nemohu naběhnout. Tento parametr jsme opravili.
spark.port.maxRetries=50Dále povím o některých nastaveních, které již nejsou příliš spojeny se specifikou našeho případu.
Pro rychlejší spuštění Sparku je doporučeno zarchivovat složku jars, která se nachází v domácím adresáři SPARK_HOME, a umístit ji na HDFS. Pak nebude trávit čas načítáním těchto jar souborů po workerech.
spark.yarn.archive=hdfs:///tmp/spark-archive.zipTaké pro rychlejší provoz se doporučuje jako serializátor používat kryo. Je lépe optimalizovaný než ten, který je nastaven jako výchozí.
spark.serializer=org.apache.spark.serializer.KryoSerializerA je zde také dlouhodobý problém Spark, že často padá z důvodu nedostatku paměti. To se často stává právě ve chvíli, kdy pracovníci všechno vypočítali a posílají výsledek na řidiče. Tento parametr jsme si nastavili větší. Ve výchozím nastavení je 1 GB, my jsme to dali na 3.
spark.driver.maxResultSize=3072A nakonec, jako dezert. Jak aktualizovat Spark na verzi 2.1 na distribuci HortonWorks – HDP 2.5.3.0. Tato verze HDP obsahuje předinstalovanou verzi 2.0, ale jednou jsme se rozhodli, že Spark se vyvíjí docela aktivně a každá nová verze opravuje různé chyby a také přináší nové možnosti, včetně pro python API, takže jsme se rozhodli provést aktualizaci.
Stáhli jsme verzi z oficiálních stránek pro Hadoop 2.7. Rozbalili jsme ji, umístili do složky s HDP. Upravili jsme symlink podle potřeby. Spustili jsme to — nenastartovalo to. Píše to velmi nejasnou chybu.
java.lang.NoClassDefFoundError: com/sun/jersey/api/client/config/ClientConfigPo vyhledání na Googlu jsme zjistili, že Spark se rozhodl nečekat, až Hadoop vydá novou verzi a rozhodli se použít novou verzi jersey. V JIRA se o tom navzájem hádají. . Umístit to do složky jars v SPARK_HOME, znovu zipnout a nahrát na HDFS.
Tuto chybu jsme obejšli, ale objevila se nová a docela záhadná.
org.apache.spark.SparkException: Yarn application has already ended! It might have been killed or unable to launch application masterPřitom zkoušíme spustit verzi 2.0 — všechno je v pořádku. Zkus uhádnout, v čem je problém. Podívali jsme se do logů této aplikace a viděli něco takového:
/usr/hdp/${hdp.version}/hadoop/lib/hadoop-lzo-0.6.0.${hdp.version}.jarCelkově, z nějakého důvodu se hdp.version neresolvovala. Po vyhledání jsme našli řešení. Je potřeba v Ambari jít do nastavení YARN a přidat tam parametr do custom yarn-site:
hdp.version=2.5.3.0-37Tato magie pomohla a Spark vzlétl. Otestovali jsme několik našich jupyter notebooků. Všechno funguje. Na první lekci o Sparku v sobotu (už zítra) jsme připraveni!
UPD. Na lekci se objevila další problém. V určitém okamžiku YARN přestal vydávat kontejnery pro Spark. V YARN bylo potřeba opravit parametr, který byl ve výchozím nastavení 0.2:
yarn.scheduler.capacity.maximum-am-resource-percent=0.8 To znamená, že pouze 20 % zdrojů se podílelo na distribuci zdrojů. Změnili jsme parametry a restartovali YARN. Problém byl vyřešen a ostatní účastníci také mohli spustit spark context.
Zdroj: habr.com
