Vous êtes sur la page 1sur 18

TP2: HDFS et Map Reduce

Note:

Pour réaliser ce TP, une machine virtuelle nommée ubuntu-big-data a été mise à votre
disposition. Veuillez vous connecter en utilisant l'utilisateur hadoop-user avec le mot de passe :
hadoop-user. Cet environnement contient toutes les configurations nécessaires pour mener à
bien les tâches requises.

Introduction:

L'objectif de la première séance de TP était de vous initier à la configuration de l'outil Hadoop à


partir de zéro. Nous avons entamé cette démarche en configurant les fichiers de base
core-site.xml, hdfs-site.xml, et mapred-site.xml. Parallèlement, nous avons abordé la
configuration de SSH, un protocole sécurisé qui permettra de garantir la confidentialité et
l'intégrité des échanges entre les nœuds. Cependant, dans notre cas, les nœuds sont exécutés
sur un seul serveur, à savoir le localhost.

Pour cette session, nous abordons Docker, une technologie de conteneurisation qui nous
permet de déployer des nœuds dans des environnements virtualisés appelés conteneurs. Ces
derniers sont logiquement indépendants les uns des autres, disposant d'adresses IP distinctes,
ce qui nous permet de simuler un environnement réaliste. Pour ce faire, vous utiliserez la
machine virtuelle ubuntu-big-data, enrichie des technologies et configurations nécessaires,
fournie avec ce TP.

Note: Le choix de la technologie de virtualisation n'a pas porté sur WSL, car l'installation de
Docker sous Windows est complexe et dépendante de l'environnement spécifique de chaque
utilisateur, rendant la configuration difficile à envisager. Cependant, pour les curieux, voici les
prérequis et les instructions vous permettant de le faire sous Windows et WSL2:
https://docs.docker.com/desktop/install/windows-install/

En utilisant des conteneurs Docker, nous allons envisager un réseau composé de trois nœuds
: un nœud maître (hadoop-master) et deux nœuds esclaves (hadoop-workers).

Pour commencer, déployez la machine virtuelle dans votre hyperviseur en suivant les
instructions suivantes :

● Sous Virtual Box:

1/ Ouvrez VirtualBox et cliquez sur "Fichier". Ensuite, sélectionnez "Importer un appareil virtuel".

2/ Une fenêtre va s'afficher pour sélectionner le fichier. Cliquez sur l'icône à gauche pour
parcourir les fichiers, puis importez le fichier "Ubuntu-big-data.ova".
Ensuite, valider en important le fichier.

● Sous VMware Workstation:

Pour ceux utilisant l'hyperviseur VMware Workstation, veuillez vous référer à ce tutoriel fourni
par la documentation officielle pour l'importation du fichier OVA :

https://docs.vmware.com/en/VMware-Workstation-Player-for-Windows/17.0/com.vmware.player.
win.using.doc/GUID-DDCBE9C0-0EC9-4D09-8042-18436DA62F7A.html .

Lancement du TP:

Le TP débute maintenant ! Pour faciliter votre travail, Docker a été installé avec l'image Docker
requise. Une image Docker représente une version préconfigurée d'une application ou d'un
environnement, contenant tous les éléments nécessaires à son exécution dans un conteneur
Docker. Ouvrez un terminal en tapant sur les touches Ctrl + Alt + T. Pour accéder à Docker,
vous devez être root, donc exécutez la commande sudo su pour vous connecter en tant
qu'utilisateur root.

Commencez par exécuter la commande docker ps -a, qui permet d'afficher la liste de tous les
conteneurs Docker sur le système, y compris ceux qui sont arrêtés. Pour l'instant, aucun
conteneur n'a été créé, donc rien n'est affiché. Tapez maintenant la commande docker images
afin d'afficher la liste des images Docker disponibles sur le système.

Vous devez avoir un affichage semblable à ça:

L'affichage indique la présence de l'image TP-hadoop/hadoop-node:latest dans votre référentiel


Docker. C'est évidemment celle que nous allons utiliser pour créer nos conteneurs. Avant cela,
nous devons d'abord créer le réseau de nœuds (appelé cluster) en utilisant la commande
suivante :

docker network create --driver=bridge hadoop

À noter que les images Docker peuvent être soumises (push) et récupérées (pull) depuis le
Docker Hub, une plateforme en ligne permettant le partage d'images Docker entre les
utilisateurs.

Voici les commandes pour créer et lancer les conteneurs des nœuds :

docker run -itd --net=hadoop -p 9870:9870 -p 8088:8088 -p 7077:7077 -p 16010:16010


--name hadoop-master --hostname hadoop-master TP-hadoop/hadoop-node:latest

Voici l’explication de la commande:

● docker run: Cette commande est utilisée pour exécuter un nouveau conteneur à partir
d'une image Docker.
● -itd: Ces options permettent de démarrer le conteneur en mode interactif (-i), attacher le
terminal (-t), et en mode détaché (-d) afin qu'il s'exécute en arrière-plan.
● --net=hadoop: Cette option spécifie le réseau auquel le conteneur appartient. Ici, le
conteneur est connecté au réseau nommé "hadoop".
● -p 9870:9870 -p 8088:8088 -p 7077:7077 -p 16010:16010: Ces options mappent les
ports de l'hôte (à gauche) aux ports du conteneur (à droite), permettant d'accéder aux
services exposés par le conteneur depuis l'hôte.
● --name master-node: Cette option définit le nom du conteneur en tant que
"master-node".
● --hostname master-node: Cette option définit le nom d'hôte à l'intérieur du conteneur en
tant que "master-node".
● TP-hadoop/hadoop-node:latest: Ceci est l'image Docker utilisée pour créer le conteneur,
dans ce cas, "TP-hadoop/hadoop-node:latest".

docker run -itd -p 8040:8042 --net=hadoop --name hadoop-worker1 --hostname h


adoop-worker1 TP-hadoop/hadoop-node:latest

Cette commande est similaire à la précédente, mais elle crée un conteneur nommé
"hadoop-worker1" avec le nom d'hôte “hadoop-worker1".

docker run -itd -p 8041:8042 --net=hadoop --name hadoop-worker2 --hostname


hadoop-worker2 TP-hadoop/hadoop-node:latest

Encore une fois, similaire aux commandes précédentes, mais pour le conteneur nommé
“hadoop-worker2" avec le nom d'hôte “hadoop-worker2".

Maintenant, si vous exécutez docker ps, vous obtiendrez une sortie similaire à celle illustrée
dans la figure ci-dessous:

Pour accéder au conteneur master-node, exécutez la commande suivante : docker exec -it
hadoop-master bash. Une fois à l'intérieur, tapez ls pour voir le contenu du dossier courant.
Vous remarquerez un fichier nommé start-all.sh, que vous pouvez exécuter en tapant
./start-hadoop.sh, ce qui démarrera Hadoop et YARN.
Le résultat de l'exécution doit ressembler à ceci:

Parfait ! Passons maintenant à l'étape suivante où nous utiliserons les commandes HDFS. Pour
vous aider, voici un rappel des commandes les plus essentielles:

Commande Description

hdfs dfs -ls [répertoire] Affiche le contenu du répertoire spécifié dans


HDFS.

hdfs dfs -mkdir [dossier] Crée un répertoire avec le nom spécifié dans
HDFS.

hdfs dfs -put [fichier_local] Copie le fichier spécifié du système local vers
[chemin_destination_HDFS] le chemin spécifié dans HDFS.

hdfs dfs -get [fichier_HDFS] Copie le fichier spécifié de HDFS vers le


[chemin_destination_local] chemin spécifié dans le système local.

hdfs dfs -cat [fichier] Affiche le contenu du fichier spécifié dans


HDFS.

hdfs dfs -rm [chemin] Supprime le fichier ou le répertoire spécifié


dans HDFS.

hdfs dfs -mv [source] [destination] Déplace le fichier ou le répertoire source vers
la destination spécifiée dans HDFS.

hdfs dfs -cp [source] [destination] Copie le fichier ou le répertoire source vers la
destination spécifiée à l'intérieur de HDFS.

hdfs dfs -du [chemin] Affiche la taille de l'espace disque utilisé par
le fichier ou le répertoire spécifié dans HDFS.

hdfs dfs -chown [utilisateur:groupe] Change le propriétaire du fichier ou du


[fichier_ou_dossier] répertoire spécifié dans HDFS.

hdfs dfs -chmod [permissions] Change les autorisations d'accès du fichier


[fichier_ou_dossier] ou du répertoire spécifié dans HDFS.

hdfs dfs -touchz [fichier] Crée un fichier vide avec le nom spécifié
dans HDFS.

hdfs dfs -stat [fichier_ou_dossier] Affiche les informations sur le fichier ou le


répertoire spécifié dans HDFS.

hdfs dfs -tail [fichier] Affiche les derniers kilobytes du fichier


spécifié dans HDFS.

Vous allez commencer par créer un dossier nommé "input". Pour ce faire, tapez la commande
suivante : hdfs dfs -mkdir -p input.

Le dossier "input" que vous venez de créer servira comme entrée pour le job MapReduce. Pour
quitter le conteneur hadoop-master, exécutez la commande exit. Ensuite, copiez le fichier
"purchases.txt" qui se trouve dans le répertoire "~/Documents" de votre machine hôte vers le
répertoire "/root" du conteneur en utilisant la commande suivante : docker cp
~/Documents/purchases.txt hadoop-master:/root. Après cela, réaccédez au conteneur
hadoop-master en tapant la commande suivante : docker exec -it hadoop-master bash.
Enfin, exécutez la commande suivante : hdfs dfs -put purchases.txt input pour placer le
fichier "purchases.txt" sous le répertoire "input" du système de fichiers HDFS.

Pour afficher le contenu du dossier "input", utilisez la commande suivante : hdfs dfs -ls input.
Vous devriez voir le fichier "purchases.txt" qui y a été placé.

Vous pouvez ainsi lister le dernier kilo-octet du fichier "purchases.txt" en utilisant la commande
suivante: hdfs dfs -tail input/purchases.txt
Attachez vos ceintures car nous allons bientôt examiner les jobs MapReduce, mais avant cela,
amusez-vous en accédant aux interfaces graphiques du Resource Manager (YARN) sur
localhost:8088, celle du NameNode sur localhost:9870, et celles des deux DataNodes sur
localhost:8040 et localhost:8041. Vous pouvez voir à quoi ressemblent ces interfaces dans les
captures ci-dessous. Prenez le temps de comprendre les différentes fonctionnalités.

L'étape suivante consiste à programmer un job MapReduce. le Map est une opération qui traite
et transforme les données en paires clé-valeur, tandis que le Reduce est une opération qui
agrège et consolide les données en fonction de leur clé (voir la figure ci-dessous). Nous
utiliserons l'IDE IntelliJ disponible sur vos machines virtuelles pour la programmation. L'accès
nécessite une licence, que vous pouvez obtenir en vous inscrivant à l'adresse URL:
https://www.jetbrains.com/shop/eform/students. Utilisez vos emails de l'UIT pour vous inscrire
et obtenir une licence étudiant gratuite.

Dans un premier temps, nous allons tester un programme MapReduce grâce à un exemple très
simple, le WordCount. Le Wordcount permet de calculer le nombre de mots dans un fichier
donné, en décomposant le calcul en deux étapes:

L'étape de Mapping, qui permet de découper le texte en mots et de délivrer en sortie un flux
textuel, où chaque ligne contient le mot trouvé, suivi de la valeur 1 (pour dire que le mot a été
trouvé une fois)
L'étape de Reducing, qui permet de faire la somme des 1 pour chaque mot, pour trouver le
nombre total d'occurrences de ce mot dans le texte.

Commençons par créer un projet Maven dans Intellij. Nous utiliserons dans notre cas JDK 1.8.

Pour lancer IntelliJ (n'oubliez pas de fournir vos licences le cas échéant), commencez par ouvrir
le logiciel. Ensuite, créez un nouveau projet en cliquant sur "Fichier" > "Nouveau", ce qui
affichera une fenêtre similaire à celle ci-dessous :

Comme illustré dans la figure ci-dessus, configurez les paramètres comme suit :

● Nom : WordCount,
● Build system : Maven,
● JDK : 1.8.

Ensuite, ouvrez les paramètres avancés et configurez GroupID sur hadoop.mapreduce et


ArtifactID sur WordCount. Vous créez le projet.

Double-cliquez sur le fichier pom.xml qui se trouve dans la structure de votre projet.
Sous la balise </properties>, insérez le code suivant:

<dependencies>
<!-- https://mvnrepository.com/artifact/org.apache.hadoop/hadoop-common -->
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<version>3.3.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.hadoop/hadoop-mapreduce-client-core -->
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-mapreduce-client-core</artifactId>
<version>3.3.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.hadoop/hadoop-hdfs -->
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-hdfs</artifactId>
<version>3.3.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.hadoop/hadoop-mapreduce-client-common
-->
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-mapreduce-client-common</artifactId>
<version>3.3.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.hadoop/hadoop-mapreduce-client-jobclient
-->
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-mapreduce-client-jobclient</artifactId>
<version>3.3.6</version>
</dependency>
</dependencies>

Ainsi, votre fichier pom.xml doit ressembler à ceci :

Maintenant, faites un clic droit sur le package hadoop.mapreduce, sélectionnez New, puis
sélectionnez Java Class. . Cela ouvrira une boîte de dialogue de création de classe Java.
Entrez le nom de la classe "TokenizerMapper" comme indiqué ci-dessous :
Appuyez sur Entrée, puis ajoutez le code suivant à la classe TokenizerMapper :

package hadoop.mapreduce;

import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
import java.io.IOException;
import java.util.StringTokenizer;

public class TokenizerMapper


extends Mapper<Object, Text, Text, IntWritable>{
private final static IntWritable one = new IntWritable(1);
private Text word = new Text();
public void map(Object key, Text value, Mapper.Context context
) throws IOException, InterruptedException {
StringTokenizer itr = new StringTokenizer(value.toString());
while (itr.hasMoreTokens()) {
word.set(itr.nextToken());
context.write(word, one);
}
}
}

Refaites les mêmes étapes de création de classes, mais cette fois-ci pour la classe appelée
"IntSumReducer". Une fois que c'est fait, ajoutez le code suivant :

package hadoop.mapreduce;

import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
import java.io.IOException;
public class IntSumReducer
extends Reducer<Text,IntWritable,Text,IntWritable> {
private IntWritable result = new IntWritable();
public void reduce(Text key, Iterable<IntWritable> values,
Context context
) throws IOException, InterruptedException {
int sum = 0;
for (IntWritable val : values) {
System.out.println("value: "+val.get());
sum += val.get();
}
System.out.println("--> Sum = "+sum);
result.set(sum);
context.write(key, result);
}
}

Enfin, modifiez la classe Main en y insérant ce code:

package hadoop.mapreduce;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

public class Main {


public static void main(String[] args) throws Exception {
Configuration conf = new Configuration();
Job job = Job.getInstance(conf, "word count");
job.setJarByClass(Main.class);
job.setMapperClass(TokenizerMapper.class);
job.setCombinerClass(IntSumReducer.class);
job.setReducerClass(IntSumReducer.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
FileInputFormat.addInputPath(job, new Path(args[0]));
FileOutputFormat.setOutputPath(job, new Path(args[1]));
System.exit(job.waitForCompletion(true) ? 0 : 1);
}
}

Maintenant, dans le dossier "Resources" de votre système de fichiers, créez le répertoire "input"
(clic droit sur "Resources" > New > Directory), puis créez le fichier "file.txt" dans le
sous-répertoire "input" (clic droit sur "input" > New > File). Ouvrez le fichier "file.txt" (double-clic)
et insérez les lignes suivantes :

hello world!
hello hadoop!
Working with Hadoop is so much fun!

Maintenant, retournez à la classe Main et lancez l'exécution en cliquant sur la flèche verte près
du nom de la classe.

Vous obtiendrez une erreur indiquant que l'application exige deux paramètres pour pouvoir être
lancée : "input", qui contient le fichier "file.txt", et le dossier "output", qui contiendra le résultat de
l'exécution du job MapReduce.
Pour configurer ces paramètres et indiquer au programme de les prendre en compte, accédez à
l'option "Modifier la configuration" en cliquant sur "Main".

Sous la section "Build and Run", dans le champ "Program Arguments", insérez ces arguments
comme suit : src/main/resources/input/file.txt src/main/resources/output , puis cliquez sur "Run"
en bas.

Et voilà ! Le programme s'exécute et crée le répertoire "output" sous le répertoire "resources".


En affichant son contenu, vous retrouverez le fichier "part-r-00000" qui contient le résultat de
l'exécution.

Jusqu'à présent, ce que vous avez fait est l'exécution d'un job MapReduce sur votre machine
locale (machine virtuelle), mais l'objectif final est de pouvoir exécuter ce job sur le cluster de
nœuds. Ainsi, vous allez construire le fichier JAR à partir de votre projet. Pour ce faire, vous
allez utiliser Maven. Maven est un outil de gestion de projets et de construction de logiciels qui
permet d'automatiser les tâches de compilation, de test, de packaging et de déploiement d'une
application. Il utilise le fichier de configuration XML appelé "pom.xml" pour définir la structure du
projet, les dépendances, les plugins et les différentes étapes du cycle de vie du projet. En
exécutant des commandes Maven telles que "mvn clean package", Maven gère
automatiquement le téléchargement des dépendances, la compilation du code source, la
génération de rapports, la création du JAR ou du WAR, etc. Cela permet de simplifier le
processus de construction et de garantir la cohérence du projet à travers les différentes phases
de développement.

Pour pouvoir encapsuler toutes les dépendances du projet dans le fichier JAR à exporter,
ajouter le plugin suivant dans le fichier pom.xml de votre projet (sous la balise
</dependencies>):

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.6.0</version> <!-- Use latest version -->
<configuration>
<archive>
<manifest>
<mainClass>hadoop.mapreduce.Main</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
<executions>
<execution>
<id>make-assembly</id> <!-- this is used for inheritance merges -->
<phase>package</phase> <!-- bind to the packaging phase -->
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

Ensuite, ouvrez l'onglet Maven à gauche. En cliquant sur le nom du projet (WordCount), vous
verrez des options. Cliquez sur "Lifecycle". Vous trouverez des commandes Maven parmi
lesquelles vous sélectionnez "install". Ensuite, effectuez un clic droit dessus et cliquez sur "Run
Maven Build".

Vous pouvez suivre le journal de l'exécution de la commande affiché dans la fenêtre Run de
l'éditeur en bas. Une fois l'exécution terminée, le message "Build Success" est affiché, indiquant
la finalisation de la génération du fichier JAR. Ce fichier se trouve dans le répertoire "target"
sous le nom "WordCount-1.0-SNAPSHOT-jar-with-dependencies.jar".

Accédez au terminal de l'éditeur IntelliJ et connectez-vous en tant que root. Ensuite, exécutez la
commande suivante:

docker cp target/WordCount-1.0-SNAPSHOT-jar-with-dependencies.jar
hadoop-master:/root/wordcount.jar
Cette commande permet de copier le fichier JAR dans le répertoire /root/ du nœud
hadoop-master.

Revenez au shell du conteneur master, et lancer le job map reduce avec cette commande:
hadoop jar wordcount.jar input output

Le Job sera lancé sur le chier purchases.txt que vous aviez préalablement chargé dans le
répertoire input de HDFS. Une fois le Job terminé, un répertoire output sera créé. Si tout se
passe bien, vous obtiendrez un affichage ressemblant au suivant:

Il vous est possible de monitorer vos Jobs Map Reduce, en allant à la page:
http://localhost:8088. Vous trouverez votre Job dans la liste des applications comme suit:

Il est également possible de voir le comportement des noeuds workers, en allant à l'adresse:
http://localhost:8041 pour worker1, et http://localhost:8042 pour worker2. Vous obtiendrez ce qui
suit:
Travail à réaliser:

● Refaites le TP en suivant les instructions fournies.


● Fournissez une capture d'écran à chaque étape pour constituer un compte rendu.
● Déposez le compte rendu sur la plateforme moodle à la fin.

Questions bonus:

● Modifiez le MapReduce job pour déterminer les catégories de produits les plus
populaires.
● Modifiez le MapReduce job pour calculer le montant total des achats par catégorie de
produit.

Aide:

● Pour extraire le premier champ (date d'achat) du fichier :

String[] fields = value.toString().split("\t");


String productValue = fields[1];

N'hésitez pas à me demander si vous avez besoin de plus d'informations ou d'aide


supplémentaire !

Bon travail!

–fin–

Vous aimerez peut-être aussi