Habr, zdravo! Jučer na , od ekipe iz Rambler&Co, bilo je dosta pitanja od učesnika vezanih za konfiguraciju ovog alata. Odlučili smo da podelimo svoje iskustvo. Tema nije jednostavna — stoga vas pozivamo da podelite svoja iskustva i u komentarima, možda i mi nešto ne razumemo ili koristimo pogrešno.
Kratko uvodno — kako mi koristimo Spark. Imamo trojmesečni program , a tokom drugog modula naši učesnici rade sa ovim alatom. Shodno tome, naš zadatak kao organizatora je da pripremimo klaster za korišćenje u okviru takvog projekta.
Karakteristika naše upotrebe leži u tome da broj ljudi koji istovremeno rade na Sparku može biti jednak celoj grupi. Na primer, na seminaru, kada svi istovremeno nešto probavaju i ponavljaju za našim predavačem. A to je ponekad čak 40 ljudi. Verovatno, nije mnogo kompanija u svetu koje se susreću sa takvim scenarijem korišćenja.
Dalje ću objasniti kako i zašto smo biračili određene parametre konfiguracije.
Počnimo od samog početka. Spark ima 3 načina rada na klasteru: standalone, korišćenjem Mesosa i korišćenjem YARN-a. Odlučili smo se za treću opciju, jer nam je bila logična. Već imamo Hadoop klaster. Naši učesnici su dobro upoznati sa njegovom arhitekturom. Hajde da koristimo YARN.
spark.master=yarnDalje postaje zanimljivije. Svaka od ovih 3 opcija implementacije ima 2 načina deploy-a: client i cluster. Na osnovu i različitih linkova na internetu, može se, zaključno reći, da je client pogodna za interaktivni rad — na primer, preko Jupyter notebooka, dok je cluster više orijentisan na produkcijska rešenja. U našem slučaju, zanimala nas je interaktivna upotreba, stoga:
spark.deploy-mode=clientU suštini, od ovog trenutka Spark će nekako raditi na YARN-u, ali to nam nije bilo dovoljno. Budući da imamo program o velikim podacima, ponekad učesnicima nije bilo dovoljno onoga što se dobijalo u okviru ravnomernog raspoređivanja resursa. I ovde smo pronašli zanimljivu stvar — dinamičku alokaciju resursa. U kratkim crtama, osnovna ideja je sledeća: ako imate teški zadatak i klaster je slobodan (na primer, ujutro), tada vam ova opcija može omogućiti da Spark dobije dodatne resurse. Potražnja se računa prema složenoj formuli. Nećemo se upuštati u detalje — ona funkcioniše prilično dobro.
spark.dynamicAllocation.enabled=truePostavili smo ovaj parametar, i prilikom pokretanja Spark je javio grešku i nije se pokrenuo. U pravu je, jer je trebalo više paziti. Tamo piše da da bi sve bilo u redu, potrebno je uključiti još jedan dodatni parametar.
spark.shuffle.service.enabled=trueZašto je to potrebno? Kada našem poslu više nisu potrebni toliki resursi, Spark ih treba vratiti u zajednički pool. Najteža faza u skoro svakoj MapReduce zadatku je faza Shuffle. Ovaj parametar omogućava čuvanje podataka koji se generišu u ovoj fazi i oslobađanje executora. A executor je proces koji na radnoj stanici sve obračunava. Ima određeni broj procesorskih jezgara i određenu količinu memorije.
Dodali smo ovaj parametar. Sve je delovalo da radi. Postalo je primetno da učesnicima zaista više resursa biva dodeljeno kada im je to potrebno. Ali se javio drugi problem — u nekom trenutku i drugi učesnici su se probudili i takođe želeli koristiti Spark, a sve je bilo zauzeto, i nisu bili zadovoljni. Mogu ih razumeti. Počeli smo gledati u dokumentaciju. Tu se ispostavilo da postoji još nekoliko parametara pomoću kojih se može uticati na proces. Na primer, ako je executor u režimu čekanja — koliko vremena treba proći pre nego što se mogu oduzeti resursi?
spark.dynamicAllocation.executorIdleTimeout=120sU našem slučaju — ako vaši executors ništa ne rade unutar dva minuta, molimo vas da ih vratite u zajednički bazen. No, ni taj parametar uvijek nije bio dovoljan. Bilo je očito da osoba već dugo ne radi, a resursi se ne oslobađaju. Ispostavilo se da postoji još jedan specijalni parametar — nakon koliko vremena uzimat će se executors koji sadrže keširane podatke. Prema zadanim postavkama ovaj parametar je bio — beskonačno! Ispravili smo ga.
spark.dynamicAllocation.cachedExecutorIdleTimeout=600sTo jest, ako vaši executors ništa ne rade unutar 5 minuta, vratite ih u zajednički bazen. U tom režimu brzina oslobađanja i dodjele resursa za veliko broj korisnika postala je zadovoljavajuća. Broj nezadovoljnih se smanjio. No, odlučili smo ići dalje i ograničiti maksimalni broj executorsa po aplikaciji — u suštini po jednom sudioniku programa.
spark.dynamicAllocation.maxExecutors=19Sada, naravno, pojavili su se nezadovoljni s druge strane — "klaster stoji, a ja imam samo 19 executorsa", ali što da radimo — potreban je neki ispravan balans. Neće svi biti sretni.
I još jedna mala priča, povezana sa specifičnošću naše situacije. Nekoliko ljudi zakasnilo je na praktičnu nastavu i nije im iz nekog razloga pokrenuo Spark. Pogledali smo na broj slobodnih resursa — činilo se da ih ima. Spark bi trebao pokrenuti. Na sreću, do tog trenutka dokumentacija se već negdje usadila u pamćenje, i sjetili smo se da Spark prilikom pokretanja traži port na kojem će se pokrenuti. Ako je prvi port u rasponu zauzet, prelazi na sljedeći redom. Ako je slobodan, zauzima ga. I tu je parametar koji ukazuje na maksimalan broj pokušaja za to. Prema zadanim postavkama — to je 16. Taj broj je manji od broja ljudi u našoj grupi na nastavi. Stoga je Spark nakon 16 pokušaja odustao i rekao da ne može započeti. Ispravili smo taj parametar.
spark.port.maxRetries=50Dalje ću govoriti o nekim postavkama koje nisu previše povezane sa specifičnošću naše situacije.
Za brži početak Sparka, preporučuje se arhivirati mapu jars koja se nalazi u domaćem direktoriju SPARK_HOME i staviti je na HDFS. Tada neće gubiti vrijeme na učitavanje tih jarova po radnicima.
spark.yarn.archive=hdfs:///tmp/spark-archive.zipTakođer, za brži rad preporučuje se korištenje kryo serializer-a. On je optimiziraniji od onog koji je zadan.
spark.serializer=org.apache.spark.serializer.KryoSerializerPostoji i stari problem sa Spark-om, da često pada zbog nedostatka memorije. To se obično dešava kada radnici završe sve proračune i šalju rezultate na vozač. Mi smo ovaj parametar postavili na veću vrijednost. Zadano je 1GB, mi smo stavili 3.
spark.driver.maxResultSize=3072I na kraju, kao desert. Kako ažurirati Spark na verziju 2.1 na HortonWorks distribuciji — HDP 2.5.3.0. Ova verzija HDP sadrži unaprijed instaliranu verziju 2.0, ali smo nekako jednom odlučili da se Spark aktivno razvija, i svaka nova verzija ispravlja neke greške, plus donosi dodatne mogućnosti, uključujući i za python API, stoga smo odlučili da trebamo napraviti ažuriranje.
Preuzeli smo verziju sa službene stranice za Hadoop 2.7. Raspakirali smo je, stavili u mapu s HDP. Napravili smo simbolične linkove kako treba. Pokrećemo — ne počinje. Ispisuje vrlo nejasnu grešku.
java.lang.NoClassDefFoundError: com/sun/jersey/api/client/config/ClientConfigIstražujući, otkrili smo da Spark nije čekao da Hadoop pokrene novu verziju jersey. Oni se međusobno svađaju oko ovog problema na JIRA. Rješenje je bilo — preuzeti . Staviti to u mapu jars u SPARK_HOME, ponovo zipovati i staviti na HDFS.
Ovu grešku smo zaobišli, ali se pojavila nova i prilično nejasna.
org.apache.spark.SparkException: Yarn aplikacija je već završila! Mogla je biti ubijena ili nije mogla pokrenuti aplikacijski master.Usput, pokušavamo pokrenuti verziju 2.0 — sve je u redu. Pokušaj pogoditi u čemu je problem. Ušli smo u logove ove aplikacije i vidjeli nešto ovako:
/usr/hdp/${hdp.version}/hadoop/lib/hadoop-lzo-0.6.0.${hdp.version}.jarUglavnom, iz nekih razloga hdp.version nije rezolviran. Istražujući, pronašli smo rješenje. Treba ući u Ambari, u postavke YARN-a i dodati parametar u custom yarn-site:
hdp.version=2.5.3.0-37Ova magija je pomogla, i Spark je poletio. Testirali smo nekoliko naših jupyter-notebook-a. Sve radi. Spremni smo za prvi sat o Sparku u subotu (već sutra)!
UPD. Na satu se pojavila još jedna problema. U nekom trenutku YARN je prestao isporučivati kontejnere za Spark. U YARN-u je trebalo ispraviti parametar koji je prema zadanim postavkama bio 0.2:
yarn.scheduler.capacity.maximum-am-resource-percent=0.8 To je značilo da je samo 20% resursa bilo uključeno u raspodelu resursa. Promenili smo postavke i ponovo pokrenuli YARN. Problem je rešen, a ostali učesnici su takođe mogli da pokrenu spark kontekst.
Izvor: habr.com
