CLOVER🍀

That was when it all began.

CDH 5.4.4で、HDFS+YARNのDockerイメージを作る

ちょっと後に使うかなと思いまして、CDHを使ってHDFS+YARNのDockerイメージを作ってみることにしました。

目標は、

  • HDFSをインストール&起動
  • YARNをインストール&起動
  • クライアントもインストールし、HDFS/YARNが起動中のコンテナへ、別コンテナで起動したクライアントから接続できる
  • ClouderaManagerは今回はパスして、自分でインストールする(ムダに頑張る)

という環境と手順の確立するところまで、とします。

Dockerの勉強も兼ねて、ということで…。

こういうのもあるらしいんですけどね。
https://github.com/sequenceiq/hadoop-docker

CDHを使ってのインストール手順

で、今回CDHを使ってインストールすると言ったわけですが、その前提条件をちょっと確認…。

Javaは、Oracle JDK 1.7がよいみたいです。

Java Development Kit Installation
http://www.cloudera.com/content/cloudera/en/documentation/core/latest/topics/cdh_ig_jdk_installation.html

yumリポジトリの場所などは、こちら。

CDH Download Information
http://www.cloudera.com/content/cloudera/en/documentation/core/latest/topics/cdh_vd_cdh_download.html

OSは、CentOS 6.6のDockerイメージを選ぶことにします。

https://registry.hub.docker.com/_/centos/

今回は、「centos:6.6」を選びました。

あと、CDHでのHDFSおよびYARNのインストール方法自体は、こちらを参考に。

CDH4でhadoopクラスタを構築する
http://qiita.com/toshiro3/items/cc3771735195b07cc00e

CDH自体は、5.4.4を選択します。

Dockerfileの作成

いろいろあって、作成したDockerfileはこのようになりました。

Dockerfile

FROM centos:6.6

ENV JDK_VERSION 7u79
ENV JDK_BUILD_NO b15
ENV JDK_RPM jdk-${JDK_VERSION}-linux-x64.rpm

ENV JAVA_HOME /usr/java/default

EXPOSE 8020 8030 8031 8032 8033 8040 8042 8089 10020 10033 13562 19888 40906 50010 50020 50070 50075
# -p 8020:8020 -p 8030:8030 -p 8031:8031 -p 8032:8032 -p 8033:8033 -p 8040:8040 -p 8042:8042 -p 8089:8089 -p 10020:10020 -p 10033:10033 -p 13562:13562 -p 19888:19888 -p 40906:40906 -p 50010:50010 -p 50020:50020 -p 50070:50070 -p 50075:50075

RUN yum install -y wget \
                   sudo && \
    sed -ri 's/Defaults    requiretty/Defaults:root    !requiretty/' /etc/sudoers && \
    wget -q -O /tmp/jdk.rpm --no-check-certificate --no-cookies --header "Cookie: oraclelicense=accept-securebackup-cookie" http://download.oracle.com/otn-pub/java/jdk/${JDK_VERSION}-${JDK_BUILD_NO}/${JDK_RPM} && \
    rpm -ivh /tmp/jdk.rpm && \
    wget -q -O /etc/yum.repos.d/cloudera-cdh5.repo http://archive.cloudera.com/cdh5/redhat/6/x86_64/cdh/cloudera-cdh5.repo && \
    sed -ri 's/cdh\/5/cdh\/5.4.4/' /etc/yum.repos.d/cloudera-cdh5.repo && \
    yum install -y hadoop \
                   hadoop-hdfs \
                   hadoop-hdfs-namenode \
                   hadoop-hdfs-secondarynamenode \
                   hadoop-hdfs-datanode \
                   hadoop-yarn \
                   hadoop-yarn-resourcemanager \
                   hadoop-yarn-nodemanager \
                   hadoop-mapreduce \
                   hadoop-mapreduce-historyserver \
                   hadoop-client

RUN cp -Rp /etc/hadoop/conf.empty /etc/hadoop/conf.mine && \
    alternatives --install /etc/hadoop/conf hadoop-conf /etc/hadoop/conf.mine 50 && \
    alternatives --set hadoop-conf /etc/hadoop/conf.mine && \
    mkdir -p /var/lib/hadoop-hdfs/cache/hdfs/name && \
    chown -R hdfs.hdfs /var/lib/hadoop-hdfs/cache/hdfs/name && \
    chmod 775 /var/lib/hadoop-hdfs/cache/hdfs/name && \
    mkdir -p /var/lib/hadoop-hdfs/cache/hdfs/dfs && \
    chown -R hdfs.hdfs /var/lib/hadoop-hdfs/cache/hdfs/dfs && \
    chmod 775 /var/lib/hadoop-hdfs/cache/hdfs/dfs && \
    mkdir -p /var/lib/hadoop-hdfs/cache/hdfs/tmp && \
    chown -R hdfs.hdfs /var/lib/hadoop-hdfs/cache/hdfs/dfs && \
    chmod 775 /var/lib/hadoop-hdfs/cache/hdfs/dfs

ADD core-site.xml /etc/hadoop/conf.mine/core-site.xml
ADD hdfs-site.xml /etc/hadoop/conf.mine/hdfs-site.xml
ADD mapred-site.xml /etc/hadoop/conf.mine/mapred-site.xml
ADD yarn-site.xml /etc/hadoop/conf.mine/yarn-site.xml
ADD fix-hostname.sh fix-hostname.sh
ADD init-and-start.sh init-and-start.sh

RUN chmod a+x fix-hostname.sh
RUN chmod a+x init-and-start.sh

CMD ./init-and-start.sh && tail -f /dev/null

やっていることは、まずはOracle JDKのインストール、Clouderaのyumリポジトリの追加。

インストールされるCDHは、5.4.4固定にしました。

    sed -ri 's/cdh\/5/cdh\/5.4.4/' /etc/yum.repos.d/cloudera-cdh5.repo && \

それから各種インストール。

    yum install -y hadoop \
                   hadoop-hdfs \
                   hadoop-hdfs-namenode \
                   hadoop-hdfs-secondarynamenode \
                   hadoop-hdfs-datanode \
                   hadoop-yarn \
                   hadoop-yarn-resourcemanager \
                   hadoop-yarn-nodemanager \
                   hadoop-mapreduce \
                   hadoop-mapreduce-historyserver \
                   hadoop-client

設定ファイルは、/etc/hadoop/conf.emptyから/etc/hadoop/conf.mineというディレクトリにコピーして、こちらを使うように設定しました。

RUN cp -Rp /etc/hadoop/conf.empty /etc/hadoop/conf.mine && \
    alternatives --install /etc/hadoop/conf hadoop-conf /etc/hadoop/conf.mine 50 && \
    alternatives --set hadoop-conf /etc/hadoop/conf.mine && \

設定ファイルと起動スクリプトの追加。

ADD core-site.xml /etc/hadoop/conf.mine/core-site.xml
ADD hdfs-site.xml /etc/hadoop/conf.mine/hdfs-site.xml
ADD mapred-site.xml /etc/hadoop/conf.mine/mapred-site.xml
ADD yarn-site.xml /etc/hadoop/conf.mine/yarn-site.xml
ADD fix-hostname.sh fix-hostname.sh
ADD init-and-start.sh init-and-start.sh

RUN chmod a+x fix-hostname.sh
RUN chmod a+x init-and-start.sh

利用ポートをEXPOSE。

EXPOSE 8020 8030 8031 8032 8033 8040 8042 8089 10020 10033 13562 19888 40906 50010 50020 50070 50075

デフォルトはNameNodeなどの各種サービスを起動するようにしていますが、bashでログインして使ったりもするのでENTRYPOINTは今回はやめておくことにします。

CMD ./init-and-start.sh && tail -f /dev/null

「tail -f /dev/null」しているのは、フォアグラウンドなっていて欲しいから…。

Dockerfileはここまで。

設定ファイルと起動スクリプト

Dockerfileの中でADDしていた各種設定ファイルは、こんな感じ。
core-site.xml

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>

<configuration>
  <property>
    <name>fs.defaultFS</name>
    <value>hdfs://localhost:8020</value>
  </property>
</configuration>

hdfs-site.xml

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>

<configuration>
  <property>
    <name>dfs.replication</name>
    <value>1</value>
  </property>
  <property>
    <name>hadoop.tmp.dir</name>
    <value>file:///var/lib/hadoop-hdfs/cache/hdfs/dfs/tmp</value>
  </property>
  <property>
    <name>dfs.namenode.name.dir</name>
    <value>file:///var/lib/hadoop-hdfs/cache/hdfs/dfs/name</value>
  </property>
  <property>
    <name>dfs.datanode.data.dir</name>
    <value>file:///var/lib/hadoop-hdfs/cache/hdfs/dfs/data</value>
  </property>
</configuration>

mapred-site.xml

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>

<configuration>
  <property>
    <name>mapreduce.framework.name</name>
    <value>yarn</value>
  </property>
  <property>
    <name>mapreduce.jobhistory.address</name>
    <value>localhost:10020</value>
  </property>
  <property>
    <name>mapreduce.jobhistory.webapp.address</name>
    <value>localhost:19888</value>
  </property>

  <property>
    <name>yarn.app.mapreduce.am.staging-dir</name>
    <value>/tmp/hadoop-yarn/staging</value>
  </property>
</configuration>

yarn-site.xml

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>

<configuration>
  <property>
    <name>yarn.nodemanager.aux-services</name>
    <value>mapreduce_shuffle</value>
  </property>

  <property>
    <name>yarn.nodemanager.aux-services.mapreduce_shuffle.class</name>
    <value>org.apache.hadoop.mapred.ShuffleHandler</value>
  </property>

  <property>
    <name>yarn.log-aggregation-enable</name>
    <value>true</value>
  </property>

  <property>
    <name>yarn.resourcemanager.address</name>
    <value>localhost:8032</value>
  </property>
  <property>
    <name>yarn.resourcemanager.scheduler.address</name>
    <value>localhost:8030</value>
  </property>
  <property>
    <name>yarn.resourcemanager.resource-tracker.address</name>
    <value>localhost:8031</value>
  </property>
  <property>
    <name>yarn.resourcemanager.admin.address</name>
    <value>localhost:8033</value>
  </property>
  <property>
    <name>yarn.resourcemanager.webapp.address</name>
    <value>localhost:8089</value>
  </property>

  <property>
    <description>List of directories to store localized files in.</description>
    <name>yarn.nodemanager.local-dirs</name>
    <value>file:///var/lib/hadoop-yarn/cache/${user.name}/nm-local-dir</value>
  </property>

  <property>
    <description>Where to store container logs.</description>
    <name>yarn.nodemanager.log-dirs</name>
    <value>file:///var/log/hadoop-yarn/containers</value>
  </property>

  <property>
    <description>Where to aggregate logs to.</description>
    <name>yarn.nodemanager.remote-app-log-dir</name>
    <value>hdfs://localhost:8020/var/log/hadoop-yarn/apps</value>
  </property>

  <property>
    <description>Classpath for typical applications.</description>
     <name>yarn.application.classpath</name>
     <value>
        $HADOOP_CONF_DIR,
        $HADOOP_COMMON_HOME/*,$HADOOP_COMMON_HOME/lib/*,
        $HADOOP_HDFS_HOME/*,$HADOOP_HDFS_HOME/lib/*,
        $HADOOP_MAPRED_HOME/*,$HADOOP_MAPRED_HOME/lib/*,
        $HADOOP_YARN_HOME/*,$HADOOP_YARN_HOME/lib/*
     </value>
  </property>
</configuration>

ほとんど参考サイトのまんまなんですけど。意味がよくわかっていないところも多いので、不要な設定が入っているかも…。

ホスト名は「localhost」としていますが、後のスクリプトで書き換えます。

CMDで指定していた起動スクリプトHDFSのフォーマットも含めています。
init-and-start.sh

#!/bin/bash

sh fix-hostname.sh

## Initialize HDFS.
sudo -u hdfs hdfs namenode -format

## Startup HDFS Process.
service hadoop-hdfs-namenode start
service hadoop-hdfs-datanode start
# service hadoop-hdfs-secondarynamenode start

## Startup Yarn Process.
service hadoop-yarn-resourcemanager start
service hadoop-yarn-nodemanager start

## create tmp directory.
sudo -u hdfs hdfs dfs -mkdir /tmp
sudo -u hdfs hdfs dfs -chmod 777 /tmp

service hadoop-mapreduce-historyserver start

SecondaryNameNodeは、うちの環境ではメモリ不足でYARNと合わせると足かせになったので、やめました…。

tmpディレクトリだけは、しれっと作っています。

## create tmp directory.
sudo -u hdfs hdfs dfs -mkdir /tmp
sudo -u hdfs hdfs dfs -chmod 777 /tmp

あと、ホスト名を修正するスクリプトを仕込んでいます。
fix-hostname.sh

#!/bin/bash

if [ "$1" == "" ]; then
    TARGET_HOSTNAME=$HOSTNAME
else
    TARGET_HOSTNAME=$1
fi

# fix hostname
sed -ri s/localhost/$TARGET_HOSTNAME/ /etc/hadoop/conf.mine/core-site.xml
sed -ri s/localhost/$TARGET_HOSTNAME/ /etc/hadoop/conf.mine/hdfs-site.xml
sed -ri s/localhost/$TARGET_HOSTNAME/ /etc/hadoop/conf.mine/mapred-site.xml
sed -ri s/localhost/$TARGET_HOSTNAME/ /etc/hadoop/conf.mine/yarn-site.xml

Dockerイメージのビルド

以下のコマンドで、ビルド。

$ docker build -t kazuhira/centos6-cdh5:5.4.4 .

HDFS&YARNの起動

では、ビルドしたDockerイメージからコンテナを起動してみます。

$ docker run -d --name cdh-yarn -p 8020:8020 -p 8030:8030 -p 8031:8031 -p 8032:8032 -p 8033:8033 -p 8040:8040 -p 8042:8042 -p 8089:8089 -p 10020:10020 -p 10033:10033 -p 13562:13562 -p 19888:19888 -p 40906:40906 -p 50010:50010 -p 50020:50020 -p 50070:50070 -p 50075:50075 kazuhira/centos6-cdh5:5.4.4

めっちゃポートの指定がありますが…。

コンテナの名前は、「cdh-yarn」としました。このコンテナ内で、NameNode、DataNode、ResourceManager、NodeManagerが起動しています。

クライアントから接続する

続いて、クライアント側から接続してみます。別のコンテナからつなぎますよ、と。

$ docker run -it --rm --link cdh-yarn --name cdh-yarn-client kazuhira/centos6-cdh5:5.4.4 bash

クライアントでは、HDFSやYARNは起動しません。

この時、先ほど起動しておいたコンテナとlinkしておきます。

--link cdh-yarn

linkしておくと、hostsファイルにlinkしたホストの情報が書かれるらしいので

# cat /etc/hosts
172.17.0.13	e7ab9b1da65d
127.0.0.1	localhost
::1	localhost ip6-localhost ip6-loopback
fe00::0	ip6-localnet
ff00::0	ip6-mcastprefix
ff02::1	ip6-allnodes
ff02::2	ip6-allrouters
172.17.0.12	cdh-yarn 4935efc1c6cc

この情報を使って、クライアント側の設定ファイル内のホスト名を修正します。

# ./fix-hostname.sh 4935efc1c6cc

ちょっとディレクトリを作ってみます。

# su - hdfs
$ hdfs dfs -mkdir /user
$ hdfs dfs -chmod 777 /user

1度hdfsユーザーを抜けて、今度はmapredユーザーへ。

# su - mapred

サンプルを実行してみましょう。

$ yarn jar /usr/lib/hadoop-mapreduce/hadoop-mapreduce-examples-2.6.0-cdh5.4.4.jar pi 5 300

なんか、動き出します…。

Number of Maps  = 5
Samples per Map = 300
Wrote input for Map #0
Wrote input for Map #1
Wrote input for Map #2
Wrote input for Map #3
Wrote input for Map #4
Starting Job
15/07/30 12:37:14 INFO client.RMProxy: Connecting to ResourceManager at 4935efc1c6cc/172.17.0.12:8032
15/07/30 12:37:15 INFO input.FileInputFormat: Total input paths to process : 5
15/07/30 12:37:15 INFO mapreduce.JobSubmitter: number of splits:5
15/07/30 12:37:15 INFO mapreduce.JobSubmitter: Submitting tokens for job: job_1438259518400_0001
15/07/30 12:37:16 INFO impl.YarnClientImpl: Submitted application application_1438259518400_0001
15/07/30 12:37:16 INFO mapreduce.Job: The url to track the job: http://4935efc1c6cc:8089/proxy/application_1438259518400_0001/
15/07/30 12:37:16 INFO mapreduce.Job: Running job: job_1438259518400_0001
15/07/30 12:37:23 INFO mapreduce.Job: Job job_1438259518400_0001 running in uber mode : false
15/07/30 12:37:23 INFO mapreduce.Job:  map 0% reduce 0%
15/07/30 12:37:28 INFO mapreduce.Job:  map 20% reduce 0%
15/07/30 12:37:29 INFO mapreduce.Job:  map 40% reduce 0%
15/07/30 12:37:30 INFO mapreduce.Job:  map 60% reduce 0%
15/07/30 12:37:31 INFO mapreduce.Job:  map 80% reduce 0%
15/07/30 12:37:32 INFO mapreduce.Job:  map 100% reduce 0%
15/07/30 12:37:34 INFO mapreduce.Job:  map 100% reduce 100%
15/07/30 12:37:34 INFO mapreduce.Job: Job job_1438259518400_0001 completed successfully
15/07/30 12:37:34 INFO mapreduce.Job: Counters: 49
	File System Counters
		FILE: Number of bytes read=116
		FILE: Number of bytes written=654903
		FILE: Number of read operations=0
		FILE: Number of large read operations=0
		FILE: Number of write operations=0
		HDFS: Number of bytes read=1345
		HDFS: Number of bytes written=215
		HDFS: Number of read operations=23
		HDFS: Number of large read operations=0
		HDFS: Number of write operations=3
	Job Counters 
		Launched map tasks=5
		Launched reduce tasks=1
		Data-local map tasks=5
		Total time spent by all maps in occupied slots (ms)=16396
		Total time spent by all reduces in occupied slots (ms)=2785
		Total time spent by all map tasks (ms)=16396
		Total time spent by all reduce tasks (ms)=2785
		Total vcore-seconds taken by all map tasks=16396
		Total vcore-seconds taken by all reduce tasks=2785
		Total megabyte-seconds taken by all map tasks=16789504
		Total megabyte-seconds taken by all reduce tasks=2851840
	Map-Reduce Framework
		Map input records=5
		Map output records=10
		Map output bytes=90
		Map output materialized bytes=140
		Input split bytes=755
		Combine input records=0
		Combine output records=0
		Reduce input groups=2
		Reduce shuffle bytes=140
		Reduce input records=10
		Reduce output records=0
		Spilled Records=20
		Shuffled Maps =5
		Failed Shuffles=0
		Merged Map outputs=5
		GC time elapsed (ms)=209
		CPU time spent (ms)=3180
		Physical memory (bytes) snapshot=1627062272
		Virtual memory (bytes) snapshot=8320311296
		Total committed heap usage (bytes)=1495793664
	Shuffle Errors
		BAD_ID=0
		CONNECTION=0
		IO_ERROR=0
		WRONG_LENGTH=0
		WRONG_MAP=0
		WRONG_REDUCE=0
	File Input Format Counters 
		Bytes Read=590
	File Output Format Counters 
		Bytes Written=97
Job Finished in 19.749 seconds
Estimated value of Pi is 3.15200000000000000000

とりあえず、動いたようです。

最初、SecondaryNameNodeがいた状態だとメモリが足りなかったらしくて、サンプルの実行に10分くらいかかっていたのですが…SecondaryNameNodeがなくなったら、とりあえず動くようになりました…。

ギリギリですね。

その他にもいろいろ苦労したのですが、なんとかやってみたかったところまで通せてよかったです。

今度同じようなことをやる時は、ClouderaManagerを使った方がいいのかもしれないなぁと思いました…。