前回の続きです
前の記事 : 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
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 だ
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とポート番号に展開してくれる
#!/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に以下定義を追加する
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
<?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にさらに以下を追加する
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配下にノードを置いてしまおう。
リロードする度にちゃんとノードがグリグリしてれば大成功!
いやー、久々に
インフラ屋の魂が◯◯する!! by ゴールデンカムイ
長くなっちゃったので次の記事で「秘技ノード落とし」とか「必殺300コンテナ起動」とか書くとしますかね。
パトラッシュ、僕はもう疲れたよ。。。