Kubernetes上でetcdサーバーを分散させてみた

#概要
[次回記事](http://qiita.com/mmitti/private/413e99077670eb9341d1)で各Podの処理内容や進捗状況を管理するため全ノードにetcdサーバーを配置しました。

Kubernetesの全ノード上でetcdサーバーを動かすサンプルです。
KubernetesのDaemon setを利用し各ノード上で1ポッド動かします。

なお、Kubernetesの構築[(記事はこちら)](http://qiita.com/mmitti/items/6c1bedbcffdda6a63473)が完了していることが前提となります。
###Daemon setについて
Kubernetesの全ノード上で同じPodを動かすことができます。
その為ノードのポートをバインドしてサービスを動かしたりhostPathをマウントして利用するのもやりやすいと思います。

各ノード上でモニタリングツールを動かしたりログ収集ツールを動かすなどが公式の例として上がっています。


#動作環境
[Kubernetesの構築](http://qiita.com/mmitti/items/6c1bedbcffdda6a63473)が完了している状態とほとんど同じです。

なお、今回は複数ノードで動かすため2ノード動かします。

||masterサーバー|nodeサーバー1|nodeサーバー2|
|:----|:----|:----|:----|
|ホスト名|master|node1|node2|
|IPアドレス|192.168.137.20|192.168.137.21|192.168.137.22|
|動作しているサービス|etcd<BR>kube-apiserver<BR>kube-controller-manager<BR>kube-scheduler|kubelet<BR>kube-proxy|kubelet<BR>kube-proxy|

#構成
今回はディスカバリー用のetcdとしてmasterサーバーで動いているetcdを利用しました。
Deamonsetを利用して各ノード上で立ち上がるetcdポッドは最初にディスカバリー用のetcdに問い合わせを行いクラスターを構築します。

![etcd.png](https://qiita-image-store.s3.amazonaws.com/0/140084/091b8580-a94e-9828-857b-461c0f2e3290.png)

各Podはetcdのデータを保存するためノード上の/data/etcdフォルダーをマウントします。
また、各ノードの2379/2380番ポートをPodの2379/2380番ポートをバインドします。
これにより各ノードの2379/2380番ポートにアクセスすることでetcdサーバーに接続できます。

#ディスカバリーサーバーの準備
ここからはmasterサーバーのetcdがhttp://192.168.137.20:2379 で動いているとし、ディスカバリー用に/discovery/test以下を利用するとします。

まず、クラスターのサイズを登録します。
指定数のetcdが接続しないと起動しないのでノード数と同じ値にします。（適当な値にしてしまい動かないというトラブルが起きました。)

クラスターのサイズはディスカバリー用のパス/_config/sizeに登録します。

```
etcdctl --endpoints http://192.168.137.20:2379 set /discovery/test/_config/size 2
```

#サンプルコード
Dockerfile及び起動用のスクリプトはGithub( https://github.com/mmitti/kube-etcd )にあります。
podのipアドレスを求めるためpythonのスクリプト経由でetcdを動かしています。

Deamonsetの環境変数として以下2つを指定してください。
DISCOVERY:etcdのディスカバリーのアドレス　例http://192.168.137.20:2379/v2/keys/discovery/test
DATADIR:etcdのデータの保存先　hostPathをマウントするパスと同じにしてください。

[etcd.yaml](https://github.com/mmitti/kube-etcd/blob/master/etcd.yaml)

```
apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
  labels:
    name: etcd
  name: etcd
spec:
  template:
    metadata:
      labels:
        app: etcd
    spec:
      containers:
        - name: etcd
          image: mmitti/etcd
          imagePullPolicy: Always
          ports:
            - containerPort: 2379
              hostPort: 2379
              name: p1
            - containerPort: 2380
              hostPort: 2380
              name: p2
          env:
          - name: DISCOVERY
            value: http://192.168.137.20:2379/v2/keys/discovery/test
          - name: DATADIR
            value: /data
          volumeMounts:
          - name: etcd-data
            mountPath: /data
      volumes:
        - name: etcd-data
          hostPath:
            path: /data/etcd
---
apiVersion: v1
kind: Service
metadata:
  name: etcd
  labels:
    app: etcd
spec:
  ports:
  - port: 2379
    name: p1
  - port: 2380
    name: p2
  clusterIP: None
  selector:
    app: etcd

```

こちらのyamlをKubernetesに登録します。

```
$ kubectl create -f etcd.yaml
```

#動作確認
Podが起動しているか確認します。

```
$ kubectl get po -l app=etcd
NAME         READY     STATUS    RESTARTS   AGE
etcd-9w1sw   1/1       Running   1          21h
etcd-zrn62   1/1       Running   1          21h
```

また、各Podが動いているノードを確認します。

```
$ kubectl describe po  -l app=etcd
Name:           etcd-9w1sw
Namespace:      default
Node:           node1/192.168.137.21
--省略--
Name:           etcd-zrn62
Namespace:      default
Node:           node2/192.168.137.22
--省略--
```

最後にetcdサーバーに値を書き込んで読み出してみます。

```
$ etcdctl --endpoints http://192.168.137.21:2379 set /hoge testtest
testtest

$ etcdctl --endpoints http://192.168.137.21:2379 get /hoge
testtest

$ etcdctl --endpoints http://192.168.137.22:2379 get /hoge
testtest
```

書き込んだ値を読み出せれば成功です。

#追記
ノード再起動後PodのIPアドレスが変わる恐れがあり通信できなくなる場合があります。
ディスカバリーに登録するIPアドレスはポッドのものでなくノードのもののほうが良さそうです。