Tech Sketch Bucket of Technical Chips by TIS Inc.

マルチホストDockerネットワーキング(1)

Pocket

みなさんDockerを利用しているでしょうか。Dockerを使ったことのある方は、そのネットワーク周りに不便を感じたことはないでしょうか。今回はDockerのネットワーク周りを概観し、Dockerをコンテナ型仮想化エンジンとして見た場合のネットワーク周りの問題点について解説します。また次回以降の記事で、その問題点を解決する既存の取り組みやツールについて触れていきます。

Docker

Dockerとは

そもそもDockerとは、当時のdotCloud社(現Docker社)が自社のパブリックPaaSを実現するために、アプリケーションの実行環境をポータブルにしていつでも簡単に立ち上げられるように開発した技術が根幹となっています。様々なLinuxコンテナ技術を用いたリソース隔離も、コピーオンライトで差分管理を行うファイルシステムも、またOSやミドルウェアを自動構成してデプロイする仕組みも、もともとはPaaSを形づくるために開発されたものなのです。

一方で今日のDockerは、LXCOpenVZのような、「コンピューティングリソースをオンデマンドに提供してくれるコンテナ型仮想化エンジン」としても注目されています。しかしDockerが提供するコンテナを、いわゆるHypervisorが提供する「仮想マシン」と同列に扱おうとした場合、様々な問題が表出します。ホストOSのカーネルを共有することに起因するコンテナ技術の本質的な問題から、Dockerという若いプロダクトが内包するセキュリティの問題、あるいはOVFのようなコンテナイメージの標準仕様がない問題などいろいろとありますが、今回はDockerのネットワーク周りの問題について記します。

検証した環境

以降の検証は、Ubuntu 14.04.2上に最新版のgolangとdockerをインストールして実施しています。

DistributionUbuntu 14.04.2 LTS
Kernel3.13.0-45-generic
golang1.4.2
docker Client & Server1.5.0

単一ホストでのDockerネットワーク

単一ホストでDockerコンテナを立ち上げると、次のようなネットワークが構成されます。

docker_networking_1_01

Dockerデーモンを立ち上げると、ホスト内部にdocker0という仮想ブリッジが作成され、docker0から外部に接続できるように、iptablesにルールが追加されます。

コンテナを立ち上げるとDockerがvethペアを作成し、コンテナのnetwork namespaceとdocker0に接続します。これにより、ホストOSからコンテナに通信できるようになります。またコンテナを立ち上げる際に -p オプションが指定されていれば、Dockerはポート変換ルールもiptablesに追加します。これにより、ホストの当該ポートに通信が届けば、コンテナの適切なポートに転送されるようになります。

この状態でコンテナ1⇔コンテナ2は問題無く通信できますし、ホストOSからコンテナ2の80ポートへアクセスすることもできます。

Dockerをコンテナ型仮想化エンジンとして見た場合の問題点

Dockerコンテナをワンボックスの開発環境として使うのであればこれで十分ですが、「仮想マシン」のように単独で動作するサーバとして利用したい場合、次のような問題があります。

  1. コンテナに与えられるIPアドレスを制御できない
    • docker0に与えるIPアドレスは、dockerデーモン起動時のオプションで指定できる。しかしコンテナのIPアドレスは、docker0が所属するアドレス空間から使っていないIPアドレスをdockerが自動採番する。
    • コンテナを再作成した際に、以前のIPアドレスを再利用するように指定することはできない。
  2. ホストOSの外部からコンテナにアクセスするのが面倒
    • コンテナに与えられたIPアドレスは、ホストOSの外部からはアクセスできない。
    • コンテナ内のプロセスにアクセスするためには、-pオプションを指定してホストOSへポートフォワードしておく必要がある。

これらの問題に対応するために、Dockerは「コンテナ間を接続してコンテナ名でアクセスする」仕組み( --linkオプション)を提供しています。

単一ホストでのコンテナ間接続

他のコンテナから接続されるコンテナを起動する際に、--name オプションでコンテナ名を定め、かつ --expose オプションを用いて他のコンテナに公開するポートを指定しておきます。

このコンテナに接続するコンテナを立ち上げる際には、 --link <コンテナ名>:<エイリアス> オプションを用いて接続したいコンテナの名前を指定します。この「エイリアス」として指定したキーワードをホスト名として、立ち上げたコンテナの/etc/hostsに接続先コンテナが設定されます。また公開されているポート番号なども、立ち上げたコンテナの環境変数として設定されます。

これにより、接続先コンテナのIPアドレスがわからなくても、指定したエイリアスを用いて公開ポートへアクセスすることができます。

docker_networking_1_02

異なるホストのコンテナへの接続

上記のlink機能は、単一ホストのDockerコンテナ間では上手く動作します。では、異なるホスト上のコンテナへ接続したい場合はどうすれば良いのでしょうか。この場合、Ambassador Patternを用いると良いとDockerは言っています。異なるホストを仲立ちするコンテナ(ambassador:大使)を介して、コンテナを接続するのです。

まずは単一ホストの場合と同様に、ホスト1上で接続されるコンテナを立ち上げます。

次にホスト1上で、ambassadorコンテナを立ち上げます。この際、接続されるコンテナが公開しているポートは、自動的にホスト1へポートフォワードされます。

今度はホスト2上で、ambassadorコンテナを立ち上げます。この際、ホスト1のIPアドレスとフォワードされているポートを環境変数として与えます。

最後に、ホスト1のコンテナに接続するDockerコンテナをホスト2上で立ち上げます。この際、link先としてambassadorコンテナを指定します。

これにより、異なるホストのコンテナへも、指定したエイリアスを用いて公開ポートへアクセスすることができます。

docker_networking_1_03

Docker link機能の問題点

これで万事問題がなければ幸せなのですが、そうもいきません。link機能とambassadorを用いて全てのコンテナの接続情報を管理する場合、コンテナ数が多くなると設定が非常に煩雑になるため、そのままではおそらく破綻します。 progrium/ambassadordのような、分散KVSを用いた動的Ambassadorの実装を検討したほうが良いでしょう。

ただし動的Ambassadorを実現できたとしても、コンテナに付与されたIPアドレスがコンテナが起動しているホストに閉じており、外部のネットワークからアクセスできないという問題は依然として残ったままです。この問題を解決するためには、何らかの手段でDockerのネットワークを拡張しなければなりません。

Dockerネットワーク拡張ツール

このようなDockerのネットワークの諸問題に困っている人はたくさんいるようで、様々なDockerネットワーク拡張ツールが公開されています。jpetazzo/pipeworkcoreos/flannelweaveworksranchersocketplane・・・

次回以降の記事では、これらのDockerネットワーク拡張ツールについて触れていきたいと思います。

次回は

ということで次回は、GREjpetazzo/pipeworkを用いたマルチホストでのDockerネットワーク拡張について解説します。

エンジニア採用中!私たちと一緒に働いてみませんか?