EatSmartシステム部ブログ

ウェブサイトの開発や運営に関する情報です。

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で作成したオーバーレイ・ネットワークからそのまま切り替えることは難しいようですが、リリースに向けて動作の確認を進めていく予定です。