LoginSignup
0
1

More than 3 years have passed since last update.

ローカルマシンで Docker を使って Hadoop の分散処理環境の構築を試みた

Last updated at Posted at 2019-12-13

1. 背景

ローカルマシンで Docker を使って Hadoop の分散処理環境の構築を試みたときのログです。
Docker を使い始めた頃に挑戦した内容のため、最適化されていない部分が多々あります。
とりあえず、動くものを作りたいというのが動機でした。

Windows10 上の VirtualBox で Ubuntu18.04を動作させ、その上で Docker を用いて Hadoop 環境をインストールしようとしています。
その VirtualBox には、8192MBのメモリと、2CPUを割り当てています。

ホスト ゲスト(VirtualBox) 割当メモリ 割当CPU
Windows10 Ubuntu18.04 8192MB 2CPU

今では、Hadoop クラスタの Docker イメージは企業からも公開されています。HDPからも sandbox でありますが、私のローカル環境ではスペック不足でフリーズしてしまいました。
sandbox

Docker バージョンの確認

この記事を書くために、動かした Docker のバージョンです。
実際に挑戦したのは、2年ほど前だったと思いますので、当時動かしたときはもっと古いバージョンだったはずです。

version
$ docker version
Client:
 Version:           18.09.7
 API version:       1.39
 Go version:        go1.10.1
 Git commit:        2d0083d
 Built:             Wed Jul  3 12:13:59 2019
 OS/Arch:           linux/amd64
 Experimental:      false

Server:
 Engine:
  Version:          18.09.7
  API version:      1.39 (minimum version 1.12)
  Go version:       go1.10.1
  Git commit:       2d0083d
  Built:            Mon Jul  1 19:31:12 2019
  OS/Arch:          linux/amd64
  Experimental:     false

2. Dockerfile

ファイル名は "Dockerfile.hadoop" です。
とにかく RUN コマンドで順番にインストールするようにしていました。
RUN コマンドを実行する度にイメージレイヤーが生成され、イメージのサイズが大きくなるなど知らなかったため、ほとんどのコマンド一つにつき、RUN コマンドを書いています。
ベースを centos6 にした理由は単に馴染みがあったためです。

Dockerfile.hadoop
FROM centos:centos6
LABEL maintainer "blueskyarea"

USER root

# 外部コマンドのインストール
RUN yum update -y libselinux; \
    yum install -y curl which tar sudo openssh-server openssh-clients rsync; \
    yum clean all

# パスワードなし ssh
# 複数のコンテナ間で ssh 接続することを想定
RUN ssh-keygen -q -N "" -t dsa -f /etc/ssh/ssh_host_dsa_key
RUN ssh-keygen -q -N "" -t rsa -f /etc/ssh/ssh_host_rsa_key
RUN ssh-keygen -q -N "" -t rsa -f /root/.ssh/id_rsa
RUN cp /root/.ssh/id_rsa.pub /root/.ssh/authorized_keys

# Javaのインストール
RUN yum install -y java-1.8.0-openjdk
ENV JAVA_HOME /usr/lib/jvm/jre
ENV PATH $PATH:$JAVA_HOME/bin
RUN rm /usr/bin/java && ln -s $JAVA_HOME/bin/java /usr/bin/java

# HDP レポジトリを登録
ENV HADOOP_GROUP hadoop
ADD http://public-repo-1.hortonworks.com/HDP/centos6/2.x/updates/2.6.2.0/hdp.repo /etc/yum.repos.d/hdp.repo

# zookeeperのインストール
# ディレクトリ構成に一貫性がなくて分かりにくい
ENV ZOOKEEPER_USER zookeeper
ENV ZOOKEEPER_LOG_DIR /var/log/zookeeper
ENV ZOOKEEPER_PID_DIR /var/run/zookeeper
ENV ZOOKEEPER_DATA_DIR /grid/hadoop/zookeeper/data
ENV ZOOKEEPER_CONF_DIR /etc/zookeeper/conf

RUN yum install -y zookeeper-server hadoop

RUN mkdir -p $ZOOKEEPER_LOG_DIR
RUN chown -R $ZOOKEEPER_USER:$HADOOP_GROUP $ZOOKEEPER_LOG_DIR
RUN chmod -R 755 $ZOOKEEPER_LOG_DIR

RUN mkdir -p $ZOOKEEPER_PID_DIR
RUN chown -R $ZOOKEEPER_USER:$HADOOP_GROUP $ZOOKEEPER_PID_DIR
RUN chmod -R 755 $ZOOKEEPER_PID_DIR

RUN mkdir -p $ZOOKEEPER_DATA_DIR
RUN chmod -R 755 $ZOOKEEPER_DATA_DIR
RUN chown -R $ZOOKEEPER_USER:$HADOOP_GROUP $ZOOKEEPER_DATA_DIR

# なぜ、一度削除しているのか
RUN rm -r $ZOOKEEPER_CONF_DIR
RUN mkdir -p $ZOOKEEPER_CONF_DIR
RUN chmod a+x $ZOOKEEPER_CONF_DIR
RUN chown -R $ZOOKEEPER_USER:$HADOOP_GROUP $ZOOKEEPER_CONF_DIR/../
RUN chmod 755 $ZOOKEEPER_CONF_DIR/../

# Hadoopのインストール
RUN umask 0022
RUN yum install -y hadoop-hdfs hadoop-libhdfs hadoop-yarn hadoop-mapreduce hadoop-client openssl

# Snappyのインストール
# インストールする必要はあったのか?
RUN yum install -y snappy snappy-devel

# LZOのインストール
# インストールする必要はあったのか?
RUN yum install -y lzo lzo-devel hadooplzo hadooplzo-native

# NameNodeの環境設定
ENV DFS_NAME_DIR /grid/hadoop/hdfs/nn
ENV HDFS_USER hdfs
RUN mkdir -p $DFS_NAME_DIR
RUN chown -R $HDFS_USER:$HADOOP_GROUP $DFS_NAME_DIR
RUN chmod -R 755 $DFS_NAME_DIR

# SecondaryNameNodeの環境設定
ENV FS_CHECKPOINT_DIR /grid/hadoop/hdfs/snn
RUN mkdir -p $FS_CHECKPOINT_DIR
RUN chown -R $HDFS_USER:$HADOOP_GROUP $FS_CHECKPOINT_DIR
RUN chmod -R 755 $FS_CHECKPOINT_DIR

# DataNodeの環境設定
ENV DFS_DATA_DIR /grid/hadoop/hdfs/dn
RUN mkdir -p $DFS_DATA_DIR
RUN chown -R $HDFS_USER:$HADOOP_GROUP $DFS_DATA_DIR
RUN chmod -R 750 $DFS_DATA_DIR

# NodeManagerの環境設定
ENV YARN_LOCAL_DIR /grid/hadoop/yarn/local
ENV YARN_USER yarn
RUN mkdir -p $YARN_LOCAL_DIR
RUN chown -R $YARN_USER:$HADOOP_GROUP $YARN_LOCAL_DIR
RUN chmod -R 755 $YARN_LOCAL_DIR

ENV YARN_LOCAL_LOG_DIR /grid/hadoop/yarn/logs
RUN mkdir -p $YARN_LOCAL_LOG_DIR
RUN chown -R $YARN_USER:$HADOOP_GROUP $YARN_LOCAL_LOG_DIR
RUN chmod -R 755 $YARN_LOCAL_LOG_DIR

# HDFS Logsの環境設定
ENV HDFS_LOG_DIR /var/log/hadoop/hdfs
RUN mkdir -p $HDFS_LOG_DIR
RUN chown -R $HDFS_USER:$HADOOP_GROUP $HDFS_LOG_DIR
RUN chmod -R 755 $HDFS_LOG_DIR

# Yarn Logsの環境設定
ENV YARN_LOG_DIR /var/log/hadoop/yarn
RUN mkdir -p $YARN_LOG_DIR
RUN chown -R $YARN_USER:$HADOOP_GROUP $YARN_LOG_DIR
RUN chmod -R 755 $YARN_LOG_DIR

# HDFS Processの環境設定
ENV HDFS_PID_DIR /var/run/hadoop/hdfs
RUN mkdir -p $HDFS_PID_DIR
RUN chown -R $HDFS_USER:$HADOOP_GROUP $HDFS_PID_DIR
RUN chmod -R 755 $HDFS_PID_DIR

# Yarn Process IDの環境設定
ENV YARN_PID_DIR /var/run/hadoop/yarn
RUN mkdir -p $YARN_PID_DIR
RUN chown -R $YARN_USER:$HADOOP_GROUP $YARN_PID_DIR
RUN chmod -R 755 $YARN_PID_DIR

# JobHistory Server Logsの環境設定
ENV MAPRED_LOG_DIR /var/log/hadoop/mapred
ENV MAPRED_USER mapred
RUN mkdir -p $MAPRED_LOG_DIR
RUN chown -R $MAPRED_USER:$HADOOP_GROUP $MAPRED_LOG_DIR
RUN chmod -R 755 $MAPRED_LOG_DIR

# JobHistory Server Process IDの環境設定
ENV MAPRED_PID_DIR /var/run/hadoop/mapred
RUN mkdir -p $MAPRED_PID_DIR
RUN chown -R $MAPRED_USER:$HADOOP_GROUP $MAPRED_PID_DIR
RUN chmod -R 755 $MAPRED_PID_DIR

# Hadoop の環境設定
ENV HADOOP_CONF_DIR /etc/hadoop/conf
RUN chown -R $HDFS_USER:$HADOOP_GROUP $HADOOP_CONF_DIR/../
RUN chmod -R 755 $HADOOP_CONF_DIR/../

# 設定ファイルおよび、スクリプトをコンテナにコピー
COPY files/zookeeper/* $ZOOKEEPER_CONF_DIR/
COPY files/hadoop/* /etc/hadoop/conf/
COPY scripts/hdfs-setup.sh /
COPY scripts/start-zookeeper.sh /
COPY scripts/cmd.sh /

# ポート開放
EXPOSE 8020 8040 8088 9000 19888 50070

# コンテナ起動時に呼ばれるスクリプト
CMD ["/cmd.sh"]

3. docker-compose

上記の Dockerfile.hadoop を元に、hadoop273 という名前で Docker イメージを作成します。
その Docker イメージを元に、2つのコンテナを生成します。
- Resource Manager 用のコンテナ(container-hdp-rm1001)
- Node Manager 用のコンテナ(container-hdp-nm2001)

また、専用の docker network(hadoop_nw)を構築します。
これはコンテナ間でのネットワーク通信用です。

docker-compose.yml
version: '2'

services:
  # Resource Manager
  rm1001:
    build:
      context: .
      dockerfile: ./Dockerfiles/Dockerfile.hadoop
    image: hadoop273
    hostname: hdp-rm1001.docker
    container_name: container-hdp-rm1001
    ports:
      - "8040:8040"
      - "8088:8088"
      - "9000:9000"
      - "19888:19888"
      - "50070:50070"
    mem_limit: 1g
    networks:
      hadoop_nw:
        ipv4_address: 172.20.0.2
    extra_hosts:
      - "hdp-nm2001.docker:172.20.0.3"
    environment:
      TAG: RM

  # Node Manager
  nm2001:
    build:
      context: .
      dockerfile: ./Dockerfiles/Dockerfile.hadoop
    image: hadoop273
    hostname: hdp-nm2001.docker
    container_name: container-hdp-nm2001
    ports:
      - "45454:45454"
      - "50010:50010"
    depends_on:
      - "rm1001"
    mem_limit: 1g
    networks:
      hadoop_nw:
        ipv4_address: 172.20.0.3
    extra_hosts:
      - "hdp-rm1001.docker:172.20.0.2"
    environment:
      TAG: NM

networks:
  hadoop_nw:
    driver: bridge
    ipam:
      driver: default
      config:
        - subnet: 172.20.0.0/16
          gateway: 172.20.0.254

4. スクリプトファイルたち

cmd.sh (コンテナ起動時に呼ばれるスクリプト)

上記の docker-compose.yml で定義したTAG情報を元に、そのコンテナを ResourceManager(とNameNode) として起動するか、NodeManager(とDataNode)として起動するか判断させています。
また、このスクリプトが終了してしまうと、コンテナも終了してしまうため、スクリプトの最後で無限ループをつくり、スクリプト(コンテナ)が終了してしまわないようにしています。

cmd.sh
#!/bin/bash
if [ "$TAG" = 'RM' ]; then
  # ResourceManager の起動
  /usr/hdp/2.6.2.0-205/hadoop-yarn/sbin/yarn-daemon.sh start resourcemanager &&
  rm -rf /tmp/hadoop-root/dfs/data/current &&
  # NameNode の起動
  echo "Y" | /usr/hdp/2.6.2.0-205/hadoop/bin/hdfs --config /etc/hadoop/conf namenode -format &&
  /usr/hdp/2.6.2.0-205/hadoop/sbin/hadoop-daemon.sh start namenode &&
  sleep 60 &&
  /bin/bash /hdfs-setup.sh &&
  # JobHistoryServer の起動
  /usr/hdp/2.6.2.0-205/hadoop-mapreduce/sbin/mr-jobhistory-daemon.sh start historyserver &&
  # 無限ループをつくり、このスクリプトが終了しないようにしている
  while true; do sleep 1000; done
elif [ "$TAG" = 'NM' ]; then
  rm -rf /grid/hadoop/hdfs/dn/* &&
  sleep 30 &&
  # NodeManager の起動
  /usr/hdp/2.6.2.0-205/hadoop-yarn/sbin/yarn-daemon.sh start nodemanager &&
  # DataNode の起動
  /usr/hdp/2.6.2.0-205/hadoop/sbin/hadoop-daemon.sh start datanode &&
  /bin/bash /start-zookeeper.sh &&
  # 無限ループをつくり、このスクリプトが終了しないようにしている
  while true; do sleep 1000; done
fi
hdfs-setup.sh (cmd.sh 内で呼ばれるスクリプト)

HDFSに /user/my_data ディレクトリを作成しようとしています。必須ではなく、単にディレクトリを作成しようとしているだけです。

hdfs-setup.sh
#!/bin/bash
set -e
hdfs dfs -mkdir -p /user/my_data
exit 0
start-zookeeper.sh (cmd.sh 内で呼ばれるスクリプト)

zookeeper を起動しようとしています。そのコンテナが NodeManager の場合、このスクリプトが呼び出されます。

start-zookeeper.sh
#!/bin/bash
set -e
su - zookeeper -c "export ZOOCFGDIR=/usr/hdp/2.6.2.0-205/zookeeper/conf ; export ZOOCFG=zoo.cfg; source /usr/hdp/2.6.2.0-205/zookeeper/conf/zookeeper-env.sh ; export ZOO_LOG_DIR=/var/log/zookeeper ;  /usr/hdp/current/zookeeper-server/bin/zkServer.sh start"
exit 0

5. 実行

docker-compose.yml ファイルが存在するディレクトリ上にて、docker-compose up -d を実行すると、ビルド・プルを自動で行ってから、依存関係に沿った順番でコンテナを起動してくれるはずです。
コマンドをまとめずに、RUNコマンドを一つずつのコマンドに対して書いたため、95ステップもあります。

docker-compose
$ docker-compose up -d
Creating network "273_hadoop_nw" with driver "bridge"
Building rm1001
Step 1/95 : FROM centos:centos6
centos6: Pulling from library/centos
...
 ---> 89b88369b754
Successfully built 89b88369b754
Successfully tagged hadoop273:latest

約20分ほどで起動できました。
docker image のサイズが 約1.7GB と大きいです。
元のイメージ centos6 から 約1.5GB 増えています。
RUNコマンドを呼ぶ回数を減らして、イメージレイヤーを最適化できれば、数百MB程度は削れるかもしれません。

images
$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
hadoop273           latest              89b88369b754        18 minutes ago      1.71GB
centos              centos6             d0957ffdf8a2        9 months ago        194MB

コンテナも ResourceManager(NameNode)用 と NodeManager(DataNode)用が起動されています。

containers
$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                                                                                                                                  NAMES
9028c6b408bd        hadoop273           "/cmd.sh"           11 minutes ago      Up 11 minutes       8020/tcp, 8040/tcp, 8088/tcp, 9000/tcp, 0.0.0.0:45454->45454/tcp, 19888/tcp, 50070/tcp, 0.0.0.0:50010->50010/tcp                       container-hdp-nm2001
f6d1d724aca4        hadoop273           "/cmd.sh"           11 minutes ago      Up 11 minutes       0.0.0.0:8040->8040/tcp, 0.0.0.0:8088->8088/tcp, 0.0.0.0:9000->9000/tcp, 0.0.0.0:19888->19888/tcp, 8020/tcp, 0.0.0.0:50070->50070/tcp   container-hdp-rm1001

試しにNodeManager側のコンテナで bash を起動し、hdfs コマンドを実行してみます。
hdfs-setup.sh 内で作成する /user/my_data が生成されていることが確認できましたので HDFS が使える状態になっていることが分かります。

$ docker exec -it container-hdp-nm2001 bash
# hdfs dfs -ls /user
Found 1 items
drwxr-xr-x   - root supergroup          0 2019-12-13 13:50 /user/my_data

6. さいごに

当初の目標であった"ローカルマシンで Docker を使って Hadoop の分散処理環境の構築"は確認することができました。
ここに HBase や Spark などを使って、実際のデータで分散処理や格納などを行うとすると、やはりスペック的に動作ができないなど、厳しい面が出てくると思われますが、小さいデータでも扱えるテスト環境が作れると楽しいだろうと思います。
イメージサイズが大きいことや、コンテナを起動し続けるために、無理やり while ループを使っているなど、ほかにも改善ポイントはたくさんありますが、初めての docker を見よう見まねでも動いたことには嬉しかったです。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1