ようへいの日々精進XP

よかろうもん

Consul を利用して pull 型デプロイを実現する stretcher を試してみた(1)

Consul を利用して構成されたクラスタの各ノードにアプリケーションコンテンツ(ソースコード)を配布するデプロイツール stretcher というツールを教えて頂いたので試してみた。

github.com

tech.kayac.com

stretcher の詳細については上記の GitHub リポジトリ又は以下の紹介記事をご一読あれ。m(__)m


とは言っても、stretcher について自分なりに纏めてみた

stretcher が行うこと

動作イメージ

f:id:inokara:20150705002434p:plain

  1. consul event によって通知されたイベントを各ノードで稼働している consul watch が検知
  2. stretcher が Amazon S3 又は Web サーバー、ファイルからデプロイの手順が記載されたマニフェストを取得する
  3. マニフェストに記載されたソースアーカイブを取得して展開し rsync を利用してソースを指定されたディレクトリに展開
  4. アプリケーションのプロセスの再起動等を行い更新を反映(再起動の手順等は任意に実装する)

実践

Dockerfile と Playbook

実践に際しては以下の Dockerfile と Ansible の Playbook を利用した。

構成

構成図。

f:id:inokara:20150705090253p:plain

コンテナの一覧。

$ 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 のクラスタ構成については以下の記事に詳しく記載されている。

www.creationline.com

クラスタ構成は以下の通り。

$ 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 watchdeploy_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 deploydeploy success が出力されていることが判る。 また、curl を利用して以下のようにコンテナホストからアクセスして確認してみる。

$ curl localhost:32787
hostname: consul01
$ curl localhost:32789
hostname: consul02
$ curl localhost:32791
hostname: consul03

おお。


ということで

stretcher 自身は...

  • イベントを受け取ったらマニフェストとコンテンツアーカイブを取得
  • コンテンツアーカイブを配置
  • 任意のデプロイコマンドを実行

ととてもシンプルなので、Consul でクラスタが構成されていれば既存のデプロイ手順に組み込み易いのではないかと感じた。

引き続き、デプロイ時のエラーハンドリング等について確認してみたい。