ELB+Swarm+Compose+Consul+Registratorで夢は叶うのか(2)

# 前回の続きです

前の記事 : <a href="http://qiita.com/zERobYTe/items/dd9b2365c93da2638221">ELB+Swarm+Compose+Consul+Registratorで夢は叶うのか(1)</a>

# スプリットブレイン状態なConsulを立てる

Swarmは全ノードとコンテナを知っているがConsulは自ノードの設定しか責任を持たないようにしたかった。

Dockermanの/root/docker-host/にnodesを掘ってそこに新しいdocker-compose.ymlを書こう

```bash
# cd /root/docker-host
# mkdir nodes
# cd nodes
# nano docker-compose.yml
```

```docker-compose.yml
consul:
  command: -server -bootstrap -ui-dir /ui
  image: progrium/consul:latest
  ports:
  - "8300"
  - "8400"
  - "8500"
  - "8600/udp"
  environment:
  - "affinity:container!=nodes_consul_*"
  net: "host"

registrator:
  command: consul://127.0.0.1:8500
  image: progrium/registrator:latest
  volumes:
  - "/var/run/docker.sock:/tmp/docker.sock"
  environment:
  - "affinity:container!=nodes_registrator_*"
  net: "host"
```

この構成では

* 各ノードでConsulが8500で待ち受けし
* Registratorが自分が所属しているノードのdockerコンテナを把握して自分と同じノードのConsulに通知

となっていてサービス定義のenvironmentにある`affinity:container!=ほにゃらら`は同じノードでの多重起動を防止するために設定されているもの

ここまでは悩むところも無いのでさくっと起動してしまおう
DOCKER_HOSTを2380ポート(swarm manage)に向けてからdocker-composeをすればSwarm API経由でコンテナの制御が出来るようになる

```bash
# export DOCKER_HOST=tcp://localhost:2380
# pwd
/root/docker-host/nodes
# docker-compose up -d consul
Creating nodes_consul_1...
# docker-compose scale consul=3
Creating nodes_consul_2...
Creating nodes_consul_3...
Starting nodes_consul_2...
Starting nodes_consul_3...
# docker-compose up -d registrator
Creating nodes_registrator_1...
# docker-compose scale registrator=3
Creating nodes_registrator_2...
Creating nodes_registrator_3...
Starting nodes_registrator_2...
Starting nodes_registrator_3...
# docker-compose ps
       Name                      Command               State   Ports
--------------------------------------------------------------------
nodes_consul_1        /bin/start -server -bootst ...   Up
nodes_consul_2        /bin/start -server -bootst ...   Up
nodes_consul_3        /bin/start -server -bootst ...   Up
nodes_registrator_1   /bin/registrator consul:// ...   Up
nodes_registrator_2   /bin/registrator consul:// ...   Up
nodes_registrator_3   /bin/registrator consul:// ...   Up
```

ああ、なんて楽ちんなんでしょう。

# docker-loadbalancerをビルドする

このdocker-loadbalancerはConsulと通信しサービスの状態変化に合わせてConsul-templete起動、設定ファイルを展開して、Nginxを制御してくれる。

ノードのどれでもいいので、展開して設定を修正しよう

```bash
# pwd
/root/docker-host
# git clone https://github.com/bellycard/docker-loadbalancer.git loadbalancer
Cloning into 'loadbalancer'...
remote: Counting objects: 40, done.
remote: Total 40 (delta 0), reused 0 (delta 0), pack-reused 40
Unpacking objects: 100% (40/40), done.
Checking connectivity... done.
# cd loadbalancer
# ls -al
-rwxr-xr-x 1 root root  151 Mar  7 10:21 consul-template.service
-rw-r--r-- 1 root root  619 Mar  7 10:21 Dockerfile
-rw-r--r-- 1 root root  164 Mar  7 10:21 .envrc
-rw-r--r-- 1 root root  532 Mar  7 10:21 fig.yml
drwxr-xr-x 8 root root 4096 Mar  7 10:21 .git
-rw-r--r-- 1 root root  420 Mar  7 10:21 nginx.conf
-rwxr-xr-x 1 root root  123 Mar  7 10:21 nginx.service
-rw-r--r-- 1 root root 1064 Mar  7 10:21 README.md
```

ここで編集が必要なのは nginx.conf と consul-template.service だ

```nginx.conf
upstream nohost {
  least_conn;
  server 127.0.0.1:65535;
}

upstream testweb {
  least_conn;
  {{range service "production.testweb"}}server {{.Address}}:{{.Port}} max_fails=3 fail_timeout=60 weight=1;
  {{else}}server 127.0.0.1:65535; # force a 502{{end}}
}

server {
  listen 80 default_server;

  location / {
    proxy_pass http://nohost;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
  }
}

server {
  listen 80;
  server_name docker.example.com;

  location / {
    proxy_pass http://testweb;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
  }
}
```

こうすることで

サービス名(SERVICE_NAME) testweb
タグ名(SERVICE_TAGS) production

という環境変数がついたコンテナが増減した時、上記設定ファイルをConsul-templeteが{{.Address}}:{{.Port}}部分を実際のIPとポート番号に展開してくれる

```sh:consul-template.service
#!/bin/sh

exec consul-template \
     -consul=127.0.0.1:8500 \
     -template "/etc/consul-templates/nginx.conf:/etc/nginx/conf.d/app.conf:sv hup nginx"
     
```

Consul-templateの起動オプションにConsulの接続先が書いてあるのでここを書き換えている。
(127.0.0.1だとこのコンテナ内なんじゃないの？と思われたあなたは鋭い.ホストネットワーク機能のおかげです)

書き換えが完了したならばビルドしてプライベートレジストリにpushしてしまおう

```bash
# docker build .
Sending build context to Docker daemon 113.2 kB
Sending build context to Docker daemon
Step 0 : FROM nginx:1.7
 ---> 2485b0f89951
Step 1 : MAINTAINER Shane Sveller <shane@bellycard.com>
 ---> Using cache
 ---> 8b00a2293a97
...
...
...
Step 5 : ADD nginx.service /etc/service/nginx/run
 ---> b1b0b05a408b
Removing intermediate container ee6e15440531
Step 6 : ADD consul-template.service /etc/service/consul-template/run
 ---> 1daacc85e833
Removing intermediate container bb545c650ac1
...
...
...
Removing intermediate container 33bab34d8d86
Successfully built d0b8fa1bb2df
# docker tag d0b8fa1bb2df 10.10.0.110:5000/zerobytezero/docker-loadbalancer:latest
# docker push 10.10.0.110:5000/zerobytezero/docker-loadbalancer:latest
The push refers to a repository [10.10.0.110:5000/zerobytezero/docker-loadbalancer] (len: 1)
Sending image list
Pushing repository 10.10.0.110:5000/zerobytezero/docker-loadbalancer (1 tags)
...
...
...
1daacc85e833: Image successfully pushed
c878bca3ec0c: Image successfully pushed
9201627715ed: Image successfully pushed
d0b8fa1bb2df: Image successfully pushed
Pushing tag for rev [d0b8fa1bb2df] on {http://10.10.0.110:5000/v1/repositories/zerobytezero/docker-loadbalancer/tags/latest}
```

pushが終わったなら、各ノードで予めpullしておけばOK.

Dockermanに戻ってdocker-compose.ymlに以下定義を追加する

```docker-compose.yml
loadbalancer:
  image: 10.10.0.110:5000/zerobytezero/docker-loadbalancer:latest
  ports:
  - "80"
  expose:
  - "80"
  environment:
  - "affinity:container!=nodes_loadbalancer_*"
  net: "host"
```

docker-loadbalancerも各ノードに1つだけになるよう制限が必要だ
よーしloadbalancerも立ち上がっておしまい！

```bash
# docker-compose up -d loadbalancer
Creating nodes_loadbalancer_1...
# docker-compose scale loadbalancer=3
Creating nodes_loadbalancer_2...
Creating nodes_loadbalancer_3...
Starting nodes_loadbalancer_2...
Starting nodes_loadbalancer_3...
```

残るは中身だけだっ

# Webコンテナを準備する

なにか表示するものが無いと寂しいので...

```bash
# mkdir -p /docker-volumes/testweb
# nano /docker-volumes/testweb/index.php
```

```php:index.php
<?php ini_set('date.timezone', 'Asia/Tokyo'); ?>
<html>
<head>
  <title>docker-container</title>
</head>
<body>
  <h1>
    docker00:<?php echo $_SERVER['SERVER_ADDR']; ?>
  </h1>
  <ul>
  <?php
    foreach (getallheaders() as $name => $value) {
      echo "<li>$name: $value</li>";
    }
  ?>
  </ul>
</body>
</html>
```

各ノードにそれぞれ、こんなPHPファイルを配置した。
"docker00"の部分を各ノード名に書き換えておけばどのノードがコンテンツを返しているのかわかりやすいと思う。

Dockermanのdocker-compose.ymlにさらに以下を追加する

```docker-compose.yml
webapps:
  image: php:5.6-apache
  volumes:
   - "/docker-volumes/testweb:/var/www/html"
  ports:
   - "80"
  environment:
   - "affinity:container!=nodes_webapps_*"
   - SERVICE_80_NAME=http
   - SERVICE_NAME=testweb
   - SERVICE_TAGS=production

scalewebapps:
  image: php:5.6-apache
  volumes:
   - "/docker-volumes/testweb:/var/www/html"
  ports:
   - "80"
  environment:
   - SERVICE_80_NAME=http
   - SERVICE_NAME=testweb
   - SERVICE_TAGS=production
```

### なんで２つにしちゃったの？
Nginxが生きてるのに1台もWebコンテナが無いと502が返ってしまうので、最低限ノード数と同じだけのWebサーバを用意しておく必要があって、これがwebappsサービス。

scalewebappsはルールに無用でramdomにノードに散らばっていく設定として、パワー不足はこいつをスケールすることで対応していく。

レッツ起動

```bash
# docker-compose up -d webapps
Creating nodes_webapps_1...
# docker-compose scale webapps=3
Creating nodes_webapps_2...
Creating nodes_webapps_3...
Starting nodes_webapps_2...
Starting nodes_webapps_3...
```

```logs
registrator_2 | 2015/03/07 23:21:37 registrator: added: 540cc048a95b ip-10-10-0-50:nodes_webapps_1:80
consul_2      |     2015/03/07 23:21:37 [INFO] agent: Synced service 'ip-10-10-0-50:nodes_webapps_1:80'
registrator_3 | 2015/03/07 23:21:39 registrator: added: 665e35e82457 ip-10-10-0-51:nodes_webapps_2:80
consul_1      |     2015/03/07 23:21:39 [INFO] agent: Synced service 'ip-10-10-0-51:nodes_webapps_2:80'
registrator_1 | 2015/03/07 23:21:39 registrator: added: e9aaf6a0c092 ip-10-10-0-49:nodes_webapps_3:80
consul_3      |     2015/03/07 23:21:39 [INFO] agent: Synced service 'ip-10-10-0-49:nodes_webapps_3:80'
```

ちゃんと、RegistratorがConsulに登録してくれてるぽい！

Macbookに/etc/hostsを書いてアクセスして各ノードがちゃんと動いているようなら、ELB配下にノードを置いてしまおう。

![スクリーンショット 2015-03-08 8.48.49.png](https://qiita-image-store.s3.amazonaws.com/0/36701/26e2b768-af51-a7ba-878a-9b23dba76d82.png "スクリーンショット 2015-03-08 8.48.49.png")

リロードする度にちゃんとノードがグリグリしてれば大成功！

いやー、久々に

>インフラ屋の魂が◯◯する！！ by ゴールデンカムイ

長くなっちゃったので次の記事で「秘技ノード落とし」とか「必殺300コンテナ起動」とか書くとしますかね。

パトラッシュ、僕はもう疲れたよ。。。


次の記事 : <a href="http://qiita.com/zERobYTe/items/3415338f7fac5ced7577">ELB+Swarm+Compose+Consul+Registratorで夢は叶うのか(3)</a>