Docker1.9のマルチホストネットワーク

2015年11月25日(水)
松井 暢之
Docker 1.9で新たに導入されたマルチホストネットワーク機能について、これまでのDockerのネットワークと比較しながら、紹介します。

Dockerとはそもそも、dotCloud社(現Docker社)が自社のPaaSを構成するパーツのうち、アプリケーション実行環境をイメージとして標準化し、そのイメージから実際の実行環境を素早く提供するための仕組みをOSS化したものだ。そのため、現在Docker Engineと呼ばれている「アプリケーションの実行環境を、Dockerイメージから素早くコンテナとして立ち上げるDockerのコア機能」には独特のクセがあり、初学者を悩ませる。今回は、代表的な悩みの一つであるDockerのネットワークについて、最新リリース(1.9)で導入されたマルチホストネットワーク機能についても触れながら掘り下げる。

従来のDockerのネットワーク

まずは、従来から利用されているDockerのネットワークを見ていこう。

Dockerネットワーク(bridgeモード)の実装

Dockerがデフォルトで利用するbridgeネットワークは、次のような手順で動作している。

図1:一般的なDockerのネットワーク構成

図1:一般的なDockerのネットワーク構成

  1. Dockerデーモン起動
    ホストOSのネットワークスタックにdocker0という仮想ブリッジを作成
    docker0に接続したコンテナから外部ネットワークへ出ていけるように、ホストOSのNetfilterにIPマスカレードルールを追加
  2. Dockerコンテナ起動
    Dockerコンテナ専用のNetwork Namespaceを作成
    vethペアを作成し、一方をdocker0へ、もう一方をDockerコンテナに割り当てられたNetwork Namespaceへ接続
  3. Dockerコンテナに割り当てられた仮想NICへ、Dockerデーモンが既存のDockerコンテナと重複しないIPアドレスを付与(現在の実装ではDHCPなどは用いず、単なる昇順でIPアドレスを割り当てている)
  4. Dockerコンテナ起動時にオプションとして指定されていれば、外部ネットワークからDockerコンテナの特定ポートに接続できるように、ホストOSのNetfilterにDNATルールを追加
※vethペア:Linuxカーネルに搭載された機能で、一対の仮想的なイーサネットインターフェース(仮想NIC)を仮想的なクロスケーブルで接続したものとして働く。vethペアの一方の仮想NICに投入されパケットは、もう一方の仮想NICから出てくる。
※仮想ブリッジ:Linuxカーネルに搭載された、ホストOS内に仮想的なL2スイッチを作成する機能。同じ仮想ブリッジに接続された仮想NICや物理NICは、仮想ブリッジを経由してお互いにパケットを交換できる。
※Network Namespace:Linuxカーネルに搭載された機能で、ホストOSのネットワークから隔離された独立したネットワークスタックを構成するもの。あるNetwork Namespace内に作られた仮想NICや仮想ブリッジは、ホストOSや他のNetwork Namespaceからは見えなくなる。
※Netfilter:Linuxカーネルに実装された、設定したルールに従ってパケットフィルタリングやネットワークアドレス変換機能を実現するフレームワーク。iptablesコマンドを用いて制御する。

なおDockerコンテナ起動時に「--net」オプションで明示的に指定すれば、仮想ブリッジやNetwork Namespaceを使わずホストOSのネットワークスタックをそのまま利用したり(hostモード)、他のDockerコンテナに割り当てられているネットワークスタックに相乗りしたり(containerモード)することもできるが、本稿では詳細な解説は割愛する。

同一ホスト内でのDockerコンテナ連携(bridgeモード)

上記のような手順で、Dockerコンテナは仮想ブリッジdocker0に接続され、付与されたIPアドレスを用いてDockerコンテナ間の通信が可能になる。

ただしdocker0が利用するネットワークアドレスを指定することはできるが、どのようなIPアドレスをDockerコンテナに付与するかは、Dockerデーモンに一任されている。

そのため、利用者はDockerコンテナに付与されるであろうIPアドレスを事前に知ることはできないし、DHCPサーバがDockerコンテナへIPアドレスを割り当てることも、利用者が静的にIPアドレスを指定することもできない。これはDockerの初学者をよく悩ませるポイントの一つである。

実はDockerコンテナ起動時に「--link」オプションで他のコンテナを指定すると、そのDockerコンテナのIPアドレスや公開ポート等の情報が「/etc/hosts」や環境変数に埋め込まれた状態で、Dockerコンテナが立ち上がる。また「--publish(もしくは-p)」オプションを用いれば、起動したDockerコンテナの特定ポートをホストOSのネットワークに公開することもできる。

これらの機能を用いれば、個々のDockerコンテナのIPアドレスを意識することなくアプリケーション実行環境を立ち上げられる。しかしDockerの初学者にとって、「--link」や「--publish」といったDockerネットワーク特有の流儀を学び適切に利用するのは、ハードルが高いのは確かである。

複数ホストをまたがったDockerコンテナ連携

ここまで説明したDockerのネットワークの仕組みは、単一サーバ上にアプリケーション実行環境を立ち上げて利用する上で必要十分な機能を持っている。しかし、複数のホストを束ねてDockerの可用性向上や負荷分散を図る場合、複数ホストに分散したDockerコンテナ間を、以下のような機能を持った何らかの仕組みで接続できるようにしなければならない。

  • 複数ホスト間で適切なサブネットを持ち、重複しないIPアドレスを割り当てる機能
  • 複数のホスト上に分散するコンテナ間の通信をトンネルし、適切なホストにルーティングする機能
  • ホスト間のネットワークがセキュアでない場合、外部ネットワークを通るパケットを暗号化する機能

このような複数ホストを接続する仮想ネットワーク機能は、従来Docker単体では利用できなかった。そのため、以下に挙げたような様々なサードパーティ製のツールが利用されてきた。

flannel:CoreOS社が公開しているOSSで、Kubernetesのバックエンドネットワークの構成にもよく利用される
rancher:Rancher Labs社が公開しているプライベートなコンテナサービスプラットフォームを構成するためのOSSで、WebUIからホストの登録やコンテナ起動が実施できる
weave net:Weaveworks社が公開しているOSSで、複数ホストを跨いでコンテナ間の仮想ネットワークを構成する機能に特化している

しかし2015/11/04にリリースされたDocker 1.9では、とうとうDocker自身に「overlayモード」と呼ばれるマルチホストネットワーク機能が搭載された。ここからは、従来のbridgeモードのネットワークと見比べながら、overlayモードの実装について見ていこう。

Docker 1.9で新たに採用されたマルチホストネットワーク機能「overlayモード」

実は二世代前のDocker 1.7から、Dockerはネットワーク関係の機能をコア機能から切り離し、Container Network Model(CNM)というコンセプトを持つdocker/libnetworkというライブラリに再編成している。

Container Network Model(CNM)とは

DockerのContainer Network Model(CNM)は、Sandbox、Endpoint、Networkの3つのコンポーネントから構成される(詳細はlibnetworkのdesignドキュメントを参照して欲しい)。

図2:DockerのContainer Network Model(出典:Docker Blog <a href="https://blog.docker.com/2015/04/docker-networking-takes-a-step-in-the-right-direction-2/" class="link">Docker Networking takes a step in the right direction</a>)

図2:DockerのContainer Network Model(出典:Docker Blog Docker Networking takes a step in the right direction

  • Sandbox
    • コンテナのネットワークスタックの設定(インターフェースやルーティングテーブル、DNS設定等)を隠蔽するもの
    • LinuxであればNetwork Namespace、FreeBSDであればJail等を用いて実装される
    • 一つのSandboxが複数のEndpointを持っていても良い
  • Endpoint
    • SandboxをNetworkに参加させるもの
    • Endpointはveth pairやOpen vSwitch internal port等を用いて実装される
    • Endpointは一つのNetwork、一つのSandboxに所属する
  • Network
    • お互い直接通信できるEndpointの集合を表すもの
    • NetworkはLinux bridgeやVLAN等を用いて実装される
    • Networkは複数のEndpointを持つ

libnetworkは、これら3つのコンポーネントとAPIのエンドポイントとなるNetworkControllerを実装している。具体的なNetworkの実装はDriverとして切り出されており、Dockerコンテナ起動時にNetworkController経由で望みのNetwork実装を選択できるようになっている。

libnetworkに実装されているDriver

Docker 1.9のlibnetworkには、以下の5種類のDriverが実装されている。従来の「--net=none, bridge, host」に相当するDriverに加え、Overlay DriverとRemote Driverが追加されている(「--net=container」は、他コンテナのネットワークスタックに相乗りするため、専用のDriverはない)。

ドライバ備考
Nulldriverが備えるべきAPIの空実装。ネットワークが不要なコンテナの場合に利用する(従来の「--net=none」に相当)
Bridge仮想ブリッジとNetfilterを用いたネットワーク。 仮想ブリッジを作り、Endpointと仮想ブリッジ間をvethペアで接続する(従来からデフォルトで利用されるbridgeネットワーク「--net=bridge」に相当)
HostホストOSのネットワークスタックをそのまま利用する(従来の「--net=host」に相当)
OverlayVXLANでカプセリングしたオーバーレイネットワークを、複数ホスト間に延伸する
Remote動的に登録されるDriverの接続ポイントとなる。Remoteパッケージ自体はDriverの実装を持たず、libnetworkのpluginとして動作するdriverプロセスへのプロキシを提供する

上記以外にもwindows.goなるソースコードもあるが、現時点では「TODO Windows. This is a placeholder for now」とコメントされているだけで、中身はnull driverとほぼ同じでしかない。

Overlay Driverの準備

では実際にOverlay Driverを動作させ、Docker 1.9のマルチホストネットワーク機能を検証してみよう。まずはOverlay Driverを利用できるように環境を整える。

Overlay Driverを動作させる前提条件

いきなりハードルが高いことに、Overlay Driverを動作させるためには、以下の環境が必要となる。

  • 3.16以上のLinux kernel
    • Ubuntu 14.04 LTSのカーネルは3.13のため、カーネルアップデートが必要
    • CentOS 7.1のカーネルは3.10のため、カーネルを自力でビルドして入れ替えが必要
  • 分散KVS
    • libnetworkのoverlay Driverは、NetworkやEndpoint等のメタデータを保持するためにlibkvを用いる
    • libkvは現時点で次の分散KVSをサポートしている
      • consul >= 0.5.1
      • etcd >= 2.0
      • zookeeper >= 3.4.5

本稿の検証は、以下の環境で行った。

 バージョン
distributionUbuntu 14.04.3 LTS
kerne3.19.0-33-generic
consul0.5.2
docker1.9.0, build 76d6bc9

consulクラスタの起動

まずは「consul agent -server」コマンドを用い、Dockerデーモンを動作させる全てのホストが参加するconsulクラスタを起動しておく(consulの詳細については本稿の範囲を超えるため、サービス・ディスカバリのためのConsul入門などを参照のこと)。

Dockerが公開しているGet started with multi-host networkingでは、consul専用のDockerコンテナを一つ起動し、全てのDockerデーモンはそのDockerコンテナのconsulを参照するという手順を紹介している。しかしこの手順では、Dockerデーモンが「consulが起動しているIPアドレス」を事前に知っている必要があるため、Dockerデーモンの起動オプションを静的に定義するには不都合だ。そこで本稿では、全てのホストでconsulを起動してクラスタを組み、Dockerデーモンはlocalhostのconsulを参照するような手順を取る。

Dockerデーモンの起動オプションの変更

Overlay Driverを利用するためには、Dockerデーモン起動時にlibkvが用いる分散KVSを指定しなければならない。そのためDockerデーモンの起動設定ファイル(UbuntuにaptからDockerをインストールした場合は「/etc/default/docker」)に次のオプションを追加する。

【/etc/default/dockerに起動オプションを設定】
root@docker01:~# echo 'DOCKER_OPTS="--cluster-store=consul://localhost:8500 --cluster-advertise=eth0:2376"' >> /etc/default/docker
Dockerデーモン間の通信を行うNIC名がeth0以外の場合、「--cluster-advertise」オプションを適切に変更すること。

Dockerデーモンの再起動

consulクラスタが起動していることを確認した後、Dockerデーモンを再起動してオプション設定を反映させる。

【Dockerデーモン再起動】
root@docker01:~# service docker restart

Overlay Driverの検証

ここまでの手順によりOverlay Driverを利用する準備が整ったので、実際に動作させ検証を行う。

Overlay Networkの生成

「docker network create」コマンドを用いて、Overlay Networkを構成する。構成された情報はconsulを通じてすべてのホストで共有されるため、どのホストでコマンドを実行してもかまわない。

下記のように「--driver=overlay」を明示的にDriverを指定しないと、Bridge Driverを用いて仮想ネットワークが作成されてしまうので注意すること。また「--subnet」オプションを用いれば、Dockerが生成する仮想ネットワークのネットワークアドレスを指定することもできる。どのようなオプションが使えるかは、「docker network create --help」で確認してほしい。

root@docker01:~# docker network create --driver=overlay --subnet=192.168.0.0/24 vnet

本稿では、「vnet」という名前のOverlay Networkを「192.168.0.0/24」というサブネットアドレスで作成したものとして検証を進める。

Dockerコンテナを起動してOverlay Networkに接続

Overlay Networkに接続するためには、以下の2通りの手順がある。

  1. Dockerコンテナを起動した後、DockerコンテナをOverlay Networkに参加させる
  2. 「docker run」のオプションでOverlay Networkを指定し、Overlay Neworkに接続した状態でDockerコンテナを起動する

どちらの手順を取っても、Overlay Network上でDockerコンテナ間の通信が可能となるが、Dockerデーモンによって自動的に構成されるネットワークの状態が少し異なる。その差異は後述するが、まずはそれぞれの手順を見ていこう。

Dockerコンテナ起動後にOverlay Networkに接続

まずは、Dockerコンテナを通常どおり起動させる。

【Dockerコンテナ起動】
root@docker01:~# docker run -itd --name=c01 --hostname=c01 ubuntu:14.04

この段階で、Dockerコンテナには従来通り、docker0に接続されたeth0が差し込まれる。

その後、DockerコンテナをOverlay Networkに接続する。

【DockerコンテナをOverlayNetworkに接続】
root@docker01:~# docker network connect vnet c01

この段階でDockerコンテナには、Overlay Networkに接続されたeth1が差し込まれる。

Overlay Networkに接続した状態でDockerコンテナを起動

「--net」オプションを用いることで、Overlay Networkに接続された状態でDockerコンテナを起動させることができる。

【OverlayNetworkを指定してDockerコンテナ起動】
root@docker02:~# docker run -itd --name=c02 --hostname=c02 --net=vnet ubuntu:14.04

これにより、Overlay Networkに接続されたeth0を持ったDockerコンテナが起動する。

ただしOverlay NetworkはSandbox内に隠蔽されており、Snadbox外部のネットワークとは直接の通信ができない。この問題を解決するために、ホストOSのネットワークスタック上に仮想ブリッジ(docker_gwbridge)が自動的に作成され、docker_gwbridgeと接続したeth1がDockerコンテナに自動的に差し込まれる。

以上の2通りの手順を比較すると、eth0とeth1が逆転していることがわかるだろう。実際に行う際には注意が必要だ。

--linkオプションを用いないDockerコンテナの名前解決

従来、他のDockerコンテナにコンテナ名でアクセスするためには、Dockerコンテナ起動時に「--link」オプションを指定する必要があった。

しかし新しいDockerネットワーク機能を用いると、コンテナが接続するたびに、「コンテナ名」や「コンテナ名.ネットワーク名」というエントリが、そのネットワークに参加している全てのコンテナの「/etc/hosts」に自動的に追加される。またこれらのエントリは、「docker stop」でコンテナを停止すれば、自動削除される。

そのため「--link」オプションを明示的に指定せずとも、複数のコンテナ間をコンテナ名でアクセスすることができ、非常に便利である。

【/etc/hostsの例】
root@docker01:~# docker exec c01 cat /etc/hosts
172.17.0.2  c01
127.0.0.1   localhost
192.168.0.3 c02
192.168.0.3 c02.vnet

この「/etc/hosts」を用いたDockerコンテナの名前解決は、Overlay DriverだけでなくBridge Driverでも適用される。そのため、単一ホストでDockerを運用する場合であっても、従来のdocker0と--linkオプションを使うのではなく、新しいDockerネットワークのBridge Driverを用いた方が使い勝手が良い場合もあるだろう。

Dockerコンテナ間の疎通確認

このようにして、複数のホストに分散したDockerコンテナを同じOverlay Networkに接続すれば、Dockerコンテナどうしが同じサブネットに所属しているものとして、通信が可能となる。pingやnetcat等を用いて、パケットの到達状況を確認してみると良いだろう。

またOverlay Networkに接続するDockerコンテナは、Overlay Networkだけでなく、ホストのネットワークにつながった仮想ブリッジにも接続されている。Dockerコンテナのdefault gatewayは、このBridge Networkに向けられているため、Dockerコンテナからホストのネットワークスタックを経由して外部ネットワークとの通信も可能である。

Overlay Networkの状態

では最後に、Overlay Driverが構成する仮想ネットワークの状態を確認してみよう。

たとえば同一サブネットの2台のホスト+別サブネットの1台のホストで、Overlay Networkを構成した場合、図3のようにSandboxに隠蔽された仮想ブリッジbr0がお互いにVXLANで接続されて、仮想ネットワークが構成される。

上述した「Dockerコンテナ起動 → Overlay Networkに接続」というステップでDockerコンテナをOverlay Networkに接続した場合、Dockerコンテナはdocker0に接続したeth0とOverlay Networkに所属したeth1を持つことになる(下図のc01)。一方「--net」オプションを用いてOverlay Networkに接続したDockerコンテナを直接起動した場合、DockerコンテナはOverlay Networkに接続したeth0と、新たに作成された仮想ブリッジ(docker_gwbridge)に接続したeth1を持つ(下図のc02とc03)。

Overlay Networkのネットワークアドレスは、「docker network create」実行時に指定できるが、各コンテナに割り当てられるIPアドレスは、従来と同様にDockerデーモンが決めており、利用者が明示的に指定することはできない。ただし前述した「/etc/hosts」を用いた名前解決機能があるため、実用上は問題にならないだろう。

図3:Overlay Driverで構成される仮想ネットワークの実装例

図3:Overlay Driverで構成される仮想ネットワークの実装例

まとめ

最新リリースのDocker 1.9では、Docker単体でマルチホストネットワークを構成できるようになった。Docker Swarm等のコンテナスケジューリングツールと上手に連携させれば、単一ホスト上でDockerを動作させているような利便性を保持したまま、複数ホスト利用による可用性向上や負荷分散が行えるようになるだろう。

皆さんにもぜひ、この新しいDockerネットワーク機能を試してみてほしい。

TIS株式会社

戦略技術センター エキスパート
アーキテクトとして様々なプロジェクトに参画したのち、現部門では新規技術の 研究開発に従事している。
現在はOSSのクラウドオーケストレーションツールCloudConductorの企画開発や、IoT関連技術の研究開発を推進中。

連載バックナンバー

運用・管理

事例から考えるDockerの本番利用に必要なこと

2016/5/26
本番環境へのDockerの導入が進むために必要な条件を、各社の事例を元に考察する。
運用・管理技術解説

Dockerコンテナ環境のバックアップツール「Convoy」を使う

2016/3/30
Docker環境のバックアップツールとして注目されるConvoyのインストールから使用方法までを解説します。
運用・管理技術解説

CoreOS&Docker環境においてOracle Database 11g Release 2をインストールするためのポイント

2016/3/23
データベースの定番であるOracle Databse 11g Release 2を、コンテナ環境に導入する手順を紹介します。

Think ITメルマガ会員登録受付中

Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。

Think ITメルマガ会員のサービス内容を見る

他にもこの記事が読まれています