13
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Hadoop multi node cluster on Docker

Last updated at Posted at 2015-11-20

Motivation

Hadoopの開発をしていると複数ノードを持ったクラスタが欲しくなる時がある。なんとなく全体的な挙動を見たいときとかHDFS Erasure Codingのような最低6つのノードがないと動かないような機能もある。

こんな時にEC2のような環境に自分でビルドしたHadoopを使ってクラスタ構築をする必要がでてくるのだけれど、通常これはビルド目的にしては大変めんどくさい上に時間がかかる。お金もかかる。VirtualBoxを使って手元で複数ノードを立ち上げることもできるがこれも時間がかかる。(Stormで同じようなことをやってみたことがある

開発をしていると1行変えては試し、1文字変えては試しということがたまにある。その度に数十分待たされるという非常にストレスフルな開発を解決したかった。

A solution

というわけでDockerを使うと一定の動作環境が整ったisolateされたノードが数秒で立ち上がることがわかったのでこれを使ってHadoop Clusterを構築してみた。

作ったDocker imageがこちら

docker-hadoop-cluser

Hadoopのimage自体はSequenceIQのDocker imageをほぼそのまま使っている。ちょっと手を加えて点としてはcontainer同士のネットワーク周りの名前解決をさせるためにoption(-h)が必要なので起動scriptを整えた点。jarが入ったimageとmaster, slave用のimageは分けた。非常にシンプルに自分でビルドしたHadoopで簡単に複数ノードのclusterが立ち上がる。

On Linux

はじめから自分はMacで使うように作っていたのでLinuxではテストしていなかった。どうせ同じだろうとも思っていたが先日動かしてみると動かない。どうやらcontainer間の名前解決ができていないようだった。いろいろ試してみると以下のことがわかった。

  • Mac上で動くdocker-machine(or docker daemon?)は同ネットワーク(bridge)に後から追加された他のcontainerのホスト名を自動で/etc/hostsに追記をしてくれる
  • Linuxで動くdocker daemonは後から追加された他のcontainerのホスト名を追記してはくれない

そのためなんとなく-hだけ指定して起動させて動いていたMacとは違いLinuxでは各containerのIPとホスト名のひも付けができなくなる問題が起きていた。

Link

単一ホスト上でのDocker container同士をつなぐにはlinkという機能が一般的だけれど、実は今回はこいつは使えない。この機能はすでに立ち上がっているcontainerのホスト名のaliasを指定して環境変数、/etc/hostsへの追記などを行ってくれる機能だ。Hadoop clusterは通常Master -> Slaveの順番で立ち上げるがじつは両者ともお互いのホスト名の名前解決ができないといけない。MasterはSlaveから送られてきたヘルスチェックやブロックレポートにのっているホスト名を元にコマンドを送り返したりする。この時に

  • Master -> Slave

の順で起動するとlinkの機能を使ってSlaveはMasterの情報を得られるが、MasterはSlaveのことを知ることはできない。Masterが起動した時はSlaveがいなかったからだ。この問題はもちろん順番を変えても解決しない。linkはお互いの名前解決をするようにはできていないらしい。

Resolve hostname

いろいろ試行錯誤した結果、名前解決をしたいんだったら/etc/hostsが更新されればよいということで、Slave立ち上げたあとにIPとホスト名のひも付けを配布するようにした。

#!/usr/bin/env python

def get_ipaddress(host):
    (code, out, err) = exec_process('sudo docker inspect {}'.format(host))
    conf = json.loads(out)
    return conf[0]['NetworkSettings']['IPAddress'] 

docker inspectコマンドでそのcontainerの情報が取得できるのでcontainerがたちあがったあと、IPを収集して/etc/hostsを配布する。これで問題は解決した。

実はこれを配布する時にはすでにMaster, Slaveが立ち上がっているのだけれど、ファイルを作成したりMapReduceを走らせないと直ちにMasterからSlaveにアクセスが行くことはないのでSlaveにだけMasterへのlinkを設定することで起動時のエラーも起きることがないようになっている。

Conclusion

結構泥臭いやり方になってしまったので、これを調べているときに見つけたfigを導入してみようかとも思っています。あと単一ホスト上でのcontainer同士の接続がもっと簡単になるような方法があればぜひ教えていただけると嬉しいです。

Thanks!

追記

MacとLinuxによる挙動の違いはplatformの違いではなくversionに依るものだった。


ubuntu $ docker --version
Docker version 1.9.1, build a34a1d5
mac $ docker --version
Docker version 1.8.3, build f4bf5c7

1.9.0からの挙動の違いはbugではなくdesign.
https://github.com/docker/docker/pull/17230

もともとdefault network(bridge)で/etc/hostsに自動追加されていた機能(implicit discovery)は意図されたものではなく、それが元に戻ったという認識みたい。

default networkでなく、user defined networkなら直ちにホスト同士繋がるか試してみたところ果たしてうまくいった。

[e387acc] Create user defined network for connecting each contaner immediately

13
11
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
13
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?