RancherのRabbitMQカタログを改良してみる

この記事は[Rancher Advent Calendar 2017](https://adventar.org/calendars/2436)の12/17の記事です。

さて、先週先々週と[confd](https://qiita.com/m-daichang/items/d4e135b53b984cd414ef)、[giddyup](https://qiita.com/m-daichang/items/a9c2577f8e13a2130e6b)というRancherのStackを構成する上で重要なソフトを取り上げてきました。今週はそれを踏まえた上で一つ既存のカタログを改良してみたいと思います。

今回のターゲットはRabbitMQです。

RabbitMQって？
===
今更語るまでもない超有名なメッセージキューイング(MQ)のOSSですね。[Erlang](https://ja.wikipedia.org/wiki/Erlang)という他の言語とはまるで異なる文法で書かれています。
もっと詳しく知りたいかは公式ページや他の方が書かれたわかりやすいページを是非ご覧ください(丸投げ

- [公式ページ(英語)](https://www.rabbitmq.com/)
- [新人プログラマに知ってもらいたいRabbitMQ初心者の入門の入門](https://qiita.com/gambaray/items/3cc02b419c860a96bc94)

コミュニティカタログのRabbitMQについて
===
既にRancherのコミュニティカタログにはRabbitMQが存在します。
<img width="262" alt="rabbitmqカタログイメージ.png" src="https://qiita-image-store.s3.amazonaws.com/0/153776/0ab01258-d01b-6e6e-95bc-d9804b06def9.png">
んじゃこれでいいじゃん、となるところですが使ってみると

- 1node構成
- ユーザ/パスワード/vhostなどの本来rabbitmqコンテナイメージが持っている起動時の設定が出来ない
- 他にもpermissoinやpolicyの設定もできない

などあり、特に1node構成などは **せっかくcluster構成が取りやすいソフトウェアなのに勿体ない！** となります。

#### (前もってゴメンナサイm(. .)m)
今回は基本的にバージョン0.2.0を対象にしています。改良を始めた頃はまだ0.2.1はなかったんや　｡･ﾟ･(ﾉД`)･ﾟ･｡
<img width="593" alt="スクリーンショット 2017-12-17 16.48.11.png" src="https://qiita-image-store.s3.amazonaws.com/0/153776/0896b8bc-5535-7f5e-a7e2-363ab92d5378.png">
やった！出来たぜ！ってなった時に改めてみたら0.2.1が...

コミュニティカタログのRabbitMQを試してみる
===
コミュニティカタログでRabbitMQを作成した場合、以下のような構成になります。コミュニティカタログでは割とオーソドックスな形です。

<img width="400" alt="rancher_rabbitmqカタログ.png" src="https://qiita-image-store.s3.amazonaws.com/0/153776/c09b7074-eb45-b8ee-b05a-fd704ff36606.png">

sidekickでvolumeコンテナを立ち上げて、conf用コンテナが設定ファイルを生成、volumeに設置、そしてメインコンテナがサービスを提供する、という形です。

あれ？何か変ですね。上の図だとメインがsidekickにいます。何故でしょう？

これは恐らくですがrabbitmq-confイメージ内に含まれている[confd](https://github.com/kelseyhightower/confd)に理由がありそうです。[以前書いた記事](https://qiita.com/m-daichang/items/d4e135b53b984cd414ef)をみるとわかるのですが、[confd](https://github.com/kelseyhightower/confd)はRancherの[metadata-service](http://rancher.com/docs/rancher/latest/en/rancher-services/metadata-service/)と連携しているのですが、confdがsidekick側にいると上手くコンテナ名とかが取得できずに設定ファイルが生成出来ないのでは？と考えています。細かく検証していないのであくまで推論ですが...(なおsidekickにしたら失敗したっところまではやりました)

それとsidekick側にメインサービスがいて、ちゃんとそのコンテナにパケットが飛ぶのか？という疑問もあります。その秘密はnetworkにあります。

```yaml:docker-compose.ymlからの抜粋
 rabbitmq-base: 
(中略)
   net: "container:rabbitmq" 
```

[dockerのドキュメント](https://docs.docker.com/engine/reference/run/#network-settings)をみると `conteiner:<name/id>` は指定したコンテナのnetworkを間借りするみたいです。つまりここでは `rabbitmq` つまり `rabbitmq-conf` イメージのコンテナとnetworkを共有していることになります。ちゃんとサービスが提供できるわけです。

rabbitmq-confの中身をみてみる
===
ではrabbitmq-confの中身をみていきましょう。ここにDockerfileなどがあります
https://github.com/ddmng/catalog-dockerfiles/tree/master/rabbitmq/0.2.0
ディレクトリ構成はこんな感じ

```
|--Dockerfile         <- 言わずもがなDockerfile
|--README.md
|--conf.d
|  |--rabbitmq.toml   <- confdが出力するファイルの設定
|--dockerentry.sh     <- このコンテナのentrypoint
|--run.sh             <- rabbitmq本体が起動するためのシェル、
|--templates
|  |--rabbitmq.tmpl   <- confdがファイルを作成するためのテンプレート
```

confd用のファイルが確認出来ますね。rabbitmq.tmplを覗いてみましょう。

```text:rabbitmq.tmpl
    {cluster_nodes,
      {[
{{range $idx, $cnt := gets "/self/service/containers/*"}}{{ if (ne $idx 0) }},{{end}} 'rabbit@{{.Value}}'{{end}}
      ], disc} },
```

https://github.com/ddmng/catalog-dockerfiles/blob/master/rabbitmq/0.2.0/templates/rabbitmq.tmpl#L4-L7

おや？なんかscale数に応じてnodeが増えそうに見える？？
試しにscaleを3つに増やしてみましょう。

<img width="246" alt="スクリーンショット 2017-12-17 20.37.20.png" src="https://qiita-image-store.s3.amazonaws.com/0/153776/c33fb6ad-7997-e5f4-4a39-3e11fe328ebf.png">

適当にproxyつけてGUIでみてみます。15672portがGUIです。
<img width="1000" alt="スクリーンショット 2017-12-17 20.39.05.png" src="https://qiita-image-store.s3.amazonaws.com/0/153776/f15b4155-c9d6-f5a3-6ba2-e17dab1466bd.png">

http://[自分のIP]:15672 にアクセスしてみます。
......

いけるやん！！
<img width="1000" alt="スクリーンショット 2017-12-17 20.40.43.png" src="https://qiita-image-store.s3.amazonaws.com/0/153776/f4918d5d-7bed-6582-5831-d8b0afa3c185.png">

これはもしかして何もしなくていいのでは？？

そんなことはない。コミュニティカタログ版の問題点
===
じゃあ、なんでコミュニティカタログは最初からscaleが3じゃないのでしょう？scaleを最初から3にして起動してみましょう。

あれ？1nodeだけ？
<img width="1000" alt="スクリーンショット 2017-12-17 21.15.05.png" src="https://qiita-image-store.s3.amazonaws.com/0/153776/e57fa2f3-e093-75d7-9182-58e7ed3e20c2.png">
リロードしてみましょう
<img width="1000" alt="スクリーンショット 2017-12-17 21.15.19.png" src="https://qiita-image-store.s3.amazonaws.com/0/153776/be16493c-9f27-de57-267e-03a6373e6ea5.png">
？？？も一回リロード
<img width="1000" alt="スクリーンショット 2017-12-17 21.15.37.png" src="https://qiita-image-store.s3.amazonaws.com/0/153776/bcc9a0bb-5eae-0998-151f-9db99b6b88c7.png">

これはどういうことでしょう？つまり全てが **俺が！Primaryだっ！！** って主張してバラバラに動いてるんですね。これは困りました。

1台をPrimaryとして上手く立ち振舞えないでしょうか？

Giddyupがあるじゃない！！
===
ここで前回取り上げた[Giddyup](https://qiita.com/m-daichang/items/a9c2577f8e13a2130e6b)です。
[リーダーかどうかをチェックする機能](https://qiita.com/m-daichang/items/a9c2577f8e13a2130e6b#%E8%87%AA%E5%88%86%E3%81%8C%E3%83%AA%E3%83%BC%E3%83%80%E3%83%BC%E3%81%8B%E3%81%A9%E3%81%86%E3%81%8B%E3%83%81%E3%82%A7%E3%83%83%E3%82%AF%E3%81%99%E3%82%8B)があります。それにコンテナが規定数に達するまで待つこともできます。


```shell:run.shに以下コードを追加
# Waiting for leader service up
if [ "$( ${GIDDYUP} leader check ; echo $? )" -ne "0" ] ; then
  echo "waiting..."
  ${GIDDYUP} service wait scale --timeout 120
  # add wait
  sleep 30
fi
```

https://github.com/m-daichang/catalog-dockerfiles/blob/master/rabbitmq/0.2.1/run.sh#L7-L13

`sleep 30` を入れているのは実は `giddyup service wait scale` を入れてるのは、本体はsidekick側にいるので **正確にリーダーのサービス起動のタイミングが確認出来ない** という問題があり、しょうがなくsleepでごまかしてる感じです。わざわざcurl入れたくないし、いい手段があったらどなたか教えてくださいm(. .)m
(自分でgoあたりで簡単にチェックするアプリ作るのが一番なんでしょうけどね)

他の問題にも対応する
===
さて、最初に他にも問題があることを言いました。

- ユーザ/パスワード/vhostなどの本来rabbitmqコンテナイメージが持っている起動時の設定が出来ない
- 他にもpermissoinやpolicyの設定もできない

本来ならカタログからポチーっで全て設定したいのに、作った後にちまちま設定するのは非常に億劫です。

本来Rabbitmqイメージだとだと `RABBITMQ_DEFAULT_USER` `RABBITMQ_DEFAULT_PASS` `RABBITMQ_DEFAULT_VHOST` で設定できるのですが、これをコミュニティカタログに追加したバージョンを作っても反映されません。どうやらconfdでrabbitmq.confが作成されたせいで、後からの変更を受け付けないことが原因です。

はてさて、どうしたものかと調べていると以下の記事が引っかかりました。

- [RabbitMQの設定情報をファイルに書き出す](https://qiita.com/tototoshi/items/83c5897bb4890a328067)

なるほど。既存の設定をjsonファイルに書き出してそれを設定して読み出すことが可能なようです。
ということで別に手動で設定したRabbitmqの設定情報をjsonファイルに出力してコネコネいじってconfd用のテンプレートを作成してみました。

https://github.com/m-daichang/catalog-dockerfiles/blob/master/rabbitmq/0.2.1/templates/rabbitmq_definitions.tmpl

というわけでrabbitmq-confの改良版をまとめるとこんな感じになります。

```text
|--Dockerfile
|--README.md
|--conf.d
|  |--rabbitmq_config.toml      <- rabbitmq.tomlから変更
|  |--rabbitmq_definitions.toml <- 追加
|--dockerentry.sh
|--run.sh
|--templates
|  |--rabbitmq_config.tmpl      <- rabbitmq.tmplから変更
|  |--rabbitmq_definitions.tmpl <- 追加
```
https://github.com/m-daichang/catalog-dockerfiles/tree/master/rabbitmq/0.2.1

そこから作成したイメージファイルはこちらです。
https://hub.docker.com/r/mdaichang/rabbitmq-conf/

集大成
===
さて、rabbitmq-confの改良が出来上がったところで、カタログに反映してみましょう。
https://github.com/m-daichang/rancher-catalog/tree/master/templates/rabbitmq-3/1

ユーザ/パスワード/vhostなどを設定できるようにしてみました。

<img width="1187" alt="スクリーンショット 2017-12-17 23.24.46.png" src="https://qiita-image-store.s3.amazonaws.com/0/153776/a76ffe0b-addb-971c-75a6-41111088170c.png">

stackを作ってみましょう。んで、proxyの設定を行い外から接続できるようにします。
<img width="1000" alt="スクリーンショット 2017-12-17 23.28.13.png" src="https://qiita-image-store.s3.amazonaws.com/0/153776/26c6972b-2069-5e81-f8d3-dc58f8e51935.png">
やった！ちゃんと最初から3node見えてますね。他の設定は？

ユーザ/パスワード
<img width="449" alt="スクリーンショット 2017-12-17 23.30.30.png" src="https://qiita-image-store.s3.amazonaws.com/0/153776/b934c54b-ec7b-f7f7-81f2-4dc6509f6895.png">

vhost
<img width="681" alt="スクリーンショット 2017-12-17 23.30.37.png" src="https://qiita-image-store.s3.amazonaws.com/0/153776/3873364d-9f9e-83b4-a0bd-cfe5a7421646.png">

policy
<img width="491" alt="スクリーンショット 2017-12-17 23.30.44.png" src="https://qiita-image-store.s3.amazonaws.com/0/153776/9d458330-f521-236d-351b-c8fd247d3eeb.png">

ちゃんと作成時に設定した情報が反映されているようです！

まとめ
===
さて、今回は先週先々週の集大成としてRabbitMQカタログの改修を行いました。
RabbitMQは元々clusterを作成することを考慮されて作られているため(というよりErlangがもともとそういう作りになってる？)、HA構成が取りにくいdockerでも比較的簡単に？clueter構成を取ることが出来ました。


今回はちょっと長めで、それでもあちこち端折ったので、内容的にはわかりづらかったかもしれませんm(. .)m
ただ、なんとなくやりたいことはわかっていただけたかと。このページを元に各ソースを眺めていただき、それでもわからない際はコメントいただけると助かります。