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
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メルマガ会員のサービス内容を見る

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