Teil 1 ist geschafft, Linux und Java laufen gut zusammen und das Netzwerk in die VM hinein und hinaus steht auch; dann auf zur Installation von Hadoop. Im Januar 2013 sind Version 1.0.4 stabil, Version 1.1.1 beta und Version 2.0.2 alpha; ich werde mich hier auf 1.0.4 beziehen.

Das Hadoop-Release mit wget herunterladen, entpacken und wie auch Java in /opt verschieben.

[manuel@dexter ~]$ wget http://mirror.derwebwolf.net/apache/hadoop/common/hadoop-1.0.4/hadoop-1.0.4.tar.gz
[manuel@dexter ~]$ tar xfz hadoop-1.0.4.tar.gz
[manuel@dexter ~]$ sudo mv hadoop-1.0.4/ /opt/

Mit der Standalone Operation lässt sich testen, ob Hadoop prinzipiell läuft:

[manuel@dexter ~]$ cd /opt/hadoop-1.0.4/
[manuel@dexter hadoop-1.0.4]$ mkdir input
[manuel@dexter hadoop-1.0.4]$ cp conf/*.xml input/
[manuel@dexter hadoop-1.0.4]$ bin/hadoop jar hadoop-examples-1.0.4.jar grep input output 'dfs[a-z.]+'
[manuel@dexter hadoop-1.0.4]$ cat output/*
1 dfsadmin

(wobei die letzte Zeile Output ist. Und wo hier schon eine Klammer ist, ist auch Zeit, unnötige Dateien und Ordner zu löschen: Die eben erzeugten Ordner input und output braucht niemand mehr, die tarballs von hadoop und java in ~ sollten auch überflüssig geworden sein.) Jetzt ziehe ich die Konfiguration von Hadoop in /etc/hadoop um.

[manuel@dexter ~]$ sudo mkdir /etc/hadoop
[manuel@dexter ~]$ sudo cp -r /opt/hadoop-1.0.4/conf/ /etc/hadoop/

Dieses Verzeichnis muss Hadoop jetzt noch bekannt gemacht werden; dazu lege ich - analog zu Java - eine /etc/profile.d/hadoop.sh mit folgendem Inhalt an:

#!/bin/sh
HADOOP_CONF_DIR=/etc/hadoop/conf
export HADOOP_CONF_DIR

Und da es hier gerade passt, muss auch Hadoop wissen, wo Java wohnt, deshalb setze ich in /etc/hadoop/conf/hadoop-env.sh auch

export JAVA_HOME=/opt/jdk1.7.0_10

SSH und die Keys

Es wird empfohlen, passphraseless SSH einzurichten, dann mache ich das auch mal. Allerdings ändere ich den Befehl ein wenig ab:

[manuel@dexter ~]$ ssh-keygen -t dsa -P '' -f ~/.ssh/dexter_id_dsa
[manuel@dexter ~]$ cat ~/.ssh/dexter_id_dsa.pub >> ~/.ssh/authorized_keys

Eigentlich ist nur der Name des Keys anders, aber den kann man jetzt auch ins Hostsystem übernehmen, um sich auf der VM ohne Passwort anmelden zu können:

[manuel@hostsystem ~]$ scp 192.168.56.101:.ssh/dexter_id_dsa .ssh/

Da dieser Dateiname des Privatekeys normalerweise von SSH nicht benutzt wird, lege ich im Hostsystem eine ~/.ssh/config an, in der auch gleich ein schicker Hostname gesetzt wird:

Host dexter
    User manuel
    HostName 192.168.56.101
    IdentityFile ~/.ssh/dexter_id_dsa

Ab jetzt genügt

[manuel@hostsystem ~]$ ssh dexter

um in die VM zu kommen. Auch dort muss so eine ~/.ssh/config angelegt werden; die kann aber kleiner ausfallen:

Host localhost
    IdentityFile ~/.ssh/dexter_id_dsa

Pseudo-Distributed Operation

Weiter geht es mit den Einstellungen für die Pseudo-Distributed Operation, bei der alle Komponenten auf der gleichen Maschine, aber in getrennten Prozessen laufen. Die Komponenten sind:

  • namenode (HDFS Master)
  • datanode (HDFS Slave)
  • secondarynamenode
  • jobtracker (Mapreduce Master)
  • tasktracker (Mapreduce Slave)

Die ersten drei sind für Datenhaltung verantwortlich. Wie sie zusammenhängen zeigen Bild und Text hier sehr schön, oder in kurz: Der namenode ist der Masterserver und kontrolliert n datanodes. Die datanodes selbst speichern die Daten. Auf diesen Rechenknoten läuft HDFS das Hadoop Distributed File System. Der secondary namenode ist eigentlich egal und eh deprecated ;)

Die letzten beiden Komponenten dienen der Ausführung von Mapreduce-Jobs. Auch hier gibt es einen Master, den jobtracker und n Slaves, die tasktracker.

HDFS

Fürs pseudo-distributed Setup soll also ein Datanode seine Daten im lokalen Dateisystem (ich bezeichne das jetzt mal als lokales Dateisystem im Gegensatz zu HDFS) ablegen und die Mapreduce-Komponenten sollen darauf zugreifen. Hadoop ist standardmäßig eingestellt, alles in /tmp abzulegen, was ich eher weniger praktisch finde; ich spendiere deshalb einen eigenes Verzeichnis:

sudo mkdir -p /hadoop/hdfs/namenode
sudo mkdir /hadoop/hdfs/datanode
sudo chown -R manuel:users /hadoop

In /etc/hadoop/conf/hdfs-site.xml werden diese beiden Verzeichnisse und der Replikationsgrad eingestellt:

<configuration>
    <property>
        <name>dfs.replication</name>
        <value>1</value>
    </property>
    <property>
        <name>dfs.name.dir</name>
        <value>/hadoop/hdfs/namenode</value>
    </property>
    <property>
        <name>dfs.data.dir</name>
        <value>/hadoop/hdfs/datanode</value>
    </property>
</configuration>

Und in /etc/hadoop/conf/core-site.xml wird der Port angegeben

<configuration>
     <property>
         <name>fs.default.name</name>
         <value>hdfs://localhost:9000</value>
     </property>
</configuration>

Dann kann der Namenode auch mit formatiert werden:

/opt/hadoop-1.0.4/bin/hadoop namenode -format

Und wie zu erwarten war, füllt sich das oben konfigurierte Namenodeverzeichnis; da noch keine Daten gespeichert werden, ist das Datanodeverzeichnis aber noch leer. Um wirklich Daten in HDFS zu bekommen, wird zunächst der Dienst gestartet:

/opt/hadoop-1.0.4/bin/start-dfs.sh

Dieses Script loggt sich dann via SSH auf localhost ein und startet die drei o.g. Dienste. Davon kann man sich mit dem Tool jps überzeugen:

[manuel@dexter ~]$ jps
1839 NameNode
2205 Jps
2056 SecondaryNameNode
1945 DataNode

Was das auf Netzwerkebene bedeutet, kann man sich z.B. mit lsof ansehen:

[manuel@dexter ~]$ sudo lsof -i -P | grep LISTEN
sshd     185   root    3u  IPv4   7520      0t0  TCP *:22 (LISTEN)
sshd     185   root    4u  IPv6   7522      0t0  TCP *:22 (LISTEN)
java    2454 manuel   57u  IPv6  34963      0t0  TCP *:56601 (LISTEN)
java    2454 manuel   67u  IPv6  35126      0t0  TCP localhost.localdomain:9000 (LISTEN)
java    2454 manuel   77u  IPv6  35280      0t0  TCP *:50070 (LISTEN)
java    2561 manuel   57u  IPv6  35125      0t0  TCP *:40646 (LISTEN)
java    2561 manuel   67u  IPv6  35547      0t0  TCP *:50010 (LISTEN)
java    2561 manuel   68u  IPv6  35553      0t0  TCP *:50075 (LISTEN)
java    2561 manuel   75u  IPv6  35578      0t0  TCP *:50020 (LISTEN)
java    2673 manuel   57u  IPv6  35320      0t0  TCP *:47392 (LISTEN)
java    2673 manuel   69u  IPv6  35564      0t0  TCP *:50090 (LISTEN)

Was das im einzelnen ist (Ports ohne Angabe habe ich in der Dokumentation nicht gefunden; falls ich deren Bedeutung herausfinde, schreibe ich bescheid):

Port Protokoll Kommentar
22 SSH Systemprozess
56601    
9000   Port fürs HDFS-Dateisystem
50070 HTTP Webinterface zum Namenode
40646    
50010 Hadoop Datanode Server
50075 HTTP Datanode HTTP Server
50020 IPC Interprocesscommunication Server vom Datanode
47392    
50090 HTTP (Secondary Namenode HTTP Server)

Mapreduce

Nachdem das Dateisystem im Netz erreichbar ist, bekommt der JobTracker auch eine Adresse zugewiesen mit /etc/hadoop/conf/mapred-site.xml

<configuration>
     <property>
         <name>mapred.job.tracker</name>
         <value>localhost:9001</value>
     </property>
</configuration>

Und schon kann auch der gestartet werden mit

/opt/hadoop-1.0.4/bin/start-mapred.sh

Kleine Anmerkungen: Das Tool /opt/hadoop-1.0.4/bin/start-all.sh startet zuerst dfs und dann mapred, macht also das, was hier auch gerade passiert ist. Da jetzt HDFS und Mapreduce konfiguriert sind, kann das auch benutzt werden. Zum Beenden gibt es analog /opt/hadoop-1.0.4/bin/stop-all.sh. Außerdem kann man in /etc/profile.d/hadoop.sh auch noch

PATH=$PATH:/opt/hadoop-1.0.4/bin
export PATH

ergänzen, dann erspart man sich den Pfad vor den start-/stop-Scripten.

jps verrät uns, dass jetzt auch ein JobTracker und ein TaskTracker laufen. Die neuen belegten Ports sind:

Port Protokoll Kommentar
49466    
9001 IPC JobTracker
50030 HTTP Webinterface zum JobTracker
56438    
50060 HTTP Webinterface zum TaskTracker

Ausprobieren

Jetzt schauen wir, ob der Standalone-Test von oben auch hier funktioniert. Zunächst packen wir die gleichen Dateien in HDFS:

[manuel@dexter ~]$ /opt/hadoop-1.0.4/bin/hadoop fs -put /etc/hadoop/conf/ input

Der erste Parameter für put ist dabei ein Pfad im lokalen Dateisystem, der zweite ein Pfad im HDFS. Da hier eine relative Pfadangabe steht, wird der Ordner input unter /user/manuel angelegt.

Jetzt kann der Job gestartet werden

/opt/hadoop-1.0.4/bin/hadoop jar /opt/hadoop-1.0.4/hadoop-examples-1.0.4.jar grep input/conf output 'dfs[a-z.]+'

und während er durchläuft, kann man sich im Webinterface des Jobtrackers ansehen, welche Teile gerade abgearbeitet werden. Das Ergebnis kann entweder im HDFS-Webinterface oder über die Konsole begutachtet werden:

[manuel@dexter ~]$ /opt/hadoop-1.0.4/bin/hadoop fs -cat output/*
1 dfs.data.dir
1 dfs.name.dir
1 dfs.replication
1 dfs.server.namenode.
1 dfsadmin
cat: File does not exist: /user/manuel/output/_logs

(Den letzten Fehler ignorieren wir einfach :)