他のホストのコンテナに接続するパターン

他のホストのDockerコンテナには接続しづらい

1つのホストの上で複数のDockerコンテナを動かす場合、あるコンテナからあるコンテナに接続するためにはコンテナに付けた名前が利用できる。具体的には、コンテナの名前をもとにDockerが環境変数を提供してくれて、そこにアドレスとポート番号が入っている。しかし、複数のホストの上で複数のDockerコンテナを動かす場合、他のホストで動作しているコンテナに接続したいのであればこの方法は利用できない。単純に接続先のホストのアドレスとポート番号をコンテナ起動時に指定する方法もあるが、この方法では他のホストに接続する全てのコンテナに対して逐一指定する必要がある。

ホストごとにリバースプロキシを置く

他のホストのコンテナへ接続するためのリバースプロキシとなるコンテナを各ホストごとに設置する。これにより、他のホストのコンテナに接続したい場合でも、あたかもコンテナが同一ホスト内にあるかのように扱うことができ、これまで利用していたコンテナの名前ベースでの接続方法が利用できる。この方法はAmbassadorパターンと呼ばれている。Ambassadorは大使のような意味で、ホストを国に見立て、各国に存在するコンテナ同士がコミュニケーションする際には大使を通して連絡しましょうというイメージ。

appコンテナから別ホストのdbコンテナに接続する例

host1上のappコンテナをhost2上のdbコンテナに接続させる様子を図示すると下図のようになる。各ホストごとにambassadorというコンテナが動作しており、TCP接続を適切なコンテナにプロキシする。appコンテナからdbコンテナに接続する場合、2つのambassadorコンテナを経由することになる。1つ目はambassador1コンテナであり、dbコンテナに接続するためのアドレスとポート番号を知っている。接続先はambassador1起動時に環境変数として教える必要がある。なお、ambassador1はこの接続先がdbコンテナのものだと認識しているが実際にはambassador2のものである。2つ目はambassador2コンテナであり、dbコンテナのポート番号を知っている (これは同一ホスト上の接続なのでコンテナの名前で解決できる)。

network | |-host1 | | | `ambassador1 | | | `-app | `-host2 | `ambassador2 | `-db

DockerHubにホスティングされているsvendowideit/ambassadorというDockerイメージを使えば、上述の構成を簡単に実現できる。このイメージはコンテナ起動時に渡した環境変数を解析し、socatを利用して適当なリバースプロキシを起動してくれるというもの。

接続先の変更時にサービスを再起動しなくて済む

一般的にはコンテナ起動時に接続先の情報を渡すため、接続先を変更するときにはサービス自体を再起動する必要がある。しかしAmbassadorパターンに従った場合、接続先を変更するにはAmbassadorコンテナだけ再起動すれば良いため、サービス自体を再起動する必要がないという利点がある。

発展

今回紹介した例では、ambassador2はambassador1のアドレスを知る必要がある。Serf等のService Discoveryの仕組みを利用することで、Ambassadorに対してより動的な設定変更が行えるだろう。また今回はsocatを利用したが、haproxy等を利用して単純なリバースプロキシではなくロードバランサーとして動作させることもできるだろう。

参考

以下の記事で同様の内容が紹介されている。特にCenturyLink Labsの記事はより発展的な内容に触れており、figを利用して複数のコンテナの構成をYAMLに記述して管理する例や、Serfを利用して動的に設定を更新する方法などが紹介されている。Ambassadorパターンを利用しない方法としては、VPNを利用する方法や、PipeworkやOpen vSwitchなどでホスト間をトンネルのように接続する方法がある。Marek Goldmannの記事ではOpen vSwitchを利用する方法が紹介されている。しかしこれらの方法の欠点として、Dockerコンテナを動かすホスト側の設定が必要になり、Dockerコンテナとは異なるレイヤでの設定が更に必要になるということが挙げられる。