こんにちは。サイオステクノロジー 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 に興味がある方はご参考にして頂ければと思います。