Spark
10/08/2015
IST/C3S - Mehdi ERRAKI
Spark : généralités
A propos de spark
• Spark est une plateforme de traitement distribué
désignée pour être :
– rapide : in-memory, shell inte...
Spark Stack
Spark core :
• fonctionnalités basiques de spark : scheduling des tâches, gestion de mémoire, fault
recovery, ...
Spark Stack
Spark SQL:
• interface SQL qui s'intègre avec le spark core dans les mêmes applications :
• manipulation SQL d...
Spark Stack
Spark MLIB:
• Classification, régression, clustering, collaborative filtering
• évaluation des modèles
• des p...
Spark Stack
Spark Streaming:
• composant spark pour traiter les flux live de données : logfiles .. l'API streaming est pro...
Spark Stack
Spark Graphx:
• bibliothèque pour manipuler des graphes , étend l’ API spark RDD comme streaming et SQL
Cluster managers
• Spark peut continuer de « scaler » a travers un cluster de plusieurs milliers de nœuds.
• Il supporte p...
Stockage
• Spark peut utiliser tous les systèmes de stockage supportés par l’API de Hadoop:
– HDFS
– S3
– Local FS
– Cassa...
Architecture maître-esclave
• Driver program:
– Exécution du main de la classe codée par le développeur
– Initialise un « ...
Deux modes d’utilisation – shell
interactif
• Spark-shell
Deux modes d’utilisation – standalone
applications
• Scala project
– Fraud.scala
• Compilation and versionning : sbt tool
...
Spark : RDDs et plan d’exécution
RDDs
• Un RDD est l’abstraction primaire des données sous SPARK
• Un RDD correspond à une collection non mutable distribué...
RDDs opérations
• Il existe deux types d’opérations qu’on peut appliquer sur un RDD
– Les transformations : permettent de ...
RDDs opérations - transformations
RDDs operations - transformations
LAZY COMPUTING
• Principe :
– Au moment de la création d’un nouveau RDD, Spark se conten...
Persistance
• Scala java – par défault : Stockage en mémoire sous forme d’objets non serialisés
• Python – par défault : S...
Deux types de transformations
Il existe deux types de transformations sur les RDDs
• Narrow : tous les enregistrement dans...
RDDs opérations – basic actions
- Toutes ces opérations retournent des résultats au driver (sauf foreach() qui est exécuté...
Plan d’exécution d’une application spark
Vocabulaire : Application/Job/Stage/Tâche
• Application : un programme qui peut c...
Spark : mode distribué et YARN
Mode distribué
• Composants d’une application spark
– Spark driver : s’exécute dans son propre process java.
– Spark execu...
Le driver
- Le driver est un process java , dans lequel s’exécute le main du
programme codé par le développeur.
- Il exécu...
Les exécuteurs
• Un exécuteur spark est un process Java qui s’occupe de l’exécution des
tâches que lui assigne le driver. ...
Lancement d’un programme
1. L’utilisateur soumet une application spark par le biais de la
commande spark-submit
2. Le prog...
Le spark-submit
• Le format général :
 bin/spark-submit [options] <app jar | python file> [app options]
• Les options
Rappel : Yarn
Spark sur Yarn : architecture
Mode Yarn-client
Lorsque Yarn est utilisé comme
Cluster Manager, il faudrait bien
distinguer...
Spark sur Yarn– Application/Job/Stage/Tâche
• Quand une application Spark est lancée, chaque action sur
une RDD engendre u...
Exemple : programme bypass
Objectif
• A partir d’une table de CDRs sous forme d’un fichier CSV
stocké dans hdfs, calculer des agrégats (par msisdn)
–...
1. Read CDR decoded Data
 Val data = sc.textFile(« path/to/files »)
2. Constructing a paired RDD
 Val paired_data = filt...
Algorithme spark : Object_func
 rule_funct( rule : Rule, cdrs : Iterable[Cdr] ) : Boolean
Return
condition_func(rule.cond...
map groupByKeytextFile
Stage 1 Stage 2
HDFS csv file (or hive
table) of n blocks
data : RDD [String] : primary data
abstra...
Allocation des ressources pour le programme
• Rappel : capacités du cluster de la PEI :
– 2 racks, 55 Nœuds actifs, <memor...
• PARTIE 1 :
• Spark : généralités
• Spark : RDDs et plan d’exécution
• Spark : mode distribué et déploiement sur YARN
• E...
RDD pairs
Créer un RDD pair (implicite)
Principe de la conversion Implicite :
certaines transformations de RDD ne sont applicables q...
RDDs pairs : transformations
RDDs pairs : agrégations
• ReduceByKey, foldByKey, :
– Les transformations ReduceByKey, et foldByKey prennent en argument ...
RDDs pairs : agrégations
• CombineByKey:
– combineByKey(createCombiner, mergeValue,mergeCombiners,partitioner)
RDDs pairs : parallélisme
• Pour toutes les transformations sur les RDD pairs, il est possible de définir un second
paramè...
RDDs pairs : actions
RDDs pairs : partitionnement
Cas commun
• Considérons une opération de jointure qui s’effectue chaque 5 minutes, entre un ...
RDDs pairs : partitionnement
Cas commun- solution
• Il est possible au moment de la création du RDD statique userData, de ...
RDDs pairs : partitionnement
Partitionnement par défaut
• Toutes les transformations opérant sur les RDDs pairs automatiqu...
Tunning
Configurer une application
1. En utilisant sparkConf:
2. Dynamiquement (dans le spark-submit)
3. Spark-default.conf (dans ...
Paramètres de configuration
Application/Job/Stage/Tâche
• Application : un programme qui peut consister en plusieurs transformations, et actions.
• Jo...
Plan d’exécution : exemple
• Jusqu’ici aucune action n’a été appelée. Donc aucun job n’est lancée au sein de l’application...
Plan d’exécution : stages
• Le plan d’exécution de ce job est le suivant :
• Une seule transformation de type « wide » => ...
Plan d’exécution : tâches
• Chaque stage est décliné en plusieurs tâches, opérant chacune et de façon similaire sur une pa...
Parallélisme
• Règle 1 : Chaque stage comprend autant de tâches que de partitions de la RDD en entrée
• Règle 2 : Input RD...
Sérialisation
• Quand Spark transfère les données dans le réseau ou les persistent en disque, il a besoin de sérialiser le...
Gestion de la mémoire
• Au sein de chaque exécuteur, la mémoire est partagé entre trois fonctionnalités :
Stockage RDD Shu...
Annexe : tests de montée en
échelle
Tests de performances - Programme de test
• A partir d’une table de CDRs sous forme d’un fichier CSV
stocké dans hdfs, cal...
Tests de performances - Algorithme spark (scala)
• Lecture du fichiers HDFS
 val data = Sc.textFile(nom_fichier)
• Mappin...
Tests de performances - Plan d’exécution spark
filter groupByKey maptextFile map map take
filter groupByKey maptextFile ma...
filter groupByKey maptextFile map map take
Stage 1 Stage 2
Fichier HDFS stocké
de façon parallélisée
par blocs de 256 Mo
(...
• Supposons que le fichier en entrée est un fichier hdfs text (TextInputFormat)
d’une taille de 15,4 Go = 60 * 256 Mo + 40...
Jeu de test
• Application du programme de calcul des compteurs
en faisant varier :
– La taille du Fichier CSV de CDRs
– Le...
Résultats(1)
taille en Go nombre d'executeurs cœurs par excécuteurs mémoire par excuteur (Go) facteur de repartition temps...
Résultats(2)
20
25
30
35
40
45
50
55
0 2 4 6 8 10 12 14 16
petits volumes
•Même pour le traitement d’un fichier d’une tail...
Résultats(3)
35
45
55
65
75
85
95
105
0 100 200 300 400 500 600
volumes moyens
•Nous constatons que le temps d’éxécution c...
Résultats(4)
90
140
190
240
290
340
390
440
490
540
0 1000 2000 3000 4000 5000 6000
gros volumes
•Dans le ce dernier graph...
Prochain SlideShare
Chargement dans…5
×

spark_intro_1208

568 vues

Publié le

Introduction au principaux concepts de Spark (6h-8h)
- RDDs, architecture, mode distribué, tunning, RDD pairs, allocation des ressources

Publié dans : Ingénierie
0 commentaire
0 j’aime
Statistiques
Remarques
  • Soyez le premier à commenter

  • Soyez le premier à aimer ceci

Aucun téléchargement
Vues
Nombre de vues
568
Sur SlideShare
0
Issues des intégrations
0
Intégrations
11
Actions
Partages
0
Téléchargements
35
Commentaires
0
J’aime
0
Intégrations 0
Aucune incorporation

Aucune remarque pour cette diapositive

spark_intro_1208

  1. 1. Spark 10/08/2015 IST/C3S - Mehdi ERRAKI
  2. 2. Spark : généralités
  3. 3. A propos de spark • Spark est une plateforme de traitement distribué désignée pour être : – rapide : in-memory, shell interactif, traitement itératif – « general-purpose » : un seul cœur (Spark) exposant des APIs pour manipuler des collections distribuées de données (RDD) • Spark et hadoop : – Spark peut être installé dans un cluster hadoop (mais pas nécessairement) – APIs haut niveau – Spark remédie à plusieurs défauts de Mapreduce
  4. 4. Spark Stack Spark core : • fonctionnalités basiques de spark : scheduling des tâches, gestion de mémoire, fault recovery, interaction avec le système de stockage • fournit plusieurs API de définition et manipulation des RDD : abstraction primaire des donnés sous forme de collection distribuée d'items
  5. 5. Spark Stack Spark SQL: • interface SQL qui s'intègre avec le spark core dans les mêmes applications : • manipulation SQL des données structurées + analyse avancées des rdd avec scala python ou java
  6. 6. Spark Stack Spark MLIB: • Classification, régression, clustering, collaborative filtering • évaluation des modèles • des primitives bas niveau : « gradient descent algo » • toutes désignées pour le « scaling out » a travers plusieurs noeuds
  7. 7. Spark Stack Spark Streaming: • composant spark pour traiter les flux live de données : logfiles .. l'API streaming est proche de celle de spark’s core RDD API. Ceci facilite la transition pour un développeur qui programme des traitements sur des données en mémoire ou en disque , à des traitement en temps réel
  8. 8. Spark Stack Spark Graphx: • bibliothèque pour manipuler des graphes , étend l’ API spark RDD comme streaming et SQL
  9. 9. Cluster managers • Spark peut continuer de « scaler » a travers un cluster de plusieurs milliers de nœuds. • Il supporte plusieurs clusters managers : – Hadoop yarn – Apache mesos – Standalone scheduler : empty set of machines – local : tout les processus spark dans un seul thread Spark Stack
  10. 10. Stockage • Spark peut utiliser tous les systèmes de stockage supportés par l’API de Hadoop: – HDFS – S3 – Local FS – Cassandra – Hive – Hbase • INPUT et OUTPUT Format – Spark peut accéder en lecture ou en écriture à tous les formats qui utilisent les interfaces InputFormat et OutputFormat utilisées par Hadoop Mapreduce
  11. 11. Architecture maître-esclave • Driver program: – Exécution du main de la classe codée par le développeur – Initialise un « spark context » : connexion à un « computing cluster » • Spark Executors : – Des nœuds esclaves gérés par le driver pour la durée de vie d’un « spark context » – se chargent d’exécuter des tâches en parallèle
  12. 12. Deux modes d’utilisation – shell interactif • Spark-shell
  13. 13. Deux modes d’utilisation – standalone applications • Scala project – Fraud.scala • Compilation and versionning : sbt tool – Build.sbt name := « Fraud_Bypass" version := "0.0.1" scalaVersion := "2.10.4 " libraryDependencies ++="org.apache.spark" %% "spark-core" % "1.2.1" – Linux shell >> sbt package • Compilation – Linux Shell >> spark-submit –class Fraud Fraud_Bypass-0.0.1.jar arg_to_the_main_1 arg_to_the_main_2 …….
  14. 14. Spark : RDDs et plan d’exécution
  15. 15. RDDs • Un RDD est l’abstraction primaire des données sous SPARK • Un RDD correspond à une collection non mutable distribuée d’objets, qui peuvent être de n’importe quel type de données Python, Scala ou Java, ou des “user-defined class” • Chaque RDD est constitué de plusieurs partitions distribuées sur le cluster. • Spark distribue automatiquement les RDDs selon le nombre de partitions souhaitées (ou selon un nombre par défaut) • Les RDD sont “fault-tolerant”, et bénéficie d’un mécanisme de reconstitution en cas de perte d’une partition donnée • Il est possible de créer un RDD :  En chargeant un collection externe de données par le driver programme (depuis hdfs par exemple)  Ou en créant un nouveau RDD en appliquant une transformation sur un ancien RDD  Ou en distribuant une collection d’objets (List, Set..) dans le driver programme
  16. 16. RDDs opérations • Il existe deux types d’opérations qu’on peut appliquer sur un RDD – Les transformations : permettent de constituer un nouveau RDD (map– filter – groupByKey – join) – Les actions : permettent de créer un résultat à partir d’un RDD pour le retourner au driver ou l’écrire en système de stockage. (count – collect – saveAsTextFile) • Example : – A partir d’un RDD de CDRs, une transformation de filtrage permet de créer un nouveau RDD contenant uniquement les types de « 0 » – Une fois créée, on applique une action « take(3) » sur le RDD, pour récupérer au niveau du driver un tableau de 3 CDRs
  17. 17. RDDs opérations - transformations
  18. 18. RDDs operations - transformations LAZY COMPUTING • Principe : – Au moment de la création d’un nouveau RDD, Spark se contente de garder en mémoire l’ensemble des étapes nécessaires pour calculer le RDD. – Il ne le crée physiquement qu’on moment ou il doit être utilisé dans une action • Avantages : – Gestion efficace du disque et de la mémoire – Optimisation des données à charger ( Exemple de succession des transformations : textFile + filter) – Optimisation des traitements à effectuer pour exécuter une succession de transformations en les regroupant (exemple d’un succession de d’opération de mapping) – Corolaire du dernier point : Code plus léger, moins compact, et facilement compréhensible (par rapport à map reduce) • Inconvénient : – Si un RDD doit être utilisé dans n actions au sein d’une même application, il sera calculé n fois différentes – Solution : Il est possible de persister un RDD en mémoire (ou en disque). Celui-ci sera calculé une seule fois, lors de l’exécution de la première action qui utilise ce RDD, et sera gardé en mémoire de façon distribuée au niveau de tous les exécuteurs utilisés pour le calculer. Lors de l’éxécution d’une nouvelle action qui utilise ce RDD, celui est accessible rapidement en mémoire, et ne sera pas recalculé. // spark ne charge pas physiquement les données du RDD // spark mémorise comment calculer second_RDD // spark mémorise comment calculer Third_RDD // spark charge physiquement les données du RDD pour établir //first_RDD, calcule physiquement « second_RDD » et « third_RDD » // Il effectue ensuite l’action de comptage, dont la valeur retour est //retrounée au driver
  19. 19. Persistance • Scala java – par défault : Stockage en mémoire sous forme d’objets non serialisés • Python – par défault : Stockage en mémoire sous forme d’objets sérialisés • Suite à une défaillance d’un noeud, Spark re-calcule les partitions perdues au besoin. • Il est possible de répliquer les données persistées. • Si les données à cacher dépassent les capacités de mémoire, Spark supprime les anciennes partitions persistées. – En mémoire : il re-calcule les parititions supprimées au besoin – En mémoire-et-disque : Il les écrira en disque • Pour libérer la mémoire, on peut appeller la fonction “unpersist()” RDDs opérations - transformations
  20. 20. Deux types de transformations Il existe deux types de transformations sur les RDDs • Narrow : tous les enregistrement dans une des partitions du RDD fils , sont calculés uniquement à partir des enregistrements d’une et une seule partition du RDD parent • Wide : La constitution d’une partition fait intervenir plusieurs partitions  Le shuffle qui intervient dans toutes les transformations de type « wide » est une opération très consommatrice en bande passante au sein du cluster et peut créer un goulot d’étranglement.  Règle pratique : Il est important d’effectuer toutes les opérations de filtrage avant les transformations de type « wide », afin d’avoir le minimum possible de données à « shuffler » entre les noeuds RDDs opérations - transformations
  21. 21. RDDs opérations – basic actions - Toutes ces opérations retournent des résultats au driver (sauf foreach() qui est exécuté directement au niveau des « executors » )  ATTENTION : le retour de la fonction « collect » doit être supporté par une seule machine (celle du driver), et peut éventuellement causer l’echec du job
  22. 22. Plan d’exécution d’une application spark Vocabulaire : Application/Job/Stage/Tâche • Application : un programme qui peut consister en plusieurs transformations, et actions. • Job : Quand une application Spark est lancée, chaque action sur une RDD engendre un job • Stage : Un job contient N+1 stages séquentiels si N est le nombre de transformations de type « Wide ». • Tâches : Chaque stage se déroule en plusieurs tâches, chacune opérant sur une partition de la RDD en entrée du stage. • A l’issue d’un shuffle, le nombre de tâches du stage suivant est défini par le développeur ( ou égal à une valeur par défaut) map groupByKey RDDRDD Stage 1 Stage 2
  23. 23. Spark : mode distribué et YARN
  24. 24. Mode distribué • Composants d’une application spark – Spark driver : s’exécute dans son propre process java. – Spark executors : chacun s’exécute dans son propre java process • La négociation des ressources se fait entre le driver et le cluster manager (Yarn, Mesos, ..)
  25. 25. Le driver - Le driver est un process java , dans lequel s’exécute le main du programme codé par le développeur. - Il exécute deux fonctions principales : • Convertir un programme en un plan d’exécution – À partir d’un graphe acyclique logique qui caractérise le programme … – ...le driver établit un plan d’exécution physique sous forme de plusieurs stages, contenant plusieurs tâches à faire exécuter par le cluster • Ordonnancer l’exécution du plan d’exécution – Les exécuteurs s’enregistrent auprès du driver – Le driver se charge ensuite d’assigner les différentes tâches aux exécuteurs de façon à maximiser le principe de proximité des données – Le driver mémorise également les endroits de persistance des différentes partitions des RDDs (quand elles sont persistées), afin d’optimiser l’assignation des futures tâches qui accéderont à ces données
  26. 26. Les exécuteurs • Un exécuteur spark est un process Java qui s’occupe de l’exécution des tâches que lui assigne le driver. Il est lancé au début de l’application et reste disponible pendant toute la durée du job. Si un exécuteur défaille, l’application continue d’être exécutée et un mécanisme de récupération de données et/ou de tâches est géré par Spark de façon transparente. • Rappel : Un process java (ici une JVM) peut lancer parallèlement un nombre maximal de threads (tâches), qui est égal au nombre de cœurs de processeurs alloués pour le process.  Il remplit deux fonctions : – Exécuter les tâches qui leurs sont assignées et retournent des résultats au driver – Offrir du stockage en mémoire aux RDDs persistées en mémoire
  27. 27. Lancement d’un programme 1. L’utilisateur soumet une application spark par le biais de la commande spark-submit 2. Le programme driver est lancé dans un process java , et invoque la métode main du programme lancé par l’utisateur 3. Le programme driver contacte le cluster manager pour allouer des ressources pour lancer des exécuteurs, coformément aux ressources demandées par l’utilisateur 4. Le cluster manager lance les exécteurs pour le compte du driver 5. Le driver consitute le plan d’exécution et assigne des tâches élémentaires aux exécuteurs 6. Le driver se termine quand le main() finit de s’exécuter ou suite à un appel de SparkContext.stop(). 7. Il arrête les éxécuteurs et libère les ressources auprès du cluster manager.
  28. 28. Le spark-submit • Le format général :  bin/spark-submit [options] <app jar | python file> [app options] • Les options
  29. 29. Rappel : Yarn
  30. 30. Spark sur Yarn : architecture Mode Yarn-client Lorsque Yarn est utilisé comme Cluster Manager, il faudrait bien distinguer entre les deamons YARN et les deamons SPARK YARN  RM : responsable d’affectation des ressources  Spark AM : demande les ressources auprès du RM  Spark NM : nœuds contenant un ou plus de container, chacun pouvant accueillir un exécuteur spark. SPARK  Spark Driver : s’exécute chez le client en mode Yarn-client , ou dans l’application master en mode Yarn- cluster  Spark Executor : s’exécutent dans des containers YARN au sein du node manager. ATTENTION : deux exécuteurs spark peuvent se retrouver dans deux containers Yarn différents, mais au sein de la même machine (YARN node manager)
  31. 31. Spark sur Yarn– Application/Job/Stage/Tâche • Quand une application Spark est lancée, chaque action sur une RDD engendre un job qui contient N+1 stages séquentiels si N est le nombre de transformations de type « Wide ». • Chaque stage se déroule en plusieurs tâches, chacune opérant sur une partition de la RDD précédente. • Au sein d’un stage(où toutes les transformations sont de type « narrow »), le nombre de tâches est égal exactement au nombre de partitions du dernier RDD constitué, qui lui-même est (supérieur ou) égal au nombre de partitions du RDD initial (nombre de blocs hdfs sous-jacents). • A l’issue d’un shuffle, le nombre de tâches du stage suivant est défini par le développeur ( ou égal à 1 par défaut) • Une tâche est exécutée par un cœur processeur d’un exécuteur spark (container Yarn) alloué pour l’application. • Les tâches d’un stage se déroulent parallèlement si les ressources allouées le permettent. • Deux stages ou jobs indépendants peuvent être exécutés parallèlement.  Règle : Afin de paralléliser entièrement les tâches d’un stage contenant des transformations de type « Narrow », le nombre d’exécuteurs alloués * le nombre de cœurs par exécuteur DOIT ETRE SUPERIEUR OU EGAL au nombre de blocs HDFS du fichier initial  Règle : En général, Le nombre d’exécuteur alloués * le nombre de cœurs par exécuteur DOIT ETRE SUPERIEUR OU EGAL au nombre de tâches du stage (ou des stages si ceux-ci sont indépendants et donc parallèles) contenant le plus grand nombre de tâches.  Règle : Si les ressources le permettent, il est préférable d’en allouer largement au dessus des seuils précédents, afin d’avoir plus de chances d’appliquer le principe de proximité des données (chaque partition est traitée par un exécuteur lancé dans le même nœud hdfs contenant le bloc correspondant) map groupByKey RDDRDD Stage 1 Stage 2
  32. 32. Exemple : programme bypass
  33. 33. Objectif • A partir d’une table de CDRs sous forme d’un fichier CSV stocké dans hdfs, calculer des agrégats (par msisdn) – Voice total count – Voice in total count – SMS in total count – Calls dispersion – Cells disperion – Voice intenational out • Comparer ses compteurs à des valeurs seuils, pour filtrer les comportements fraudeurs
  34. 34. 1. Read CDR decoded Data  Val data = sc.textFile(« path/to/files ») 2. Constructing a paired RDD  Val paired_data = filtered_data.map( row=> ( row(msisdn) , CDR_Schema_Apply(row) ) 3. Grouping_by_key  Val grouped_by_key_data= paired_data.groupByKey(numOfPartitions) 4. Applying the rule()  Val fraud_list = grouped_by_key_data.filter( (key,value) => rule_func(value, json) ) 5. The driver collects the results in an array of Strings , (before writing back to a Hive (or Hbase table))  Val result= fraud_list .collect() Algorithme spark (code sous-jacent en scala) DAG
  35. 35. Algorithme spark : Object_func  rule_funct( rule : Rule, cdrs : Iterable[Cdr] ) : Boolean Return condition_func(rule.condition(1), cdrs) rule.binaryOperators(1) condition_funct(rule.condition(2),cdrs) rule.binaryOperators(2) condition_funct(rule.condition(3),cdrs) …….. condition_func (condition : Condition , cdrs : Iterable[Cdr] ) : Boolean Condition.comparison_op match { case « ge » => return operation_funct( condition.operations(1) , cdrs) >= operation_funct( condition.operations(2) ,cdrs) case « le » => return operation_funct( condition.operations(1) , cdrs) <= operation_funct( condition.operations(2) ,cdrs) case « g » => return operation_funct( condition.operations(1) , cdrs) > operation_funct( condition.operations(2) , cdrs) case « l » => return operation_funct( condition.operations(1) , cdrs) < operation_funct( condition.operations(2) , cdrs) case « eq » => return operation_funct( condition.operations(1) , cdrs) == operation_funct(condition.operations(2) , cdrs) case « not » => return operation_funct( condition.operations(1) , cdrs) == 0 } operation_func(Operation : operation , cdrs : Iterable[Cdr] ) : Float Operation.operation_type match { case « ratio » => return Function_funct( operations.functions(1) , cdrs) / Function_funct( operations.functions(2) , cdrs) case « product » => return Function_funct( operations.functions(1) , cdrs) *Function_funct( operations.functions(2) , cdrs) case « filter » | « static_threshlod » | « counter » => return Function_funct( operations.functions(1) , cdrs) function_funct (function : Function , cdrs : Iterable[Cdr] ) : Float Function.function_type match { case « count » => return cdrs.count(cdr => function.fields.contains(cdr.recordType)) case « static_value » => return function.fields(0).toFloat case « filter » => return sc.textFile(function.fields(0)).collect().contains(cdr.msisdn) case « disitinct_count » => countList
  36. 36. map groupByKeytextFile Stage 1 Stage 2 HDFS csv file (or hive table) of n blocks data : RDD [String] : primary data abstraction in Spark. It contains a distributed set of lines (rows) Grouped_by_key_data : Paired_RDD [(key : String ,value : Iterable[Tuple [String]])] paired_data : Paired_RDD [ (key : String , value : Tuple[String]) ] Result : Array [Strings] Fraud_list: Paired_RDD [(key : String ,value : Iterable[Tuple [String]])] collect (*) : source : Learning spark - O'Reilly Media filter  4 blocks => 4 Tasks in stage 1 num_executors * executors_cores > N !!!!! numPartions given as in input to groupByKey_ function=> numPartions Tasks in stage 2 numOfPari tions > queue_total _cores (=790 for « HQ_IST » queue » !!! (*) Plan d’exécution • Supposons que le fichier en entrée est d’une taille de 1Go = 3 * 256Mo + 232Mo (4 blocs hdfs) • L’application comporte plusieurs transformations sur le RDD et une seule action. – Un Seul job est donc lancé – Ce job comporte plusieurs transformations de type Narrow, et une seule transformation de type wide(groupByKey). – Il comprend donc deux stages séparés par le shuffle – Le premier stage se déroule sous forme de plusieurs tâches (4 tâches exactement) – Dans une tâche de ce premier stage, la partition concernée du RDD initial subit toute les transformations du premier stage. – Le second stage comprend un nombre de tâches exactement égal à la valeur de repartitionnement donnée en paramètre à la fonction groupByKey (3 partitions dans l’illustration ci-dessous)
  37. 37. Allocation des ressources pour le programme • Rappel : capacités du cluster de la PEI : – 2 racks, 55 Nœuds actifs, <memory: 4.83 TB , vCores : 1320> • Deux types de nœuds : < 58.88 GB , 24 cores > ou < 121.99 GB , 24 cores > – Capacités maximales pour la queue « HQ_IST » • Capacité maximale d’un container YARN : < 65,536 GB , 24 cores > • Capacité maximale par utilisateur : < 1012,736 GB , 265 cores > • Application master - capacité maximale : < 304,128 GB , 80 cores > • Allocation des ressources – Si le fichier en entrée est de l’ordre de N*256Mo + X (avec X<256Mo) – Pour exécuter le stage 1 de façon entièrement parallélisé, il faudrait que « num_executors » * « executor_cores » > N – Il est recommandé de fixer le nombre de executor_cores à 5, pour ensuite définir le nombre d’exécuteur à allouer selon la règle précédente. (allouer largement au dessus pour appliquer le principe de proximité des données). – Quand il n’y pas de RDD à cacher, 2 Go de RAM par cœurs de processeur est un bon ordre de grandeur. Donc, il est recommandé d’allouer « exeutor-memory » = 10G. – Le nombre de partitions à créer à l’issue du shuffle dépend du plan d’exécution et de la répartition initiale des données, et de l’assignation des tâches effectuée en stage 1. • Il est recommandé soit de créer autant de partitions que le nombre de cœurs disponibles, et continuer ensuite soit d’augmenter, soit de diminuer jusqu’à ce que les performances ne continuent plus de s’améliorer • ou de créer autant de partitions que le nombre de partitions du stage précédent. Continuer ensuite de multiplier ce nombre par 1,5 jusqu’à ce que les performances s’arrêtent de s’améliorer.
  38. 38. • PARTIE 1 : • Spark : généralités • Spark : RDDs et plan d’exécution • Spark : mode distribué et déploiement sur YARN • Exemple : Bypass • PARTIE 2 : • Spark : PairedRDD • Spark : tunning des algorithmes et de l’allocation de ressources • Annexe : tests de montée en échelle Sommaire
  39. 39. RDD pairs
  40. 40. Créer un RDD pair (implicite) Principe de la conversion Implicite : certaines transformations de RDD ne sont applicables que sur certains types particuliers de RDD. (ex : mean() and variance() sur les RDD[Double] ou join() sur les RDDs “(clef,valeur)” . Il s’agit en effet de types différents de RDD, chacun avec sa propre API. En appliquant la transformation groupByKey sur un RDD contenant des tuples (clef/valeur), il s’agit de la méthode groupByKey de la classe « PairedRDD » qui s’exécute et non pas celle de la classe « RDD » (car elle n’existe pas dans cette classe). Aucune déclaration explicite n’est nécessaire. Aussi faudrait il faire attention à ne pas consulter la sclaladoc de la classe RDD pour chercher une transformation qui s’applique uniquement sur les RDD pairs.
  41. 41. RDDs pairs : transformations
  42. 42. RDDs pairs : agrégations • ReduceByKey, foldByKey, : – Les transformations ReduceByKey, et foldByKey prennent en argument une fonction à appliquer successivement à chaque deux éléments du RDD ayant la même clef. – Cette fonction doit donc être associative. • Différence par rapport à groupByKey – Alors que ces transformations s’exécutent localement sur chaque machine avant d’engendrer un shuffle, la transformation groupByKey lance directement une opération de shuffle, induisant un trafic important entre les machines. • Règle : Si la fonction à appliquer sur un ensemble de valeurs correspondant aux mêmes clefs est associative, il faut privilégier les transformations ReduceBykey, et foldByKey • CombineByKey: – combineByKey(createCombiner, mergeValue,mergeCombiners,partitioner)
  43. 43. RDDs pairs : agrégations • CombineByKey: – combineByKey(createCombiner, mergeValue,mergeCombiners,partitioner)
  44. 44. RDDs pairs : parallélisme • Pour toutes les transformations sur les RDD pairs, il est possible de définir un second paramètre (degré de parallélisme) qui définit le nombre de partitions du RDD groupé ou agrégé par clé Default parallelism « 10 » Setting parallelism to « 3 » repartitionnement
  45. 45. RDDs pairs : actions
  46. 46. RDDs pairs : partitionnement Cas commun • Considérons une opération de jointure qui s’effectue chaque 5 minutes, entre un RDD statique et volumineux UserData et un petit RDD events correspondant aux événements reçus dans les 5 dernières minutes • Par défault, cette opération effectue un hashing sur l’ensemble des clés des deux RDDs, envoie ensuite sur le réseaux les éléments ayant la même clef hashé aux même nœuds. La jointure est ensuite effectuée.  Les données sont hashés et shufllés à travers le réseau à chaque 5 minutes?
  47. 47. RDDs pairs : partitionnement Cas commun- solution • Il est possible au moment de la création du RDD statique userData, de la hash-partitionner. Attention : Si le RDD n’est pas caché en mémoire, le hash-partitionnement devient inutile
  48. 48. RDDs pairs : partitionnement Partitionnement par défaut • Toutes les transformations opérant sur les RDDs pairs automatiquement génèrent un RDD qui est hash- partitionné Opérations qui bénéficient du partitionnement • cogroup(), groupWith(), join(), leftOuterJoin(), rightOuterJoin(), groupByKey(), reduceByKey(), combineByKey(), and lookup() • Pour les opérations qui s’exécutent sur un seul RDD (reduceByKey), si le RDD et pré-partitionné, il en résulte que l’aggrégration s’effectue localement dans chaque noeud. • Pour les opérations qui s’exécutent sur deux RDDs, le pré-partionnement des deux RDDs engendre le shuffle d’un seul des deux RDDs au maximum. • S’ils sont persistés dans la même machine, en appelant la fonction “mapValues” par exemple, ou si un des deux RDDs n’as pas encore été créé, aucun shuffle ne s’exécute dans le réseau Opérations qui affectent le partitionnement • toutes les opérations sauf : cogroup(), groupWith(), join(), leftOuterJoin(), rightOuter Join(), groupByKey(), reduceByKey(), combineByKey(), partitionBy(), sort(), mapValues() (if the parent RDD has a partitioner), flatMapValues() (if parent has a partitioner), and filter() (if parent has a partitioner)
  49. 49. Tunning
  50. 50. Configurer une application 1. En utilisant sparkConf: 2. Dynamiquement (dans le spark-submit) 3. Spark-default.conf (dans the spark directory par défaut ou dans un répertoire donné)
  51. 51. Paramètres de configuration
  52. 52. Application/Job/Stage/Tâche • Application : un programme qui peut consister en plusieurs transformations, et actions. • Job : Quand une application Spark est lancée, chaque action sur une RDD engendre un job • Stage : Un job contient N+1 stages séquentiels si N est le nombre de transformations de type « Wide ». • Tâches : Chaque stage se déroule en plusieurs tâches, chacune opérant sur une partition de la RDD en entrée du stage. • A l’issue d’un shuffle, le nombre de tâches du stage suivant est défini par le développeur ( ou égal à une valeur par défaut) map groupByKey RDDRDD Stage 1 Stage 2
  53. 53. Plan d’exécution : exemple • Jusqu’ici aucune action n’a été appelée. Donc aucun job n’est lancée au sein de l’application. Aussi, les RDDs sont paresseusement évalués. • Pour évaluer calculer physiquement les RDDs, il est possible d’appeler une action dessus (collect), qui va donc lancer un job.
  54. 54. Plan d’exécution : stages • Le plan d’exécution de ce job est le suivant : • Une seule transformation de type « wide » => donc deux stages séparés par le shuffle sont générés • Si le RDD final a été persisté lors d’une première exécution du plan précédent, une nouvelle action « comme count » par exemple engendrera uniquement un seul stage. S’il n’a pas été persisté, cette action engendrera un stage supplémentaire en plus des stages précédents.
  55. 55. Plan d’exécution : tâches • Chaque stage est décliné en plusieurs tâches, opérant chacune et de façon similaire sur une partition du RDD initial map Stage • Chaque tâche s’exécute de la façon suivante
  56. 56. Parallélisme • Règle 1 : Chaque stage comprend autant de tâches que de partitions de la RDD en entrée • Règle 2 : Input RDDs se base directement sur le stockage sous-jacent (bloc hdfs  partition du RDD  tâche) • Règle 3 : le degré de parallélisme doit être optimal (pas trop petit par rapport aux ressources allouées, et pas trop grand ) • Règle 4 : Il y a deux façons pour définir son degré de parallélisme (nombre des partitions) • Degré de parallélisation donné en paramètre aux transformations qui engendrent un shuffle • En utilisant la fonction repartition(numPartitions) ou coalesce(numPartitions) • La fonction coalesce(numPartitions) ne fonctionnent que lorsqu’on réduit le nombre de partitions. Elle est beaucoup plus optimal que la fonction repartition, car elle n’engendre pas de shuffle.
  57. 57. Sérialisation • Quand Spark transfère les données dans le réseau ou les persistent en disque, il a besoin de sérialiser les objets (classes) en des formats binaires. • Spark utilise par défaut le Java’s built in serializer. • Mais il supporte également l’utilisation d’un serializer tiers appelé « kyro »* • Sérialisation plus rapide • Représentation binaire plus compacte • Attention : les « user-defined » classes doivent implémentée l’interface Java « Serializable ». Si non, une exception de type « noptSerializableException » sera levée.
  58. 58. Gestion de la mémoire • Au sein de chaque exécuteur, la mémoire est partagé entre trois fonctionnalités : Stockage RDD Shuffle et buffers d’agrégation Le code utilisateur When you call persist() or cache() on an RDD, its partitions will be stored in memory buffers. When performing shuffle operations, Spark will create intermediate buffers for storing shuffle output data. These buffers are used to store intermediate results of aggregations in addition to buffering data that is going to be directly output as part of the shuffle Memory which is necessary for the spark code (allocation of arrays for example) spark.storage.memoryFraction spark.shuffle.memoryFraction 60% 20% 20% • Règle : Ces pourcentages doivent être paramétrées en fonction du programme à lancer. • Règle : MEMORY_AND_DISK storage level est préférable que le MEMORY_ONLY storage level pour la persistance.
  59. 59. Annexe : tests de montée en échelle
  60. 60. Tests de performances - Programme de test • A partir d’une table de CDRs sous forme d’un fichier CSV stocké dans hdfs, calculer des agrégats (par msisdn) – Voice total count – Voice in total count – SMS in total count – Calls dispersion – Cells disperion – Voice intenational out
  61. 61. Tests de performances - Algorithme spark (scala) • Lecture du fichiers HDFS  val data = Sc.textFile(nom_fichier) • Mapping (séparation des champs)  val table = data.map(ligne=>ligne.split(‘t’)) • Filtrage (CDR voix in/out et sms in uniquement)  val types_0_1_6 = table.filter(l=>l(1)==0 || l(1)==1 || l(1)==6) • Mapping (champs qui nous intéressent uniquement)  Val ready_table = types_0_1_6 .map( l => (l(4), (l(1),l(5),l(22),l(4))) • GroupByKey (regroupement par msisdn)  Val grouped_table= ready_table.groupeByKey() • Mapping (calcul des compteurs pour chaque msisdn)  Val compteurs= grouped_table.map( l=> (l._1, fonct_calcul_compteur(l._2))) • collect(récupération des résultats dans le programme du driver pour restitution stdout)  For (i<- compteurs.collect()) println(i) filter groupByKey maptextFile map map take
  62. 62. Tests de performances - Plan d’exécution spark filter groupByKey maptextFile map map take filter groupByKey maptextFile map map take Stage 1 Stage 2
  63. 63. filter groupByKey maptextFile map map take Stage 1 Stage 2 Fichier HDFS stocké de façon parallélisée par blocs de 256 Mo (ex : 15,4 Go = 60 * 256 Mo + 40 Mo ) RDD : abstraction primaire des données sous spark correspondant à une collection distribuée d’enregistrements dont les partitions correspondent aux différents blocs hdfs sous-jacents. RDD RDD RDDRDD RDD Array Tests de performances - Plan d’exécution spark
  64. 64. • Supposons que le fichier en entrée est un fichier hdfs text (TextInputFormat) d’une taille de 15,4 Go = 60 * 256 Mo + 40 Mo • La lecture du fichier par l’application spark se fait avec la fonction « textFile() », et permet de constituer un RDD (resilient distributed data). • Le RDD est composé de 61 partitions : – Chacune des partitions est physiquement stockée dans un des 61 nœuds contenant les blocs HDFS du fichier – Ces nœuds HDFS ne sont pas nécessairement les nœuds exécuteurs alloués pour exécuter les traitement sur les données. – Les nœuds exécuteurs chargeront donc ces données en mémoire (ou en disque dur à défaut de ressources) lorsque le job sera lancé • Plan d’exécution : L’application comporte plusieurs transformations sur le RDD et une seule action. – Un Seul job est donc lancé – Ce job comporte plusieurs transformation de type Narrow, et une seule transformation de type « wide »(groupByKey). – Il comprend donc deux stages séparés par le shuffle – Le premier stage se déroule sous forme de plusieurs tâches (61 tâches exactement) – Dans une tâche de ce premier stage, la partition concernée du RDD initial subit toute les transformations du premier stage. – Le second stage comprend autant de tâche que le nombre de partitions constitués suite au shuffle et qui est donné comme paramètre à la fonction « groupByKey » Tests de performances - Plan d’exécution spark
  65. 65. Jeu de test • Application du programme de calcul des compteurs en faisant varier : – La taille du Fichier CSV de CDRs – Les ressources allouées • Pour chaque combinaison (taille de fichier, ressources allouées), le programme est exécuté plus de 100 fois, avant de prendre la valeur minimale des temps d’exécution • Pour chaque taille du fichier en entrée donnée, la configuration des ressources qui donne le temps d’exécution le plus minimal est gardée
  66. 66. Résultats(1) taille en Go nombre d'executeurs cœurs par excécuteurs mémoire par excuteur (Go) facteur de repartition temps d'excution minimal 0,0045 1 1 2 1 31 0,1 1 2 4 15 38 0,5 1 2 4 2 37 1 5 5 10 25 45 5 5 5 10 30 47 15 30 5 10 150 52 30 25 5 10 120 56 45 38 5 10 400 59 92 110 5 10 359 68 150 90 10 20 1200 73 184 160 5 10 718 85 200 160 5 10 782 84 225 160 5 10 790 87 240 160 5 10 1500 86 270 160 5 10 1500 90 300 160 5 10 1700 91 394 160 5 10 2000 101 2000 180 5 10 5000 247 5000 180 5 10 18000 532
  67. 67. Résultats(2) 20 25 30 35 40 45 50 55 0 2 4 6 8 10 12 14 16 petits volumes •Même pour le traitement d’un fichier d’une taille de l’ordre de quelques Ko, un temps d’initialisation important est requis (de l’ordre de 30s) pour l’allocation des ressources et l’assignation des tâches. •Quand la taille du fichier est inférieure à la taille d’un bloc d’un fichier hdsf (256 Mo), une seule tâche est exécutée dans le premier stage •Cela ne sert à rien d’allouer plus de deux cœurs (nbr exec * nb cœurs/exec). •Dans le cas précédent, si on repartitionne suite au shuffle en deux partitions, il faudrait allouer exactement deux cœurs du cluster pour le job, car deux tâches seront exécutées dans le stage 2 • Dès que la taille du fichier en entrée dépasse 256 Mo, on peut commencer à bénéficier de la parallélisation dans le premier stage. •Etant donné que le capacité maximale de la queue utilisée est de 790 cœurs, nous pouvons bénéficier de la parallélisation totale lors du premier stage même avec un fichier d’une taille de 790*256 Mo = 200 Go. •Ceci explique l’évolution logarithmique constatée à partir de 256 Mo.
  68. 68. Résultats(3) 35 45 55 65 75 85 95 105 0 100 200 300 400 500 600 volumes moyens •Nous constatons que le temps d’éxécution continue d’évoluer logarithmiquement sur chaque segment de 200 Go. •Au bout de chaque 200 Go, un saut au niveau du temps d’éxécution est constaté •Cela est du au fait que sur un segment de 200 Go = 790 * 256 Mo , des cœurs sont toujours disponibles pour traiter d’autres tâches en parallèle. Dès que les 200 Go sont atteints, la parralélisation est maximale. •En augmentant ensuite la taille du fichier, il faudrait attendre que les 790 cœurs aient finis leur première tâche, pour en recevoir une nouvelle. En continuant d’augmenter la taille du fichier de 200 Go, on ne fait que maximiser la parallélisation. •Dès qu’on dépasse 200 Go à nouveau, un temps d’éxécution supplémentaire est ajouté qui est celui du traitement des blocs supplémentaires par rapport aux n*790 blocs initiaux. •Et ainsi de suite … •Nous remarquons aussi que l’évolution globale peut être approximé par une évolution linéaire
  69. 69. Résultats(4) 90 140 190 240 290 340 390 440 490 540 0 1000 2000 3000 4000 5000 6000 gros volumes •Dans le ce dernier graphique, nous ne prenons plus des intervalles de 200 Go. •On constate bien que l’évolution globale du temps d’exécution constatés pour les trois fichiers testés en entrée, qui sont de 500 Go, 2To, 5To, est bien linéaire. •Si nous testons plusieurs fichiers au sein de chaque segment de 200 Go, nous aurions bien constaté une évolution locale logarithmique sur chaque segment, en plus de l’évolution linéaire globale constatée

×