30分でサクッとETCDクラスターを構築する

# なにこれ
前回、Chefを使ってDockerSwarmを30分で構築するという記事を書きました。
今回は、ETCDクラスターをサクッとつくる方法について記述します。
http://qiita.com/xecus/items/d49579a9f28e1feefa4a

あと、「etcdって何？」って感じの方もいらっしゃると思いますので、１つ記事を紹介させて頂きます。
http://qiita.com/pocket8137/items/ef44ca68ffc0f4e70995


# 注意
- Production等で利用するには、セキュリティ対策が必要です
- 前回の記事と一緒にご覧くださいませ

# 手順

## etcd-v2レシピを落としてくる
```bash
$ vi Berksfile
cookbook 'etcd-v2', '~> 1.0.6'
$ berks vendor cookbooks
```

## metadataに依存レシピ追加
```bash
$ vi metadata.rb
name             'hello'
maintainer       'YOUR_COMPANY_NAME'
maintainer_email 'YOUR_EMAIL'
license          'All rights reserved'
description      'Setup Docker'
long_description IO.read(File.join(File.dirname(__FILE__), 'README.md'))
version          '0.1.0'
depends          'etcd-v2', '~> 1.0.6'
```

## レシピを書く
レシピ汚くてすみません。汗
レシピの挙動としては以下となります。

- eth1のIPを拾ってきて、ノード名生成
- etcdctl用にTCP2379を開ける
- ピア間通信用にTCP2380を開ける
- discoveryにstaticを選択し、環境変数のノード情報を結合する
- etcdをサービス登録し、有効化

```bash

$ vi recipes/etcd.rb
include_recipe "etcd-v2::default"

etcd_binary 'default'
etcd_service 'default' do

  # get my ip
  ohai_val = Ohai::System.new
  ohai_val.all_plugins
  host_address_val = ohai_val["network"]["interfaces"]["eth1"]["addresses"].select { |address_key, address_val| address_val["family"] == "inet" }
  my_ip = host_address_val.keys[0]

  node_name 'node_' + my_ip.gsub(".", "_")
  instance 'etcd_binary[default]'
  user 'etcd'
  group 'etcd'

  client_host my_ip
  client_listen '0.0.0.0'
  client_port 2379

  peer_host my_ip
  peer_listen '0.0.0.0'
  peer_port 2380

  data_dir '/var/data/etcd'
  snapshot_count 10_000
  max_snapshots 5
  max_wals 5
  heartbeat_interval 100
  election_timeout 1000

  proxy :off
  protocol :http

  discovery :static
  node['hello'][:etcd_hosts].each do |node|
    next if node[:ipaddreass] == my_ip
    peer node[:name], node[:ipaddreass], {
      :protocol => :http,
      :client_port => 2379,
      :peer_port => 2380,
      :timeout => 10
    }
  end

  service_action [:start, :enable]

end
```

## attributesにホストを定義する
なんかChef側の機能で、ホスト群をよしなに管理してくれる機能が
ありそうですが、めんどくさかったので以下の用にホストをstaticに定義。

```rb

default['hello'][:etcd_hosts] = [
  {
    :name => 'vm1',
    :ipaddreass => '192.168.33.11'
  },
  {
    :name => 'vm2',
    :ipaddreass => '192.168.33.12'
  },
  {
    :name => 'vm3',
    :ipaddreass => '192.168.33.13'
  },
  {
    :name => 'vm4',
    :ipaddreass => '192.168.33.14'
  }
]
```

## ノードにレシピを追加する　

recipe[hello::etcd]をクラスタを組みたいノードに対して追加していきます。
今回の例では、nodes/192.168.33.1X.json (X=1..4)って感じですかね。

```bash

$ vi nodes/192.168.33.11.json
{
  "run_list": [
    "recipe[hello::default]",
    "recipe[hello::etcd]"
  ],
  "automatic": {
    "ipaddress": "192.168.33.11"
  }
}
```

## レシピを回す

```bash

$ knife solo cook -i ~/.ssh/id_rsa vagrant@192.168.33.11
$ knife solo cook -i ~/.ssh/id_rsa vagrant@192.168.33.12
$ knife solo cook -i ~/.ssh/id_rsa vagrant@192.168.33.13
$ knife solo cook -i ~/.ssh/id_rsa vagrant@192.168.33.14
```

ちなみに回しているときは以下のような雰囲気になります。
![スクリーンショット 2016-12-24 11.19.56.png](https://qiita-image-store.s3.amazonaws.com/0/128123/257c4c63-88ca-df09-6a9e-0f5fdfeae4d7.png)


# 確認作業
以下の作業は、etcdノード内での作業を想定しています。

とりあえず、ちゃんと4台結合されているのが確認できます。

```bash
vagrant@slave-1:~$ etcdctl member list
2a546a8220608294: name=node_192_168_33_12 peerURLs=http://192.168.33.12:2380 clientURLs=http://192.168.33.12:2379
30293d97ef360ebb: name=node_192_168_33_14 peerURLs=http://192.168.33.14:2380 clientURLs=http://192.168.33.14:2379
5c90fcc9be19298a: name=node_192_168_33_13 peerURLs=http://192.168.33.13:2380 clientURLs=http://192.168.33.13:2379
f829e5361f1740cc: name=node_192_168_33_11 peerURLs=http://192.168.33.11:2380 clientURLs=http://192.168.33.11:2379
```

```bash
vagrant@slave-1:~$ etcdctl cluster-health
cluster is healthy
member 2a546a8220608294 is healthy
member 30293d97ef360ebb is healthy
member 5c90fcc9be19298a is healthy
member f829e5361f1740cc is healthy
```

ちなみに、リーダーノードに対して以下のようなリクエストを投げると、詳細情報が出てきます。

```bash

vagrant@slave-1:~$ curl http://192.168.33.11:2379/v2/stats/leader  | jq .
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   621  100   621    0     0  47615      0 --:--:-- --:--:-- --:--:-- 51750
{
  "followers": {
    "5c90fcc9be19298a": {
      "counts": {
        "success": 863,
        "fail": 0
      },
      "latency": {
        "maximum": 4.95161,
        "minimum": 2.1e-05,
        "standardDeviation": 0.3277805182916868,
        "average": 0.15132188412514516,
        "current": 0.052651
      }
    },
    "30293d97ef360ebb": {
      "counts": {
        "success": 669,
        "fail": 195
      },
      "latency": {
        "maximum": 3.873798,
        "minimum": 2.2e-05,
        "standardDeviation": 0.3012054727758167,
        "average": 0.14836751868460396,
        "current": 0.060824
      }
    },
    "2a546a8220608294": {
      "counts": {
        "success": 863,
        "fail": 0
      },
      "latency": {
        "maximum": 6.357807,
        "minimum": 2.3e-05,
        "standardDeviation": 0.33088303594294544,
        "average": 0.16112308922363835,
        "current": 0.078952
      }
    }
  },
  "leader": "f829e5361f1740cc"
}
```

リーダーではないと、こんな感じの表示が。

```bash
vagrant@slave-1:~$ curl http://192.168.33.12:2379/v2/stats/leader  | jq .
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    32  100    32    0     0   7731      0 --:--:-- --:--:-- --:--:--  8000
{
  "message": "not current leader"
}
```

# 番外編
etcd-browserについての紹介。etcdの値をブラウザから確認・編集できるスグレモノ。
以下の作業は、Dockerが利用でき、　etcdクラスタが見えるネットワーク内で行ってください。

```bash
$ git clone https://github.com/henszey/etcd-browser
$ cd etcd-browser
$ sudo docker build -t etcd-browser .
$ sudo docker run --rm --name etcd-browser -p 0.0.0.0:8000:8000 --env ETCD_HOST=192.168.33.11 --env ETCD_PORT=2379 --env AUTH_PASS=doe -t -i etcd-browser
proxy /api requests to etcd on 192.168.33.11:2379
etc-browser listening on port 8000
```

Dockerホストの8000にポートマッピングされているので、```http://DockerHostIP:8000```でアクセスが可能です。

<img width="1392" alt="スクリーンショット 2016-12-24 12.22.59.png" src="https://qiita-image-store.s3.amazonaws.com/0/128123/fb6932a0-b722-b80f-e073-b6c4e09f7233.png">