etcd のクラスタを構築してみた

こんにちは。サイオステクノロジー OSS サポート担当 Y です。

今回は、分散 KVS である etcd のクラスタ環境を構築してみました。(※以下の内容は etcd 3.3.11/CentOS 7.6 にて検証しています。)

■はじめに

前回は etcd をインストールして単体での動作検証を行なってみましたが、etcd は分散 KVS であり etcd 自身がクラスタリングの機能を持ってます。

今回はその機能を使って、etcd のクラスタ環境を構築してみました。

■環境構成

今回は、以下の様な 3ノード構成で etcd のクラスタを構築してみます。

[環境構成]
           +---------[etcd1.example.com]---------+
           |                                     |
           |                                     |
           |                                     |
           |                                     |
  [etcd2.example.com]-------------------[etcd3.example.com]
etcd1.example.com : 192.168.10.10
etcd2.example.com : 192.168.10.20
etcd3.example.com : 192.168.10.30

■構築

それではさっそく etcd クラスタを構築してみます。

最初に etcd のインストールが必要ですが、インストールについては前回の記事に記載されているので、そちらを参照して下さい。本記事には、各ノードに etcd をインストールした後の作業から記載します。

etcd にてクラスタを構築する場合は、各ノードにて etcd を起動する際にノード間での通信を行うための URL を指定するオプション等、複数のオプションを指定して起動します。

今回は、それぞれのノードで以下の様なオプションを付けて etcd を起動します。(各オプションの詳細については割愛します。各オプションの詳細についてはドキュメントに記載されているので、興味がある方はそちらをご参照下さい。)

[実行コマンド (1号機)]
etcd \
> --name etcd1 \
> --initial-advertise-peer-urls https://192.168.10.10:2380 \
> --listen-peer-urls https://192.168.10.10:2380 \
> --listen-client-urls https://192.168.10.10:2379,https://127.0.0.1:2379 \
> --advertise-client-urls https://192.168.10.10:2379 \
> --initial-cluster etcd1=https://192.168.10.10:2380,etcd2=https://192.168.10.20:2380,etcd3=https://192.168.10.30:2380
[実行コマンド (2号機)]
etcd \
> --name etcd2 \
> --initial-advertise-peer-urls https://192.168.10.20:2380 \
> --listen-peer-urls https://192.168.10.20:2380 \
> --listen-client-urls https://192.168.10.20:2379,https://127.0.0.1:2379 \
> --advertise-client-urls https://192.168.10.20:2379 \
> --initial-cluster etcd1=https://192.168.10.10:2380,etcd2=https://192.168.10.20:2380,etcd3=https://192.168.10.30:2380
[実行コマンド (3号機)]
etcd \
> --name etcd3 \
> --initial-advertise-peer-urls https://192.168.10.30:2380 \
> --listen-peer-urls https://192.168.10.30:2380 \
> --listen-client-urls https://192.168.10.30:2379,https://127.0.0.1:2379 \
> --advertise-client-urls https://192.168.10.30:2379 \
> --initial-cluster etcd1=https://192.168.10.10:2380,etcd2=https://192.168.10.20:2380,etcd3=https://192.168.10.30:2380

実際にコマンドを実行すると、以下の様になります。(フォアグラウンドで動作するため、以降の作業は別ターミナルから行なっています。)

[1号機起動]
[root@etcd1 /]# etcd \
> --name etcd1 \
> --initial-advertise-peer-urls https://192.168.10.10:2380 \
> --listen-peer-urls https://192.168.10.10:2380 \
> --listen-client-urls https://192.168.10.10:2379,https://127.0.0.1:2379 \
> --advertise-client-urls https://192.168.10.10:2379 \
> --initial-cluster etcd1=https://192.168.10.10:2380,etcd2=https://192.168.10.20:2380,etcd3=https://192.168.10.30:2380
2019-02-07 07:37:48.260673 I | etcdmain: etcd Version: 3.3.11
2019-02-07 07:37:48.260764 I | etcdmain: Git SHA: 2cf9e51d2
2019-02-07 07:37:48.260773 I | etcdmain: Go Version: go1.10.7
2019-02-07 07:37:48.260796 I | etcdmain: Go OS/Arch: linux/amd64
2019-02-07 07:37:48.260805 I | etcdmain: setting maximum number of CPUs to 4, total number of available CPUs is 4
2019-02-07 07:37:48.260825 W | etcdmain: no data-dir provided, using default data-dir ./etcd1.etcd
2019-02-07 07:37:48.260973 I | embed: listening for peers on https://192.168.10.10:2380
2019-02-07 07:37:48.261042 I | embed: listening for client requests on 127.0.0.1:2379
2019-02-07 07:37:48.261091 I | embed: listening for client requests on 192.168.10.10:2379
2019-02-07 07:37:48.272035 I | etcdserver: name = etcd1
2019-02-07 07:37:48.272066 I | etcdserver: data dir = etcd1.etcd
2019-02-07 07:37:48.272076 I | etcdserver: member dir = etcd1.etcd/member
2019-02-07 07:37:48.272084 I | etcdserver: heartbeat = 100ms
2019-02-07 07:37:48.272091 I | etcdserver: election = 1000ms
2019-02-07 07:37:48.272098 I | etcdserver: snapshot count = 100000
2019-02-07 07:37:48.272145 I | etcdserver: advertise client URLs = https://192.168.10.10:2379
2019-02-07 07:37:48.272159 I | etcdserver: initial advertise peer URLs = https://192.168.10.10:2380
2019-02-07 07:37:48.272187 I | etcdserver: initial cluster = etcd1=https://192.168.10.10:2380,etcd2=https://192.168.10.20:2380,etcd3=https://192.168.10.30:2380
2019-02-07 07:37:48.283219 I | etcdserver: starting member 8c632555af4d958d in cluster 59dd43cb7c449c
[2号機起動]
[root@etcd2 /]# etcd \
> --name etcd2 \
> --initial-advertise-peer-urls https://192.168.10.20:2380 \
> --listen-peer-urls https://192.168.10.20:2380 \
> --listen-client-urls https://192.168.10.20:2379,https://127.0.0.1:2379 \
> --advertise-client-urls https://192.168.10.20:2379 \
> --initial-cluster etcd1=https://192.168.10.10:2380,etcd2=https://192.168.10.20:2380,etcd3=https://192.168.10.30:2380
2019-02-07 07:38:00.183708 I | etcdmain: etcd Version: 3.3.11
2019-02-07 07:38:00.183858 I | etcdmain: Git SHA: 2cf9e51d2
2019-02-07 07:38:00.183866 I | etcdmain: Go Version: go1.10.7
2019-02-07 07:38:00.183874 I | etcdmain: Go OS/Arch: linux/amd64
2019-02-07 07:38:00.183882 I | etcdmain: setting maximum number of CPUs to 4, total number of available CPUs is 4
2019-02-07 07:38:00.183905 W | etcdmain: no data-dir provided, using default data-dir ./etcd2.etcd
2019-02-07 07:38:00.184063 I | embed: listening for peers on https://192.168.10.20:2380
2019-02-07 07:38:00.184131 I | embed: listening for client requests on 127.0.0.1:2379
2019-02-07 07:38:00.184193 I | embed: listening for client requests on 192.168.10.20:2379
2019-02-07 07:38:00.220683 I | etcdserver: name = etcd2
2019-02-07 07:38:00.220711 I | etcdserver: data dir = etcd2.etcd
2019-02-07 07:38:00.220722 I | etcdserver: member dir = etcd2.etcd/member
2019-02-07 07:38:00.220730 I | etcdserver: heartbeat = 100ms
2019-02-07 07:38:00.220737 I | etcdserver: election = 1000ms
2019-02-07 07:38:00.220745 I | etcdserver: snapshot count = 100000
2019-02-07 07:38:00.220758 I | etcdserver: advertise client URLs = https://192.168.10.20:2379
2019-02-07 07:38:00.220767 I | etcdserver: initial advertise peer URLs = https://192.168.10.20:2380
2019-02-07 07:38:00.220800 I | etcdserver: initial cluster = etcd1=https://192.168.10.10:2380,etcd2=https://192.168.10.20:2380,etcd3=https://192.168.10.30:2380
2019-02-07 07:38:00.247610 I | etcdserver: starting member 8ea0d0c11d6c5ba9 in cluster 59dd43cb7c449c
[3号機起動]
[root@etcd3 /]# etcd \
> --name etcd3 \
> --initial-advertise-peer-urls https://192.168.10.30:2380 \
> --listen-peer-urls https://192.168.10.30:2380 \
> --listen-client-urls https://192.168.10.30:2379,https://127.0.0.1:2379 \
> --advertise-client-urls https://192.168.10.30:2379 \
> --initial-cluster etcd1=https://192.168.10.10:2380,etcd2=https://192.168.10.20:2380,etcd3=https://192.168.10.30:2380
2019-02-07 07:38:20.145414 I | etcdmain: etcd Version: 3.3.11
2019-02-07 07:38:20.145504 I | etcdmain: Git SHA: 2cf9e51d2
2019-02-07 07:38:20.145512 I | etcdmain: Go Version: go1.10.7
2019-02-07 07:38:20.145519 I | etcdmain: Go OS/Arch: linux/amd64
2019-02-07 07:38:20.145527 I | etcdmain: setting maximum number of CPUs to 4, total number of available CPUs is 4
2019-02-07 07:38:20.145545 W | etcdmain: no data-dir provided, using default data-dir ./etcd3.etcd
2019-02-07 07:38:20.145685 I | embed: listening for peers on https://192.168.10.30:2380
2019-02-07 07:38:20.145760 I | embed: listening for client requests on 127.0.0.1:2379
2019-02-07 07:38:20.145839 I | embed: listening for client requests on 192.168.10.30:2379
2019-02-07 07:38:20.148641 I | etcdserver: name = etcd3
2019-02-07 07:38:20.148663 I | etcdserver: data dir = etcd3.etcd
2019-02-07 07:38:20.148673 I | etcdserver: member dir = etcd3.etcd/member
2019-02-07 07:38:20.148681 I | etcdserver: heartbeat = 100ms
2019-02-07 07:38:20.148692 I | etcdserver: election = 1000ms
2019-02-07 07:38:20.148699 I | etcdserver: snapshot count = 100000
2019-02-07 07:38:20.148713 I | etcdserver: advertise client URLs = https://192.168.10.30:2379
2019-02-07 07:38:20.148721 I | etcdserver: initial advertise peer URLs = https://192.168.10.30:2380
2019-02-07 07:38:20.148738 I | etcdserver: initial cluster = etcd1=https://192.168.10.10:2380,etcd2=https://192.168.10.20:2380,etcd3=https://192.168.10.30:2380
2019-02-07 07:38:20.161598 I | etcdserver: starting member 36cdcac79ee8e410 in cluster 59dd43cb7c449c

各ノード起動後に etcdctl cluster-health コマンドでクラスタの状態を確認してみます。

[root@etcd1 /]# etcdctl cluster-health
member 36cdcac79ee8e410 is healthy: got healthy result from https://192.168.10.30:2379
member 8c632555af4d958d is healthy: got healthy result from https://192.168.10.10:2379
member 8ea0d0c11d6c5ba9 is healthy: got healthy result from https://192.168.10.20:2379
cluster is healthy

すると、”cluster is healthy” と出力されており、無事にクラスタ構築が完了したようです。

■動作検証

今度は、構築した etcd クラスタで簡単な動作検証を実施してみます。

まずは、データがクラスタ内で同期されるかを確認するため、etcd1.example.com でデータを追加し、他のノードで参照してみます。

[root@etcd1 /]# etcdctl mk key1 "test value1"
test value1
[root@etcd1 /]# 
[root@etcd1 /]# etcdctl ls
/key1
[root@etcd1 /]# 
[root@etcd1 /]# etcdctl get key1
test value1
[root@etcd2 /]# etcdctl ls
/key1
[root@etcd2 /]# 
[root@etcd2 /]# etcdctl get key1
test value1
[root@etcd3 /]# etcdctl ls
/key1
[root@etcd3 /]# 
[root@etcd3 /]# etcdctl get key1
test value1

すると、etcd1.example.com で追加した key1 及び key1 の値を etcd2.example.com/etcd3.example.com で参照することができました。

また、以下の様に etcd2.example.com/etcd3.example.com で追加したデータを他のノードから参照することもできました。

[root@etcd2 /]# etcdctl mk key2 "test value2"
test value2
[root@etcd2 /]# 
[root@etcd2 /]# etcdctl ls
/key1
/key2
[root@etcd2 /]# 
[root@etcd2 /]# etcdctl get key2
test value2
[root@etcd1 /]# etcdctl ls
/key1
/key2
[root@etcd1 /]# 
[root@etcd1 /]# etcdctl get key2
test value2
[root@etcd3 /]# etcdctl ls
/key1
/key2
[root@etcd3 /]# 
[root@etcd3 /]# etcdctl get key2
test value2
[root@etcd3 /]# etcdctl mk key3 "test value3"
test value3
[root@etcd3 /]# 
[root@etcd3 /]# etcdctl ls
/key2
/key3
/key1
[root@etcd3 /]# 
[root@etcd3 /]# etcdctl get key3
test value3
[root@etcd1 /]# etcdctl ls
/key1
/key2
/key3
[root@etcd1 /]# 
[root@etcd1 /]# etcdctl get key3
test value3
[root@etcd2 /]# etcdctl ls
/key2
/key3
/key1
[root@etcd2 /]# 
[root@etcd2 /]# etcdctl get key3
test value3

上記の通り、各ノードで追加したデータがクラスタ内で同期されていることが確認できました。

次に、データの更新を試してみます。

[root@etcd1 /]# etcdctl mk key "inserted by etcd1.example.com"
inserted by etcd1.example.com
[root@etcd1 /]# 
[root@etcd1 /]# etcdctl ls
/key
[root@etcd1 /]# 
[root@etcd1 /]# etcdctl get key
inserted by etcd1.example.com
[root@etcd2 /]# etcdctl ls 
/key
[root@etcd2 /]# 
[root@etcd2 /]# etcdctl get key
inserted by etcd1.example.com
[root@etcd2 /]# 
[root@etcd2 /]# etcdctl set key "updated by etcd2.example.com"
updated by etcd2.example.com
[root@etcd2 /]# 
[root@etcd2 /]# etcdctl get key
updated by etcd2.example.com
[root@etcd3 /]# etcdctl ls
/key
[root@etcd3 /]# 
[root@etcd3 /]# etcdctl get key
updated by etcd2.example.com
[root@etcd3 /]# 
[root@etcd3 /]# etcdctl set key "updated by etcd3.example.com"
updated by etcd3.example.com
[root@etcd3 /]# 
[root@etcd3 /]# etcdctl get key
updated by etcd3.example.com
[root@etcd1 /]# etcdctl ls
/key
[root@etcd1 /]# 
[root@etcd1 /]# etcdctl get key
updated by etcd3.example.com

データの更新についても、特定のノードで更新した値が他のノードに同期され、更新後の値を参照することができました。

最後にデータの削除を試してみます。

[root@etcd1 /]# etcdctl mk key1 value1   
value1
[root@etcd1 /]# 
[root@etcd1 /]# etcdctl mk key2 value2
value2
[root@etcd1 /]# 
[root@etcd1 /]# etcdctl mk key3 value3
value3
[root@etcd1 /]# 
[root@etcd1 /]# etcdctl ls
/key1
/key2
/key3
[root@etcd2 /]# etcdctl ls
/key1
/key2
/key3
[root@etcd2 /]# 
[root@etcd2 /]# etcdctl rm key2
PrevNode.Value: value2
[root@etcd2 /]# 
[root@etcd2 /]# etcdctl ls
/key1
/key3
[root@etcd3 /]# etcdctl ls
/key1
/key3
[root@etcd3 /]# 
[root@etcd3 /]# etcdctl rm key3
PrevNode.Value: value3
[root@etcd3 /]# 
[root@etcd3 /]# etcdctl ls
/key1
[root@etcd1 /]# etcdctl ls
/key1

データの削除についても、特定のノードで削除したデータは他のノードでも削除 (処理が同期) されていることが確認できました。

■まとめ

今回は 3ノード構成の etcd クラスタの動作検証を実施してみました。

前回ご紹介したように、バイナリファイルをダウンロードするだけでコンパイル等の作業は不要で利用できることに加え、オプションを指定して起動するだけでクラスタを構築することもできました。

もちろん長所/短所があるため一概には言えませんが、導入及びクラスタ構築の手間も少なくて済むので、分散 KVS の利用を検討している方には選択肢の一つとなり得るのではないでしょうか。

また、etcd の長所/短所や他の類似する DB との比較などについてはこちらに記載されているので、etcd に興味がある方はご参考にして頂ければと思います。

ご覧いただきありがとうございます! この投稿はお役に立ちましたか?

役に立った 役に立たなかった

0人がこの投稿は役に立ったと言っています。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です