ども、cloudpack の デプロイ市民の かっぱ (@inokara) です。

はじめに

とりあえず AWS CodeDeploy で docker run 出来るので docker run で Web サーバーを上げておいて HAProxy のバックエンドに自動的に追加させるところまでを AWS CodeDeploy で一気にやってみたいと思ったので、まずは HAProxy のバックエンドを自動で登録する方法の確認を行います。

尚、HAProxy のバックエンドへの自動登録については以前、consul-haproxy ツールが紹介されていたのですが、現在は consul-template に内包されてしまっているようですので、こちらのツールにて行ってみたいと思います。

参考

Consul の用意

Consul とは?

これについては Qiita 内に蓄積されている Consul の各記事を御覧ください。

Consul のインストールと起動

インストールはとても簡単です。

wget https://dl.bintray.com/mitchellh/consul/0.4.1_linux_amd64.zip

で取得して…

./consul agent -server -bootstrap -client=127.0.0.1 -dc=local 
               -node=consul1 -data-dir=/tmp/consul  -bind=127.0.0.1 &

起動します。今回はお試しで一台で server と agent を担います。

試しにサービス登録

以下のようにして Consul にサービスを登録してみます。

$ cat << EOT | curl -XPUT http://127.0.0.1:8500/v1/agent/service/register -d @-
{
  "ID": "web-app",
  "Name": "web-app",
  "tags": ["web-app"],
  "port": 80
}
EOT

登録されたサービスを確認してみます。

$ curl -s http://127.0.0.1:8500/v1/catalog/services | jq .
{
  "consul": [],
  "web-app": [
    "web-app"
  ]
}

web-app が登録されていますね。

さらに web-app サービスに登録されているノード情報等を確認するには以下のように実行します

$ curl -s http://127.0.0.1:8500/v1/catalog/service/web-app | jq .
consul-template とは

Consul に登録されているサービスの一覧を取得して各ミドルウェアの設定ファイルを自動的に生成してくれるツールのようです。

サービスとして起動させることで consul に登録されるサービスを監視してサービスノードの増減に応じて対象となる設定ファイルを書き換えてサービスを起動します。

consul-temaplate を使ってみる

consul-template のインストール

こちらも Consul 同様にインストールは簡単です。

wget https://github.com/hashicorp/consul-template/releases/download/v0.3.0/consul-template_0.3.0_linux_amd64.tar.gz
tar zxvf consul-template_0.3.0_linux_amd64.tar.gz

以上です。簡単ですな。

サービスを展開する

先ほど登録したサービスを HAProxy の設定ファイルに展開してみたいと思いますので、以下のようなテンプレートファイルを用意します。

global
    daemon
    maxconn 1024

defaults
    mode http
    timeout 300

listen http-in
    bind *:8000{{range service "release.webapp"}}
    server {{.Node}} {{.Address}}:{{.Port}}{{end}}

{{...}} で囲まれている部分は Go のテンプレートになります。

以下のように consul-template を実行します。

./consul-template 
  -consul 127.0.0.1:8500 
  -template haproxy.ctmpl:./hoge 
  -dry

以下のように出力されました。

global
    daemon
    maxconn 1024

    defaults
    mode http
    timeout connect 5000ms
    timeout client 50000ms
    timeout server 50000ms

    listen http-in
    bind *:8000
    server consul1 127.0.0.1:80

おお、先ほど登録したサービスが server consul1 127.0.0.1:80 として展開されていることが判ります。

Docker コンテナの用意

Sinatra で Web アプリケーションを用意する

以下のような簡単なアプリケーションを起動するにコンテナに仕込んでおきます。

require 'json'
require 'sinatra'

get '/' do
  http_headers = request.env.select { |k, v| k.start_with?('HTTP_') }
  "#{http_headers}""n"
end

このアプリケーションを docker run 時に起動出来るように /root/web.shというファイル名で以下のようなシェルスクリプトを用意しておきます。

#!/usr/bin/env bash

cd /opt/app
ruby -rubygems test.rb -o 0.0.0.0 -p 8081

試しに起動する

docker run -t -d -p 8081 test_app /root/web.sh

引続き、以下のようにバインドされているポートを確認してアクセスを試します。

$ docker run -t -d -p 8081 test_app /root/web.sh
e94c75a1539887a7f4102e056bf72037c65fb670515191b5c4e50f1dff55347f
$ docker port e94c75a1539887a7f4102e056bf72037c65fb670515191b5c4e50f1dff55347f 8081
0.0.0.0:49190
$ curl localhost:49190
{"HTTP_USER_AGENT"=>"curl/7.38.0", "HTTP_HOST"=>"localhost:49190", "HTTP_ACCEPT"=>"/", "HTTP_VERSION"=>"HTTP/1.1"}

おお。

consul-template との連携

連携のシェルスクリプト

以下のようにコンテナを起動して consul にサービスとして登録するようなスクリプトをサクッと書きました。

 #!/usr/bin/env bash

ID=docker run -t -d -p 8081 test_app /root/web.sh
echo $ID
PORT=docker port ${ID} 8081 | sed s/0.0.0.0://g

cat << EOT | curl -XPUT http://127.0.0.1:8500/v1/agent/service/register -d @-
{
  "ID": "web-app_${ID}",
  "Name": "web-app",
  "tags": ["web-app"],
  "port": ${PORT}
}
EOT

consul-template の起動

consul-template は以下のように起動しておきます。

./consul-template 
  -consul 127.0.0.1:8500 
  -template haproxy.ctmpl:/etc/haproxy/haproxy.cfg:service haproxy restart &

demo

上記の test.sh を実行するとコンテナの起動と consul へのサービス登録が行われます。

./test.sh

以下のように出力されました。

$ ./test.sh
11870daf56ef96d9609de05b4d3b1b9a2184b610d5969696566b4deba220ed01
$     2014/11/17 01:23:18 [INFO] agent: Synced service 'web-app_11870daf56ef96d9609de05b4d3b1b9a2184b610d5969696566b4deba220ed01'
Stopping haproxy: [  OK  ]
Starting haproxy: [  OK  ]

上記のように HAProxy の再起動まで一気通貫で行われました。
一応、サービスが登録されていることを確認します。

$ curl -s http://127.0.0.1:8500/v1/catalog/service/web-app | jq .
[
  {
    "Node": "consul1",
    "Address": "127.0.0.1",
    "ServiceID": "web-app_11870daf56ef96d9609de05b4d3b1b9a2184b610d5969696566b4deba220ed01",
    "ServiceName": "web-app",
    "ServiceTags": [
      "web-app"
    ],
    "ServicePort": 49191
  }
]

また、HAProxy の設定も確認してみます。

global
    daemon
    maxconn 1024

defaults
    mode http
    timeout connect 5000ms
    timeout client 50000ms
    timeout server 50000ms

listen http-in
    bind *:8000
    server consul1 127.0.0.1:49191

ちゃんと登録されていますね。
HAProxy が Listen しているポートにアクセスしてみます。

 $ curl localhost:8000
{"HTTP_USER_AGENT"=>"curl/7.38.0", "HTTP_HOST"=>"localhost:8000", "HTTP_ACCEPT"=>"/", "HTTP_VERSION"=>"HTTP/1.1"}

おおっちゃんとアクセス出来ました!
すげえ。

最後に

ということで…
CodeDeploy で色々とやる為の事前準備として Consul と consul-template と Docker を組み合わせて HAProxy のバックエンドを自動追加してみました。
consul 自体をちゃんと勉強する必要はありますが、クラスタを動的に構築したい場合等は consul-template は色々と使えそうなツールだと思いました。

ちなみに…docker run して HAProxy への自動登録を CodeDeploy でヤル意味あんのかってところはとりあえず無視で…(汗

元記事はこちらです。
Docker + consul-template で HAProxy のバックエンド登録の自動化を試す