docker
etcd
CoreOS

Vulcand を使って Docker コンテナをブルーグリーンデプロイする

More than 1 year has passed since last update.

概要

vulcand という HTTP プロキシを使って Docker コンテナをブルーグリーンデプロイするという話。
Consul と Nginx を組み合わせて動的にロードバランスするという話とやりたいことは同じだと思う。

vulcand とは

vulcand は、Mailgun というメール系 SaaS の開発チームが作った HTTP プロキシ。

  • etcd をバックエンドに利用した HTTP プロキシ
  • 再起動なしで設定を反映できる
  • HTTP API と CLI
  • プラガブルなミドルウェア
  • ゼロダウンタイムデプロイのサポート
  • リアルタイムメトリクスレポート
  • TLS と certificate の管理

といった特徴を持つ。設定ファイルを etcd に保存して再起動なしで設定反映できるというのが非常に便利なポイントだと思う。

vulcand の仕組み

vulcand に 以下の 2 つの主要な概念がある。

  • locations
  • upstreams

Location

Location はホスト名とリクエストパスの組み合わせからなり、名前を持つ。リクエストされた URL に対して正規表現マッチが行われる。

  • search という名前の location で example.com を host として /search を正規表現マッチャとして使う
  • user という名前の location で example.com を host として /^users.*$/ を正規表現マッチャとして使う

そして Location は後述する upstream を持ち、正規表現マッチしたリクエストをupstreams へプロキシする。

Upstream

Upstream は後述する endpoint の集合であり、名前を持つ。

  • search-backend という名前の upstream で endpoint は http://10.10.1.2:8080http://10.10.1.3:8080

Endpoint

Endpoint は最終的にリクエストを処理するところ。 <schema>://<host>:<port> 形式で定義する

  • http://localhost:5000

Vulcand を使って Docker コンテナのブルーグリーンデプロイ

Vagrant で起動した CoreOS マシン上の vulcand に OS X から接続してブルーグリーンデプロイを試す。

イメージ図:

例として、example というアプリケーションがあるとして、v1 から v2 への切り替えをするデプロイをする。

OS X 側の hosts を設定しておく

# on CoreOS
core@core-01 ~ $ cat /etc/environment
COREOS_PUBLIC_IPV4=172.17.8.101
COREOS_PRIVATE_IPV4=172.17.8.101
# on OS X
$ sudo vi /etc/hosts
##
# Host Database
#
# localhost is used to configure the loopback interface
# when the system is booting.  Do not change this entry.
##
127.0.0.1       localhost
255.255.255.255 broadcasthost
::1             localhost

172.17.8.101    example.com # 追加

サービスコンテナ v1 の起動

example アプリケーションコンテナの v1 を2 台起動する

# on CoreOS
core@core-01 ~ $ docker run -d --name example-v1.1 -p 8086:80 coreos/example:1.0.0
0940df1b68a823e68d165196a7fde12922b6c57ff6b4ad95441a2fa9fd64a95b

core@core-01 ~ $ docker run -d --name example-v1.2 -p 8087:80 coreos/example:1.0.0
e35ed7de9766ae67e14bc05f95e5c1a207d74b03e3dd4f350b99192c4ff51845

Upstream example-v1 の設定

続いて、example-v1.1, exaple-v1.2 を endpoint とする example という upstream をセットする。

# on CoreOS
core@core-01 ~ $ etcdctl set /vulcand/upstreams/example-v1/endpoints/1 http://172.17.8.101:8086
http://172.17.8.101:8086

core@core-01 ~ $ etcdctl set /vulcand/upstreams/example-v1/endpoints/2 http://172.17.8.101:8087
http://172.17.8.101:8087

Location の設定

home という名前の location を作成する。正規表現マッチャは /.* つまり全てのリクエストをマッチさせる。

# on CoreoS
core@core-01 ~ $ etcdctl set "/vulcand/hosts/example.com/locations/home/path" '/.*'
/.*

そして home location を example-v1 upstream にプロキシさせるようセットする

# on CoreOS
core@core-01 ~ $ etcdctl set /vulcand/hosts/example.com/locations/home/upstream example-v1
example-v1

vulcand コンテナの起動

# on CoreOS
core@core-01 ~ $ docker run \
  --rm \
  --name vulcan \
  -p 80:80 \
  -p 8182:8182 \
  mailgun/vulcand \
  /opt/vulcan/vulcand \
  -apiInterface="0.0.0.0" \
  -interface="0.0.0.0" \
  -etcd="http://172.17.8.101:4001" \
  -port=80 \
  -apiPort=8182

アクセスしてみる

http://example.com にアクセスしてみる。example アプリケーションの v1 が見れた :)

スクリーンショット 2014-11-05 16.32.09.png

サービスコンテナ v2 の起動

# on CoreOS
core@core-01 ~ $ docker run -d --name example-v2.1 -p 8088:80 coreos/example:2.0.0
8aa0f8cd402fa8be29b96f37bc97a2899bb32510cc3a8899d1338d0f8c8e1040

core@core-01 ~ $ docker run -d --name example-v2.2 -p 8089:80 coreos/example:2.0.0
8db14040b1207ec42a5c08236bd4c7214323db36058d47e77606256231b6f5e8

upstream example-v2 の設定

# on CoreOS
core@core-01 ~ $ etcdctl set /vulcand/upstreams/example-v2/endpoints/1 http://172.17.8.101:8088
http://172.17.8.101:8088

core@core-01 ~ $ etcdctl set /vulcand/upstreams/example-v2/endpoints/2 http://172.17.8.101:8089
http://172.17.8.101:8089

v1 から v2 に切り替え

# on CoreOS
core@core-01 ~ $ etcdctl set /vulcand/hosts/example.com/locations/home/upstream example-v2

再度アクセスしてみる

example アプリケーションの v2 になってる :)
ゼロダウンタイムブルーグリーンデプロイ。

スクリーンショット 2014-11-05 16.48.26.png

ここでまた

# on CoreOS
core@core-01 ~ $ etcdctl set /vulcand/hosts/example.com/locations/home/upstream example-v1

とすれば v1 のコンテナにプロキシされてロールバックができる。

ローリングデプロイ

上記の例だと、home location へ紐づける upstreamexample-v1 から example-v2 に切り替えてデプロイを行ったが、upstream を固定して、endpoint を追加・削除することでローリングデプロイも可能。

TODO: registrator を使って upstream の自動登録をする

registrator または docker-plugin を使って、コンテナの start hook 時に etcd に起動 IP と Port を登録する

メモと感想

HA 構成できる

etcd から設定を読むので、HA 構成が実現できそう。例えば AWS で 3 台からなる CoreOS クラスタがあるとき、全台で vulcand を起動しておき、vulcand の前段に ELB を置く事で、

  • Docker ホストレベル(=EC2 インスタンス)のロードバランスを ELB に
  • Docker コンテナレベルのロードバランスを vulcan に

やらせることで割とシンプルな構成にできそう。

# requests:
#  - www.example.com
#  - admin.example.com
#  - search.example.com


                     vulcan.1 on coreos.1 
                   /                        --> upstream `search`
                  /                       /
request --> ELB ---  vulcan.2 on coreos.2  ---> upstream `admin`
                 \                        \ 
                   \                        --> upstream `www`
                     vulcan.3 on coreos.3

Nginx と比べて

  • コンテナとして動かすのに向いている
  • プロキシとしての機能は nginx と比べると乏しい?

コンテナとして動かすことについて

nginx がコンテナとして動かそうと思うと後から設定をダイナミックには変更しづらい。なので、Docker ホスト側に Nginx をインストールすることになる。なるだけ全てをコンテナでやりたいと思うとこれは微妙。

頑張ってコンテナでやろうとするなら

  • コンテナを Nginx として動かすが設定ファイルを -v でホストと共有して docker exec で nginx を reload
  • コンテナを Nginx として動かすが、設定変更時は起動スクリプトに環境変数や引数を渡して動的に設定ファイル作ってコンテナ再起動

といった感じでできなくもないと思うが、とても複雑(他にもやり方はあると思う)

TODO: プロキシとしての機能について

ドキュメントまだ全部読めてない。deis は「色々足りてないからまだ Nginx 使うよ」 って言ってる。

REF