etcdを使ってDockerのオーバーレイ・ネットワークを構築する
サービス環境ではSwarm modeを利用していますが、機能としてはオーバーレイ・ネットワークしか利用していないので、Swarm modeへの依存を無くせないか検討しています。 オーバーレイ・ネットワークを構築するにはキーバリュー・ストア・サービスが必要とのことなので、etcdをセットアップしてみました。
各Dockerノードでetcdを可動させクラスタを構築することを想定しています。 まずサーバserver01/server02の2台でクラスタを構築したあとにサーバserver03を追加しています。これは今後のサーバ追加を想定しているためです。 作業を行った環境は以下の通りです。
- OS:Ubuntu 16.04
- サーバ:server01(IP:10.1.1.101)/server02(IP:10.1.1.102)/server03(IP:10.1.1.103)
etcd
インストール
root@server01:~# apt-get install -y etcd
server01/server02のクラスタ構築
server01/server02の/etc/default/etcdへ以下を記述します。
ETCD_NAME="docker01" ETCD_DATA_DIR="/var/lib/etcd/" ETCD_INITIAL_CLUSTER_STATE="new" ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster-01" ETCD_INITIAL_CLUSTER="docker01=http://10.1.1.101:2380,docker02=http://10.1.1.102:2380" ETCD_INITIAL_ADVERTISE_PEER_URLS="http://10.1.1.101:2380" ETCD_ADVERTISE_CLIENT_URLS="http://0.0.0.0:2379" ETCD_LISTEN_PEER_URLS="http://10.1.1.101:2380" ETCD_LISTEN_CLIENT_URLS="http://0.0.0.0:2379"
ETCD_NAME="docker02" ETCD_DATA_DIR="/var/lib/etcd/" ETCD_INITIAL_CLUSTER_STATE="new" ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster-01" ETCD_INITIAL_CLUSTER="docker01=http://10.1.1.101:2380,docker02=http://10.1.1.102:2380" ETCD_INITIAL_ADVERTISE_PEER_URLS="http://10.1.1.102:2380" ETCD_ADVERTISE_CLIENT_URLS="http://0.0.0.0:2379" ETCD_LISTEN_PEER_URLS="http://10.1.1.102:2380" ETCD_LISTEN_CLIENT_URLS="http://0.0.0.0:2379"
サービスを起動して、メンバの一覧を確認します。
root@server01:~# etcdctl member list 1924cd26a959e750: name=docker02 peerURLs=http://10.1.1.102:2380 clientURLs=http://0.0.0.0:2379 dd62a02c7fd58775: name=docker01 peerURLs=http://10.1.1.101:2380 clientURLs=http://0.0.0.0:2379 root@server02:~# etcdctl member list 1924cd26a959e750: name=docker02 peerURLs=http://10.1.1.102:2380 clientURLs=http://0.0.0.0:2379 dd62a02c7fd58775: name=docker01 peerURLs=http://10.1.1.101:2380 clientURLs=http://0.0.0.0:2379
値が同期されることを確認します。
root@server01:~# etcdctl get key1 Error: 100: Key not found (/key1) [5] root@server02:~# etcdctl get key1 Error: 100: Key not found (/key1) [5] root@server01:~# etcdctl set key1 value1 value1 root@server01:~# etcdctl get key1 value1 root@server02:~# etcdctl get key1 value1
クラスタへserver03の追加
次にノードの追加を行います。 注意点として、ノードを追加する前に既存のクラスタへメンバを追加する必要がありました。 ここで出力された設定を、ノード追加時に設定することになります。
root@server01:~# etcdctl member add server03 https://10.1.1.103:2380 Added member named server03 with ID 11b064e58e1d461e to cluster ETCD_NAME="server03" ETCD_INITIAL_CLUSTER="server03=https://10.1.1.103:2380,docker02=http://10.1.1.102:2380,docker01=http://10.1.1.101:2380" ETCD_INITIAL_CLUSTER_STATE="existing"
メンバが追加されたことを確認します。 この段階では"unstarted"となっていることがわかります。
root@server01:~# etcdctl member list 11b064e58e1d461e[unstarted]: peerURLs=https://10.1.1.103:2380 1924cd26a959e750: name=docker02 peerURLs=http://10.1.1.102:2380 clientURLs=http://0.0.0.0:2379 dd62a02c7fd58775: name=docker01 peerURLs=http://10.1.1.101:2380 clientURLs=http://0.0.0.0:2379 root@server02:~# etcdctl member list 11b064e58e1d461e[unstarted]: peerURLs=https://10.1.1.103:2380 1924cd26a959e750: name=docker02 peerURLs=http://10.1.1.102:2380 clientURLs=http://0.0.0.0:2379 dd62a02c7fd58775: name=docker01 peerURLs=http://10.1.1.101:2380 clientURLs=http://0.0.0.0:2379
docker03の設定を行います。 以下に加え上記手順で出力された設定を/etc/default/etcへ追記します。
ETCD_DATA_DIR="/var/lib/etcd/" ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster-01" ETCD_INITIAL_ADVERTISE_PEER_URLS="http://10.1.1.103:2380" ETCD_ADVERTISE_CLIENT_URLS="http://0.0.0.0:2379" ETCD_LISTEN_PEER_URLS="http://10.1.1.103:2380" ETCD_LISTEN_CLIENT_URLS="http://0.0.0.0:2379"
サービスを起動して、メンバの一覧を確認します。 docker03がメンバになっていることを確認出来ました。
root@server03:~# etcdctl member list 11b064e58e1d461e: name=docker03 peerURLs=https://10.1.1.103:2380 clientURLs=http://0.0.0.0:2379 1924cd26a959e750: name=docker02 peerURLs=http://10.1.1.102:2380 clientURLs=http://0.0.0.0:2379 dd62a02c7fd58775: name=docker01 peerURLs=http://10.1.1.101:2380 clientURLs=http://0.0.0.0:2379
root@server01:~# etcdctl member list 11b064e58e1d461e: name=docker03 peerURLs=https://10.1.1.103:2380 clientURLs=http://0.0.0.0:2379 1924cd26a959e750: name=docker02 peerURLs=http://10.1.1.102:2380 clientURLs=http://0.0.0.0:2379 dd62a02c7fd58775: name=docker01 peerURLs=http://10.1.1.101:2380 clientURLs=http://0.0.0.0:2379
root@server02:~# etcdctl member list 11b064e58e1d461e: name=docker03 peerURLs=https://10.1.1.103:2380 clientURLs=http://0.0.0.0:2379 1924cd26a959e750: name=docker02 peerURLs=http://10.1.1.102:2380 clientURLs=http://0.0.0.0:2379 dd62a02c7fd58775: name=docker01 peerURLs=http://10.1.1.101:2380 clientURLs=http://0.0.0.0:2379
追加されたノードでも値が同期されることを確認します。
root@server03:~# etcdctl get key1 value1 root@server03:~# etcdctl set key2 value2 value2
root@server01:~# etcdctl get key2 value2
root@server02:~# etcdctl get key2 value2
クラスタからserver03の削除
最後にメンバの削除を行ってみます。 メンバの一覧から削除されたことが確認出来ます。
root@server01:~# etcdctl member remove 11b064e58e1d461e Removed member 11b064e58e1d461e from cluster root@server01:~# etcdctl member list 1924cd26a959e750: name=docker02 peerURLs=http://10.1.1.102:2380 clientURLs=http://0.0.0.0:2379 dd62a02c7fd58775: name=docker01 peerURLs=http://10.1.1.101:2380 clientURLs=http://0.0.0.0:2379
削除されたノードではメンバを参照出来ない状態になりました。 一度削除したノードを再度メンバに追加出来ないか試していますが、今のところ再度のセットアップが必要なようです。
root@server03:~# etcdctl member list Error: dial tcp 127.0.0.1:2379: getsockopt: connection refused
Docker
Swarm modeの停止
既存のコンテナをSwarmから離脱させます。 オプション無しだとエラーが発生したので"--force"を付けました。
root@server01:~# docker swarm leave --force Node left the swarm. root@server01:~# docker node ls Error response from daemon: This node is not a swarm manager. Use "docker swarm init" or "docker swarm join" to connect this node to swarm and try again.
etcdの設定
/lib/systemd/system/docker.serviceを編集してetcdを利用するようにします。 各Dockerノードで可動するetcdを参照するため、"--cluster-store"には"etcd://127.0.0.1:2379"を指定しています。
ExecStart=/usr/bin/dockerd -H fd:// --cluster-store=etcd://127.0.0.1:2379 --cluster-advertise=<ホストのIPアドレス>:2376
"--cluster-advertise"にはネットワークインターフェース名を指定することも可能なようです。
ExecStart=/usr/bin/dockerd -H fd:// --cluster-store=etcd://127.0.0.1:2379 --cluster-advertise=<ネットワークインターフェース名>:2376
設定を反映させるためDockerを再起動します。
systemctl daemon-reload systemctl restart docker
オーバーレイ・ネットワークの作成
Swarm modeの時と同じ手順でオーバーレイ・ネットワークを作成します。 新たにオーバーレイ・ネットワークが作成されていることが確認出来ました。
root@server01:~# docker network create -d overlay \ > --subnet=10.1.3.0/24 \ > --attachable \ > -o "com.docker.network.bridge.mtu=8192" \ > original-overlay-network a8358f86a50c6c41b757d52f737c6828ff836f8e355b7b74ec1d8cc94bb02b22 root@server01:~# docker network ls NETWORK ID NAME DRIVER SCOPE 38d745409fd6 bridge bridge local 15d99e4689c0 docker_gwbridge bridge local 407cf1c223e9 host host local a8358f86a50c original-overlay-network overlay global c98b6152f763 none null local
Swarm modeとの違い
以上の手順でオーバーレイ・ネットワークが作成出来ましたが、コンテナをデプロイする段階で問題が発生しました。 Swarm modeではserver01/server02で"apache"という名前のコンテナを稼働させていました(Serviceではなく通常のコンテナ)。 これが、今回の作業後からserver02で起動に失敗するようになりました。
root@server02:~# docker run -d \ > --name apache\ > --network=original-overlay-network \ > --restart=always \ > registry.eatsmart.local/apache dc432cc6723f32cfb7fd0890d624e9e2be2e3f0067492aa8021eff5ea107b19b docker: Error response from daemon: endpoint with name apache already exists in network original-overlay-network.
ネットワーク上に"apache"という名前が既に存在するというエラーですが、そもそもなぜいままでは起動できていたのか不思議です。 そこで以前のネットワークを確認してみました。
Swarm modeのオーバーレイ・ネットワーク
root@server01:~# docker network inspect original-overlay-network [ { "Name": "original-overlay-network", "Id": "apx4l1858tkb9n5k1e7isqdyy", "Created": "2018-03-19T04:37:57.046347368+09:00", "Scope": "swarm", "Driver": "overlay", "EnableIPv6": false, "IPAM": { "Driver": "default", "Options": null, "Config": [ { "Subnet": "10.1.3.0/24", "Gateway": "10.1.3.1" } ] }, "Internal": false, "Attachable": true, "Ingress": false, "ConfigFrom": { "Network": "" }, "ConfigOnly": false, "Containers": { "46242739d12ca4fa562f01d0ea6b8db8d544d532548a50d2573bc79717df46c0": { "Name": "app01", "EndpointID": "027dd422df8d3b1b20895ba0a67e94bd2f76112c29d579a4feaf948eb312197d", "MacAddress": "02:42:0a:01:03:b4", "IPv4Address": "10.1.3.180/24", "IPv6Address": "" }, "d90d007a1b7b91fb2cf4524065392c17f0c213e938ca6853f6c59f0a6d1a0963": { "Name": "app02", "EndpointID": "cd5e42c4c82446ed8e354cf9d4d372b66bab7fdcb68e1ff4d09846b3f522e9ab", "MacAddress": "02:42:0a:01:03:b2", "IPv4Address": "10.1.3.178/24", "IPv6Address": "" }, "f48e32b2e77ab3883f8e622db07d63aeb097f6a59b2994411040ed15a1218e57": { "Name": "apache", "EndpointID": "593ab983633bce29230055c2ba357667b3bb7ea7a5a46a58e41e00ba91bbd366", "MacAddress": "02:42:0a:01:03:e5", "IPv4Address": "10.1.3.229/24", "IPv6Address": "" } }, "Options": { "com.docker.network.bridge.mtu": "8192", "com.docker.network.driver.overlay.vxlanid_list": "4099" }, "Labels": {}, "Peers": [ { "Name": "1aa3da59d05e", "IP": "10.1.1.101" }, { "Name": "6ea9bd5a9413", "IP": "10.1.1.102" }, { "Name": "24914465b868", "IP": "10.1.1.103" } ] } ]
etcdを利用して作成したオーバーレイ・ネットワーク
root@server01:~# docker network inspect original-overlay-network [ { "Name": "original-overlay-network", "Id": "a8358f86a50c6c41b757d52f737c6828ff836f8e355b7b74ec1d8cc94bb02b22", "Created": "2019-03-22T15:52:50.259468549+09:00", "Scope": "global", "Driver": "overlay", "EnableIPv6": false, "IPAM": { "Driver": "default", "Options": {}, "Config": [ { "Subnet": "10.1.3.0/24" } ] }, "Internal": false, "Attachable": true, "Ingress": false, "ConfigFrom": { "Network": "" }, "ConfigOnly": false, "Containers": { "ep-1544411a0c1fbbf03b085a24486e517657274426cac181c9275444696fb6b6bc": { "Name": "app01", "EndpointID": "1544411a0c1fbbf03b085a24486e517657274426cac181c9275444696fb6b6bc", "MacAddress": "02:42:0a:01:03:07", "IPv4Address": "10.1.3.7/24", "IPv6Address": "" }, "ep-33d97bb106751e12add32c2696fb8bc902f78c035d4fb43aab248a9c6e6ef9b9": { "Name": "app02", "EndpointID": "33d97bb106751e12add32c2696fb8bc902f78c035d4fb43aab248a9c6e6ef9b9", "MacAddress": "02:42:0a:01:03:05", "IPv4Address": "10.1.3.5/24", "IPv6Address": "" }, "ep-39ec8fe7b8e664319743d25fd52aa3bc7942e118f827a6d92dcc30ddf9b71327": { "Name": "app03", "EndpointID": "39ec8fe7b8e664319743d25fd52aa3bc7942e118f827a6d92dcc30ddf9b71327", "MacAddress": "", "IPv4Address": "10.1.3.6/24", "IPv6Address": "" }, "ep-4a01bd151cef82b92c298d7840883c63490087b1bdedffc2d7bd17bc5c08e6dc": { "Name": "app04", "EndpointID": "4a01bd151cef82b92c298d7840883c63490087b1bdedffc2d7bd17bc5c08e6dc", "MacAddress": "", "IPv4Address": "10.1.3.8/24", "IPv6Address": "" }, "ep-65f59ec16192b5a38cb9b8315e665dac6f9d43a695495603513038241e461b6d": { "Name": "elasticsearch", "EndpointID": "65f59ec16192b5a38cb9b8315e665dac6f9d43a695495603513038241e461b6d", "MacAddress": "02:42:0a:01:03:03", "IPv4Address": "10.1.3.3/24", "IPv6Address": "" }, "ep-93a3c1c16f0ce3fac1e2355f562246b046c01848b40455890d3985baad2e6b7d": { "Name": "apache", "EndpointID": "93a3c1c16f0ce3fac1e2355f562246b046c01848b40455890d3985baad2e6b7d", "MacAddress": "02:42:0a:01:03:04", "IPv4Address": "10.1.3.4/24", "IPv6Address": "" }, "ep-aecf02608f3be4ad0dadb4073c0b56fe3f12af0d5e52c89f7ede3dc1be57096c": { "Name": "redis", "EndpointID": "aecf02608f3be4ad0dadb4073c0b56fe3f12af0d5e52c89f7ede3dc1be57096c", "MacAddress": "02:42:0a:01:03:02", "IPv4Address": "10.1.3.2/24", "IPv6Address": "" } }, "Options": { "com.docker.network.bridge.mtu": "8192" }, "Labels": {} } ]
内容を比較してみると、以前のContainersにはserver01で稼働しているコンテナのみが含まれています。 これに対して現在のContainersにはオーバーレイ・ネットワークに含まれる全てのコンテナが含まれています。 このことから、以前はサーバごとにネットワークが別れていたため同じ名前のコンテナが別のノードで稼働出来たものと思われます。
まとめ
etcdを利用してオーバーレイ・ネットワーク構築してみました。 これで、Swarm modeの停止に向けた準備が整いました。 Swarm modeで作成したオーバーレイ・ネットワークからそのまま切り替えることは難しいようですが、リリースに向けて動作の確認を進めていく予定です。