Im ersten Teil haben wir die Grundlagen des MapReduce erlebt, heute ist es dann endlich soweit, wir starten mit Hadoop, dem beliebtesten Framework um MapReduce verteilt über eine Menge Computer laufen zu lassen. Dafür steht heute ein nerviger Teil an: Die Installation. Wir verlassen uns auf die neueste Version von Cloudera CDH4, und hier gibt es eine gute Installationsanleitung. Aber irgendetwas klemmt immer, und daher habe ich meinen Weg aufgeschrieben, eine funktionierende lokale Version von CDH4 (pseudo-verteilt) zu installieren. Zwar lief das alles schon auf meinem Arbeitscomputer, doch ich wollte es auch nochmal auf meinen Privatlaptop bringen. Der ist aber schon auf Ubuntu 12.10, für das es noch kein offizielles CDH4 gibt und zu allem Unglück auch noch 32bit, was zu ein paar Problemen geführt hat – die Version für Ubuntu 12.04 läuft zwar im großen und ganzen, aber ein bisschen was muss man von Hand installieren. Vielleicht hilft das dem einen oder anderen.
Das Ziel ist, das neue (noch nicht produktionsreife) MapReduce 2.0 mit Yarn zu installieren – und zwar so schnell wie möglich, ohne Rücksicht auf Dinge die Systemadministratoren die Haare zu Berge stehen lassen. Solange wir es zum Testen auf unserem Privatlaptop verwenden, finde ich das in Ordnung, wir wollen schließlich mit den Daten arbeiten und nicht dem Ops-Team beitreten und Linux verwalten.
Für das übliche “alte” MapReduce in Hadoop gibt es hier ein großartiges Tutorial.

Installation

Beginnen wir langsam, in die Welt von Hadoop einzusteigen. Zunächst müssen wir sicherstellen, dass die richtige Java-Version installiert ist. Cloudera empfiehlt JDK 1.6.0_31, der Download dazu lässt sich hier finden. Für mein Linux nehme ich jdk-6u31-linux-i586.bin, wenn ihr 64 bit braucht, nehmt jdk-6u31-linux-x64.bin. Oracle nervt euch dann mit einer Anmeldung, ist aber kostenlos. Sagen wir, ihr speichert diese Datei in /home/joerg/installs:

cd ~/installs
chmod +x jdk-6u31-linux-i586.bin
sudo ./jdk-6u31-linux-i586.bin

Am Ende drückt nur Enter und ignoriert Registrierungswünsche. Bei mir ist Chrome so freundlich, sudo Browser erst gar nicht zu öffnen. Jetzt folgt eine wichtige Sache, das Setzen der Umgebungsvariable damit Hadoop das richtige Java findet. Die JDK ist jetzt in ~/installs installiert, öffnet also .bashrc in eurem Home und folgt die folgende Zeile am Ende ein (und denkt dran euren Benutzernamen/Ordernamen zu verwenden):

export JAVA_HOME=/home/joerg/installs/jdk1.6.0_31

Eventuell kanns Probleme mit den Zugriffsrechten geben. Für lokale Installationen mach ich da nie rum, und setz einfach alles auf 777. Natürlich darf man das nie für Server zu machen, aber was schert es uns auf unserem privaten Rechner:

sudo chmod -R 777 /home/joerg/installs/jdk1.6.0_31

Zurück in der Konsole vergesst nicht bash um die Umgebung neu zu starten, oder führt die obige Zeile in der Konsole als Befehl aus, um die Umgebungsvariable für die aktuelle Session zu setzen.
Als kleiner Tipp, bei allem was wir so installieren, ist es immer ratsam in die Konfiguration (meistens in /etc/hadoop/conf/…-env.sh o.ä.) auch noch JAVA_HOME zu setzen. Das hilft oft bei Problemen. Jetzt, falls ihr es noch nicht habt, installiert aptitude, das ist mir lieber als apt-get, weil es Suche einfacher macht…

sudo apt-get install aptitude

Cloudera ist so freundlich, für CDH4 ein repository anzubieten. Es gibt bislang noch keines für Ubuntu 12.10, daher fügen wir das für Ubuntu 12.04 hinzu und hoffen. Fügt am Ende von /etc/apt/sources.list folgende Zeile hinzu:

deb [arch=amd64] https://archive.cloudera.com/cdh4/ubuntu/precise/amd64/cdh precise-cdh4 contrib
deb-src https://archive.cloudera.com/cdh4/ubuntu/precise/amd64/cdh precise-cdh4 contrib

Und installieren ein wenig Hadoop:

sudo aptitude update
sudo aptitude install hadoop-conf-pseudo

Da bin ich dann in viele tragische Probleme gelaufen weil ein Paket, bigtop-jsvc, nicht für mein 32bit System verfügbar ist. Also habe ich versucht, es zu kompilieren.

cd ~/installs
sudo aptitude install git autoconf automake subversion build-essential dh-make debhelper devscripts reprepro sharutils
git clone https://github.com/apache/bigtop.git
cd bigtop
make bigtop-jsvc-deb
sudo dpkg -i output/bigtop-jsvc/bigtop-jsvc_1.0.10-1_i386.deb

Wie gesagt, das braucht ihr nur falls die Installation von hadoop-conf-pseudo Probleme bereitet (wenn, dann installiert bigtop-jsvc und danach erneut obige Installation für hadoop-conf-pseudo aus. Und nehmt das Apache Bigtop von Git, nicht das Cloudera). Nun haben wir die wichtigsten Teile eines grundlegenden Hadoop-Systems, um Hadoop MapReduce in HDFS laufen zu lassen. Moment, HDFS?

HDFS

Hadoop dreht sich um große Daten. Wirklich, wirklich große Datenmengen. Und wenn man soviele Daten hat, kann man die nicht einfach mehr auf einer Festplatte halten. Außerdem will Hadoop freies Skalieren möglich machen – wenn man mehr Daten hat sollte es so einfach sein, einfach mehr Rechner dazu zu stellen.
Daher gibt es das Hadoop Distributed File System, ein verteiltes Dateisystem. Die Daten werden dazu über sogenannte Datenknoten/data nodes verteilt, die von einem Namensknoten/name node verwaltet werden. Der name node ist eine Schwachstelle, geht dieser Rechner unter, ist HDFS nicht mehr erreichbar. Die Datenspeicherkapazität kann aber vergrößert werden, indem man mehr data nodes einfügt. Zur Datensicherheit und -verfügbarkeit beim Ausfall von Knoten, werden diese mehrfach repliziert (ähnlich wie bei einem RAID). Wer mehr Details haben will, kann dies z.B. hier nachlesen.

Back to Business

Randbemerkung: Eine Sache fehlt noch in Ubuntu, eine dumme Einstellung die viel Unheil anrichten kann. Falls ihr je Fehler seht mit Verbindungsproblemen zwischen dem Namen eures Computers und localhost oder 127.0.0.1, editiert /etc/hosts und setzt in der zweite Zeile die IP von 127.0.1.1 auf 127.0.0.1. Aus irgendeinem Grund stellt Ubuntu da den eigenen Hostnamen auf 127.0.1.1 um, was beispielsweise HBase völlig verwirrt und sicher auch sonst mal für Probleme sorgen kann.

Und nun stellen wir sicher, dass auch Hadoop das richtige Java findet! Editiert dazu /etc/hadoop/conf/hadoop.env.sh und fügt die bekannte Zeile am Ende ein (wieder mit eurem Usernamen statt meinem…):

export JAVA_HOME=export JAVA_HOME=/home/joerg/installs/jdk1.6.0_31

Zum Starten und Stoppen von Hand (normalerweise starten die Services jetzt beim Reboot) legen wir ein Skript in ~/bin an und nennen es hadoopStart.sh

for service in /etc/init.d/hadoop-hdfs-*; do
    sudo $service start
done

Und eines zum Stoppen als ~/bin/hadoopStop.sh:

for service in /etc/init.d/hadoop-hdfs-*; do
    sudo $service stop
done

Das wir auch gleich ausführen

bash ~/bin/hadoopStop.sh

Jetzt sind wir bereit, HDFS zu formatieren, HDFS zu starten und die Verzeichnisstruktur anzulegen.

sudo -u hdfs hdfs namenode -format
sudo chmod -R 777 /var/lib/hadoop-*
bash ~/bin/hadoopStart.sh
sudo -u hdfs hadoop fs -mkdir /tmp
sudo -u hdfs hadoop fs -chmod -R 1777 /tmp
sudo -u hdfs hadoop fs -mkdir /tmp/hadoop-yarn/staging
sudo -u hdfs hadoop fs -chmod -R 1777 /tmp/hadoop-yarn/staging
sudo -u hdfs hadoop fs -mkdir /tmp/hadoop-yarn/staging/history/done_intermediate
sudo -u hdfs hadoop fs -chmod -R 1777 /tmp/hadoop-yarn/staging/history/done_intermediate
sudo -u hdfs hadoop fs -chown -R mapred:mapred /tmp/hadoop-yarn/staging
sudo -u hdfs hadoop fs -mkdir /var/log/hadoop-yarn
sudo -u hdfs hadoop fs -chown yarn:mapred /var/log/hadoop-yarn

Ihr seht, dass dort bereits die Services für HDFS gestartet werden, der data node und der name node. Da wir pseudo-verteilt sind, laufen diese natürlich lokal auf unserem Rechner und simulieren nur, was sie normal über Rechner verteilt anstellen. Es gibt auch noch den secondary name-node, der einen Teil der Arbeit übernimmt falls der name node ausfällt, den brauchen wir hier überhaupt nicht. Wenn ihr wollt, könnt ihr den Service abschalten oder gar deinstallieren. Jetzt fehlt nur noch, die MapReduce-Services zu starten…

sudo chown mapred /var/log/hadoop-mapreduce
sudo service hadoop-yarn-resourcemanager start
sudo service hadoop-yarn-nodemanager start
sudo service hadoop-mapreduce-historyserver start

(Wenn ihr wollt, legt für die letzten drei Services auch noch Start- und Stopscripts an…aber wie gesagt, sollten eh beim Booten starten). Noch den Homefolder in HDFS anlegen…
Nebenbei, wenn ihr das etablierte MRv1 statt dem neuen Yarn verwenden wollt, müsstet ihr hier die hadoop-0.20-mapreduce Services Jobtracker und Tasktracker starten.

sudo -u hdfs hadoop fs -mkdir /user/joerg
sudo -u hdfs hadoop fs -chown joerg /user/joerg

Und eine Umgebungsvariable in ~/.bashrc hinzufügen

export HADOOP_MAPRED_HOME=/usr/lib/hadoop-mapreduce

Endlich Wörter zählen!

Und wir sind endlich soweit, unser Beispiel vom letzten Mal ausführen zu können. Wir verwenden dazu Hadoop Streaming. Um das “eigentliche” MapReduce zu verwenden, müssten wir Code in Java schreiben, und nach der schmerzhaften Installation wollen wir das nun nicht auch noch. Daher übernimmt das Hadoop Streaming jetzt, was wir vorher in der Shell von Hand gemacht haben: Den input über stdin an einen Mapper füttern, die Ausgabe sortieren, und an die Reducer verteilen. Dann den output in HDFS abspeichern, und zwar in einer Datei je Reducer.
Theoretisch, hätten wir eine echte Serverinstallation von Hadoop, könnten wir jetzt beliebig viele Computer als Mapper einsetzen, und auch als Reducer. Unser Code ist frei skalierbar geworden, und dank HDFS auch unsere Datenspeicherkapazität.
Wenn wir map.py und reduce.py vom letzten Mal verwenden wollen, müssen wir noch als erste Zeile in jedem dieser Skripte

#!/usr/bin/python

ergänzen und Ausführrechte setzen:

chmod a+x map.py reduce.py

Streaming erhält als Parameter die Skripte für mapper und reducer, und mit dem -file Parameter die Dateien die verschickt werden sollen und auf JEDEM Rechner landen. Die Eingabedatei ist aber sehr groß (jedenfalls wenn wir MapReduce ernsthaft für Big Data einsetzen), und daher verteilt auf dem HDFS. Dazu kopieren wie die Eingabedatei ins HDFS. Ich habe hier den Volltext von Moby Dick, aber ihr könnt eine beliebige Textdatei nehmen.

hadoop fs -copyFromLocal pg2701.txt .

Zeit, den Streaming-Job zu starten. Stellt sicher, dass ihr keine Fehlermeldungen seht, Warnungen kann man im allgemeinen ignorieren.

hadoop jar /usr/lib/hadoop-mapreduce/hadoop-streaming.jar -mapper map.py -reducer reduce.py -input /user/joerg/pg2701.txt -output /user/joerg/output -file ./map.py -file ./reduce.py

Die Ausgabedatei liegt jetzt im HDFS. Ihr könnt sie zurückkopieren. Für jeden Reducer wird es eine Datei geben, aber da wir nur einen verwendet haben, ist die Datei part-r-00000 im Ausgabeordner. Mit cat könnt ihr sie ausgeben lassen und das Resultat der harten Installationsarbeit bewundern:

hadoop fs -copyToLocal /user/joerg/output .
cat output/part-r-00000

Bevor ihr erneut streamen könnt, müsst ihr den Ausgabeordner im HDFS löschen:

hadoop fs -rm -r /user/joerg/output

Herzlichen Glückwunsch! Jetzt habt ihr ein funktionierendes MapReduce-Framework, und beim nächsten Mal können wir uns näher mit den Kommandozeilenparametern für Streaming beschäftigen und ein neues Problem lösen.

Kommentare (2)

  1. […] dem schmerzhaften zweiten Teil, der Installation, haben wir jetzt ein Hadoop-System zum Spielen. Es läuft lokal, reagiert aber […]

  2. […] dem schmerzhaften zweiten Teil, der Installation, haben wir jetzt ein Hadoop-System zum Spielen. Es läuft lokal, reagiert aber […]