Consul を利用して構成されたクラスタの各ノードにアプリケーションコンテンツ(ソースコード)を配布するデプロイツール stretcher というツールを教えて頂いたので試してみた。
stretcher の詳細については上記の GitHub リポジトリ又は以下の紹介記事をご一読あれ。m(__)m
とは言っても、stretcher について自分なりに纏めてみた
stretcher が行うこと
- デプロイの手順が記された YAML ファイル(以下、マニフェスト)を取得
- マニフェストに記載されたソースリポジトリよりアプリリケーションコンテンツ(ソースコード)を取得して展開
- 展開したファイルを rsync を利用してターゲットディレクトリに転送(rsync は –delete オプション)
- 以降はマニフェストに記述されたコマンドを実行する(アプリケーションの再起動等)
動作イメージ
consul event
によって通知されたイベントを各ノードで稼働しているconsul watch
が検知- stretcher が Amazon S3 又は Web サーバー、ファイルからデプロイの手順が記載されたマニフェストを取得する
- マニフェストに記載されたソースアーカイブを取得して展開し rsync を利用してソースを指定されたディレクトリに展開
- アプリケーションのプロセスの再起動等を行い更新を反映(再起動の手順等は任意に実装する)
実践
Dockerfile と Playbook
実践に際しては以下の Dockerfile と Ansible の Playbook を利用した。
構成
構成図。
コンテナの一覧。
$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES f10ce4109c37 inokappa/centos-base "/bin/sh -c '/usr/sb 2 seconds ago Up 1 seconds 0.0.0.0:32792->22/tcp, 0.0.0.0:32791->8000/tcp consul_03 4058ac8b3103 inokappa/centos-base "/bin/sh -c '/usr/sb 3 seconds ago Up 3 seconds 0.0.0.0:32790->22/tcp, 0.0.0.0:32789->8000/tcp consul_02 c1996e97905a inokappa/centos-base "/bin/sh -c '/usr/sb 3 seconds ago Up 3 seconds 0.0.0.0:32788->22/tcp, 0.0.0.0:32787->8000/tcp consul_01 5e040dd6e31e inokappa/centos-base "/bin/sh -c '/usr/sb 31 hours ago Up 31 hours 0.0.0.0:32771->22/tcp consul_deploy
尚、コンテナは以下のようなオプションで起動している。
$ docker run --name=consul_01 --hostname=consul01 -t -d -p 22 -p 8000 inokappa/centos-base $ docker run --name=consul_02 --hostname=consul02 -t -d -p 22 -p 8000 inokappa/centos-base $ docker run --name=consul_03 --hostname=consul03 -t -d -p 22 -p 8000 inokappa/centos-base $ docker run --name=consul_deploy -t -d -p 22 -p 8000 inokappa/centos-base
Consul のクラスタを構成する
今回は Atlas を利用してクラスタ構成するので、予め token を取得して以下のように Consul の設定を用意した。(Playbook によって用意される)
{ "atlas_join": true, "atlas_infrastructure": "inokappa/stretcher", "atlas_token": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "server": true, "bootstrap_expect": 3, "log_level": "INFO", "node_name": "consul_deploy", "data_dir": "/opt/consul" }
Atlas を利用するので Consul プロセスを再起動するだけでクラスタが構成されるのは嬉しい。 尚、Consul のクラスタ構成については以下の記事に詳しく記載されている。
クラスタ構成は以下の通り。
$ consul members -detailed Node Address Status Tags consul_deploy 172.17.0.11:8301 alive expect=3,role=consul,dc=dc1,vsn=2,vsn_min=1,vsn_max=2,build=0.5.2:9a9cc934,port=8300 consul_03 172.17.0.27:8301 alive port=8300,expect=3,role=consul,dc=dc1,vsn=2,vsn_min=1,vsn_max=2,build=0.5.2:9a9cc934 consul_01 172.17.0.25:8301 alive port=8300,expect=3,role=consul,dc=dc1,vsn=2,vsn_min=1,vsn_max=2,build=0.5.2:9a9cc934 consul_02 172.17.0.26:8301 alive dc=dc1,vsn=2,vsn_min=1,vsn_max=2,build=0.5.2:9a9cc934,port=8300,expect=3,role=consul
stretcher のビルド
まずは stretcher をビルドする。 既に手元に Go 環境がある前提で以下のように Ubuntu 14.04 環境でビルドした。
cd $GOPATH/src/ git clone https://github.com/fujiwara/stretcher.git cd stretcher make get-deps make
$GOPATH/src/stretcher/cmd/
以下にビルドされたバイナリが作成される。 作成したバイナリを各コンテナに配布しておく。(今回は上記の Playbook にて配布した。)
stretcher の起動
stretcher を consul_01 から consul_03 にて consul watch
がイベントを検知した際に実行されるように起動しておく。
$ /usr/local/bin/consul watch -type event -name deploy_demo /usr/local/bin/stretcher &
&
をつけてバックグラウンドにて起動させておく。
コンテンツとマニフェストの用意
app/html/index.html
コンテンツ。hostname:
の後に稼働するコンテナのホスト名が入る。
hostname: ${デプロイ時に hostname -s の結果が追記される}
start.sh
アプリケーション(Web サーバー)を起動するシェルスクリプト。
python -m SimpleHTTPServer & exit 0
毎度おなじみの Python のワンライナー Web サーバーを起動する。
コンテンツ一式をアーカイブしてハッシュ値を取得する
$ tar cvf app.tar.gz app app/ app/start.sh app/html/ app/html/index.html $ md5sum app.tar.gz 4b4fedd8ef18df2b42134fa81bbc75bc app.tar.gz
取得したハッシュ値はマニフェストの checksum
に記載する。
マニフェスト
以下のようにマニフェストファイルを用意しておく。
src: http://consul_deploy:8000/app.tar.gz checksum: 4b4fedd8ef18df2b42134fa81bbc75bc dest: /tmp/service commands: pre: - echo 'staring deploy' >> /tmp/stretcher.log post: - sed -i "s/$/`hostname -s`/g" /tmp/service/app/html/index.html - pkill python; pushd /tmp/service/app/html; /tmp/service/app/start.sh > /dev/null 2>&1 success: - echo 'deploy success' >> /tmp/stretcher.log failure: - echo 'deploy failed!!' >> /tmp/stretcher.log
ファイル名は deploy.yml
としておく。
コンテンツとマニフェストの設置
今回は HTTP 経由でマニフェストファイルやコンテンツアーカイブを配布するので、公開用ディレクトリを作成して python Web サーバーを起動しておく。
$ sudo mkdir /home/stretcher/html $ sudo chown -R ${maintenance_user}:${maintenance_group} /home/stretcher $ cd /home/stretcher/html $ mv /path/to/deploy.yml . $ mv /path/to/app.tar.gz . $ python -m SimpleHTTPServer & Serving HTTP on 0.0.0.0 port 8000 ...
念のために確認。
$ curl localhost:8000/deploy.yml localhost - - [04/Jul/2015 22:42:07] "GET /deploy.yml HTTP/1.1" 200 - src: http://consul_deploy:8000/app.tar.gz checksum: 1d6733959974d84bb90832df90dadaec dest: /tmp/service commands: pre: - echo 'staring deploy' >> /tmp/stretcher.log post: - sed -i "s/$/`hostname -s`/g" /tmp/service/app/html/index.html - pkill python; pushd /tmp/service/app/html; /tmp/service/app/start.sh > /dev/null 2>&1 success: - echo 'deploy success' >> /tmp/stretcher.log failure: - echo 'deploy failed!!' >> /tmp/stretcher.log
デプロイ
では、実際にデプロイを行ってみたいので、consul_deploy コンテナにて consul event
を実行して各コンテナで常駐している consul watch
に deploy_demo
というイベントを送る。
$ /usr/local/bin/consul event -node="consul_0" -name deploy_demo http://consul_deploy:8000/deploy.yml Event ID: 180590c8-aa7b-e6d9-a57a-8b90c803fcd5
尚、-node="consul_0"
のように対象となる Consul ノードを指定することも可能。-node
以外にも -dc
や -tag
オプションを利用してイベントの送信対象を絞り込むことが出来る。
イベントを送信と同時に以下のように各コンテナ(Consul ノード)からマニフェストとコンテンツアーカイブの取得が行われたログ(アクセスログ)が出力される。(※Web サーバー次第でログの出力の有無、出力先は異なるので注意)
xxx.xx.0.26 - - [04/Jul/2015 22:54:34] "GET /deploy.yml HTTP/1.1" 200 - xxx.xx.0.25 - - [04/Jul/2015 22:54:34] "GET /deploy.yml HTTP/1.1" 200 - xxx.xx.0.27 - - [04/Jul/2015 22:54:34] "GET /deploy.yml HTTP/1.1" 200 - xxx.xx.0.26 - - [04/Jul/2015 22:54:34] "GET /app.tar.gz HTTP/1.1" 200 - xxx.xx.0.25 - - [04/Jul/2015 22:54:34] "GET /app.tar.gz HTTP/1.1" 200 - xxx.xx.0.27 - - [04/Jul/2015 22:54:34] "GET /app.tar.gz HTTP/1.1" 200 -
各コマンド実行時 /tmp/stretcher.log
以下にログを出力するようにしていたので consul exec
を利用して確認する。
$ consul exec -node="consul_0" 'cat /tmp/stretcher.log' consul_01: staring deploy consul_01: deploy success consul_01: consul_03: staring deploy consul_03: deploy success consul_03: ==> consul_01: finished with exit code 0 ==> consul_03: finished with exit code 0 ==> consul_02: finished with exit code 0 consul_02: staring deploy consul_02: deploy success consul_02: 3 / 3 node(s) completed / acknowledged
各ノードで staring deploy
と deploy success
が出力されていることが判る。 また、curl を利用して以下のようにコンテナホストからアクセスして確認してみる。
$ curl localhost:32787 hostname: consul01 $ curl localhost:32789 hostname: consul02 $ curl localhost:32791 hostname: consul03
おお。
ということで
stretcher 自身は…
- イベントを受け取ったらマニフェストとコンテンツアーカイブを取得
- コンテンツアーカイブを配置
- 任意のデプロイコマンドを実行
ととてもシンプルなので、Consul でクラスタが構成されていれば既存のデプロイ手順に組み込み易いのではないかと感じた。
引き続き、デプロイ時のエラーハンドリング等について確認してみたい。