dockerを使ってシステムを構成する場合、孤立したコンテナを使うことはあまりなくて、複数のコンテナを連携させるのが普通だと思いますが、動的に構成が変動する場合など、なかなか考えることが多くて大変です。
あるフロントのコンテナから別なバックエンドのコンテナに接続する場合、フロント側にconsul-templateやconfdを組み込んだイメージを作成し、registratorなどでetcdやconsul上に登録されたバックエンドを見つけて接続を確立するのがどうやら主流のようです。
しかしこのやり方だと、せっかくポータブルに作成されたフロント側のコンテナを再度buildする必要があり、サービスの数だけフロントのコンテナイメージを用意する必要があって美しくないと思っていました。コンテナとコンテナの橋渡しは docker run
のオプションだけで完結したほうが良いですよね。
ということで、Gantryというものを作りました。
Gantryの機能は、基本的にはconsul-templateやconfdと同様、
- 指定した設定ファイルのテンプレートからetcdのサービス登録状況を参照して動的に設定ファイルを生成すること
-
-e GANTRY_CHECK
での設定ファイルのチェック -
-e GANTRY_RELOAD
でのフロントプロセスのリロード - それに加えて
CMD
で指定されたプロセスの起動も行います。
設定ファイルのテンプレートは -v
でマウントすれば、基本的に全て docker run
のオプションだけでコンテナ間の接続を確立できます。
Gantryの構成
Gantryはetcdを監視してサービス登録状況を把握するserver
と、Gantry経由で起動されるフロントコンテナ側でserver
に接続するclient
に分かれています。
Gantryサーバーは各ホストに1つ起動しておきます。クライアントとの通信はホスト上のvolume (/var/lib/gantry/socket)に作られた UNIX domain socketを使います。そのため、通常のdockerコンテナのIPアドレスのように動的に変動せず、Gantryサーバーが停止した場合でも、クライアントは再び起動するのを待つことができます。これによってシステムを停止せずにGantryコンテナ自体をアップグレード (stop -> rm -> run) できるように作られています。
Gantryを使うためには、sh
, awk
, nc -U
が入っている必要があります。busybox
などの軽量コンテナにも入っているので大体大丈夫だとおもいますが、ない場合でも busyboxのバイナリ(1〜2MBぐらい)を -v
でマウントして-e NETCAT="/path/to/busybox nc -U"
のようにすればokです。
Gantryはetcdをバックエンドとして参照しますが、普通のRegistratorが登録する情報"IP:PORT"
だけでは情報が少なすぎて困るので、consulと同程度の情報をJSONで格納するように少しカスタマイズしたもの (docker hub) を使っています。