heroku

2013年1月23日 (水)

HerokuのPostgreSQLをアップグレードする

こんにちは。
HerokuのPostgreSQLを9.2にアップグレードする作業を行ったのでその手順を残しておきます。

 

1、WebコンソールでアプリのオーナーアカウントのAPIキーを確認
有償Addonの追加を行うのでコマンドはアプリオーナーのアカウントで叩く必要があります。

 

2、コマンドラインでherokuアカウントの切り替え

 

set HEROKU_API_KEY=xxxxx

 

簡単なのでいつもこの方法でアカウントを切り替えてますが、ちゃんと切り替えようとするならコマンドプラグインの「heroku-accounts」を入れるのが良いらしいです。
この方法だとgitにはアクセスできないですしね。

 

3、メンテナンスモードにして外部からのアクセスを遮断

 

heroku maintenance:on --a my-app

 

4、PostgreSQLのデータバックアップ
Addonのpgbackupsを使ってデータをバックアップします。
pgbackupsは無償で毎日バックアップを取ってくれるのでPostgreSQLを使うなら必ず入れた方が良いです。

 

heroku pgbackups:capture -a my-app

バックアップを取り終わったら表示されたバックアップIDをメモしておきます。

 

5、既存のPostgreSQLを入れ替え
バックアップが終わったら既存のDBを破棄して9.2で新しいDBを作成します。

 

heroku addons:remove heroku-postgresql:crane -a my-app
heroku addons:add heroku-postgresql:crane --version=9.2 -a my-app

 

「--version=9.2」がPostgreSQLのバージョン指定です。
多分現時点ではまだ無指定の場合は9.1になると思うけど近日デフォルト9.2に変わるはずです。

DB作成時にforkオプションを指定するとを使うとDBの作成とデータの移行を同時にやってくれるはずですが、同一プランでのDBの追加ができるのかどうかがいまいち確信が持てなかったので先に既存DBを破棄してから追加しました。

DBの作成が完了するまでには数分かかるので

 

heroku pg -a my-app

 

でStatusがavailableになるのを待ちます。

 

6、データのリストアとDATABASE_URLの切り替え

 

heroku pgbackups:restore HEROKU_POSTGRESQL_XXXX_URL bxxx -a  my-app
heroku pg:promote HEROKU_POSTGRESQL_XXXX_URL -a  my-app

 

作成されたDBにバックアップをリストアします。
DATABASE_URLの切り替えは「heroku config:add」で行っても同じです。
(pg:promoteは内部的にheroku config:addを実行しているだけです。)

 

7、メンテナンスモード解除

 

heroku maintenance:off --a my-app

 

以上、動作確認して終わりです。
データ量にもよると思いますが概ね10分くらいで作業できます。

2013年1月18日 (金)

Herokuにやられた日

こんにちは。珍しくHerokuではまったのでその顛末を書いておきます。
しかも間抜けなことに同じ問題で2回はまりました。。。(--

さて、それがどういう問題だったかと言うと。。。

Procfileの行末がCRLFだとDynoが正しく起動できなくなる。

というものです。(--
これ、最初から動かなかったわけではなくて少なくとも12月の中旬ごろまでは間違いなく動いていました。
それが何かの修正のタイミングで行末がCRLFだった場合は最後のCRをコマンドの一部として実行するようになったようです。

最初から動いてなければそういうものとして納得することができるんですが、ある日突然動かなくなったので非常に焦りました。
現象の現れ方としては最初に書いたとおり2パターンありました。

 

★パターン1 - アプリが起動しない

あるアプリに1カ月ぶり位にちょっとした修正を加えてgit pushしたところいきなりアプリが起動しなくなりました。
何故かログには「NoClassDefFoundError: 」とか出力されて止まっています。
修正自体はささいなものだったので、それが原因とは考えられず最初は何が起こっているのかまったくわかりませんでした。

原因を追究するよりも先にまずはアプリを起動状態に戻さなければならなかったので修正をリバートしたものをgit pushしたんですが状況は改善しません。(--
なかば途方にくれながら今まで一度も使ったことがないコマンド「heroku releases:rollback」を使用して以前のバージョンに戻すとようやく起動するようになりました。

□□□□

話は少しそれますが、この「heroku releases:rollback」というコマンドはコンパイル済みのスラグと環境変数一式をどこかにとっておいてまるっと差し替えるコマンドのようです。
Git repository自体には手が入らないので次回にgit pushした時にはまた最新のrepositoryから新しいスラグが作成されます。(ただし環境変数はそのまま)

動作原理がわかっているといざという時に迷わず使えて便利そうではあります。抑えておいて損はないコマンドだと思います。
(途中にAddOnの追加があった場合はどうなるんだろうという疑問はありますけど。)

□□□□

さて、これでとりあえずアプリは動作するようになったわけですが、原因を調べようにもちょっとした変更をgit pushするだけでもれなくクラッシュするのでまたしても途方にくれてしまいます。(--
この時点でHerokuのサポートにあげても良かったんですが、もうちょっと自分で調べようと思い新しいHerokuアプリを作成してそっちにソース一式をpushして調査を継続することにしました。こういうことが手軽にできるのはHerokuの良いところではありますね。(と、フォローしてみる)

そんなこんなで紆余曲折、艱難辛苦の末にたどりついた原因がProcfileだったわけですが、これには本気で脱力しました。。。(--

その時のProcfileがこれ。

 

web: play run --http.port=$PORT $PLAY_OPTS <CRLF>

 

改行コードがCRLFであることと行末にスペースが入っていることがポイントです。
playコマンドの最後の引数として単独の「<CR>」が渡されていて、それがクラス名表示のないNoClassDefErrorを引き起こしていたらしい。。。。(--
マジでか!!!

HerokuがLinux上で動くプロダクトである以上、あらゆるファイルの改行コードはLFで統一すべきという指摘はもっともだと思うし、実際エディタの設定等は気をつけてはいるんですが、Procfileはすっかりechoとリダイレクトで作成する習慣がついていたので気が付かなかったんですよね。。。

先にも書きましたがこれが最初から起こっていた現象であるなら特に文句はありません。
しかし、ある日突然動かなくなったら「こんなの気づきっこねー」と愚痴のひとつも言いたくなるというものです。

結局この日はここまでで力尽きて帰りました。

★パターン2 - アプリの起動が重くなる

さて、今にして思えばこの時点ですべてのHerokuアプリのProcfileを見直しておけば良かったんですが、何しろその日はやろうと思っていたことが何一つできなかったので次の日はそれどころではありません。一応Herokuのサポートにレポートをあげたんですが、あっさりと「ごめん、直しとく」みたいな返答がきたこともあってこの問題については自分の中ですっかりかたがついてしまっていました。

そうこうしているうちに別のアプリでやけに起動に時間がかかるという問題に気が付きます。具体的には以下のような現象が発生していました。

  • git pushや環境変数の変更後ブラウザからの応答が長時間なくなる
  • ログを見るとR10(Boot timeout)がしばしば記録されている。つまりHerokuがDyno起動をリクエストした後1分間起動が完了せずに再起動を繰り返すことがある
  • たまに起動直後からR14(Memory quota exceeded)が記録されることがある。起動直後に大きなメモリを使用するような処理はないので明らかにおかしい

これも最初原因がわからなかったんですが、再起動時のログをtailで眺めていると常に「Precompiling...」というログを出力した後で一度ログの流れが止まることに気が付きました。あれ?起動時にPrecompileとかって走るんだっけ???

□□□□

で、調べてみるとPrecompileはPRODモードでかつ起動オプションに「-Dprecompiled=true」が指定されていない時に実行されることがわかりました。しかし、「-Dprecompiled=true」は環境変数のPLAY_OPTSで指定されているはず。
ということでもう一回Procfileです。

 

web: play run --http.port=$PORT $PLAY_OPTS<CRLF>

 

前のと同じに見えますか?(^^;
いえ、今回はCRLFの前にスペースがないことがポイントです。

環境変数「PLAY_OPTS」の末尾は「-Dprecompiled=true」となっていますが、これが行末のCRと結合して「-Dprecompiled=true<CR>」として引数化され、結果precompiled=trueとは解釈されないという。。。(--
またお前かよ!

実のところ2回目は問題解決にそれほど大きく時間を取られたわけではないのですが、脱力のあまりこのブログを書き始めてしまったので結局結構な時間が。。。。(--

まぁ別にいいんですけどね。(^^;;;

2012年12月18日 (火)

社内Heroku勉強会資料を公開しました

ご無沙汰です。

最近個別案件に貼りつき気味なのですっかりブログ書くのがとどこおっています。(ネタはなくはないんですが。。。)
今回もリンクのシェアだけの簡単な内容になります。(^^;;;

Heroku勉強会資料

社内勉強会向けに作成したHerokuの資料を公開したのでぜひご覧ください。
実際にHerokuで開発を行う場合に知っておいた方が良いことはだいたい書いたつもりです。

特に再起動周りは挙動をちゃんと把握しておかないとはまりがちなので厚めに書きました。

ご意見ご感想あればコメントくださいませ。間違いの指摘などツッコミ大歓迎です。(^^;;;

2012年11月 5日 (月)

HerokuのWorker再起動問題を考える

HerokuのWorker再起動問題を考える

こんにちは。

9月にこんな記事を書きました。

JavaでHerokuのWorkerを作成する

Heroku上のWebアプリをPlayframeworkで作るなら、WorkerもPlayで作るのが楽だよね!という話だったんですが。。。
実はこれ、ちょっと微妙な罠がありました。(--

 

★すべてのDynoは1日に一回再起動する

Herokuを運用しているとわりとすぐに気が付くと思いますが、WebDynoはだいたい24時間サイクルで再起動しています。
このあたりの挙動は以下のドキュメントに記載されています。

https://devcenter.heroku.com/articles/dynos#automatic-restarts

1日に一回サイクルすると書いてありますね。
僕はWebDynoについてはこの挙動はアリだと思っています。
WebDynoが仕事中であるか否かはRouterは把握しているはずなので、Routerが適切にWebリクエストをせきとめてくれるなら少なくとも原理上はリクエストの取りこぼしは発生しませんし、耐障害性を考えてもこのアーキテクチャは優れていると思うからです。

(prebootが早く標準になってほしいというのはありますけど。気になる人は「heroku labs」と叩いてみましょう。(^^v)

しかしWorkerDynoまで同じサイクルで再起動するのはどうなんでしょうね???
ちなみに再起動の方法は以下のドキュメント

https://devcenter.heroku.com/articles/ps#graceful-shutdown-with-sigterm

最初にSIGTERMを送って10秒待っても終了しなかった場合はSIGKILLで殺すっていう感じですね。
Workerが仕事中であるかどうかは外部からは知りようがないので夜間バッチなどの長時間かかるタスクを実行中であっても問答無用で再起動がかかりそうです。。。(--

 

★Workerの動作を観察する

んで、実際にWorker( on Playframework1.2.5)を数日間動かしてみて以下のような観測結果を得ました。

  • 再起動間隔は約24時間だが厳密ではなくかなり前後する
  • 予想通り長時間のJobを実行中であっても問答無用で再起動はかかる
  • SIGTERMはRuntime#addShutdownHookで捕まえることができるが、フックしたところでできることがほとんどない

さてさて困ったものです。(--
再起動間隔はざっと見た感じ24時間よりも若干長い傾向があるので仮に最初に午前中にWorkerを開始したとしても時間の経過とともに、夜間バッチの時間帯に再起動がかかるということはありえます。
ていうか、そもそも「Workerは夜間バッチの時間から遠く離れた時間に起動すること」みたいな謎の運用ルールはアウトでしょう。

つまりここまでの結論としては、Workerは

  • 実行に長時間かかる中断不能な処理
  • 処理が途中で中断された場合にやり直しのきかない処理

を扱うには不適ということになってしまいます。。。
マジでかーーー!!!(--

 

★Schedulerを併用する

さて。気を取り直して、どうにかしてこの問題に対処しなければならないわけですが、最初に考えたのはSchedulerでherokuコマンドを実行するというものです。
Jobを動かす時に「heroku ps:scale worker=1」として不要になったら「heroku ps:scale worker=0」とするという作戦です。
この方法にはWorkerのdyno時間を節約できるというメリットもあります。

Schedulerで実行できるコマンドは「heroku run bash」で確認できるのでとりあえず試してみます。

 

>heroku run bash
Running `bash` attached to terminal... up, run.1
~ $ heroku ps
bash: heroku: command not found
~ $

 

あ~。。。(--
heroku環境にherokuコマンドはないようです。。。。
時間帯によるWebDynoの増減とか割と用途はある気がするんだけどなぁ。。???(--

ちなみにJavaのHerokuAPIを入れるとそれはちゃんと動きました。
ひょっとするとSSHの鍵から入れないといけないんじゃないかと憂鬱だったけどそれは必要ないらしい。(gitは入っているので、そこで必要なんだと思う。)

でもAPIキーをどっかから取ってきて、HerokuAPIを実行するプログラムを書いて、とちょっと面倒くさい。。。(--

□□□□

次にやったのはplayコマンドをworkerとして起動していたframeworkIDを指定して実行するという方法。

 

play run --http.port=$PORT --%worker

 

当たり前だけどこっちはちゃんと動きます。
これでWorkerとして定義したJobは実行できるようになったけど、Schedulerは永続的なプロセスではないのでJobが終了したらPlayをシャットダウンするように修正しないといけません。もうこの際Jobが終了したらそこでstopしてしまえば良いでしょう。

実際にテストで使用したクラスはこれ

 

import play.jobs.Job;
import play.jobs.OnApplicationStart;
import play.Play;
import java.util.Date;

@OnApplicationStart
public class HerokuTest extends Job {
	
	public void doJob() {
		if ("worker".equals(Play.id)) {
			System.out.println("framework id: " + Play.id);
			System.out.println("Job start: " + new Date() + ", " + Play.id);
			try {
				Thread.sleep(5 * 60 * 1000);
			} catch (InterruptedException e) {
				System.out.println("Job Interrupted: " + new Date() + ", " + Play.id);
			}
			System.out.println("Job end: " + new Date() + ", " + Play.id);
			new Thread() {
				public void run() {
					Play.stop();
					System.exit(0);
				}
			}.start();
		}
	}

}

 

stopするのに新たにスレッドを作っているのはOnApplicationのJobはPlay#start内で動くのでそこを抜けてから終了したいからです。(Play#startとstopはいずれもsynchronizedメソッドなのでstartを抜けるまでstopはブロックします。)

スケジューラのドキュメントによると数分を越えるような長い処理はWorkerを使えとありますが、たいていの場合はこれで十分でしょう。
ていうかWorkerが使えないからこんなことやってるんだっちゅーの(--

 

★まとめ

herokuのWorkerは良い仕組みだと思いますが、ちょっと惜しい感じです。
多くの場合Workerでは再起動のタイミングまでコントロールしたい気がします。(HerokuAPIを使ってアプリの中から制御するということもできそうですけど)

あとSchedulerでherokuコマンドは是非使えるようになってほしい!
いつだかのミートアップであった夜間はDynoの数を減らしたいという要件はこれができれば解決という気がします。

2012年10月 4日 (木)

Herokuの全文検索事情

こんにちは。

Herokuで全文検索(当然日本語)を行う場合にどういう選択肢があるのか調査したのでそのレポートを書いてみます。

 

★評価のポイント

Herokuには複数の全文検索Addonがあり、またPostgreSQLにも全文検索の機能があるのですが今回評価のポイントとしたのは以下の2点です。

 

・日本語に対応しているか?

全文検索は英語などのヨーロッパ圏の言語と、日本語などのアジア圏の言語(CJK)では実装の難易度が大きくが異なります。

英語なんかは単語がスペースや改行などのホワイトスペースで区切られているので、単語の分割で悩む必要がないんですね。一方の日本語の文章は基本的に切れ目なく連続して記述されるのでどうにかして単語を分割しなければなりません。

この単語分割の方法には「形態素解析」と「N-gram」という2つの有力なロジックがあってそれぞれに特徴があるのですが、とりあえずそこはまぁどちらでも良いです。

問題なのは欧米圏で作成されている全文検索のサービスは往々にしてホワイトスペース区切りの英文分割にしか対応していないということです。(--
なので全文検索対応を謳っている製品であっても初手からアウトということは結構あります。

 

・セキュリティ

調査を始めてちょっとびっくりしたことなんですが、全文検索業界(?)のセキュリティ対策はとんでもなくザルです。(--

ほとんどすべてのサービスがREST形式のWebAPIで操作を行うんですが、驚いたことに何の制限もかかっていません。
つまりURLさえ知っていれば誰でもインデックスにアクセスできてしまう訳です。

検索APIがどこからでも使えるのはまぁ良いかと思えるんですが、更新APIまでアクセスフリーなのってアリっすか???

誰かが勝手にインデックスを更新したり削除したりできるサービスというのはちょっと怖くて使えないと思うんですけど。。。

 

以降この2点を中心に評価レポートを書いていきます。

 

PostgreSQLの全文検索

HerokuはPostgreSQLのバージョンアップにはかなり良いペースでついて行っていると思います。2012年10月現在HerokuのPostgreSQLのバージョンは9.1.6(9.1系の最新)です。
この9月には9.2が出たのでこれも近いうちに追随すると思われます。

なので、9.1で入ったN-gram対応の全文検索が使えるはずです。。。

。。。が!、現在この機能を日本語圏で使用する場合はソースの一部をコメントアウトしてリビルドしなければならないらしいです。(これもかなりビックリしましたが。。。)

configパラメータすら変更できないHerokuではまぁ無理ですね。。。(--

 

WebSolr

Solrベースの全文検索サービスです。
4月のHeroku Meetupの時に中の人が来て宣伝してました。(^^;

その時には「Solr3.6からKuromojiという形態素解析エンジンが使える。それを使えば日本語の形態素解析も使えるようになる。WebSolrはまだSolrのバージョンアップをしていないが近いうちにやるつもりだ」というようなことを話していたと思いますが、いまだにKuromojiは使えないようですね。。。
N-gramでの検索は問題なく通りました。

セキュリティは困ったことに検索、更新APIともにアクセスフリーです。(--
これについてはサポートフォーラムに質問があがっていて、今作っているところだという回答もありますが、どうなっていることやら。。。

なんとなくですがスタッフまで含めて数名でまわしている気配を感じるので、あんまり多くは期待できない気がします。(これは今回調査したHerokuのAddonベンダーすべてについて感じた感想でもあります。)

あとWebの管理コンソールが貧弱なのもなんとかしてほしい。
せめて検索のテストは管理コンソール上からできるようにしてほしいです。

スキーマの設定については一応Solrのschema.xmlを丸ごと貼り付けることができるのですが、今のUIのままだとKuromojiに対応してもユーザー辞書の設定やStopWordsの設定とかができない気がします。

 

Searchfy Hosted Search

Index TankというサービスをラップしたAddonです。
これもSolrのようです。

日本語対応はおそらくN-gram。管理コンソールで検索のテストもできますが、設定可能な項目はWebSolrよりも少ないですね。

そしてこれも検索、更新ともにアクセスフリーです。。。(--

どちらかというとまだWebSolrの方が良いですかね。

 

Flying Sphinx

SphinxはPythonで作られた全文検索エンジンらしいです。
これだけ実際には試しておらずドキュメントを読んだのと日本語の対応状況を検索しただけです。

日本語の対応方法はいくつかのサイトで解説されていますが、ソースを変更しないといけないのでHerokuのアドオンでは無理なんじゃないかと思っています。(N-gramには標準で対応しているかもしれませんけど。。。)

APIアクセスにはヘッダにAPIキーを含める必要がありますが、NonceやHashとの組み合わせではないので、URLさえ知っていればアクセスできるというのとあんまり変わらないような気もしています。

試さなかったのはJava用のクライアントライブラリが用意されておらず、自分でREST APIのラッパーを書くのが面倒だったからですが他に良いのがなければまた戻ってきます。

 

Amazon Cloud Search

HerokuのAddonではないですが、AWSにもCloud Search(Beta)という全文検索のサービスがあったので試してみました。

が、日本語対応されておらずあえなく終了。。。(--

APIアクセスに関してはアクセス可能なIPアドレスの範囲を制限できるのですが、Herokuから使う場合Dynoが再起動する度にIPが変わるので使えない気がします。

ちなみに検索をかけていたら自力で日本語を分割して使用するというツワモノがいました。

自力でのワード分割は考えたことありませんでしたがアリかもしれません。

 

★自力でEC2にSolrを立てる

思考実験です。

Solrのサーバーを自分で立てるのは難しくありません。
EC2に入れてWebAPIとしてアクセスできるようにするまではすぐだと思います。

この場合、日本語対応は自力で形態素解析でもN-gramでもどちらでも自由に設定できます。

Securityに関してもServletFilterでヘッダをチェックするロジックを入れれば良いだけなので多分どうにでもなる。

ただ障害に備えて冗長化構成を取ることを考えた場合にはどうするのが良いのか迷いますね。。。
マスタ1台とスレーブ2台を用意して参照系はELBを通して常にスレーブのみを参照するようにするのが楽かな?

まぁいずれにしても最後の手段ですね。

 

★PostgreSQLに自力で分割したカラムを追加

思考実験その2です。

AWSのツワモノ記事を見て思いついたんですが、クライアント側でKuromojiを使用して分割した文字列を別カラムに登録するのはアリですね。
クエリー文字列も同様にKuromoji分割すれば複数単語の組み合わせにも対応できます。

形態素解析を行うのはクライアント側のプログラムなので辞書やStopWordの設定も自由自在です。

スコアやファセットが必要ないならこれで十分な気もします。

 

★まとめ

どんなもんでしょうか?(^^;

残念ながら現状では日本語をちゃんと扱える全文検索のサービスはどれもイマイチというのが今回の結論です。

セキュリティに目を瞑ればWebSolrも悪くはないんですが、普通はアウトなんじゃないでしょうか。

正直Solrを使えばWebSolr相当のものを作るのはそれほど難しくないと思うので、日本発のAddonの候補としては悪くないと思いますよ。 > 誰?(^^;

2012年9月14日 (金)

Heroku Meetup #6レポート その2

2日続けてこんにちは。Heroku Meetup #6レポの続きです。

今回はCodeConsultingにおける質疑とその後の懇親会で聞いた内容についてのレポートですが、これは小西が直接聞き取れた内容と通訳の方の翻訳を脳内補完した超訳です。
事前にある程度の回答予測をした上で質疑に臨んでいたこともあって、書きながらどこまでが実際にHeroku技術者の言葉でどこからが自分の推測なのかよくわからない部分があります。(^^;;;

なので、そんなこと言ってねーよ!と言う部分があれば是非ご指摘ください。m(_ _)m

 

★Scalabilityに関する質問

高負荷なアプリケーションを作成する際に注意する点としては何を考慮するべきだろう?ということを考えながら用意した質問です。

Q. DBのconfigを変更することはできないか?

A. できない

Q. DATABASE_URLは何に使われているか?また削除しても問題ないか?

A.
Rubyアプリではデフォルトで接続するDBの定義として参照する
Dyno数に応じてコネクションが増える件については詳しく調査していないが、削除しても問題はないはず。

Q. Routerの限界を突破する方法はあるか?

A. 何言ってましたっけ???
リクエストがキューにたまってそれが順番に処理されていくというRouterの基本的なアーキテクチャの説明とアプリがレスポンスを30秒間返してこない場合はRouterが503を返す(この場合はH12のエラーログが出ます)と言った説明がされていたことは覚えています。
ログがでないケースについても言及されていた気がしますが、ちょっと忘れてしまいました。

Q. 複数のDB接続をDynoごとに振り分ける方法はあるか?

例えばセッションIDでDynoを振り分ける(いわゆるSticky Session)や、EvernoteのようにURLの一部にShardingIDを含めるなどの方法が考えられると思うが何か良い方法はないか?

A.
Sticky Sessionはやらない。
Evernote的なShardingIDの可能性については。。。何か方法を検討してみるよ。的な回答だったと思うがいまいち自信がない。

Q. 高負荷のアプリがある場合Herroku内の他のアプリやネットワークにどのような影響があるか?

A.
CPUやメモリに関してはDyno毎に振り分けられているので影響はないはず。ネットワークについては何かしらの影響があるかもしれない。
しかしHeroku上では毎秒15000リクエストをさばくような高負荷のアプリも存在するが特に問題は起きていない。


全体的にはだいたい予想通りの回答です。
DevCenterでもかなりの情報が公開されていますが、アーキテクチャに関してもシンプルで特にトリッキーなことをしていないというのが個人的なHerokuの印象です。(なので、こうした場合どうなるかという挙動の予測がしやすい。)

Routerに関しては終わってから調べたことも少しあるので後で補足します。

 

★Securityに関する質問

Q. Heroku内部での通信の安全性はどのように担保されているのか?

負荷テストの過程ではアプリを分割することやPostgreSQLをやめてRDSやNoSQL系のDBを使うこともなんとなく考えていました。
ただその場合それぞれのコンポーネント間の通信は全部SSLにしないといけないのがコスト高そうだなぁとか考えてふとHeroku内部の通信はどうなっているんだということを考えたのがこの質問の背景です。

A.
Routerから各Dynosへの通信はhttp。Firewallの中での通信なので問題ない。
ログサーバーとの通信もFirewallの中。特に暗号化はしていない。

PostgreSQLはFirewallの外。機密性の高い情報であるならSSLを使うべき。
Rubyの場合はデフォルトでSSLを使用する設定になっている。

Addonはそれぞれだが考えたかとしては暗号化が必要。
Herokuアプリから他のHerokuアプリをコールする際のSSLの必要性についてはアプリ毎に判断してください。


うーん(--
PostgreSQLはHeroku謹製だからFirewallの中だと思ってたよ。。。
よくよく考えると同じURLでクライアントPCからも接続できているのでそんなはずはないんだけど。

で、あらためてDevCenterのドキュメントを見に行ってみると、やっぱりRemoteから接続する場合以外はSSL不要みたいな書き方になってんじゃん!!!

この際Herokuアプリから接続する際にもSSL必須でも良い気がするんだけどどうなんだろう?
パフォーマンスはまだしも言語によってはSSL用のドライバがなかったりするのかも。

しかし、これPostgreSQLの方ではどうやってHerokuアプリとそうでないものを見分けてるんだろう???(IPの範囲とか?)

 

★番外

他社からの質問でHireFireというDynoを自動で上げ下げするツールが紹介されました。
これはレスポンスタイムを計測してプロセスを管理しているということだったので、どこでレスポンスタイムを計っているんだろう?と聞いてみたところ。。。

「多分Gem。RouterからforwardしたHttpリクエストのヘッダにはRouterがリクエストをアクセプトした時間が書かれているから、それから計算しているんだと思う。」(超訳)

みたいな回答。え?!それじゃRubyでしか動かないじゃん!と思ってサイト見に行くと本当にRubyでしか動かないみたいね。。。。(^^;;;

こういうのがアリならPlayPluginをAddOnとして作成するのもアリかも。(嬉しい人がいるかどうかは謎だけど。。。)

 

★補足1 - ルーターのIPアドレス

今回行った負荷テストはほとんど「https://xxx.herokuapp.com/....」というアドレスに対して行いました。

本番はもちろん正規のSSL証明書をインストールした独自ドメインです。やったことがある方はご存知かと思いますが、SSL証明書をインストールすると「xxx.herokussl.com」という新たなルーターホストが作成されます。

で、これらのホストについてnslookupでIPアドレスを引くと。。。

  • xxx.herokuapp.com - 1個のアドレス
  • xxx.herokussl.com - 3個のアドレス

が返ってきます。

ドキュメントには書かれてませんが、SSL-Endpointアドオンではルーターも冗長化されてるってことですかね。

今回のテスト(herokuapp.com)では1万スレッドからの同時アクセスでルーターがパンクしましたが、herokussl.comの方を使用すればもっと多くのアクセスに耐えたのかもしれません。

 

★補足2 - StressToolのhttps

StressTool自体もPlayで作っており、そこからのHttpsリクエスト送信にはPlayのWSクラスを使用しています。

テスト中はあまり気にしてませんでしたが、この時のSSLハンドシェイクが何回行われているかをソースコードで追ったところ。。。。どうもKeep-Aliveが効いているっぽい。(多分)

つまり各スレッドで最初の1回だけハンドシェイクが行われて後は同じコネクションを利用してリクエストを投げ続けていたと思われます。

これだとテストが実運用時のリクエストを忠実に再現していないという意味でよろしくないです。(実際にはハンドシェイクがもっとたくさん行われてルーターの負荷があがると思われる。)

しかし今回の場合は性能指標がDBのInsert件数だったので、まぁ結果オーライです。(^^;;;

逆にハンドシェイクを抜かせばSSLはそんなに重くないという傍証になっているかもしれません。(この辺知識先行で経験則がないです。。。)

ちなみにHerokuのアクセスログに記録される「service=XXms」という時間はアプリでの消費時間のみを表しているらしいので、ハンドシェイクを含むルーターでの消費時間は多分含まれていません。

2012年9月13日 (木)

Heroku Meetup #6レポート その1

こんにちは。Heroku Meetup #6に参加してきました。

今回私はCode Consultingのプレゼンターの一人として発表させてもらいましたが(自分にとって)非常に有益な時間だったと感じています。
貴重な機会を与えてくださったHeroku社の方々に感謝します。

さて、そんな私のプレゼンですが後で懇親会で何人かの方から感想を伺ったところによると途中からよくわからなくなったという人多数。(--

あいすいません。m(_ _)m
昔から聴き手を置き去りにするプレゼンには定評があるんですが、今回は自分でも詰め込みすぎだったと思います。
また時間と同時通訳を考慮してカットした話も多いです。

なので、ここで完全版(?)の説明をお届けしたいと思います。当日使った資料は一応ここにありますが別に見なくても良いです。(^^;;;

 

★単純明快なシステム概要

まずネタにしたアプリですが、HTMLの装飾とかをとっぱらってシステムの骨子だけをとりだせばWEBフォームに入力するデータをDBにINSERTするだけという超単純なモノです。
例によってPlayframeworkで作りましたがサーバー側のコード自体は誰が作ってもこうなるだろう、というものでしたし別の言語を使っても似たり寄ったりだったでしょう。

はっきり言ってこれだけだとまったく箸にも棒にもかからないシロモノなわけですが、これがちょっと他と違っていたのは、そのサイトが毎年夏にやっている超有名テレビ番組から宣伝されるということです。

なので、URLがテレビに流れた直後に嘘みたいなアクセスが集中します。

 

★冗談みたいな性能要件

さて、このシステムをHerokuで実装するという前提で話を聞いたわけですが、割と最初の段階からボトルネックとなるのはDBのトランザクションだろうという予測がありました。

なので、「1秒に何件くらいINSERTできれば良いんですかね?」と聞いたところ最初の段階では900件という回答でした。
多めに見積もって1000件/秒と考えましたが、この時点ではそれ位はいくんじゃないかと思ってました。(以前に単純なテストでRonin * 2Dynosで200件/秒の登録ができることを確認していました)

。。。が、この数字が何故か数日後には5000件/秒になります。。。(--

どうもこの数字がユーザー数やPVと混同されたらしく、仮にPVがそれだけあってもトランザクション数はそこまでにはならない、ということをいくら説明してもとりあってもらえなかったようなんですね。

あとで別のところから聞いた話によるとテレビでURLが流れた場合、かなり高い確率で503 Service unavailabe(要するに過負荷でサーバーに接続できない)が返ってきてがっかり!となるため性能要件には神経質になっているということもあるようです。

なので結局僕のところでは秒5000でやってくれという話でおりてきました。。。(--
ちなみにこの時点で放送日まで2週間きってます。

最初から5000って言われてたらRDBは使わねーよ(--

 

★PostgreSQLを落とす

そんなこんなで負荷テストを開始します。
幸いなことに今回クライアントとHerokuとの契約はミートアップでも紹介されていたパッケージのさらに上位グレードの契約だったのでDynoもAddonもほぼ使い放題です。

そこでまずはPostgreSQLの限界を見極めることからテストを開始することにしました。
DBはそれほどキャッシュが必要なシステムとも思わなかったのでなんとなくIkaを選択します。

Heroku上ではPostgreSQLのconfigがMAX_CONNECTIONS=500で設定されていることはあらかじめ調べて知っていたのでとりあえずはこれを突破した時にどのような挙動になるかを確認することしにしました。
予算には余裕があるのでDynoは100台あげます。(^^;;;
Playframeworkの設定でスレッド数は50。コネクションプールは30です。

テストツールとしてはJMeterを使用して1000スレッドで100回ループさせました。(合計10万件のINSERT)

テスト条件 DB Dyno ServerThread ConnectionPool Client ClientThread LoopCount
Ika 100 50 30 JMeter 1000 100
結果 ConnectionError多数

予想通りの結果ですがDriverManager#getConnectionでエラーとなります。

HerokuはWebシステムとしてはDynoを増やすことで自在にScaleしますが、DBはScaleしないので単純にDynoだけ増やしても意味がないってことですね。

PostgreSQLのconfigは一切変更不可能なので500しかないコネクションをいかに有効に使うかが勝負になります。

そこで次にDynoの数を15まで減らしてみます。Playのスレッド数とコネクションプールは変更していないので最大コネクション数は「15 * 30 = 450」となるはずです。

テスト条件 DB Dyno ServerThread ConnectionPool Client ClientThread LoopCount
Ika 15 50 30 JMeter 1000 100
結果 250件/秒くらい

思ったほど数字伸びてないですね。(--;;;

 

★Heroku上にStressToolを実装

この時点では負荷テストのクライアントツールにローカルPC上のJMeterを使用していましたが、NewRelicで見る限りサーバー側の負荷がそれほどあがっているようには見えませんでした。にも関わらず設定をどのように変えてもあまり数字は伸びてません。

これには二つの理由が考えられます。

ネットワークレイテンシの問題

Herokuはアメリカ東海岸にあるので通信には毎回太平洋越えのネットワークレイテンシが発生します。レイテンシが平均100ms前後だとするとクライアント側で多数のスレッドがぐるぐるとループしていても各スレッドは最高でも100msに1回しかリクエストを発行していない計算になります。

クライアントリソースの問題

JMeterで1000スレッドを指定したとしてもクライアントPCの性能がそれに追いついていなければ実際に1000スレッド同時には動けません。
実際JMeterのログを検証しても早いスレッドはテストが半分完了する以前に終了していますし、最後の方は最初の競争に負けたスレッドがまとめて動いているような感じでした。
また、1000スレッド以上の負荷、例えば5000スレッド同時に動かそうとしてもクライアント側のソケット不足でまともに動かないという問題もあります。

要するにクライアント側の問題で実はHerokuにはたいして負荷がかかっていないんじゃないかというのがこの時点での推測です。

なので、ネットワークレイテンシのない東海岸からテストを実行するためにHeroku上にStressToolを作成することにしました。
ツール自体はURLをキックしたら指定個数のスレッドを起こして指定回数ループするだけのごく単純なモノです。

これをDyno100台あげて、100回キックすればHerokuルーターのラウンドロビンによっていい感じにクライアントの負荷が分散されるはずです。(^^;;;

テスト条件 DB Dyno ServerThread ConnectionPool Client ClientThread LoopCount
Ika 14 50 30 Heroku 5000 100
結果 1000件/秒くらい

とりあえず当初の目標は達成しましたね。(^^v

 

★Herokuのルーターを落とす

凶悪なStressToolを手に入れたので今度はHerokuのルーターがどこまでの負荷に耐えられるのか実験してみます。クライアントスレッド数を一気に倍の10000スレッドにしてみました。

テスト条件 DB Dyno ServerThread ConnectionPool Client ClientThread LoopCount
Ika 14 50 30 Heroku 10000 100
結果 503と接続エラー多数

ルーターのキューがあふれた場合はHerokuはログも吐かずに503を返すようです。
さらに負荷がかかった場合は多分接続エラーになっていると思います。(この辺はStressToolの作りが雑だったため正確には検証できてません。。。)

Webサーバーの動作としては割と一般的だと思いますが、503を返す場合はサマリだけでもログを出力してほしいですね。

ちなみにあとでログを調べたところ5000スレッドでもログサーバーは時々限界を越えているらしくところどころで「L10 - Drain buffer overflow」が記録されています。
最悪入力データをJSON化してログに書くだけにしようかとも思ったんですが、その案も没です。(そうは言っても一番多いところで秒間26000行以上のログが出力されてます。(^^;;;)

あまりに高負荷をかけるとHeroku社に怒られそうな気がするので1万スレッドでのテストは1度しかやっていません。(^^;;

 

★3DBでシャーディング

その後いろいろと設定を変えてテストしてみましたが、どうやっても数字はだいたい1000前後で止まります。このことから、これはもうDBがいっぱいいっぱいなんだろうと思いました。だけどDBのconfigは変えられないんですよねぇ。。。(--

そこで1DBをあきらめて複数のDBを使用して負荷分散させることを考えました。具体的には入力データの必須項目であるメールアドレスからハッシュをとってその値で使用するDBを切り替えます。
何故メアドのハッシュを使用するかというと要件の中にメアドの重複チェックだけは入っていたからです。

コネクションプールはPlayの標準のものが使えないので自前で実装。サイクリックにコネクションを使いまわしてオープンからある程度時間のたったものは破棄する単純な実装です。

DBは本当は同じグレードのものを使用するのが良いんですが、わざわざそのためだけにアプリを作るのも嫌だったので同一アプリ内でRoninとFuguを追加しました。

テスト条件 DB Dyno ServerThread ConnectionPool Client ClientThread LoopCount
Ika, Fugu, Ronin 14 50 30 * 3 Heroku 5000 100
結果 Out of memory
パラメータ調整しても900件/秒くらい

まったくもっていまいちです。。。(--

まず、最初に1DBの時と同じ設定ということで50スレッドと30のコネクションプールで動作させましたが、これはOutOfMemoryとなるDynoがいくつかでました。
実際にいくつのコネクションプールが作られたかはわかりませんが、最大で90個のコネクションがメモリに保持される計算になるので。
Herokuの各Dynoに割り当てられたメモリは512MB。けっして多くはありません。

そこでスレッド数やコネクションプール数を調整しながら何回か実行しましたが、結果は一番良い時で926件/秒。スループットは上がるどころか落ちてます。(--

正直この結果は予想外でしたが、結果から中で起こっていることを推測すると以下のようになります。

  1. 各Dynoは1DBの時と同じくらいの仕事量をこなしている
  2. 各DBの仕事量は分散によって3分の1に減っている
  3. 各DBの接続数(各Dynoでのコネクションプール数)は1DBの時と同じか少ないはず
  4. DBの同時トランザクション数は約3分の1のはず
  5. にも関わらず各トランザクションのレスポンスタイムはあまり変わっていない???
  6. スループット低下の原因はRonin, FuguがIkaよりもスペックが低いためと推測

いまいち5番目のレスポンスタイムの項が納得いかないですが、手持ちのデータから結果を無理やり説明しようとするとこうなります。

(他に何か説明可能な推測がありますかね?ちなみに自前のコネクションプールは1DBで使用した場合にPlay標準のコネクションプールを使用した場合よりも若干パフォーマンスが良いくらいなので多分問題ないです。)

エンジニアとして原因を追究したいという気持ちはあるんですが、これを検証しようとするのは結構難儀な作業です。
なんせ負荷テストをやっていた期間のログはほとんど5GB超。Papertrailでログのアーカイブはとっていましたが、翌日にならないとログを確認できないしそれをまともに開けるエディタもないという状況だったので。。。時間的にちと厳しい(--

まぁそれは置いておくとしても先のテストと併せて考えるとIkaの処理能力を使いきってないのでアプリの改修でスループットの向上が見込めることは間違いありません。
ただ、それも各DynoがすべてのDBに接続する可能性があるという条件の下では実装が難しくなる気がしました。
Herokuルーターが同一セッションのリクエストをどのDynoに飛ばすかは制御できないので、1Dynoが1DBにのみ接続するようなアーキテクチャもプラットフォーム側になんらかの手を入れない限り難しそうです。(今回のケースではDynoのIPアドレスからハッシュをとって接続DBを切り替えて後から重複チェックをするというのもギリギリありではあったんですが。)

先のOutOfMemoryの件もあるのでこの路線にはあまり未来が感じられずこの段階で一旦没としました。

 

★DATABASE_URLの削除

少し話はそれますが、この頃までにどうもConnectionErrorの発生する閾値が自分の予想よりも低いことに気が付いていました。
設定上で420コネクションしか使わないように調整してもConnectionErrorが発生することがあるのです。
PostgreSQL自体がいくらかのコネクションを予約していて500個全部が使えないのはわかるのですがなんか妙に少ない。

そこでDyno起動直後のセッション数をpg_stat_activity表から数えてみると、明らかに自アプリからの接続ではないコネクションが多数存在します。
それもDyno数に比例して増減しているようでした。

つまりパフォーマンスを求めてDyno数を増やせば増やすほど利用可能なコネクション数が減っていくという悪循環があったわけです。

これは要するにHeroku自体が裏でDBに接続して何かやっているということなんでしょうが、何をやっているのかは謎です。

試しに環境変数からDATABASE_URLを削除してみたところ謎のコネクションはなくなり、特にアプリの動作にも影響がなかったので結局削除したままで本番まで運用しました。

これで利用可能なコネクションが増えてConnectionErrorはでなくなりましたが、スループットの方にはそれほど極端な差は見られませんでした。

 

★Mecha出陣!

さて、もういい加減手詰まりな感があったんですがここで頼れるCTOから貴重なアドバイスが。

 

「EC2のラインアップを見ればPostgreSQLの各Planの土台となっているインスタンスはほぼ推測できる。
PlanによってCPUスペックがまったく違うからそれ相応のパフォーマンスアップが期待できるはず」

 

言われてEC2のインスタンスタイプのページを見に行くと確かにどこかで見たような数字が並んでいます。

これを見るとIkaの土台はおそらくラージインスタンス(4ECU)。
対して最上級プランのMechaの土台はハイメモリクアドラプルエクストララージインスタンス(26ECU)。
なんじゃ、そら。

Mechaは1ヵ月使うとさすがに予算オーバーなんですが、今回期間が短いので日割りにするとぎりぎりパッケージ予算内に収まります。
試しに実行してみた結果がこれ。

テスト条件 DB Dyno ServerThread ConnectionPool Client ClientThread LoopCount
Mecha 15 50 30 Heroku 5000 100
結果 4189件/秒

なんといきなりの4000件超!!!
なんじゃ、そら。

結局何度かテストを実施したところ一番良い時で4813件/秒まで行きました。 この数字はテスト全体でのスループットなので一番負荷の高いところから1秒を抜き出せば5000件/秒をクリアしています。

長々と書いてきて最終的な結論がパフォーマンスは金で買えということかと思うとかなりがっかりですが、まぁとりあえずまさかのミッションコンプリートです。

それにしてもCPUスペックは重要ですね。Heroku PostgresのページにはPlanの違いはCache sizeの違いとしか説明されてませんがCPUスペックも併記してほしいものです。

 

★放送当日

テレビの影響力を体感しましたがそれでも楽勝でした。(^^;;;

 

★質疑応答

このあとようやくメインイベントの質疑応答ですが、長くなったのでまた今度(^^;;;

2012年9月 7日 (金)

JavaでHerokuのWorkerを作成する

こんにちは。

このブログで何度も取り上げていることからもわかると思いますが、FLECTでHerokuアプリを作成する場合の第一選択は Playframework 1.2.xです。

これは当社にJavaの技術者が多いことが最大の理由ですが、Playの生産性は非常に高くパフォーマンスも良いので Webアプリを作る分には何の問題もありません。

その一方でHerokuのWorkerをJavaで作る場合はどうするのが良いんだろう?ということがずっと自分の中で課題として残っていました。
自前でmainを持つサーバーアプリケーションを作ってしまえば良いんですけど毎回それはさすがに面倒ですもんね。

そんな折ちょっとした示唆があってWorkerもWebアプリと一緒にPlayで作っちゃえば良いじゃん!ということに気がつきました。

以下はその方法です。

 

★PlayでJobを作成する

Playframeworkを使ったことのある人ならご存知かとは思いますが、PlayにはJobという強力なスケジューリング機構があります。

  • 何秒おき、何時間おきといった指定間隔でのインターバル実行
  • 毎日何時に実行

といったスケジューリングがAnnotationの宣言だけでできるのです。

この機能を使えばWebDynoを作る際にバックエンドJobも同時に組み込むことができますが、実のところそれはあんまりうれしくありません。

何故ならWebDynoは負荷によってスケールさせたいので何台起動するかがわからないからです。WebDynoが5台あがっていたらバックエンドJobも5回動作することになりますが、多くの場合これは意図するところではないでしょう。

なので定義したJobはWebDynoでは動かしたくありません。あくまでWorkerとしてだけ動かしたいわけです。

実はこれ簡単にできます。
以下にサンプルを示します。

 

import play.Play;
import play.jobs.Job;
import play.jobs.Every;
import java.util.Date;

//@Every("10s")
@Every("${jobtest.every}")
public class JobTest extends Job {
	
	public void doJob() {
		System.out.println("Job: " + new Date() + ", " + Play.id);
	}

}

 

上記は日付とPlayのフレームワークIDをコンソールに出力するだけの単純なJobです。
ポイントは@Everyの宣言でパラメータを使用していることです。

@Everyまたは@Onの引数に「${xxx}」というパラメータを使用した場合、その値はapplication.confから読み込まれます。

 

★application.confでスケジュールを定義

あとはフレームワークIDごとのスケジュールをapplication.confに追加してやればOKです。

デフォルトの設定で使っている場合は、

  • ローカル開発環境ではフレームワークIDなし
  • 本番(Heroku)環境では「%prod」

としている人が多いと思いますが、その場合の設定は以下のようになります。

 

jobtest.every=10s
%prod.jobtest.every=never
%worker.jobtest.every=5s
%worker.application.mode=prod

 

「%prod」「%worker」はそれぞれWebDyno用、WorkerDyno用のフレームワークIDです。
application.confでは同じキーの設定が複数回出現した場合は後に記述されたものが有効になるので、それぞれの環境毎の設定が行えます。
設定値を「never」とした場合そのJobは実行されなくなります。

最後の「%worker.application.mode=prod」はWorkerDynoでのPlayをProductionモードで動作させるための設定です。
DEVモードでは最初のHTTPリクエストが届くまでPlayは動作しないので、これがないとWorkerDynoはうまく動きません。

 

★Procfileの作成

最後にWorkerでもPlayを動かすようにProcfileを定義します。

 

web: play run --http.port=$PORT $PLAY_OPTS
worker: play run --http.port=$PORT --%worker

 

上の例ではWebDynoのフレームワークIDはHeroku環境変数「PLAY_OPTS」で、WorkerDynoのフレームワークIDは直接指定していますがこの辺はお好きなように。

WorkerDynoでもportにbindしてHTTPリクエストを待ちうけていますが、HerokuのルーターはWebDynoにしかリクエストをforwardしないのでここにHTTPリクエストが来ることはありません。

 

★メリット、デメリットを考える

いかがでしょう?(^^;

この方式ではWebDynoとWorkerDynoを一つのアプリケーション内で同時に作成できるのが最大のメリットです。
ローカル環境で開発/テスト中はWebとWorkerを1プロセスの中で同時に動かすことができます。

WebDynoでは使用しないJobのインスタンスがひとつ余計に作られることになりますが、その程度ではアプリの動作に何の影響もありません。

WorkerDynoはまったく使用しないWebアプリをまるまる抱えることになり、それはまぁちょっと気になるところではありますが。。。。
おそらくWorkerDynoのスペックもWebDynoと同等と思われるので気にすることはないでしょう。

WorkerDynoが待ちうけているportは多分外からアクセスできないと思うのでこれも問題ないはず。
ここにリクエストが飛んでくるようならそれはHerokuのセキュリティホールです。(^^;

Heroku上にステージング環境を持つ場合でもフレームワークIDでどうにでも対応できるので柔軟性にも問題なし。

他に何か考慮すべきことあるかな???

ん~、圧倒的にメリットの方が多い気がします。
こうして考えるとPlayでHerokuアプリを作る場合Workerはこれがスタンダードでも良い気がしますね。(^^v

2012年7月20日 (金)

Herokuの負荷テスト

こんにちは。小西です。
Herokuの負荷テストの結果を公開したので興味のある方はご覧ください。

http://excelnote.herokuapp.com/share/note/s91/90bf7b19-d654-40ab-a6c0-640980967ab0/edf9ab2230042e456ca286843763788e

考察は自分用のメモなので文章が荒いのは気にしないように。(Evernoteだし)

えぇ、このエントリの主目的はきっぱりはっきりDEVCUPの宣伝です。(^^;
ちょっとでも役に立ったという方は是非ExcelNoteに一票投じてください。m(_ _)m

ExcelNoteにはシートの一部のみをiframeで表示する機能もあるので、表の部分だけをこちらのブログに貼り付けようかとも思ったんですが、表が少し幅があるためブログにきれいに収まらないことからこういう形をとりました。

背景の白い部分がExcelで作成した資料なわけですがコメントがつけられたりリンクが貼れたりするのは地味に便利です。
ちなみにExcelNoteのサンプル一覧はこちら

https://www.evernote.com/shard/s91/sh/ff42fdc3-a143-4019-a1f6-9828fb03faa9/3f441690243667851b8711e2e82939a3

一般投票期間中はできるだけサンプルを増やそうかと思ってるですが、普段Excelをほとんど使わないのでネタがなくて困ってます。(--

さて、これだけでは何なので負荷テストの結果を踏まえてHerokuとPlay(1系)でアプリを作成する際のポイントを簡単にまとめておきます。

・Memcachedは必須
dynoを増やすとリクエストはラウンドロビンで各dynoに割り振られます。
セッションは考慮されないのでセッション変数的なものを使用する場合はMemcacheが必須になります。
(Playのsessionの実体はCookieなので文字列しか保存できないしセキュリティ的に使えないことも多い)
ちなみにHerokuのアドオンとしてはMemcacheとMemcachierの2つがありますが、無料枠が大きいのでMemcachierの方がお得です。

・パフォーマンスは金で買える
単純なラウンドロビンなので基本的にはDynoを増やせばそれだけで負荷分散できます。
もちろんアプリの中でstaticな変数を使ってはいけないなどスケールさせるために考慮しなければならない事項はありますが、それ以外はアクセス数やメモリ使用量に関してそれ程シビアに考える必要はなさそうです。

・負荷はNewRelicで計測
Web transactions -> Slowest average response time
Dynos -> Average memory usage per dyno

あたりを眺めていれば、なんとなくDynoの増減のタイミングはつかめそうです。

・ログはPaperTrailがお勧め
Herokuのログ管理アドオンはやたらとたくさんあって、いくつか試してみたんですが個人的にはPaperTrailがダントツでお勧めです。
WebUIも見やすいですし、APIでログを自由に取得できるのも良いです。
Herokuのアクセスログには処理時間やDyno番号など結構な情報量があるので、いずれはこのログを解析して負荷や傾向を分析するツールも作成しようと思っています。
あと地味に日時表示のタイムゾーンを変換してくれるのもありがたいです。

・application.confに「XForwardedSupport=all」を必ず設定する
Herokuへのアクセスがhttpsであっても、そこから各dynoへのリクエストはhttpで行われます。
そのため、この設定がない場合リダイレクト時に元がhttpsであってもhttpにリダイレクトされてしまいます。
これは知らないと気がつきにくいので注意が必要です。

とりあえずはこんなところですかね。
他にもいろいろあるんですが、それはまたおいおい。(^^;

採用情報

株式会社フレクトでは、事業拡大のため、
Salesforce/Force.comのアプリケーション
開発
HerokuやAWSなどのクラウドプラッ
トフォーム上でのWebアプリケーション開発

エンジニア、マネージャーを募集中です。

未経験でも、これからクラウドをやってみた
い方、是非ご応募下さい。

フレクト採用ページへ

会社紹介

株式会社フレクトは、
認定コンサルタント
認定上級デベロッパー
認定デベロッパー
が在籍している、
セールスフォースパートナーです。
heroku partnersにも登録されています。
herokuパートナー
株式会社フレクトのSalesforce/Force.com
導入支援サービス
弊社の認定プロフェッショナルが支援致します。
・Visualforce/Apexによるアプリ開発
・Salesforceと連携するWebアプリ開発
も承っております。
セールスフォースご検討の際は、
お気軽にお問合せください。
Powered by Six Apart