Διαμόρφωση Spark στο YARN

Χαμπρ, γεια! Χθες στις συνάντηση αφιερωμένη στον Apache Spark, από τα παιδιά της Rambler&Co, υπήρξαν πολλές ερωτήσεις από τους συμμετέχοντες σχετικά με τη διαμόρφωση αυτού του εργαλείου. Αποφασίσαμε να ακολουθήσουμε τα βήματά του και να μοιραστούμε την εμπειρία μας. Το θέμα δεν είναι εύκολο - γι' αυτό σας προσκαλούμε να μοιραστείτε την εμπειρία σας στα σχόλια, ίσως και εμείς καταλαβαίνουμε και χρησιμοποιούμε κάτι λάθος.

Μια μικρή εισαγωγή στο πώς χρησιμοποιούμε το Spark. Έχουμε ένα τρίμηνο πρόγραμμα «Ειδικός Big Data», και σε όλη τη δεύτερη ενότητα οι συμμετέχοντες μας εργάζονται σε αυτό το όργανο. Ως εκ τούτου, το καθήκον μας, ως διοργανωτές, είναι να προετοιμάσουμε το cluster για χρήση σε μια τέτοια περίπτωση.

Η ιδιαιτερότητα της χρήσης μας είναι ότι ο αριθμός των ατόμων που εργάζονται ταυτόχρονα στο Spark μπορεί να είναι ίσος με ολόκληρη την ομάδα. Για παράδειγμα, σε ένα σεμινάριο, όταν όλοι δοκιμάζουν κάτι ταυτόχρονα και επαναλαμβάνουν μετά τον δάσκαλό μας. Και αυτό δεν είναι πολύ - μερικές φορές έως και 40 άτομα. Μάλλον δεν υπάρχουν πολλές εταιρείες στον κόσμο που αντιμετωπίζουν τέτοια περίπτωση χρήσης.

Στη συνέχεια, θα σας πω πώς και γιατί επιλέξαμε ορισμένες παραμέτρους διαμόρφωσης.

Ας τα πάρουμε από την αρχή. Το Spark έχει 3 επιλογές για να τρέξει σε ένα σύμπλεγμα: αυτόνομο, χρησιμοποιώντας Mesos και χρήση ΝΗΜΑ. Αποφασίσαμε να επιλέξουμε την τρίτη επιλογή γιατί μας έκανε νόημα. Έχουμε ήδη ένα σύμπλεγμα hadoop. Οι συμμετέχοντες μας γνωρίζουν ήδη καλά την αρχιτεκτονική του. Ας χρησιμοποιήσουμε ΝΗΜΑ.

spark.master=yarn

Ακόμη πιο ενδιαφέρον. Κάθε μία από αυτές τις 3 επιλογές ανάπτυξης έχει 2 επιλογές ανάπτυξης: πελάτη και σύμπλεγμα. Με βάση τεκμηρίωση και διάφορους συνδέσμους στο Διαδίκτυο, μπορούμε να συμπεράνουμε ότι ο πελάτης είναι κατάλληλος για διαδραστική εργασία - για παράδειγμα, μέσω του σημειωματάριου jupyter και το σύμπλεγμα είναι πιο κατάλληλο για λύσεις παραγωγής. Στην περίπτωσή μας, μας ενδιέφερε η διαδραστική εργασία, επομένως:

spark.deploy-mode=client

Γενικά, από εδώ και πέρα ​​το Spark θα δουλεύει με κάποιο τρόπο στο YARN, αλλά αυτό δεν ήταν αρκετό για εμάς. Δεδομένου ότι έχουμε ένα πρόγραμμα για μεγάλα δεδομένα, μερικές φορές οι συμμετέχοντες δεν είχαν αρκετό από αυτό που αποκτήθηκε στο πλαίσιο ενός ομοιόμορφου τεμαχισμού των πόρων. Και μετά βρήκαμε ένα ενδιαφέρον πράγμα - τη δυναμική κατανομή πόρων. Εν ολίγοις, το θέμα είναι το εξής: εάν έχετε μια δύσκολη εργασία και το σύμπλεγμα είναι ελεύθερο (για παράδειγμα, το πρωί), τότε χρησιμοποιώντας αυτήν την επιλογή το Spark μπορεί να σας δώσει επιπλέον πόρους. Η αναγκαιότητα υπολογίζεται εκεί σύμφωνα με έναν πονηρό τύπο. Δεν θα μπούμε σε λεπτομέρειες - λειτουργεί καλά.

spark.dynamicAllocation.enabled=true

Ορίσαμε αυτήν την παράμετρο και κατά την εκκίνηση το Spark συνετρίβη και δεν ξεκίνησε. Σωστά, γιατί έπρεπε να το διαβάσω τεκμηρίωση πιο προσεκτικά. Αναφέρει ότι για να είναι όλα εντάξει πρέπει να ενεργοποιήσετε και μια επιπλέον παράμετρο.

spark.shuffle.service.enabled=true

Γιατί χρειάζεται; Όταν η δουλειά μας δεν απαιτεί πλέον τόσους πολλούς πόρους, το Spark θα πρέπει να τους επιστρέψει στην κοινή πισίνα. Το πιο χρονοβόρο στάδιο σε σχεδόν οποιαδήποτε εργασία MapReduce είναι το στάδιο Shuffle. Αυτή η παράμετρος σάς επιτρέπει να αποθηκεύσετε τα δεδομένα που δημιουργούνται σε αυτό το στάδιο και να απελευθερώσετε τους εκτελεστές ανάλογα. Και ο εκτελεστής είναι η διαδικασία που υπολογίζει τα πάντα στον εργάτη. Έχει έναν ορισμένο αριθμό πυρήνων επεξεργαστή και μια συγκεκριμένη ποσότητα μνήμης.

Αυτή η παράμετρος έχει προστεθεί. Όλα έδειχναν να λειτουργούν. Έγινε αντιληπτό ότι οι συμμετέχοντες είχαν στην πραγματικότητα περισσότερους πόρους όταν τους χρειάζονταν. Αλλά προέκυψε ένα άλλο πρόβλημα - κάποια στιγμή άλλοι συμμετέχοντες ξύπνησαν και ήθελαν επίσης να χρησιμοποιήσουν το Spark, αλλά όλα ήταν απασχολημένα εκεί και ήταν δυσαρεστημένοι. Μπορούν να γίνουν κατανοητά. Αρχίσαμε να εξετάζουμε την τεκμηρίωση. Αποδείχθηκε ότι υπάρχει μια σειρά από άλλες παραμέτρους που μπορούν να χρησιμοποιηθούν για να επηρεάσουν τη διαδικασία. Για παράδειγμα, εάν ο εκτελεστής είναι σε κατάσταση αναμονής, μετά από ποιο χρονικό διάστημα μπορούν να ληφθούν πόροι από αυτόν;

spark.dynamicAllocation.executorIdleTimeout=120s

Στην περίπτωσή μας, εάν οι εκτελεστές σας δεν κάνουν τίποτα για δύο λεπτά, τότε παρακαλούμε επιστρέψτε τους στην κοινή πισίνα. Αλλά αυτή η παράμετρος δεν ήταν πάντα αρκετή. Ήταν σαφές ότι το άτομο δεν έκανε τίποτα για μεγάλο χρονικό διάστημα και ότι οι πόροι δεν απελευθερώνονταν. Αποδείχθηκε ότι υπάρχει επίσης μια ειδική παράμετρος - μετά από πότε πρέπει να επιλέξετε εκτελεστές που περιέχουν αποθηκευμένα δεδομένα. Από προεπιλογή, αυτή η παράμετρος ήταν άπειρο! Το διορθώσαμε.

spark.dynamicAllocation.cachedExecutorIdleTimeout=600s

Δηλαδή, αν οι εκτελεστές σας δεν κάνουν τίποτα για 5 λεπτά, δώστε τους στην κοινή πισίνα. Σε αυτήν τη λειτουργία, η ταχύτητα αποδέσμευσης και έκδοσης πόρων για μεγάλο αριθμό χρηστών έχει γίνει αξιοπρεπής. Το μέγεθος της δυσαρέσκειας έχει μειωθεί. Αλλά αποφασίσαμε να προχωρήσουμε παραπέρα και να περιορίσουμε τον μέγιστο αριθμό εκτελεστών ανά εφαρμογή - ουσιαστικά ανά συμμετέχοντα στο πρόγραμμα.

spark.dynamicAllocation.maxExecutors=19

Τώρα, φυσικά, υπάρχουν δυσαρεστημένοι άνθρωποι στην άλλη πλευρά - "το σύμπλεγμα είναι αδρανές, και έχω μόνο 19 εκτελεστές", αλλά τι μπορείτε να κάνετε; Χρειαζόμαστε κάποιο είδος σωστής ισορροπίας. Δεν μπορείς να τους κάνεις όλους χαρούμενους.

Και μια ακόμη μικρή ιστορία που σχετίζεται με τις ιδιαιτερότητες της περίπτωσής μας. Κάπως έτσι, αρκετοί άνθρωποι άργησαν για ένα πρακτικό μάθημα και για κάποιο λόγο δεν ξεκίνησε το Spark για αυτούς. Εξετάσαμε το ποσό των δωρεάν πόρων - φαίνεται να υπάρχει. Το Spark πρέπει να ξεκινήσει. Ευτυχώς, μέχρι εκείνη τη στιγμή η τεκμηρίωση είχε ήδη προστεθεί στον υποφλοιό και θυμηθήκαμε ότι όταν εκτοξευόταν, το Spark αναζητά μια θύρα από την οποία θα ξεκινήσει. Εάν η πρώτη θύρα της περιοχής είναι κατειλημμένη, μετακινείται στην επόμενη με τη σειρά. Αν είναι δωρεάν, συλλαμβάνει. Και υπάρχει μια παράμετρος που υποδεικνύει τον μέγιστο αριθμό προσπαθειών για αυτό. Η προεπιλογή είναι 16. Ο αριθμός είναι μικρότερος από τον αριθμό των ατόμων στην ομάδα μας στην τάξη. Ως εκ τούτου, μετά από 16 προσπάθειες, ο Spark τα παράτησε και είπε ότι δεν μπορούσα να ξεκινήσω. Διορθώσαμε αυτήν τη ρύθμιση.

spark.port.maxRetries=50

Στη συνέχεια θα σας πω για ορισμένες ρυθμίσεις που δεν σχετίζονται πολύ με τις ιδιαιτερότητες της περίπτωσής μας.

Για να ξεκινήσετε πιο γρήγορα το Spark, συνιστάται να αρχειοθετήσετε το φάκελο jars που βρίσκεται στον αρχικό κατάλογο SPARK_HOME και να τον τοποθετήσετε σε HDFS. Τότε δεν θα χάσει χρόνο φορτώνοντας αυτά τα jarnik από εργάτες.

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

Συνιστάται επίσης η χρήση του kryo ως σειριοποιητή για ταχύτερη λειτουργία. Είναι πιο βελτιστοποιημένο από το προεπιλεγμένο.

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

Και υπάρχει επίσης ένα μακροχρόνιο πρόβλημα με το Spark ότι συχνά κολλάει από τη μνήμη. Συχνά αυτό συμβαίνει τη στιγμή που οι εργαζόμενοι έχουν υπολογίσει τα πάντα και στέλνουν το αποτέλεσμα στον οδηγό. Κάναμε αυτήν την παράμετρο μεγαλύτερη για εμάς. Από προεπιλογή, είναι 1 GB, το κάναμε 3.

spark.driver.maxResultSize=3072

Και τέλος, ως επιδόρπιο. Πώς να ενημερώσετε το Spark στην έκδοση 2.1 στη διανομή HortonWorks - HDP 2.5.3.0. Αυτή η έκδοση του HDP περιέχει μια προεγκατεστημένη έκδοση 2.0, αλλά κάποτε αποφασίσαμε μόνοι μας ότι το Spark αναπτύσσεται αρκετά ενεργά και κάθε νέα έκδοση διορθώνει ορισμένα σφάλματα και παρέχει πρόσθετες λειτουργίες, συμπεριλαμβανομένου του API python, οπότε αποφασίσαμε , τι χρειάζεται να γίνει είναι μια ενημέρωση.

Κατέβασε την έκδοση από τον επίσημο ιστότοπο για το Hadoop 2.7. Αποσυμπιέστε το και βάλτε το στον φάκελο HDP. Εγκαταστήσαμε τους συμβολικούς συνδέσμους όπως απαιτείται. Το εκκινούμε - δεν ξεκινά. Γράφει ένα πολύ περίεργο λάθος.

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

Μετά από γκουγκλάρισμα, ανακαλύψαμε ότι ο Spark αποφάσισε να μην περιμένει μέχρι να γεννηθεί ο Hadoop και αποφάσισε να χρησιμοποιήσει τη νέα έκδοση της φανέλας. Οι ίδιοι μαλώνουν μεταξύ τους για αυτό το θέμα στο JIRA. Η λύση ήταν να κατεβάσετε jersey έκδοση 1.17.1. Τοποθετήστε το στο φάκελο jars στο SPARK_HOME, συμπιέστε το ξανά και μεταφορτώστε το στο 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 δεν επιλύθηκε. Αφού γκουγκλάραμε, βρήκαμε λύση. Πρέπει να μεταβείτε στις ρυθμίσεις YARN στο Ambari και να προσθέσετε μια παράμετρο εκεί στον προσαρμοσμένο ιστότοπο νήματος:

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 context.

Πηγή: www.habr.com

Προσθέστε ένα σχόλιο