LoginSignup
20
22

More than 5 years have passed since last update.

Kuryrについて

Last updated at Posted at 2015-11-11

OpenStack Summitで結構大々的に発表されてたkuryr(喋ってたのを聴いてるとクリル?(クリア?参考)と読むっぽい)について、あんまり情報がないので、一番それっぽい公式ドキュメント(2015/11/18現在)のこれとかこれを読んでみて、物凄い適当に訳すとこんな感じになった。

この記事はOpenStack Summitの発表を聴いて、あるいはその発表内容の記事を基に書いてるわけではないので、今現在は既にちょっと違ってるかもしれない。ドキュメントの内容も構成も時々変わっていて、変更を追いきれてないかもしれない。
しかし、さすがにそんなに大ブレしたりはしないとは思うので、参考までに。

ちなみに今Docker1.9対応で大忙しなのか、実際動かしてみると、色々直さないと動かなかったりするので、評価はもう少し待ってみたほうがいいらしい。

ソースコードはこちら

Kuryrとは何か

CS_D8hKWsAIcZOM.jpg

『KuryrはDockerのlibnetwork remote network driverを実装したもので、OpenStack NeutronのAPI呼び出しにマッピングするもの。libnetworkのContainer Networking Model(CNM)とNeutronのネットワークモデルの間の通訳係として働き、コンテナとホスト間、またはコンテナとVM(nested VM)の間のバインディングを提供する。』

Neutronのネットワークモデルについてはこちら、CNMについてはこちらこちらが参考になるが、一応以下にざっくり書いておく。

Neutronのネットワークモデル

neutron.png

(この図は色々なのを参考に適当に描いたので合ってないかも)

  • Network
    • Networkは仮想的に隔離されたL2ブロードキャストドメインを指す
    • 明示的に"共有"と設定しない限り、作成したTenantに属する
    • Tenantは、Tenantあたりの上限Network数まで、複数のNetworkを作ることができる
    • NetworkはNeutron APIの主要なエンティティ
    • PortとSubnetは必ずNetworkに紐付いてる
  • Subnet
    • SubnetはVMに付与するIPが所属すするIPアドレスブロックを表現するもの
    • 各Subnetは必ずCIDRを持ち、Networkに関連付けられている必要がある
    • IPアドレスはSubnetのCIDRから選択されるか、ユーザの指定する"allocation pools"から割り当てられる
    • SubnetはGateway(gateway_ip)、DNSサーバ(dns_servers)、ホストのRoute(host_routes)を持ち得る
  • Port
    • Portは、仮想スイッチ上のスイッチポートを表現するもの
    • VMは、自身のNICをそのポートと接続する
    • Portは、そのPortに接続するNICに関連付けられるMACアドレスとIPアドレスも定義する
    • Portは、暗にSubnetに関連付き、IPアドレスはSubnetのallocation poolから取得される

あと何かこれらに付随する細かい要素が色々あるが割愛。

Container Networking Model

Container Networking Model

(こっちの図は本家から引用)

  • Sandbox (Network Sandbox)
    • DockerコンテナのネットワークスタックやNICを隔離する環境
    • Sandboxは複数のNetworkに繋がる複数のEndpointを持ち得る
    • 要するにnetns
  • Endpoint
    • 特定ネットワークで通信するためのNIC
    • Endpointは1つのNetworkに参加
    • 要するにveth
  • Network
    • 一意に識別できる、お互いが通信できるEndpointの集合
    • 例えばFrontendとかBackendというネットワークを作ることができて、それはお互い完全に隔離される
    • 要するにbridgeとかVLAN

Container Networking ModelはNetworkとコンテナの間における以下の約束事を提供する。

  • 同じNetworkの全てのコンテナは自由にお互い通信できる
  • 複数Networkはコンテナ間のトラフィックを分ける手段であり、全てのドライバでサポートされるべき
  • コンテナ毎の複数Endpointは、コンテナが複数のNetworkに所属するための手段
  • Endpointは、そのEndpointへの接続性提供のためにNetwork Sandboxに追加される

目標および目的

これは何が嬉しいのか。

『Kuryrを通すことで、あらゆるNeutronプラグインは、余計な苦労をしなくてもlibnetworkのバックエンドとして使える事になる。Neutron API自体はベンダ非依存なので、libnetworkから一旦Neutronを通せば、ちょっとした軽量スニペット(多分VIF Bindingのことで、原文にnovaがどうこう、と書いてあるのは多分novaのVIF driverの話)で色々なNeutronプラグインをDockerネットワーキングのバックエンドとしても使えるようになる』よね、というもの。

(docker -> libnetwork -> kuryr (と軽量スニペット) -> neutron -> 各種プラグイン -> 欲しい仮想ネットワーク、という感じ?)

『上述のとおり、Kuryrはコンテナのnetns内外を繋ぐVirtual Ethernet Pair(veth)の片方を、VIF Bindingによってホスト側のPort(Linux Bridge Portとか、OVS PortとかMidonet Portなど)にバインドする点についても面倒を見る。』

"Endpointは、そのEndpointへの接続性提供のためにNetwork Sandboxに追加される" を満たす上でも必須だし、これがないとプラグインで作った仮想ネットワークにコンテナをくっつけられないし、それもそうかと考えられる。

ユースケース

『Kuryrはコンテナネットワーキングの観点でOpenStack Magnumのユースケースに取り掛かるべきで、Magnumだけでなく、コンテナ活用を必要とする他のOpenStackプロジェクトへの、Neutron APIを通したコンテナネットワーキングの統一的なインタフェースとして動作すべき。この辺を考えると、KuryrはVM nested コンテナのユースケースをサポートするNeutronプラグインの活用や、それらのケースをサポートするためのNeutron APIの強化(OVNとか)を目指している。』

要するに、各所で言われてるとおり、将来的にOpenStackで扱うVM、VM上のコンテナなどのネットワークを一緒に扱う(API統一的にも、ネットワークへの所属的にも)ことができればいいですね、とかいうことだと考えられ、

OpenStack -> (コンテナの場合:Magnumとか -> docker -> libnetwork -> kuryr (と軽量スニペット)) -> neutron -> 各種プラグイン -> コンテナ、VM関わらず欲しい仮想ネットワーク

という感じ?

今のところMagnumで作られたコンテナクラスタのネットワークはNeutronと連携していないが、そのうちKuryrによってさりげなくNeutron管理に入るんだろうと考えられる。

Kuryrワークフロー

『Kuryrはdockerコンテナが実行されるそれぞれのホストに存在し、libnetwork remote driverに要求されるAPI実装として動く。Neutronの新しい特徴として、ユーザによってNeutronのリソースにtagを付けられるというのがあるが、kuryrではそれをNeutronのリソースIDとDockerのID(UUID)をマッピングするために使う予定?』
※今ソースを見てもそういうのは入ってないように見える

  1. 『libnetworkはplugin discovery mechanismを経由してkuryrを見つける』
    ※この処理中に、libnetworkは/Plugin.ActiveにHTTP POSTを行い、それがnetwork driverかどうかをチェックする
  2. 『Kuryrをlibnetworkのremote driverとして登録する』
  3. 『ユーザは、libnetworkに対してドライバをkuryrにするよう指定してリクエストを作成する』
    ※--driver=kuryr とか、 -d kuryrをdockerコマンドにつける
  4. 『kuryrがリクエストを受け取ると、Neutron clientを使ってNeutron APIを呼び出す』
  5. 『Neutronからレスポンスを受け取って、libnetworkへのレスポンスに変換する』
  6. 『libnetworkに返却』
  7. 『libnetworkはその返却された情報をKV(libkvのバックエンド)に格納する』

libnetworkとユーザのワークフロー (kuryrをremote network driverとして使う) - ホストネットワーキング

ネットワークの作成、コンテナの追加や削除、ネットワークの削除をした時の処理を見てみる。

1. 『ユーザがfooというdockerのネットワークを作るとする』

『remote driverにはkuryrを指定する。』

$ sudo docker network create --driver=kuryr foo
286eddb51ebca09339cb17aaec05e48ffe60659ced6f3fc41b020b0eb506d364

『このコマンドを実行すると、以下の様なjsonのリクエストが/NetworkDriver.CreateNetworkに対してPOSTで発行される。』

{
    "NetworkID": "286eddb51ebca09339cb17aaec05e48ffe60659ced6f3fc41b020b0eb506d364",
    "IPv4Data": [{
        "Pool": "172.18.0.0/16",
        "Gateway": "172.18.0.1/16",
        "AddressSpace": ""
    }],
    "IPv6Data": [],
    "Options": { "com.docker.network.generic": {}}
}

『Kuryr remote network driverは、下地になるNeutronのネットワークを作るため、Neutron APIリクエストを生成する。
NeutronでNetworkが作られると、Kuryr remote network driverは空の成功レスポンスをdockerデーモンに対して生成して、NeutronのNetworkにDockerのNetworkIDでタグ付けする。』

2. 『ユーザがさっき作ったfooというネットワークでDockerコンテナを起動する』

$ sudo docker run --net=foo -itd --name=container1 busybox
78c0458ba00f836f609113dd369b5769527f55bb62b5680d03aa1329eb416703

『このコマンドを実行すると、以下の様なjsonのリクエストが/NetworkDriver.CreateEndpointに対してPOSTで発行される。』

{
    "NetworkID": "286eddb51ebca09339cb17aaec05e48ffe60659ced6f3fc41b020b0eb506d364",
    "Interface": {
        "AddressIPv6": "",
        "MacAddress": "",
        "Address": "172.18.0.2/16"
    },
    "Options": {
        "com.docker.network.endpoint.exposedports": [],
        "com.docker.network.portmap": []
    },
    "EndpointID": "edb23d36d77336d780fe25cdb5cf0411e5edd91b0777982b4b28ad125e28a4dd"
}

『この後、Kuryr remote network driverはNeutron subnetとportを作成するためのNeutron APIへのリクエストを、リクエスト内のinterfaceへのマッチングフィールドと合わせて作成する。
Kuryrは、interfaceのIPについて何も情報を載せないように動的にsubnetを作成する必要がある。』

『以下の様な手順を踏む。』

(1) 『Endpointの作成時、Kuryrは要求されたAddressかIPv6Addressに対応するCIDRのsubnetが存在するかどうかをチェックする』
(2) 『既にsubnetがあれば、Kuryrは新しいsubnetを作らないで再利用しようとするし、なければ与えられたCIDRの新しいsubnetを作る』
(3) 『Kuryrはportを作り、IPを割り当て、(2)で作成したsubnetに関連付ける』
(4) 『Neutron subnetとportに、EndpointIDでタグ付けする』

『上記(2)と(3)のsubnetの作成で、Kuryrはallocation_poolを特定せず、とにかくallocation poolを掴もうとする。allocation_poolなしだと、Neutron APIリファレンスに書かれてるとおり、Neutronは全てのIPをsubnetのCIDRのレンジで割り当てる。』

『Neutron portが作成されたとき、Kuryr remote driverはdocker daemonに、SUCCESSを示す空のレスポンス({})を生成する。(参考)』

{
    "Interface": {"MacAddress": "08:22:e0:a8:7d:db"}
}

『上記SUCCESSを受け取ると、libnetworkは以下のようなjsonリクエストを/NetworkDriver.JoinにHTTP POSTで発行する。』

{
    "NetworkID": "286eddb51ebca09339cb17aaec05e48ffe60659ced6f3fc41b020b0eb506d364",
    "SandboxKey": "/var/run/docker/netns/052b9aa6e9cd",
    "Options": null,
    "EndpointID": "edb23d36d77336d780fe25cdb5cf0411e5edd91b0777982b4b28ad125e28a4dd"
}

『Kuryrは、以下の手順でコンテナを対応したNeutron networkに接続する。』

(1) 『vethペアを作成する』
(2) 『vethの片方をDockerが作ったコンテナのnetnsに設定する』
(3) 『Neutron Portのtype(binding:vif_type)に基づいたVIF Bindingを実施する』

『VIF Bindingの完了後、Kuryr remote network driverはDocker daemonに対して、libnetworkのドキュメントに書いてある、joinリクエストとしてレスポンスを生成する』

ちなみにVIF Bindingは具体的にどう実行されるかというと、今の実装ではPortのbinding:vif_typeの値(ovsとかmidonetとか)を見て、対応するコマンドをキックすることで実施する。コマンドはデフォルトでは「/usr/libexec/kuryr/<vif_typeの値>」で、引数が「Neutron Portのid、 コンテナのvethのifname」。例えばvif_typeがmidonetであれば、/usr/libexec/kuryr/midonetというコマンドの中で、mm-ctl --bind-portを実行したりする。

3. 『ユーザがNetworkの情報を要求』

$ sudo docker network inspect foo
 {
     "name": "foo",
     "id": "286eddb51ebca09339cb17aaec05e48ffe60659ced6f3fc41b020b0eb506d364",
     "scope": "local",
     "driver": "kuryr",
     "ipam": {
         "driver": "default",
         "config": [
             {}
         ]
     },
     "containers": {
         "78c0458ba00f836f609113dd369b5769527f55bb62b5680d03aa1329eb416703": {
             "endpoint": "edb23d36d77336d780fe25cdb5cf0411e5edd91b0777982b4b28ad125e28a4dd",
             "mac_address": "02:42:c0:a8:7b:cb",
             "ipv4_address": "172.18.0.2/24",
             "ipv6_address": ""
         }
     }
 }

4. 『ユーザが別のコンテナをNetworkにつないでみる』

$ sudo docker network connect foo container2
 d7fcc280916a8b771d2375688b700b036519d92ba2989622627e641bdde6e646

$ sudo docker network inspect foo
 {
     "name": "foo",
     "id": "286eddb51ebca09339cb17aaec05e48ffe60659ced6f3fc41b020b0eb506d364",
     "scope": "local",
     "driver": "kuryr",
     "ipam": {
         "driver": "default",
         "config": [
             {}
         ]
     },
     "containers": {
         "78c0458ba00f836f609113dd369b5769527f55bb62b5680d03aa1329eb416703": {
             "endpoint": "edb23d36d77336d780fe25cdb5cf0411e5edd91b0777982b4b28ad125e28a4dd",
             "mac_address": "02:42:c0:a8:7b:cb",
             "ipv4_address": "172.18.0.2/24",
             "ipv6_address": ""
         },
         "d7fcc280916a8b771d2375688b700b036519d92ba2989622627e641bdde6e646": {
             "endpoint": "a55976bafaad19f2d455c4516fd3450d3c52d9996a98beb4696dc435a63417fc",
             "mac_address": "02:42:c0:a8:7b:cc",
             "ipv4_address": "172.18.0.3/24",
             "ipv6_address": ""
         }
     }
 }

5. 『ユーザがNetworkからコンテナを切断してみる』

$ CID=d7fcc280916a8b771d2375688b700b036519d92ba2989622627e641bdde6e646
$ sudo docker network disconnet foo $CID

『この操作では、/NetworkDriver.Leaveに以下のようなJSONがHTTP POSTで送られる。』

{
    "NetworkID": "286eddb51ebca09339cb17aaec05e48ffe60659ced6f3fc41b020b0eb506d364",
    "EndpointID": "a55976bafaad19f2d455c4516fd3450d3c52d9996a98beb4696dc435a63417fc"
}

『Kuryr remote network driverはコンテナとNeutron portの間のVIF bindingを削除して、Docker daemonに空のレスポンスを返し、libmetworkは/NetworkDriver.DeleteEndpointに以下のようなJSONリクエストをHTTP POSTで送る。』

{
    "NetworkID": "286eddb51ebca09339cb17aaec05e48ffe60659ced6f3fc41b020b0eb506d364",
    "EndpointID": "a55976bafaad19f2d455c4516fd3450d3c52d9996a98beb4696dc435a63417fc"
}

『Kuryr remote network driverは、関連するNeutron portを削除するNeutron APIのリクエストを生成し、この際関連したsubnetのportが空になる場合、Kuryrはsubnet自体も削除するようなリクエストも生成する。Neutron API実行後、Docker daemonに空のレスポンス({})を返す。』

6. 『ユーザが作ったfooというNetworkを削除する』

$ sudo  docker network rm foo

『この操作では、/NetworkDriver.DeleteNetworkに以下のようなJSONがHTTP POSTで送られる。』

   {
       "NetworkID": "286eddb51ebca09339cb17aaec05e48ffe60659ced6f3fc41b020b0eb506d364"
   }

『Kuryr remote network driver は関連するNeutron Networkを削除するNeutron APIへのリクエストを生成する。Networkが削除されたら、Docker daemonに空のレスポンス({})を返す。』

libnetworkとNeutronのモデルの対応

『何回か記述が出てきたけど、KuryrはNeutron clientを通してNeutronと通信し、libnetworkとNeutronの間のモデルを翻訳して橋渡しをする。』

『以下に、2つのモデルの間の対応を示す。』

libnetwork Neutron
Network Network
Sandbox Subnet, Port, netns
Endpoint Port

絵にすると多分こんな感じ。

kuryr2.png

ワークフローを見てもわかるけど、正式に1:1ではないので注意。

『libnetworkのSandboxとEndpointはNeutronのSubnetとPortにマッピングされるが、Sandboxはユーザからは直接は見えず、
Endpointがユーザから直接見え、かつコンテナにアタッチできる唯一のリソースになる。SandboxはEndpointによって裏で自動的に公表される情報を管理する。』

20
22
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
20
22