Edited at

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

More than 3 years have passed since last update.


前回の続きです

前の記事 : ELB+Swarm+Compose+Consul+Registratorで夢は叶うのか(1)


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

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

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

# 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経由でコンテナの制御が出来るようになる

# 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を制御してくれる。

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

# 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とポート番号に展開してくれる


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してしまおう

# 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も立ち上がっておしまい!

# 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コンテナを準備する

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

# mkdir -p /docker-volumes/testweb

# nano /docker-volumes/testweb/index.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



なんで2つにしちゃったの?

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

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

レッツ起動

# 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...

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

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

いやー、久々に


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


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

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

次の記事 : ELB+Swarm+Compose+Consul+Registratorで夢は叶うのか(3)