Docker, Inc.に買収されたSocketPlaneに関する雑多な話
今月始めにDocker, Inc.がSocketPlane, Inc.を買収したと発表がありました(日本記事はこちら)。 SocketPlaneが出来た経緯などは、docker/dockerのIssue#8951 を見るとよさそうです(ここから買収の話に繋がったのかな?)。
SocketPlane は、Open vSwitchによってDockerコンテナのネットワークをマルチホストで接続するためのツールおよびデーモンです。 開発は2014年12月の初め頃から始まってるみたいです。
ちなみに、Dockerのホストが複数になることによってネットワーク周りが複雑になるのはよくある話で、解決方法は既に様々なものがあります。 そのあたりはDockerコンテナ接続パターン (2014年冬)にて詳しくまとめられています。
SocketPlaneには下記のような特徴があります(README.mdより抜粋)。
- Open vSwitch integration
- ZeroConf multi-host networking for Docker
- Elastic growth of a Docker/SocketPlane cluster
- Support for multiple networks
- Distributed IP Address Management (IPAM)
既存の解決方法と比べて特筆すべき点としては
- Open vSwitchにてネットワークを形成している
- ネットワークおよびクラスタの形成を自動的に行う
- 高可用性なIPAMを提供している
といったところでしょうか。
Open vSwitchにてネットワークを形成している
DockerのネットワークはNetwork Namespace(このあたりのスライドが詳しいです)を利用していて、仮想的な docker0
ブリッジを作って、その下にコンテナ用のネットワークを形成しています。
一方SocketPlaneでは、Open vSwitchにて仮想的な docker-ovs0
ブリッジを作っています。
Weaveやflannelが、docker0
を被せるようにネットワークを形成しているのとは異なり、docker0
そのものを docker-ovs0
に置き換えています。
(https://github.com/docker/docker/issues/8951/Figure - 2より転載)
またマルチホストの場合は各ホストの docker-ovs0
がOverlay Tunnelsにて接続されるようです。
(https://github.com/docker/docker/issues/8951/Figure - 3より転載)
なおなぜOpen vSwitchなのかについては、Issue#8951のSolution Componentsの1. Programmable vSwitchにて述べられています。
ネットワークおよびクラスタの形成を自動的に行う
SocketPlaneではmulticast DNSを用いてZeroConf を実現しています。 multicast DNSとはその言葉どおりですが、名前解決のリクエストをマルチキャストで送って返答を受け取るというもので、同一セグメント内で起動しているSocketPlaneデーモンを探しだしてConsulのクラスタを形成します。 コードだとこのへん
- https://github.com/socketplane/socketplane/blob/master/daemon/bonjour.go
- https://github.com/socketplane/bonjour/blob/master/bonjour.go
socketplane/bonjourでmDNSが実装されていて、新しいメンバーが見つかるとJoinDatastore
でConsulクラスタに参加し、AddPeer
でOpen vSwitchのネットワークに参加するようです。
高可用性なIPAMを提供している
これがどれだけ有益なことなのかはよくわからないのですが、SocketPlandeで扱っているIPはConsulのKVSに格納されていて自由に参照できるようになっています。 KVSのkeysは以下のようになっていました。
vagrant@socketplane-1:~$ curl -s http://localhost:8500/v1/kv/?keys | jq . [ "ipam/10.1.0.0/16", "network/default", "vlan/vlan" ]
実際に使ってみる
既に試してる方が居ましたので参考にさせて頂きました。 tkhrssk.hatenablog.com
Vagrantfileが用意されているので手っ取り早く試すにはこちらを使うのがよいです。 ただubuntu-14.10をベースにDockerやらSocketPlane用のDockerイメージやらを3台のVMにインストールしていくのでそれなりに時間かかります(手元環境で20分ぐらいかかりました)。
立ち上がったら
$ vagrant ssh socketplane-1
で中に入って、起動しているコンテナの様子を見てみます
vagrant@socketplane-1:~$ sudo docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES d9a5a04e708a clusterhq/powerstrip:v0.0.1 "twistd -noy powerst 11 minutes ago Up 11 minutes powerstrip 0d071143a86c socketplane/socketplane:latest "socketplane --iface 13 minutes ago Up 13 minutes socketplane
Powerstrip modeでSocketPlaneが動いてるみたいです。
vagrant@socketplane-1:~$ sudo socketplane network list [ { "gateway": "10.1.0.1", "id": "default", "subnet": "10.1.0.0/16", "vlan": 1 } ] vagrant@socketplane-1:~$ consul members Node Address Status Type Build Protocol socketplane-1 10.254.101.21:8301 alive server 0.4.1 2 socketplane-2 10.254.101.22:8301 alive client 0.4.1 2 socketplane-3 10.254.101.23:8301 alive client 0.4.1 2
10.1.0.0/16のネットワークが形成されていて、Consulによるクラスタも組まれています。
コンテナの内部に入って様子を見てみます(socketplane run
を利用します)。
vagrant@socketplane-1:~$ sudo socketplane run -it ubuntu /bin/bash root@b103601ab2f5:/# ip a 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 18: ovsc34d394: <BROADCAST,UP,LOWER_UP> mtu 1440 qdisc noqueue state UNKNOWN group default link/ether 02:42:0a:01:00:02 brd ff:ff:ff:ff:ff:ff inet 10.1.0.2/16 scope global ovsc34d394 valid_lft forever preferred_lft forever inet6 fe80::d4e7:3aff:fe1e:641e/64 scope link tentative dadfailed valid_lft forever preferred_lft forever
SocketPlaneのネットワークに接続されていることがわかります。 ネットワークを追加してみます。
vagrant@socketplane-1:~$ sudo socketplane network create web 10.2.0.0/16 { "gateway": "10.2.0.1", "id": "web", "subnet": "10.2.0.0/16", "vlan": 2 } vagrant@socketplane-1:~$ sudo socketplane network list [ { "gateway": "10.1.0.1", "id": "default", "subnet": "10.1.0.0/16", "vlan": 1 }, { "gateway": "10.2.0.1", "id": "web", "subnet": "10.2.0.0/16", "vlan": 2 } ]
socketplane run
に -n オプションでネットワークIDを指定するとそのネットワークに接続されたコンテナが立ち上がります。
vagrant@socketplane-1:~$ sudo socketplane run -n web -it ubuntu /bin/bash root@a31cc2a3266c:/# ip a 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 22: ovsd77f749: <BROADCAST,UP,LOWER_UP> mtu 1440 qdisc noqueue state UNKNOWN group default link/ether 02:42:0a:02:00:02 brd ff:ff:ff:ff:ff:ff inet 10.2.0.2/16 scope global ovsd77f749 valid_lft forever preferred_lft forever inet6 fe80::4c10:cdff:fe48:e7a9/64 scope link tentative dadfailed valid_lft forever preferred_lft forever
所感
SocketPlaneがどのようなものでどのように使うか紹介しました。 Docker, Inc.に買収されたということで、SocketPlaneがそのまま使われるとは限りませんが、Open vSwitchを使ったリッチなネットワークがデフォルトで提供されるようになるでしょう。
個人的にはConsulの使い方が気になるところで、今のままだとKVSとしてしか使っていないのでcoreos/etcdの方がいいのではないかなと思います。 が、どちらにしてもますますCoreOSが望む方向とは別の方向に進んでいるようですね。
Consul lock を動かしてみる
お待ちかね Consul 0.5.0 が出ました -> https://hashicorp.com/blog/consul-0-5.html
今回のリリースでいろんな機能が追加されましたが lock が気になったのでとりあえず動かしてみます。
環境準備
Consulと使いそうなツールを幾つか含んだDockerコンテナで検証してみます。
FROM ubuntu:trusty MAINTAINER foostan ks@fstn.jp RUN apt-get update && apt-get install -y wget curl unzip telnet dnsutils RUN wget http://stedolan.github.io/jq/download/linux64/jq RUN chmod +x jq RUN mv jq /usr/bin ## consul RUN wget -P /tmp https://dl.bintray.com/mitchellh/consul/0.5.0_linux_amd64.zip RUN unzip /tmp/0.5.0_linux_amd64.zip -d /tmp RUN mv /tmp/consul /usr/bin
複数のターミナルでコンテナ起動
とりあえずサーバ1台と
$ docker run -it foostan/consul /bin/bash root@a95dafd4c471:/# consul agent -server -bootstrap -data-dir /tmp &
クライアント3台起動します
$ docker run -it foostan/consul /bin/bash root@730cc2b3a957:/# consul agent -join 172.17.0.34 -data-dir /tmp &
$ docker run -it foostan/consul /bin/bash root@2804f185fe4a:/# consul agent -join 172.17.0.34 -data-dir /tmp &
$ docker run -it foostan/consul /bin/bash root@14e9577a1104:/# consul agent -join 172.17.0.34 -data-dir /tmp &
メンバー確認
root@a95dafd4c471:/# consul members Node Address Status Type Build Protocol a95dafd4c471 172.17.0.34:8301 alive server 0.5.0 2 730cc2b3a957 172.17.0.32:8301 alive client 0.5.0 2 2804f185fe4a 172.17.0.33:8301 alive client 0.5.0 2 14e9577a1104 172.17.0.31:8301 alive client 0.5.0 2
lock コマンドを使ってみる
ドキュメントが相変わらずしっかりしてるのありがたいですね -> https://consul.io/docs/commands/lock.html
1つのコンテナで consul lock 実行してみます。
-verbose
つけると詳細なログが出るので挙動が確認しやすいです。
root@2804f185fe4a:/# consul lock -verbose service/foo/lock "while true; do date; sleep 1; done" Setting up lock at path: service/foo/lock/.lock Attempting lock acquisition Starting handler 'while true; do date; sleep 1; done' Fri Feb 20 16:41:13 UTC 2015 Fri Feb 20 16:41:14 UTC 2015 Fri Feb 20 16:41:15 UTC 2015 Fri Feb 20 16:41:16 UTC 2015
1秒単位で date
の結果が流れ始めます。
この状態で別のノードから同じコマンドを実行してみます。
root@14e9577a1104:/# consul lock -verbose service/foo/lock "while true; do date; sleep 1; done"Setting up lock at path: service/foo/lock/.lock Attempting lock acquisition
こちらはコマンドが実行されません。 この状態で、先ほど実行していた方を停止してみます。
Fri Feb 20 16:44:42 UTC 2015 ^CShutdown triggered, killing child Terminating child pid 2177 Error running handler: signal: terminated signal: terminated Child terminated Cleanup aborted, lock in use
すると、実行されずに待機していたほうが実行され始めました。
root@14e9577a1104:/# consul lock -verbose service/foo/lock "while true; do date; sleep 1; done" Setting up lock at path: service/foo/lock/.lock Attempting lock acquisition Starting handler 'while true; do date; sleep 1; done' Fri Feb 20 16:44:42 UTC 2015 Fri Feb 20 16:44:43 UTC 2015 Fri Feb 20 16:44:44 UTC 2015
文字だけだと分かりにくいので動画とりました。
こちらの動画では -n 2
をオプションで付けているため最大2台実行され、それ以降はコマンドを実行してもロックされます。
また途中でコマンドを停止するとロックされていたクライアントから実行するべきノードが選出されて実行されます。
簡単な解説
ロックする仕組み(実行を開始しても待機していた部分)に関しては 0.3.0 で導入されたセッションを利用しています -> http://www.consul.io/docs/internals/sessions.html
また、実行中のクライアントが停止したときに、残りの別のクライアントで再開する部分は、今回のバージョンで新しく追加された client-side Leader Election によって実現されています。
今までLeader Election(リーダー選出)はサーバ間でのみ行っており、クライアントからの要求を受けるサーバを選出するためのものでした。 またそのアルゴリズムとしてRaft(https://consul.io/docs/internals/consensus.html)が利用されていました。
で、今回追加されたのは文字通りクライアント側のリーダー選出の仕組みです。 上記動画では4台のクライアント(厳密には1台はサーバ)のうち2台がコマンドを実行し、2台は待機している状態でした。そして、1台実行を停止して2台のうち1台で実行が開始されましたが、このときにRaftによるリーダ選出が行われるわけです。 利用する側はとくに何も考えずにクライアントが選出されるのでありがたいですね。
Raftがどのようなアルゴリズムになっているかは今後論文を読んで理解を深めたいところです。
Contributeした話
consul lock は関係ないですが、Consulに投げた幾つかのPRをマージしてもらって今回のバージョンで無事リリースされました(OSSへの初PR初マージ)。
注目度の高いプロダクトにContributeすることができて単純に嬉しかったのとOSS活動へのモチベーションが上がりました。
今後も普段利用しているOSSや注目しているOSSへの感謝の気持ちを忘れずに、可能であればContributeを続けて行きたいと思います。
ちなみにマージされたPRの内容は以下の2件です。
Multiple DNS recursors
DNS Interface には recursor
という設定項目があって、これはConsul外部の問い合わせがあった時にフォワードする先を指定するもので、
{ "recursor": "8.8.8.8" }
などと設定しておくと、Consulで名前解決出来ないものを "8.8.8.8" に問い合わせてその結果を返してくれます。 で、今回送ったPRではこれを複数対応するもので
{ "recursors": ["8.8.8.8", "8.8.4.4"] }
という具合に指定できます。 運用しようと思うと地味に必要になってくるかなということで知っておくと案外助かるかもしれません。