Open7

HadoopをDockerで動かす

gtnaogtnao

動機

データエンジニアリングに関わる仕事をしているもののも、世代的にHadoopを直接触った経験がなかったため勉強がてらに1から環境を構築してみたい。

方針

前提知識が少ないので、気軽に試行錯誤できるようにDockerコンテナ上で動かす。
既に諸々入っているイメージもあると思うが、勉強のために最低限の状態(JDKイメージ)から順を追って構築していきたい。
また、完成系のDockerfileをシェアするだけだと、私のような初学者としては1行1行が何のためにあるのか分かりづらいと思うので、エラーなど含め試行錯誤の垂れ流す。

前提知識

  • Hadoopエコシステムで触ったことがあるのはSparkのみ。AWS Glue上で動かしたことがある程度。人が書いたコードの改修程度なのでそんなに突っ込んだことはやっていない。
  • ビッグデータ系の書籍でHDFSやMapReduceといった概念だけかいつまんだことがある程度。上述の通り実際に手を動かしたことはなし。
gtnaogtnao

OpenJDKのイメージはDeprecatedだったため、eclipse-temurinを使う。とりあえず何でもいいと思う。

$ docker run -it --rm eclipse-temurin:8u382-b05-jdk-jammy bash

とりあえず、bashで入って試行錯誤する。

gtnaogtnao

何はともあれ、Hadoopを入れる。

https://www.apache.org/dyn/closer.cgi/hadoop/common/hadoop-3.3.6/hadoop-3.3.6.tar.gz

$ wget https://dlcdn.apache.org/hadoop/common/hadoop-3.3.6/hadoop-3.3.6.tar.gz
$ tar -xzvf hadoop-3.3.6.tar.gz

これで、カレントディレクトリにhadoop-3.3.6というディレクトリができて、ここにバイナリとか設定ファイルとか全て入っている。

そのままでもいいが、/usr/local/以下に移動。
バージョンを毎回気にするのが面倒なのでシンボリックリンクを貼っておく。
パスも通しておく。

$ mv hadoop-3.3.6 /usr/local/
$ ln -s /usr/local/hadoop-3.3.6 /usr/local/hadoop
$ export HADOOP_HOME=/usr/local/hadoop
$ export PATH=$HADOOP_HOME/bin:$HADOOP_HOME/sbin:$PATH
$ hadoop version
Hadoop 3.3.6
Source code repository https://github.com/apache/hadoop.git -r 1be78238728da9266a4f88195058f08fd012bf9c
Compiled by ubuntu on 2023-06-18T08:22Z
Compiled on platform linux-x86_64
Compiled with protoc 3.7.1
From source with checksum 5652179ad55f76cb287d9c633bb53bbd
This command was run using /usr/local/hadoop-3.3.6/share/hadoop/common/hadoop-common-3.3.6.jar
gtnaogtnao

Hadoopの設定ファイルを書いていく。

その前にvimが入ってなかったので入れておく。

$ apt-get update
$ apt-get install -y vim

まずは、$HADOOP_HOME/etc/hadoop/core-site.xml
ファイルは既に存在するが空なので、とりあえず以下の最低限の設定で上書き。

core-site.xml
<configuration>
  <property>
    <name>fs.default.name</name>
    <value>hdfs://localhost:9000</value>
  </property>
</configuration>

次に、$HADOOP_HOME/etc/hadoop/hdfs-site.xml
こちらもとりあえず最低限の設定で。

hdfs-site.xml
<configuration>
  <property>
    <name>dfs.replication</name>
    <value>1</value>
  </property>
</configuration>

果たしてこれで動くのか。

gtnaogtnao

まずはhdfsの初期化を行う。

$ hdfs namenode -format

これは成功。

次に、hdfsのデーモンを起動する。
(このシェルは$HADOOP_HOME/sbin/start-dfs.shにある。)

$ start-dfs.sh
Starting namenodes on [localhost]
ERROR: Attempting to operate on hdfs namenode as root
ERROR: but there is no HDFS_NAMENODE_USER defined. Aborting operation.
Starting datanodes
ERROR: Attempting to operate on hdfs datanode as root
ERROR: but there is no HDFS_DATANODE_USER defined. Aborting operation.
Starting secondary namenodes [9e9b5fb43079]
ERROR: Attempting to operate on hdfs secondarynamenode as root
ERROR: but there is no HDFS_SECONDARYNAMENODE_USER defined. Aborting operation.

rootユーザーで実行していることで怒られた。
調べてみるとhadoopユーザーを切っている例が多いが、とりあえず運用的なことは無視してDocker上動かしたいだけなので、rootユーザーのままどうにかできないか考える。

HDFS_NAMENODE_USERHDFS_DATANODE_USERHDFS_SECONDARYNAMENODE_USERこの辺の環境変数にrootを指定してみる。

$ export HDFS_NAMENODE_USER=root
$ export HDFS_DATANODE_USER=root
$ export HDFS_SECONDARYNAMENODE_USER=root
$ start-dfs.sh
Starting namenodes on [localhost]
ERROR: JAVA_HOME is not set and could not be found.
Starting datanodes
ERROR: JAVA_HOME is not set and could not be found.
Starting secondary namenodes [9e9b5fb43079]
ERROR: JAVA_HOME is not set and could not be found.

今度は別のエラー。JAVA_HOMEが見当たらないと言われているが、JDKのベースイメージなので環境変数には設定されているはず。
調べてみると$HADOOP_HOME/etc/hadoop/hadoop-env.shで明示的にJAVA_HOMEを設定するらしいので追記。

export JAVA_HOME=/opt/java/openjdk

果たして上手くいくか。

gtnaogtnao
$ start-dfs.sh
Starting namenodes on [localhost]
localhost: /usr/local/hadoop-3.3.6/bin/../libexec/hadoop-functions.sh: line 982: ssh: command not found
Starting datanodes
localhost: /usr/local/hadoop-3.3.6/bin/../libexec/hadoop-functions.sh: line 982: ssh: command not found
Starting secondary namenodes [9e9b5fb43079]
9e9b5fb43079: /usr/local/hadoop-3.3.6/bin/../libexec/hadoop-functions.sh: line 982: ssh: command not found

ぐぬぬ。
また別のエラー。なかなか動かすだけでも大変ですね。

https://hadoop.apache.org/docs/stable/hadoop-project-dist/hadoop-common/SingleCluster.html

ssh must be installed and sshd must be running to use the Hadoop scripts that manage remote Hadoop daemons if the optional start and stop scripts are to be used. Additionally, it is recommmended that pdsh also be installed for better ssh resource management.

sshdが動いている必要があるとのこと。
sshを入れて、諸々設定する。

$ apt-get install -y ssh
$ ssh-keygen -t rsa -P '' -f ~/.ssh/id_rsa
$ cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
$ chmod 0600 ~/.ssh/authorized_keys

sshdを起動。

$ /usr/sbin/sshd
Missing privilege separation directory: /run/sshd

/run/sshdディレクトリが存在しないので作成して再度実行。

$ mkdir /run/sshd
$ /usr/sbin/sshd

ここまでやって動いた。

$ start-dfs.sh
Starting namenodes on [localhost]
localhost: Warning: Permanently added 'localhost' (ED25519) to the list of known hosts.
Starting datanodes
Starting secondary namenodes [9e9b5fb43079]
9e9b5fb43079: Warning: Permanently added '9e9b5fb43079' (ED25519) to the list of known hosts.

$ jps
3778 DataNode
4020 SecondaryNameNode
3653 NameNode
4283 Jps
gtnaogtnao

有名なWordCountを動かしてみる。

ファイルを作成して、HDFS上のディレクトリにコピーする。
hdfs dfsに続けて、-mkdirなど見知ったファイル操作のオプション名を付けて操作する。

$ echo "foo bar baz foo" > sample.txt
$ hdfs dfs -mkdir /input
$ hdfs dfs -put sample.txt /input
$ hdfs dfs -ls /input
Found 1 items
-rw-r--r--   1 root supergroup         16 2023-09-24 16:38 /input/sample.txt

サンプルのjarファイルが既に存在するのでこれを使う。
自分でコードを書くのはまた今度。

$ hadoop jar /usr/local/hadoop/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.3.6.jar wordcount /input /output

$ hdfs dfs -ls /output
Found 2 items
-rw-r--r--   1 root supergroup          0 2023-09-24 16:42 /output/_SUCCESS
-rw-r--r--   1 root supergroup         18 2023-09-24 16:42 /output/part-r-00000

HDFS上の/output/part-r-00000が結果みたいなので-catで見てみる。

$ hdfs dfs -cat /output/part-r-00000
bar     1
baz     1
foo     2

ここまで長かったですが、とりあえず動かせました!