Configurazione di Spark su YARN

Habr, ciao! Ieri in poi incontro dedicato ad Apache Spark, da parte dei ragazzi di Rambler&Co, ci sono state molte domande da parte dei partecipanti relative alla configurazione di questo strumento. Abbiamo deciso di seguire le sue orme e condividere la nostra esperienza. L'argomento non è facile, quindi ti invitiamo a condividere la tua esperienza nei commenti, forse anche noi capiamo e usiamo qualcosa di sbagliato.

Una piccola introduzione su come utilizziamo Spark. Abbiamo un programma di tre mesi “Specialista dei Big Data”, e durante il secondo modulo i nostri partecipanti lavorano su questo strumento. Di conseguenza, il nostro compito, come organizzatori, è quello di preparare il cluster per l'utilizzo in un caso del genere.

La particolarità del nostro utilizzo è che il numero di persone che lavorano contemporaneamente su Spark può essere pari all'intero gruppo. Ad esempio, in un seminario, quando tutti provano qualcosa contemporaneamente e ripetono dopo il nostro insegnante. E questo non è molto, a volte fino a 40 persone. Probabilmente non sono molte le aziende al mondo che si trovano ad affrontare un simile caso d’uso.

Successivamente, ti dirò come e perché abbiamo selezionato determinati parametri di configurazione.

Cominciamo dall'inizio. Spark dispone di 3 opzioni per l'esecuzione su un cluster: autonomo, utilizzando Mesos e utilizzando YARN. Abbiamo deciso di scegliere la terza opzione perché per noi aveva senso. Abbiamo già un cluster Hadoop. I nostri partecipanti conoscono già bene la sua architettura. Usiamo YARN.

spark.master=yarn

Ancora più interessante. Ognuna di queste 3 opzioni di distribuzione ha 2 opzioni di distribuzione: client e cluster. Basato documentazione e vari collegamenti su Internet, possiamo concludere che il client è adatto per il lavoro interattivo, ad esempio tramite jupyter notebook, e il cluster è più adatto per soluzioni di produzione. Nel nostro caso eravamo interessati al lavoro interattivo, quindi:

spark.deploy-mode=client

In generale da ora in poi Spark funzionerà in qualche modo su YARN, ma questo non ci bastava. Poiché abbiamo un programma sui big data, a volte i partecipanti non ne avevano abbastanza di ciò che è stato ottenuto nell'ambito di una suddivisione uniforme delle risorse. E poi abbiamo scoperto una cosa interessante: l'allocazione dinamica delle risorse. In breve, il punto è questo: se hai un compito difficile e il cluster è libero (ad esempio al mattino), utilizzando questa opzione Spark può darti risorse aggiuntive. Lì la necessità viene calcolata secondo una formula astuta. Non entreremo nei dettagli: funziona bene.

spark.dynamicAllocation.enabled=true

Abbiamo impostato questo parametro e all'avvio Spark si è bloccato e non si è avviato. Esatto, perché dovevo leggerlo documentazione più attentamente. Si precisa che affinché tutto vada bene è necessario abilitare anche un parametro aggiuntivo.

spark.shuffle.service.enabled=true

Perché è necessario? Quando il nostro lavoro non richiederà più così tante risorse, Spark dovrebbe restituirle al pool comune. La fase che richiede più tempo in quasi tutte le attività di MapReduce è la fase Shuffle. Questo parametro consente di salvare i dati generati in questa fase e rilasciare di conseguenza gli esecutori. E l'esecutore è il processo che calcola tutto sul lavoratore. Ha un certo numero di core del processore e una certa quantità di memoria.

Questo parametro è stato aggiunto. Tutto sembrava funzionare. È diventato evidente che ai partecipanti venivano effettivamente fornite più risorse quando ne avevano bisogno. Ma è sorto un altro problema: a un certo punto anche altri partecipanti si sono svegliati e volevano usare Spark, ma lì era tutto occupato ed erano insoddisfatti. Possono essere compresi. Abbiamo iniziato a guardare la documentazione. Si è scoperto che esistono numerosi altri parametri che possono essere utilizzati per influenzare il processo. Ad esempio, se l'esecutore è in modalità standby, dopo quanto tempo è possibile prelevarne le risorse?

spark.dynamicAllocation.executorIdleTimeout=120s

Nel nostro caso, se i tuoi esecutori testamentari non fanno nulla per due minuti, restituiscili alla riserva comune. Ma questo parametro non era sempre sufficiente. Era chiaro che la persona non faceva nulla da molto tempo e le risorse non venivano liberate. Si è scoperto che esiste anche un parametro speciale: dopo quanto tempo selezionare gli esecutori che contengono dati memorizzati nella cache. Per impostazione predefinita, questo parametro era infinito! L'abbiamo corretto.

spark.dynamicAllocation.cachedExecutorIdleTimeout=600s

Cioè, se i tuoi esecutori non fanno nulla per 5 minuti, consegnali al pool comune. In questa modalità, la velocità di rilascio ed emissione di risorse per un gran numero di utenti è diventata decente. La quantità di malcontento è diminuita. Ma abbiamo deciso di andare oltre e limitare il numero massimo di esecutori per applicazione, essenzialmente per partecipante al programma.

spark.dynamicAllocation.maxExecutors=19

Ora, ovviamente, dall'altra parte ci sono persone insoddisfatte: "il cluster è inattivo e ho solo 19 esecutori", ma cosa puoi fare? Abbiamo bisogno di una sorta di equilibrio corretto. Non puoi rendere tutti felici.

E ancora una piccola storia relativa alle specificità del nostro caso. In qualche modo, diverse persone erano in ritardo per una lezione pratica e per qualche motivo Spark non è iniziata per loro. Abbiamo esaminato la quantità di risorse gratuite: sembra che ci siano. Dovrebbe iniziare la scintilla. Fortunatamente, a quel punto la documentazione era già stata aggiunta da qualche parte nella sottocorteccia e ci siamo ricordati che all'avvio Spark cerca una porta da cui avviare. Se la prima porta dell'intervallo è occupata, si passa a quella successiva in ordine. Se è gratuito, cattura. E c'è un parametro che indica il numero massimo di tentativi per questo. Il valore predefinito è 16. Il numero è inferiore al numero di persone nel nostro gruppo in classe. Di conseguenza, dopo 16 tentativi, Spark si è arreso e ha detto che non potevo iniziare. Abbiamo corretto questa impostazione.

spark.port.maxRetries=50

Successivamente ti parlerò di alcune impostazioni poco legate alle specificità del nostro caso.

Per avviare Spark più velocemente, si consiglia di archiviare la cartella jars situata nella directory home SPARK_HOME e metterla su HDFS. Quindi non perderà tempo a caricare questi jarnik da parte dei lavoratori.

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

Si consiglia inoltre di utilizzare kryo come serializzatore per un funzionamento più rapido. È più ottimizzato di quello predefinito.

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

E c'è anche un problema di vecchia data con Spark che spesso si blocca dalla memoria. Spesso ciò accade nel momento in cui gli operai hanno calcolato tutto e inviano il risultato all'autista. Abbiamo ampliato questo parametro per noi stessi. Per impostazione predefinita è 1 GB, ne abbiamo impostati 3.

spark.driver.maxResultSize=3072

E infine come dessert. Come aggiornare Spark alla versione 2.1 sulla distribuzione HortonWorks - HDP 2.5.3.0. Questa versione di HDP contiene una versione 2.0 preinstallata, ma una volta abbiamo deciso da soli che Spark si sta sviluppando abbastanza attivamente e che ogni nuova versione risolve alcuni bug e fornisce funzionalità aggiuntive, inclusa l'API Python, quindi abbiamo deciso cosa deve essere essere fatto è un aggiornamento.

Scaricata la versione dal sito ufficiale per Hadoop 2.7. Decomprimilo e inseriscilo nella cartella HDP. Abbiamo installato i collegamenti simbolici secondo necessità. Lo lanciamo: non si avvia. Scrive un errore molto strano.

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

Dopo aver cercato su Google, abbiamo scoperto che Spark ha deciso di non aspettare la nascita di Hadoop e ha deciso di utilizzare la nuova versione della maglia. Loro stessi discutono tra loro su questo argomento in JIRA. La soluzione era scaricare versione maglia 1.17.1. Inseriscilo nella cartella jars in SPARK_HOME, comprimilo di nuovo e caricalo su HDFS.

Abbiamo aggirato questo errore, ma ne è emerso uno nuovo e piuttosto snello.

org.apache.spark.SparkException: Yarn application has already ended! It might have been killed or unable to launch application master

Allo stesso tempo, proviamo a eseguire la versione 2.0: è tutto ok. Prova a indovinare cosa sta succedendo. Abbiamo esaminato i log di questa applicazione e abbiamo visto qualcosa del genere:

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

In generale, per qualche motivo hdp.version non si risolveva. Dopo aver cercato su Google, abbiamo trovato una soluzione. Devi andare alle impostazioni YARN in Ambari e aggiungere lì un parametro al sito di filati personalizzato:

hdp.version=2.5.3.0-37

Questa magia ha aiutato e Spark è decollato. Abbiamo testato molti dei nostri laptop Jupyter. Tutto funziona. Siamo pronti per la prima lezione di Spark sabato (domani)!

UPD. Durante la lezione è emerso un altro problema. Ad un certo punto, YARN ha smesso di fornire contenitori per Spark. In YARN è stato necessario correggere il parametro, che di default era 0.2:

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

Cioè, solo il 20% delle risorse ha partecipato alla distribuzione delle risorse. Dopo aver modificato i parametri, abbiamo ricaricato YARN. Il problema è stato risolto e anche il resto dei partecipanti è stato in grado di eseguire il contesto Spark.

Fonte: habr.com

Aggiungi un commento