訳: Docker libnetworkデザイン

最近Dockerネットワークの機能がいろいろ増えたりしてるけどあんまり使われてないように思います.

Docker enginのドキュメントだとDockerが叩くAPIからの説明しか無いので,機能の実装側であるlibnetworkについて自分の勉強も兼ねてまとめていこうと思います.まずはlibnetworkについて根本思想となるContainer Network Modelについて理解するために公式のdesign.mdを和訳してみました.
思想とlibnetworkの実装が混ざっていてちょっとわかりづらい部分も有るが,やはり全体を把握するには一番いい資料ですので.今後各ドライバなどについても詳解するつもりで,その中でわかってくることもあるかと思います.

Design

libnetworkのバージョンやゴールはroadmapにハイライトされている.
このドキュメントはlibnetworkが如何にデザインされて今の形になっているかを説明する.
特定のリリースでの情報はProject Pageを参照のこと.

多くのデザインの方向性はDocker v1.6のネットワークデザインを参考にしている.
Docker v1.6のネットワークデザインについてはDocker v1.6 Designを参照のこと.

Goal

libnetworkプロジェクトはdeveloping small, highly modular and composable tools that works well independentlyの哲学に従っている.
libnetworkはコンテナのネットワークに必要な機能を満足させることを目的とする.

The Container Network Model

LibnetworkはContainer Network Model (CNM)の実装という位置付けである.
CNMはコンテナに対し様々なネットワーク機能を提供するための抽象化の枠組みで,主に以下の3コンポーネントから成る.

Sandbox

Sandboxはコンテナのネットワークスタックを定義する.
コンテナのインターフェースやルーティングテーブル,DNSの設定などである.
実装としてはLinuxのネットワークネームスペース,FreeBSDのJailなどがこれに相当する.
Sandboxは複数のendpointを持ち,そのEndpointはそれぞれ異なるnetworkに所属することが可能である.

Endpoint

EndpointはSandboxをネットワークに参加させる役割を持つ.
実装としてはvethやOpen vSwitchのinternal portなどがある.
Endpointは必ず1つのネットワークに所属し,1つの以下のSnadboxに所属することができる.

Network

NetworkはEndpointのグループであり,それぞれのEndpointが直接通信するが可能である.
実装としてはLinux bridgeやVLANなどである.
当然,ネットワークは複数のEndpointから成る.

CNM Objects

NetworkController
NetworkController はDockerにおけるDocker daemonのようにAPIを提供し,Networkを管理する.
libnetworkはinbuilt・remote問わず複数のdriverが同時に動作することをサポートしている.

Driver
Driver は実際にネットワークを作成する実装となる.
libnetworkではDriver特有のoption, labelをDriverへ透過的に渡すためのAPIが提供されている.
DriverにはBridgeやHost, None, overlay といったのinbuiltのものとサードパーティの提供するremoteがある.
Driverが提供するネットワークに対し責任を持ち,管理する.
将来的に多くのネットワーク機能を管理できるように幾つかのDriverを用意する予定である.

Network
NetworkオブジェクトはCNMモデルにおけるNetworkの実装となる.
NetworkControllerNetworkオブジェクトを作成,管理するAPIを提供する.
Networkが作成されたり,情報の更新があった際にDriverへイベントの通知が送られる.
libnetworkでは同一Networkに所属するendpoint間での疎通を可能にし,他のNetworkと隔離するというレベルの抽象化を行っている,
実際にはDriverが疎通と隔離を管理する必要がある.
このNetworkがグローバルスコープである場合には,同一ホスト内及び複数ホスト間の両方で疎通が可能である.

Endpoint
Endpointはサービスのエンドポイントであり,あるコンテナのサービスがネットワーク内の別コンテナのサービスへ疎通可能となる.
NetworkオブジェクトがEndpointの作成及び管理のAPIを提供します.あるEndpointは1つのNetworkにアタッチされる.
Endpointの作成は,対応するSandboxのリソース管理を行うDriverオブジェクトによって行われる.
Endpointはコンテナに特有である必要はなく,クラスタ内でグローバルスコープを持つのが好ましい.
(
補足:クラスタ内で一意であり,クラスタを跨いだ別のSandboxにアタッチなどが可能)

Sandbox
Sandboxオブジェクトはコンテナ内のネットワーク設定,例えばIPアドレスやMACアドレス,ルーティングテーブル,DNSエントリなどを表す.
SandboxオブジェクトはユーザがNetworkに対しEndpointの作成を要求したタイミングで作成される.
そのNetworkを管理するDriverはIPアドレスなどのリソースを割り当て,libnetwrokへSandboxInfoを返す.
libnetworkはネットワーク情報を格納するSandboxの実装としてOSごとに適した構成を用いている(例えばLinuxではnetns).
Sansboxは異なるnetwrokに属する複数のendpointをアタッチすることが可能である.
Sandboxはあるホストの特定のコンテナに紐づくため,そのコンテナが存在するホストに閉じたもの(ローカルスコープ)となる.

CNM Attributes

Options
Options はDriver固有の設定を行う共通的かつ柔軟な枠組みである.
Optionsはkey-valueのペアでkeyは文字列,valueは一般的なオブジェクトである(例えばgo言語のinterface{}など).
libnetworkはkeynet-labelsパッケージに定義されたLabelに一致するときのみOptionsを受け付ける.
Optionsは下記で説明するLabelsの概念を包含する.
エンドユーザーがUIからOptionsを確認することはできないがLabaelsは可能である.

Labels
LabelsOptionsと非常に似ており,実質的にOptionsの一部である.
Labelsはエンドユーザーから確認することが可能であり,--labelsオプションを指定することで明示的にUIで指定可能である.
UIからDriverへと渡され,Driverはこれを使用し,そのDriver固有の操作を行うことができる(例えばネットワークにIPアドレスを割り当てたりなど).

CNM Lifecycle

CNMの利用者,たとえばDockerはCNMオブジェクトおよびそのAPIを介して操作すべきコンテナのネットワークをやり取りを行う.

  1. DriversNetworkControllerを登録する.Build-inのドライバはlibnetworkの内部から,remoteドライバはPluginメカニズムを介して行う(Pluginメカニズムについては今後).各driverは特定のnetworkTypeを持つ.
  2. NetworkControllerオブジェクトはlibnetwork.New()APIを用いてネットワークの割当やDockerへのOptionsを用いてDriverへの設定を行う.
  3. NetworkはコントローラのNewNetwork()APIによってnamenetworkTypeを与えられる.
    networkTypeNetwork作成時にどのDriverを選択すべきかを示す.
    これ以降,Networkに対する操作は全てDriverから行われる.
  4. controller.NewNetwork() APIはoptionsパラメータを取ることができる.
    これはドライバ固有のオプションやLabelsを与えるものである.
  5. network.CreateEndpoint() は新しいEndpointをネットワークが作成する際に呼ばれる.
    このAPIはoptionsパラメータを取ることができる.
    これらのoptionsは定義されたラベルかつドライバ固有のラベルを指定できる.
    そしてドライバがdriver.CreateEndpointによって呼ばれ,NetworkEndpointが作成された時に予約されたIPv4/IPv6アドレス群の中からアドレスが選ばれる.
    このIP/IPv6アドレスはそのエンドポイントが待ち受けるポートと共にサービスのエンドポイントとして定義されなければならない.
    なぜならサービスのエンドポイントの本質はそのコンテナが待ち受けているネットワークアドレスとポート番号に他ならないからである.
  6. endpoint.Join()はコンテナにEndpointをアタッチする際に用いられる.
    このJoinはそのコンテナのためのSandboxが未だ作成されていなければ作成する.
    ドライバはSandboxのIDを複数のエンドポイントが同じコンテナにアタッチされるために利用することができる.
    このAPIはまたoptionsパラメータを指定可能である.
  7. libnetworkの直接的なデザインの問題ではないがDockerのようにendpoint.Join()が呼ばれるのは,コンテナが作成が呼び出されるStart() ライフライクルであることが強く推奨される.
  8. エンドポイントのjoin()APIについてのFAQにおいて,エンドポイントのcreateとjoinを別のAPIにする必要があるのかというものがあった.
    • これに対する答えはエンドポイントはサービスを表現しており,それはコンテナとは独立である.エンドポイントが作成された時,リソースが確保されいかなるコンテナもそのエンドポイントにあとからアタッチすることが可能であり,一貫したネットワークの挙動を示す.
  9. endpoint.Leave() はコンテナがストップした際に呼ばれる.
    DriverJoin()の時に確保したものを削除する.
    libnetworkはSandboxの削除を,それを参照する最後のエンドポイントがleaveした時に行う.
    しかしlibnetworkはエンドポイントが持つIPアドレスを持ち続け,コンテナ(同じか別かに関わらず)再びjoinした時にそのアドレスを使用する.
    これはコンテナのリソースがストップし,再スタートしても変わらないことを保証する.
  10. endpoint.Delete() はエンドポイントをネットワークから削除するときに用いられる.
    エンドポイントが削除されるとsandbox.Infoからも取り除かれる.
  11. network.Delete() はネットワークを削除するときに用いられる.
    libnetworkはネットワークに所属するエンドポイントが存在する場合にはそのネットワークを削除する事はできない.

Implementation Details

Networks & Endpoints

libnetworkのネットワークとエンドポイントのAPIは基本的にCNMで示されるオブジェクトと抽象化のレベルに一致させている.
CNMで示したように本質的な実装はドライバで行われている.参考:the drivers section

Sandbox

libnetworkは複数のOSでSandboxを実装するためのフレームワークを提供している.
現状Linuxではnamespace_linux.goconfigure_linux.gosandboxパッケージじ含まれる.
これはファイルシステムにおいて唯一のパスを持つネットワークネームスペースをそれぞれのサンドボックスごとに持つ.
Netlinkがグローバルネットワークからサンドボックスのネームスペースにインターフェースを移動するために用いられる.
Netlinkはネットワーク内でのルーティングテーブルの操作も行う.

Drivers

API

ドライバはlibnetworkを拡張し,上で述べたlibnetworkのAPIを実装している部分となる.
そのため全てのNetworkEndpoint の以下のAPIに1対1で対応するものを持つ.

  • driver.Config
  • driver.CreateNetwork
  • driver.DeleteNetwork
  • driver.CreateEndpoint
  • driver.DeleteEndpoint
  • driver.Join
  • driver.Leave

ドライバはユニークなID (networkid,endpointid,…) を持つ.
これはユーザがAPIを操作する際に目にする名前に対応している.

APIは未だ過渡的なものであり,特にマルチホストネットワークを実現する際には変わり得るものである.

Driver semantics

  • Driver.CreateEndpoint

このメソッドはInterfaceAddInterfaceメソッドによってEndpointInfoインターフェースを渡す.
Interface の返り値がnon-nilの場合,ドライバはそこに含まれるインターフェースの情報を利用(例えばアドレスは静的に与えられているものとして扱う)し,それが不可能な場合はerrorを返さなくてはならない.
返り値がnilの場合にはドライバが新しいインターフェースを割り当てなければならないを意味し,AddInterfaceによってそれらを登録するか,不可能な場合にはerrorを返す.

AddInterfaceInterfacenon-nilの場合には使用できない.

Implementations

libnetworkは以下のドライバを持つ.

  • null
  • bridge
  • overlay
  • remote

Null

nullドライバはAPIとしてnoopの実装となっており,ネットワーキングを必要としない時にのみ利用される.
これはDockerの --net=noneオプションのバックワードコンパチビリティのために用意されている.

Bridge

bridgeドライバはLinux Bridgeをベースとするブリッジング接続を提供する.
より詳細はブリッジドライバの章で説明する.

Overlay

Overlayドライバの実装はVXLAN等のカプセリングによるオーバレイネットワークによりマルチホストネットワークを実現する.
より詳細はオーバレイドライバの章で説明する.

Remote

remoteパッケージはドライバを提供しておらず,リモート接続のドライバをサポートする方法が示されている.
貴方は好きな言語でドライバを実装することができる.
より詳細はリモートドライバの章で説明する.