etcdクラスタ構築(static方式)

#1. 構成
・3台構成(VMware上の仮想マシン)
・仮想マシンのOSはCentOS7.2、etcdの版数はversion 2.3.7
・ホスト名は、master1,master2,master3
・DB(データベース）は、etcdデーモンの起動ディレクトリに作成される。


| etcdデーモンが使うポート番号v2(v1)| 用途 |
|:------------|:--------------------------------------------------------------------------------|
| 2379(4001)  | etcdctlコマンドのようなクライアントとの通信に使う                                   |
| 2380(7001)  | etcdクラスタの中で、どのetcdをリーダにするか?、といったような制御情報のやりとりに使う  |



```


  +------ master1(CentOS7.2) -----------+   +- master2(CentOS7.2) -+   +- master3(CentOS7.2) -+
  |                                     |   |                      |   |                      |
  |  +- etcdctl -+   +----- etcd ----+  |   |  +----- etcd ----+   |   |   +----- etcd ----+  |
  |  |           |   |               |  |   |  |               |   |   |   |               |  |
  |  |           |   |               |  |   |  |               |   |   |   |               |  |
  |  +-----------+   +- 2379 - 2380 -+  |   |  +- 2379 - 2380 -+   |   |   +- 2379 - 2380 -+  |
  |        A                     A      |   |               A      |   |      A         A     |
  |        |     +----+          |      |   |       +----+  |      |   |      | +----+  |     |
  |        |     | DB |          |      |   |       | DB |  |      |   |      | | DB |  |     |
  |        |     +----+   +------+      |   |       +----+  |      |   |      | +----+  |     |
  |        |              |             |   |               |      |   |      |         |     |
  +--------|----- eth0 ---|-------------+   +-------- eth0 -|------+   +------|- eth0 --|-----+
           |        |     |                            |    |                 |   |     |
           |        |     |                            |    |                 |   |     |
           |        |     |                            |    |                 |   |     |
  +--------|--------+-----|------- 仮想スイッチ --------+----|-----------------|---+-----|-----+
  |        |              |                                 |                 |         |     |
  |        |              +=================================+=================|=========+     |
  |        |                                                                  |               |
  |        +==================================================================+               |
  |                                                                                           |
  +-------------------------------------------------------------------------------------------+


```

#2. etcdクラスタの構築方法
ここでは、static方式について説明をする。

| 方式             | 内容                                                   |
|:-----------------|:------------------------------------------------------|
| static           | あらかじめクラスタのメンバを定義ファイルに指定しておく     |
| etcd discovery   | http://qiita.com/hana_shin/items/f8de83a8cc91adae6262 |
| DNS  discovery   | 調査中                                                |




#3. インストールするパッケージ

```
仮想マシンにetcdパッケージをインストールする。
[root@master1 ~]# yum -y install etcd
[root@master2 ~]# yum -y install etcd
[root@master3 ~]# yum -y install etcd
```

#4. 設定内容

##4.1 設定ファイルと設定内容
/etc/etcd/etcd.conf

| 項目 | 意味 |
|:------------------------------|:-----------------------------------|
|ETCD_NAME   | ホスト名を設定する  |
|ETCD_DATA_DIR   | DBのパスを設定する。デフォルトのパスは/var/lib/etcd/default.etcd  |
|ETCD_LISTEN_PEER_URLS   | etcdデーモン間の送受信に使うIPアドレスとポート番号を設定する  |
|ETCD_INITIAL_CLUSTER   | etcdクラスタのメンバを設定する。  |
|ETCD_INITIAL_CLUSTER_STATE   | newを設定する  |
|ETCD_INITIAL_CLUSTER_TOKEN   | etcdクラスタを識別するための文字列。同一クラスタ内のメンバは同じ文字列を設定する  |

##4.2 master1の設定内容
```
[root@master1 ~]# cat /etc/hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.0.10  master1
192.168.0.20  master2
192.168.0.30  master3

有効な行だけ表示する（コメント行と空白行は省略）
[root@master1 ~]# cat /etc/etcd/etcd.conf |grep -v ^# |grep -v ^$
ETCD_NAME=master1
ETCD_DATA_DIR="/var/lib/etcd/default.etcd"
ETCD_LISTEN_PEER_URLS="http://192.168.0.10:2380"
ETCD_LISTEN_CLIENT_URLS="http://0.0.0.0:2379"
ETCD_INITIAL_ADVERTISE_PEER_URLS="http://192.168.0.10:2380"
ETCD_INITIAL_CLUSTER="master1=http://192.168.0.10:2380,master2=http://192.168.0.20:2380,master3=http://192.168.0.30:2380"
ETCD_INITIAL_CLUSTER_STATE="new"
ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster"
ETCD_ADVERTISE_CLIENT_URLS="http://192.168.0.10:2379"
```


##4.3 master2の設定内容
```
[root@master2 ~]# cat /etc/hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.0.10  master1
192.168.0.20  master2
192.168.0.30  master3

有効な行だけ表示する（コメント行と空白行は省略）
[root@master2 ~]# cat /etc/etcd/etcd.conf |grep -v ^# |grep -v ^$
ETCD_NAME=master2
ETCD_DATA_DIR="/var/lib/etcd/default.etcd"
ETCD_LISTEN_PEER_URLS="http://192.168.0.20:2380"
ETCD_LISTEN_CLIENT_URLS="http://0.0.0.0:2379"
ETCD_INITIAL_ADVERTISE_PEER_URLS="http://192.168.0.20:2380"
ETCD_INITIAL_CLUSTER="master1=http://192.168.0.10:2380,master2=http://192.168.0.20:2380,master3=http://192.168.0.30:2380"
ETCD_INITIAL_CLUSTER_STATE="new"
ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster"
ETCD_ADVERTISE_CLIENT_URLS="http://192.168.0.20:2379"
[root@master2 ~]#
```

##4.4 master3の設定内容
```
[root@master3 ~]# cat /etc/hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.0.10  master1
192.168.0.20  master2
192.168.0.30  master3

有効な行だけ表示する（コメント行と空白行は省略）
[root@master3 ~]# cat /etc/etcd/etcd.conf |grep -v ^# |grep -v ^$
ETCD_NAME=master3
ETCD_DATA_DIR="/var/lib/etcd/default.etcd"
ETCD_LISTEN_PEER_URLS="http://192.168.0.30:2380"
ETCD_LISTEN_CLIENT_URLS="http://0.0.0.0:2379"
ETCD_INITIAL_ADVERTISE_PEER_URLS="http://192.168.0.30:2380"
ETCD_INITIAL_CLUSTER="master1=http://192.168.0.10:2380,master2=http://192.168.0.20:2380,master3=http://192.168.0.30:2380"
ETCD_INITIAL_CLUSTER_STATE="new"
ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster"
ETCD_ADVERTISE_CLIENT_URLS="http://192.168.0.30:2379"
[root@master3 ~]#

```

#5. 動作確認

##5.1 etcdを起動する。

```
[root@master1 ~]# systemctl start etcd
[root@master2 ~]# systemctl start etcd
[root@master3 ~]# systemctl start etcd

```


##5.2 etcdの版数を確認する。

```
[root@master1 ~]# etcdctl --version
etcdctl version 2.3.7
[root@master2 ~]# etcdctl --version
etcdctl version 2.3.7
[root@master3 ~]# etcdctl --version
etcdctl version 2.3.7
```


##5.3 メンバ間の同期を確認する。
### master1に書き込んだデータが、master2,master3で読めるかどうかを確認する。

```
master1でosにlinuxという値を設定する。
[root@master1 ~]# etcdctl set os linux
linux
[root@master1 ~]# etcdctl get os
linux

master2でosの値を確認する。
[root@master2 ~]# etcdctl get os
linux

master3でosの値を確認する。
[root@master3 ~]# etcdctl get os
linux

```

### master3でosに設定された値を削除する。master1,master2でosに設定された値が削除されていることを確認する。

```
[root@master3 ~]# etcdctl rm os
PrevNode.Value: linux
[root@master3 ~]# etcdctl get os
Error:  100: Key not found (/os) [41]
[root@master3 ~]#

master2でosに設定した確認する。値が削除されていることがわかる。
[root@master2 ~]# etcdctl get os
Error:  100: Key not found (/os) [41]
[root@master2 ~]#

master1でosに設定した値を確認する。値が削除されていることがわかる。
[root@master1 ~]# etcdctl get os
Error:  100: Key not found (/os) [41]
[root@master1 ~]#

```

##5.4 リーダの移動を確認する。
etcdクラスタのリーダを確認する。現在のリーダはmaster3(isLeader=true)であることがわかる。

```
[root@master1 ~]# etcdctl member list
7bf3dc7e1bfc9229: name=master3 peerURLs=http://192.168.0.30:2380 clientURLs=http://192.168.0.30:2379 isLeader=true
b19e62a463d4b18e: name=master2 peerURLs=http://192.168.0.20:2380 clientURLs=http://192.168.0.20:2379 isLeader=false
b22cf29c2bebb484: name=master1 peerURLs=http://192.168.0.10:2380 clientURLs=http://192.168.0.10:2379 isLeader=false
[root@master1 ~]#

```


master3(リーダ)のetcdデーモンを終了する。リーダが切り替わることを確認する。

```
master3で動くetcdデーモンのPIDを確認する。
[root@master3 ~]# ps -C etcd
   PID TTY          TIME CMD
  1247 pts/0    00:02:20 etcd

etcdデーモンを終了する。
[root@master3 ~]# kill 1247
[root@master3 ~]#

master1でリーダを確認する。master1がリーダになったことがわかる。
[root@master1 ~]# etcdctl member list
7bf3dc7e1bfc9229: name=master3 peerURLs=http://192.168.0.30:2380 clientURLs=http://192.168.0.30:2379 isLeader=false
b19e62a463d4b18e: name=master2 peerURLs=http://192.168.0.20:2380 clientURLs=http://192.168.0.20:2379 isLeader=false
b22cf29c2bebb484: name=master1 peerURLs=http://192.168.0.10:2380 clientURLs=http://192.168.0.10:2379 isLeader=true

master2でもリーダを確認してみる。master1がリーダであることが確認できる。
[root@master2 ~]# etcdctl member list
7bf3dc7e1bfc9229: name=master3 peerURLs=http://192.168.0.30:2380 clientURLs=http://192.168.0.30:2379 isLeader=false
b19e62a463d4b18e: name=master2 peerURLs=http://192.168.0.20:2380 clientURLs=http://192.168.0.20:2379 isLeader=false
b22cf29c2bebb484: name=master1 peerURLs=http://192.168.0.10:2380 clientURLs=http://192.168.0.10:2379 isLeader=true
[root@master2 ~]#

```


さらに、master1（リーダ)のetcdデーモンを終了する。
この時点でmaster1,master3のetcdデーモンが終了したことになる。

```

etcdデーモンのPIDを確認する。
[root@master1 ~]# ps -C etcd
   PID TTY          TIME CMD
  1792 pts/0    00:03:57 etcd

etcdデーモンを終了する。
[root@master1 ~]# kill 1792
[root@master1 ~]# ps -C etcd
   PID TTY          TIME CMD
[root@master1 ~]#

この時点でmaster2のetcdデーモンのみ生きている。
[root@master2 ~]# ps -C etcd
   PID TTY          TIME CMD
  1087 pts/0    00:05:42 etcd

master2(リーダ)でetcdクラスタのメンバを確認する。etcdクラスタが利用不可とのメッセージが出力される。
[root@master2 ~]# etcdctl member list
Failed to get leader:  client: etcd cluster is unavailable or misconfigured


master1,master3では、以下のメッセージが出力された。
etcdデーモンがいないので、TCP接続要求が拒否されたことを示している。
[root@master1 ~]# etcdctl member list
Error:  client: etcd cluster is unavailable or misconfigured
error #0: dial tcp 127.0.0.1:2379: getsockopt: connection refused
error #1: dial tcp 127.0.0.1:4001: getsockopt: connection refused

[root@master3 ~]# etcdctl member list
Error:  client: etcd cluster is unavailable or misconfigured
error #0: dial tcp 127.0.0.1:2379: getsockopt: connection refused
error #1: dial tcp 127.0.0.1:4001: getsockopt: connection refused


```

##5.5 etcdクラスタの状態を確認する。
### クラスタのメンバが残り1つになると、クラスタがunhealthyとなり、データベースからのリードはできるけど、ライトはできなくなる模様。ほんと???

```
クラスタの状態を確認する。全て(master1,master2,master3)のメンバが生きている。
[root@master1 ~]# etcdctl cluster-health
member 7bf3dc7e1bfc9229 is healthy: got healthy result from http://192.168.0.30:2379
member b19e62a463d4b18e is healthy: got healthy result from http://192.168.0.20:2379
member b22cf29c2bebb484 is healthy: got healthy result from http://192.168.0.10:2379

[root@master1 ~]# etcdctl set os linux
linux

[root@master1 ~]# etcdctl get os
linux

etcdのPIDを確認する。
[root@master3 ~]# ps -C etcd
   PID TTY          TIME CMD
  1820 pts/3    00:01:58 etcd

master3のetcdを終了する。
[root@master3 ~]# kill 1820

etcdクラスタの状態を確認する。master1,master2のetcdが生きている。master3のetcdは終了している。
[root@master1 ~]# etcdctl cluster-health
failed to check the health of member 7bf3dc7e1bfc9229 on http://192.168.0.30:2379: Get http://192.168.0.30:2379/health: dial tcp 192.168.0.30:2379: getsockopt: connection refused
member 7bf3dc7e1bfc9229 is unreachable: [http://192.168.0.30:2379] are all unreachable
member b19e62a463d4b18e is healthy: got healthy result from http://192.168.0.20:2379
member b22cf29c2bebb484 is healthy: got healthy result from http://192.168.0.10:2379
cluster is healthy

etcdに書き込みを行う。3つのメンバのうち2つが生きていれば、データベースへのライトができる。
[root@master1 ~]# etcdctl set car GT-R
GT-R

etcdのPIDを確認する。
[root@master2 ~]# ps -C etcd
   PID TTY          TIME CMD
  1790 pts/0    00:03:07 etcd

master2のetcdを終了する。
[root@master2 ~]# kill 1790

etcdクラスタの状態を確認する。master1のetcdだけが生きている。master2,3のetcdは終了していることがわかる。
[root@master1 ~]# etcdctl cluster-health
failed to check the health of member 7bf3dc7e1bfc9229 on http://192.168.0.30:2379: Get http://192.168.0.30:2379/health: dial tcp 192.168.0.30:2379: getsockopt: connection refused
member 7bf3dc7e1bfc9229 is unreachable: [http://192.168.0.30:2379] are all unreachable
failed to check the health of member b19e62a463d4b18e on http://192.168.0.20:2379: Get http://192.168.0.20:2379/health: dial tcp 192.168.0.20:2379: getsockopt: connection refused
member b19e62a463d4b18e is unreachable: [http://192.168.0.20:2379] are all unreachable
member b22cf29c2bebb484 is unhealthy: got unhealthy result from http://192.168.0.10:2379
cluster is unhealthy     <<<<<< クラスタの状態がunhealthyと判定されている。


クラスタ内のメンバが1つだけの場合、etcdへのライトができない。
[root@master1 ~]# etcdctl set month Nov
Error:  client: etcd cluster is unavailable or misconfigured
error #0: client: endpoint http://192.168.0.10:2379 exceeded header timeout
error #1: dial tcp 192.168.0.20:2379: getsockopt: connection refused
error #2: dial tcp 192.168.0.30:2379: getsockopt: connection refused

しかし、データベースからのリードはできる。
[root@master1 ~]# etcdctl ls
/os
/car

ここでmaster2のetcdを起動する。この時点でmaster1,master2が生きていることになる。
[root@master2 ~]# ps -C etcd
   PID TTY          TIME CMD
  1811 pts/0    00:00:11 etcd

monthにデータを設定する。設定することができた。
[root@master1 ~]# etcdctl set month Nov
Nov

データベースのリードもできた。
[root@master1 ~]# etcdctl ls
/month
/os
/car

```


##5.6 TCP2379番ポートへのアクセスを確認する。

```
tcpdumpをインストールする。
[root@master1 ~]# yum -y install tcpdump

tcpdumpを起動する。ポート番号に2379を指定する。
[root@master1 ~]# tcpdump -i eth0 tcp port 2379
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes



もう1つ端末を起動する。そこで、etcdctlコマンドを実行する。
[root@master1 ~]# etcdctl member list


etcdctlコマンドの実行と同期して、TCPパケットの送受信が確認できる。
最初の3パケットで、master1のetcdctlコマンドとmaster3（リーダ）のetcdデーモンの間で
TCPコネクションの確立(3 way handshake)が行われていることがわかる。


　　　master1:53186(etcdctl)         master3:2379(etcd)
       |                                    |
       +---------------- SYN  ------------->|
       |<--------------- SYN +ACK ----------|
       |---------------- ACK -------------->|



17:59:48.705597 IP master1.53186 > master3.2379: Flags [S], seq 1724156849, win 14600, options [mss 1460,sackOK,TS val 3687348 ecr 0,nop,wscale 7], length 0
17:59:48.707562 IP master3.2379 > master1.53186: Flags [S.], seq 354533212, ack 1724156850, win 14480, options [mss 1460,sackOK,TS val 3478680 ecr 3687348,nop,wscale 7], length 0
17:59:48.708494 IP master1.53186 > master3.2379: Flags [.], ack 1, win 115, options [nop,nop,TS val 3687353 ecr 3478680], length 0
17:59:48.713164 IP master1.53186 > master3.2379: Flags [P.], seq 1:109, ack 1, win 115, options [nop,nop,TS val 3687354 ecr 3478680], length 108
17:59:48.714680 IP master3.2379 > master1.53186: Flags [.], ack 109, win 114, options [nop,nop,TS val 3478688 ecr 3687354], length 0
17:59:48.715199 IP master3.2379 > master1.53186: Flags [P.], seq 1:536, ack 109, win 114, options [nop,nop,TS val 3478689 ecr 3687354], length 535
17:59:48.715242 IP master1.53186 > master3.2379: Flags [.], ack 536, win 123, options [nop,nop,TS val 3687359 ecr 3478689], length 0
17:59:48.716397 IP master1.53186 > master3.2379: Flags [P.], seq 109:224, ack 536, win 123, options [nop,nop,TS val 3687360 ecr 3478689], length 115
17:59:48.718197 IP master3.2379 > master1.53186: Flags [P.], seq 536:807, ack 224, win 114, options [nop,nop,TS val 3478692 ecr 3687360], length 271
17:59:48.720172 IP master1.53186 > master3.2379: Flags [F.], seq 224, ack 807, win 131, options [nop,nop,TS val 3687364 ecr 3478692], length 0
17:59:48.721524 IP master3.2379 > master1.53186: Flags [F.], seq 807, ack 225, win 114, options [nop,nop,TS val 3478696 ecr 3687364], length 0
17:59:48.721614 IP master1.53186 > master3.2379: Flags [.], ack 808, win 131, options [nop,nop,TS val 3687366 ecr 3478696], length 0


```

##5.6 TCP2380番ポートへのアクセスを確認する。
2380番ポートでは、常時、メッセージが送受信されている。

```
[root@master1 ~]# tcpdump -i eth0 tcp port 2380
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
18:06:40.973494 IP master2.2380 > master3.52766: Flags [P.], seq 835860753:835860814, ack 2448790313, win 122, options [nop,nop,TS val 3945499 ecr 3890848], length 61
18:06:40.973526 IP master2.2380 > master1.54930: Flags [P.], seq 4072016314:4072016376, ack 3951593931, win 122, options [nop,nop,TS val 3945499 ecr 4099518], length 62
18:06:40.973590 IP master1.54930 > master2.2380: Flags [.], ack 62, win 123, options [nop,nop,TS val 4099618 ecr 3945499], length 0
18:06:40.974528 IP master3.52766 > master2.2380: Flags [.], ack 61, win 148, options [nop,nop,TS val 3890947 ecr 3945499], length 0
18:06:40.974535 IP master3.2380 > master2.38787: Flags [P.], seq 1427255133:1427255193, ack 3674681492, win 122, options [nop,nop,TS val 3890948 ecr 3945407], length 60
18:06:40.974879 IP master2.38787 > master3.2380: Flags [.], ack 60, win 331, options [nop,nop,TS val 3945501 ecr 3890948], length 0
18:06:40.976762 IP master1.2380 > master2.36442: Flags [P.], seq 1235860774:1235860835, ack 2751211062, win 122, options [nop,nop,TS val 4099620 ecr 3945411], length 61
18:06:40.978482 IP master2.36442 > master1.2380: Flags [.], ack 61, win 331, options [nop,nop,TS val 3945505 ecr 4099620], length 0
18:06:41.073308 IP master2.2380 > master3.52766: Flags [P.], seq 61:122, ack 1, win 122, options [nop,nop,TS val 3945599 ecr 3890947], length 61
18:06:41.073337 IP master2.2380 > master1.54930: Flags [P.], seq 62:124, ack 1, win 122, options [nop,nop,TS val 3945599 ecr 4099618], length 62
18:06:41.074338 IP master3.52766 > master2.2380: Flags [.], ack 122, win 148, options [nop,nop,TS val 3891047 ecr 3945599], length 0
以下、略

```



#7. コマンドからetcdを起動する方法
##7.1 master1で実行するコマンド

```
[root@master1 ~]# etcd -name master1 \
>      -initial-advertise-peer-urls http://192.168.0.10:2380 \
>      -listen-peer-urls http://192.168.0.10:2380 \
>      -listen-client-urls http://192.168.0.10:2379,http://127.0.0.1:2379 \
>      -advertise-client-urls http://192.168.0.10:2379 \
>      -initial-cluster-token mytoken \
>      -initial-cluster master1=http://192.168.0.10:2380,master2=http://192.168.0.20:2380,master3=http://192.168.0.30:2380 \
>      -initial-cluster-state new &
```

##7.2 master2で実行するコマンド

```
[root@master2 ~]# etcd -name master2 \
>      -initial-advertise-peer-urls http://192.168.0.20:2380 \
>      -listen-peer-urls http://192.168.0.20:2380 \
>      -listen-client-urls http://192.168.0.20:2379,http://127.0.0.1:2379 \
>      -advertise-client-urls http://192.168.0.20:2379 \
>      -initial-cluster-token mytoken \
>      -initial-cluster master1=http://192.168.0.10:2380,master2=http://192.168.0.20:2380,master3=http://192.168.0.30:2380 \
>      -initial-cluster-state new &
```

##7.3 master3で実行するコマンド

```
[root@master3 ~]# etcd -name master3 \
>      -initial-advertise-peer-urls http://192.168.0.30:2380 \
>      -listen-peer-urls http://192.168.0.30:2380 \
>      -listen-client-urls http://192.168.0.30:2379,http://127.0.0.1:2379 \
>      -advertise-client-urls http://192.168.0.30:2379 \
>      -initial-cluster-token mytoken \
>      -initial-cluster master1=http://192.168.0.10:2380,master2=http://192.168.0.20:2380,master3=http://192.168.0.30:2380 \
>      -initial-cluster-state new &

```

#8. 参考情報
[Configuration flags](https://coreos.com/etcd/docs/latest/op-guide/configuration.html)