Tutumで負荷分散をやってみました


本記事はDocker Advent Calendar 2015の9日目の記事です。

概要

先日書いた以下の記事で、Tutumのチュートリアルを一通りやりました。

「簡単にDockerイメージがデプロイできてスゲエ!」ということは分かったものの、チュートリアルで作ったままの状態で安易にコンテナを増やそうとすると、エラーで怒られてしまいました。

一応、Tutumのサポートサイトに解決してくれそうな記事があるのは見つけていましたが、「今後の宿題」ってことで後回しにしていました。

ロードバランサー的な動きをするコンテナを立てて振り分けてあげれば実現できそうです。サポートサイトにもそれっぽい内容のページがありました。
Load balancing a Web Service
これは今後の宿題にしておきます。

そうこうしている内に自分の番が回ってきてしまったので、慌てて検証しつつ書いた記事がこちらとなります!

最小構成のスタックを作る

まず、WebとバックエンドのRedisのみのスタックを作成します。
Tutum公式のチュートリアルで作成するものとほとんど同じ。

Cloud Providerを追加する

せっかくなので、以前の記事でリンクしたAmazon Web Servicesに加えて、新たにDigital Oceanを使ってみます。

Link your Digital Ocean account

DigitalOceanとのリンクは説明する必要がないほど簡単でした。「Link account」ボタンを押して、開いたページからログインするだけ。

Nodeを追加する

DigitalOceanは「node_do」、AWSは「node_ec2」っていう名前でTutumのノードを作成します。

大したものは動かさないので、どちらも最小サイズのインスタンスで問題ありません。

Stackを作成する

スタックを作成して、こんな感じのStackfileを書きます。

stackfile.yml
redis:
  image: 'tutum/redis:latest'
  environment:
    - REDIS_PASS=password
web:
  image: 'honeniq/quickstart-ruby:latest'
  links:
    - redis
  ports:
    - '80:80'

全然やらなくても良かったことですが、Web用のコンテナとしてtutumcloud/quickstart-pythonを真似て、honeniq/quickstart-rubyを作ってみました。動きは全く同じ(はず)です。

動作確認

この時点で既に2つのノードにコンテナが1つずつ分散して動いているのが分かります。

スタックのEndpointsを覗いてみると、Webサーバーの表示が確認できました。

Endpointについて

Tutumからは、Service endpointsとContainer endpointsの2種類が公開されます。

Service endpoints

最終的にWebサービスとして公開するべきなのはこちら。
TutumがDNSラウンドロビンで負荷分散してくれます。

FQDNは サービス名 . スタック名 . ユーザー名 . svc .tutum.io の形式。

nslookupしてみると、こう。

Name:   web.quickstart-ruby.honeniq.svc.tutum.io
Address: 95.85.30.162

Container endpoints

各コンテナそのものにアクセスしたいときはこちら。
ポートを公開しているサービスのコンテナだけが表示されるので、 redis のコンテナは出てきません。

FQDNは コンテナ名 . スタック名 . ユーザー名 . cont .tutum.io の形式。

Name:   web-1.quickstart-ruby.honeniq.cont.tutum.io
Address: 95.85.30.162

今のところ、 web のコンテナはまだ1つしかないので、Service endpointもContainer endpointも同じIPアドレスを返しています。

コンテナを増やしてみる

ここで web のコンテナを2つに増やしてみます。
「Number of containers」を1から2に増やして「Apply」をクリック。

すぐに2つ目のコンテナが立ち上がります。

ノードの状態を確認してみたところ、web-1がnode_do、web-2がnode_ec2で実行されているようです。

TutumのDNSラウンドロビン

この状態でService endpointをnslookupしてみると・・・。

Name:   web.quickstart-ruby.honeniq.svc.tutum.io
Address: 95.85.30.162
Name:   web.quickstart-ruby.honeniq.svc.tutum.io
Address: 52.193.37.165

IPアドレスが2つ返ってくるようになりました。
もちろん、それぞれnode_doとnode_ec2のIPアドレスです。

3つには増やせない

勢いに乗って3つ目の web コンテナを作ろうとするとエラーになります。

ノードがあと1つ必要だけど、もう空きがないよ、と言ってますね。

これはStackfileでポートフォワードを設定していることが原因です。
各コンテナがノードのTCP80番を占有するので、1つのノードで2つ以降のコンテナを実行させることができません。

これを解消するには、もう一工夫が必要になります。

ロードバランサーを追加

web の上位にロードバランサーを追加して、そちらにTCP80番を持ってもらうように変更します。

HA Proxyを追加

Service dashboardで「Create service」をクリックして、新しいサービスを追加します。
イメージは[Jumpstarts]-[Proxies]にあるtutum/haproxyを選択します。

サービスの設定画面では、追加先のスタックを指定します。(今回は quickstart-ruby を指定。)
また、TCP80番を公開するように指定します。

こんな感じに設定して左下の「Next: environment variables」をクリック。

web とリンクさせる

「Link Services」に web を指定し、「+Add」を押して追加します。

こんな感じになっていればOK。
「Create and deploy」をクリックします。

まだ web がTCP80番を占有しているのでエラーが出ますが、順番的に仕方ないので無視して進めます。

web のポートフォワードを解除

web のService configurationを開いて、公開先のポートを「dynamic」に変更します。
(ノード側のポート番号を空にすれば「dynamnic」になります。)

すると、こんな感じで警告が出るはず。

実行中のコンテナには設定が反映されないので、それを警告してくれています。
「Redeploy」を押してコンテナを再起動・設定反映させます。

haproxy も再起動

web コンテナ達の状態をうまく拾ってもらうため、haproxy についても「Redeploy」を押して再起動しておきます。

動作確認

スタックのService endpointsを表示して動作を確認します。
(アクセス先のFQDNは haproxy.quickstart-ruby.honeniq.svc.tutum.io に変わっています。)

更新ボタンを押すと・・・。

地味ですが、アクセス先の web コンテナが切り替わりました。
haproxy がちゃんと分散してくれているようです。

3つ目のコンテナを動かしてみる

先ほど失敗していた web の3つ目を動かしてみます。
「Number of containers」を2から3に増やして「Apply」。

今度はエラーも出ずに3つ目が立ち上がりました。

動作確認

この状態で先ほど haproxy のendpointにアクセスしてみましたが、web-1とweb-2にしか分散してくれていませんでした。
なので、 haproxy をもう一度再起動します。

更新ボタン!

3つ目にも分散されるようになりました!

完成形のStackfile

参考までに、ここまで設定した状態のStackfileを載せておきます。
haproxy を手動で追加したせいかゴミが少し残っていたので、それは消しておきました。

stackfile.yml
haproxy:
  image: 'tutum/haproxy:latest'
  links:
    - web
  ports:
    - '80:80'
redis:
  image: 'tutum/redis:latest'
  environment:
    - REDIS_PASS=password
web:
  image: 'honeniq/quickstart-ruby:latest'
  links:
    - redis
  ports:
    - '80'
  target_num_containers: 3