OpenStack Heat templateでHadoopクラスタ

OpenStack HeatのHOTテンプレートでどの程度までプロビジョニングを簡略化できるか僕なりに調べてみました。

具体的にトライしてみたことは、[Ubuntu 12.04 LTS のクラウドイメージ](http://cloud-images.ubuntu.com/precise/current/)で５ノードのHadoopクラスタ構築です。基本的には作業の８割くらいは下記の``heat stack-create``コマンド１つで済ませることができました。（``<key name>``と ``<image name>``は任意。[テンプレートのリンクはこちら](https://raw.github.com/kjtanaka/heat_templates/master/hadoop/deploy_hadoop.yml)。）

コマンド

```
heat stack-create hadoop \
    -u https://raw.github.com/kjtanaka/heat_templates/master/hadoop/deploy_hadoop.yml \
    -P "key_name=<key name>;image_name=<image name>"
```

図（IPアドレスは環境によって異なります。）

```
 Network
 --------------------------------------------------------------------------
      | 192.168.11.1      | 192.168.11.2      | 192.168.11.3      | 192.xxx
  +----------------+  +----------------+  +----------------+  +------------
  | node01[master] |  | node02[master] |  | node03[slave]  |  | node04[slav
  | -------------- |  | -------------- |  | -------------- |  | -----------
  | NameNode       |  | SecondNameNode |  | DataNode       |  | DataNode
  | SecondNameNode |  |                |  | TaskTracker    |  | TaskTracker
  | JobTracker     |  |                |  |                |  |
  |                |  |                |  |                |  |
  +----------------+  +----------------+  +----------------+  +------------
```

（注）``heat event-list hadoop``で進行状況を確認できますが、すべてのイベントが``CREATE_COMPLETE``になってもテンプレート上のbashスクリプトが終わるまでしばらく待つことになります。完了するとhdfsというユーザ名でnode01にログインできるようになります。

手作業になったこと（２つ）
---------------------------------
1. マスターノードnode01のhdfsユーザの``.ssh/id_rsa.pub``を各ノードの``.ssh/authorized_keys``登録する作業。
2. ``/etc/hosts``の編集はnode01のみ可能でした。それ以外のノードにはnode01の``/etc/hosts``をコピーする形になりました。

これらの作業はChefやPuppetで自動化することになると思います。（僕はFabricを使いましたが、Heatの検証なのでこのポストには載せません。）

事前に必要なもの
---------------------
* Heatクライアントが利用できるOpenStack
* Ubuntu 12.04 LTSのクラウドイメージ。下記のコマンドで作成できます。（``<image name>``は任意）

  ```
  glance image-create --name <image name> \
      --location http://cloud-images.ubuntu.com/precise/current/precise-server-cloudimg-amd64-disk1.img \
      --disk-format qcow2 \
      --container-format bare \
      --is-public false
  ```

好ましい設定
---------
* ローカルのデフォルトキー``~/.ssh/id_dsa.pub``もしくは``~/.ssh/id_rsa.pub``がキーペアとして登録されているとsshの際に``-i``オプションを付けずに済みます。人それぞれの好みだと思いますが、後述はそれを前提に説明します。

  ```
  nova keypair-add --pub-key ~/.ssh/id_dsa.pub <key name>
  ```

手作業になった部分の説明
--------------------------------

1. hdfsのssh公開鍵を登録する。

   node01のauthorized_keysは編集済みなのでそれをローカルにコピーする。

   ```
   scp -p hdfs@192.168.11.1:.ssh/authorized_keys /tmp/hdfs_auth_keys
   ```

   コピーしたものをそれぞれのノードにアップロードする。

   ```
   scp -p /tmp/hdfs_auth_keys hdfs@192.168.11.2:.ssh/authorized_keys
   scp -p /tmp/hdfs_auth_keys hdfs@192.168.11.3:.ssh/authorized_keys
   scp -p /tmp/hdfs_auth_keys hdfs@192.168.11.4:.ssh/authorized_keys
   scp -p /tmp/hdfs_auth_keys hdfs@192.168.11.5:.ssh/authorized_keys
   ```

2. /etc/hostsの配布する

   node01にログインする

   ```
   ssh hdfs@192.168.11.1
   ```

   sudoが必要なのでscpではなくてcat、ssh、sudoを組み合わせてアップロードする。

   ```
   hdfs@node01:~$ cat /etc/hosts | ssh node02 'sudo sh -c "cat > /etc/hosts"'
     Are you sure you want to continue connecting (yes/no)? yes
   hdfs@node01:~$ cat /etc/hosts | ssh node03 'sudo sh -c "cat > /etc/hosts"'
     Are you sure you want to continue connecting (yes/no)? yes
   hdfs@node01:~$ cat /etc/hosts | ssh node04 'sudo sh -c "cat > /etc/hosts"'
     Are you sure you want to continue connecting (yes/no)? yes
   hdfs@node01:~$ cat /etc/hosts | ssh node05 'sudo sh -c "cat > /etc/hosts"'
     Are you sure you want to continue connecting (yes/no)? yes
   ```

   Hadoopの起動時に質問されないように、node01を ~/.ssh/known_hostsに登録する。

   ```
   hdfs@node01:~$ ssh node01 hostname
     Are you sure you want to continue connecting (yes/no)? yes
   ```

Hadoopを使ってみる
----------

1. HDFSをフォーマット

   ```
   hdfs@node01:~$ hadoop namenode -format
   ```

2. Hadoopを起動

   ```
   hdfs@node01:~$ ./bin/start-all.sh
   ```

3. WordCountを試してみる

   ```
   hdfs@node01:~$ wget http://norvig.com/big.txt -P ~/
   hdfs@node01:~$ hadoop fs -copyFromLocal ~/big.txt big.txt
   hdfs@node01:~$ hadoop jar /usr/lib/hadoop/hadoop*examples*.jar wordcount big.txt output
   hdfs@node01:~$ hadoop fs -ls
   hdfs@node01:~$ hadoop fs -get output ~/local_output
   hdfs@node01:~$ hadoop fs -rmr output
   hdfs@node01:~$ less ~/local_output/part-r-00000
   ```

4. ユーザを追加してみる。試しに``john``

   ```
   hdfs@node01:~$ sudo useradd john -m -s /bin/bash
   hdfs@node01:~$ hadoop fs -mkdir /user/john
   hdfs@node01:~$ hadoop fs -chown john:john /user/john
   ```

   ``john``というユーザーでもWordCountができることを確認する

   ```
   hdfs@node01:~$ sudo -i -u john
   john@node01:~$ wget http://norvig.com/big.txt -P ~/
   john@node01:~$ hadoop fs -copyFromLocal ~/big.txt big.txt
   john@node01:~$ hadoop jar /usr/lib/hadoop/hadoop*examples*.jar wordcount big.txt output
   john@node01:~$ hadoop fs -ls
   john@node01:~$ hadoop fs -get output ~/local_output
   john@node01:~$ hadoop fs -rmr output
   john@node01:~$ less ~/local_output/part-r-00000
   ```

5. Hadoopを停止する

   ```
   hdfs@node01:~$ ./bin/stop-all.sh
   ```

Heatコミュニティに質問してみたこと
---------

* [Heat Templateでfor-loopは使えますか？](https://answers.launchpad.net/heat/+question/240943)
* [すべてのインスタンスの/etc/hostsを簡単にアップデートする方法はありますか？](https://answers.launchpad.net/heat/+question/241010)

どちらの質問も答えはノーでした。``{ get_attr: [ name, first_address ] }``はとても便利ですが、``get_attr``がループになるとエラーになります。なのでHeatでスタックする際はマスター（あるいはマネジメント）ノードに``get_attr``で情報を集めておいて、それを別のデプロイツールで利用する形になると思います。

以上です。参考にしたリンクを以下に添えておきます。

* Heat
  * http://docs.openstack.org/developer/heat/template_guide/hot_spec.html
  * https://github.com/openstack/heat-templates
  * https://wiki.openstack.org/wiki/Heat
  * 
* Hadoop
  * http://askubuntu.com/questions/144433/how-to-install-hadoop
  * http://enterprisezine.jp/dbonline/detail/4254