youRoomにおいて発生した 2011/4/21 のAWSの障害について技術的な観点から

SonicGardenがサービスしている youRoom ( http://youroom.in/ ) が昨日障害により最大1時間程度サービスをご利用いただけない状態になりました。ご利用の方にはご迷惑をおかけしました。大変申し訳ございません。現在は、復旧し正常にサービスを提供しています。

障害の原因は、youRoomが利用している Amazon Web Service の障害により利用しているサーバが停止したことによります。

今回の障害で、AWSが危ない・不安定だという印象を受けた方もいらっしゃるかと思いますが、SonicGardenとしてはあたりまえだと思っています。そのための準備もしていました。なので、他のサービスよりも短時間でサービスを復旧できたのではないかと思っています。ただし、今回の障害でまだまだ改善する点が見つかったので、AWSの障害が起こってもサービスを継続できるように・より安定してサービスを継続できるように仕組みを見直して改善していきます。

今回の障害の流れ

17:00 ごろ
DBサーバのレスポンスが遅延する事象が発生。1分程度で復旧。今回の障害の一部かと想定している。

18:20 ごろ
APサーバがレスポンスを返さなくなる。SSHで接続をすると接続可能。メンテナンス画面を表示するためにコマンドを発行しようとするが、lsなどファイルシステムにアクセスするとレスポンスが返らない状態になる。ファイルシステムでの障害が発生した模様。

18:30 ごろ
AWSのコンソールからリブートを試みるが、反応しない。

18:50 ごろ
新しいインスタンスを起動して、切り替える作業をスタート。新しいサーバをバックアップしておいたAMIから起動開始。

19:10 ごろ
起動後、アプリケーションの最新化、動作確認などを実施し、正常に動作することを確認して、IPの付け替えを行って、新インスタンスでサービス再開。復旧。

障害発生時、このような時間の流れでした。1時間弱サービスをご利用いただけない状態になりました。

今回の障害での反省点

・新しいインスタンスに切り替える作業を開始するタイミングの遅れ
=> リブートで復旧できるのか結果がでるまでに多少の時間がかかるのと、新しいインスタンスを起動するのにも多少時間が必要。この間の時間が復旧の時間を伸ばしてしまっているので、リブート作業のタイミングで新インスタンスの準備も行って、リブートで復旧できないと判断したタイミングで新しいインスタンスで動かせるように準備するとより早くサービス復旧ができたはず。

SonicGardenの運用の考え方

システムを運用すると、サーバの障害は少なからず発生します。AWSを利用していても、実サーバを利用していても発生します。

SonicGardenとしては、その障害発生時にどれだけ早くサービスを安定した状態に戻せるのかということに重点をおいています。AWSであれば、10分もあれば新しいサーバを調達して、起動することができます。それも、AMIとしてバックアップをとっておけばほぼ動いていたサーバのそのままの状態で起動できるのです。ここまでのスピード感実現できるのは、まだAWS以外にないのではないでしょうか?
もし、AWSでなく実サーバで運用していたらと思うとぞっとします。サーバを買いに走って電気屋まで行かないといけないのですから。さらにOSインストールして。。。もしくは、障害のためにもう1台サーバを用意しておかないといけないのです。

AWSを利用していると、インスタンスが動かなくなるという現象は何度か発生しますが、今回のようにリブートもできない状態になるのは非常にまれです。ほとんどがリブートで復旧します。SonicGardenではかなりの台数のインスタンスを2年以上AWSで運用していますが、この状態になったのはほんの数回です。内2回がyouRoomのインスタンスだったりしますが。。。

SonicGardenのアプリケーション開発は、「どれだけ頑張ってもバグは発生する。0にするよりもどれだけ早く直せるかを考える」という方針です。インフラも同じく「障害は発生する。どれだけ早く復旧するか」なのです。バグを0にする・障害を0にすることは、ほぼ不可能であり実現しようとすると非常にコスト・手間がかかります。といっても、決して品質に対して手を抜くということをやる訳ではありません。過剰に品質にこだわることを捨てているのです。その保証として対応できるスピードを用意しておくのです。アプリケーション開発であれば、Railsを利用しているのは修正する時間を最短にすることができるからですし、インフラとして、AWSを利用するのはそのためでもあります。

世の中のみなさんは、今回の障害によりAWSに対して不安をいだいているかもしれませんが、私はこう思っています。

AWS最高! 」

最後に、この障害のタイミングでサーバをよりよいものに変更しました。少し費用は上がってしまいましたが、停止してしまったお詫びの気持ちもこめて、より早く・快適にご利用いただけるようになったと思います。是非、ご利用ください。
http://youroom.in/

※ SonicGardenのインフラエンジニアのトップが別の観点からのブログを書いています。こちらも御覧ください。 http://d.hatena.ne.jp/interu/20110425/1303731515

Rubyエンジニアのためのpowソースコードリーディング

先日、37signalsより "pow" というプロダクトが発表されました。Railsの開発をより簡単に行うための開発用HTTPサーバです。MaxOS向けにチューニングされていて、zero configでセットアップできるのが特徴になっています。

使い方は非常に簡単で、

curl get.pow.cx | sh

でインストールを行って

cd ~/.pow
ln -s /path/to/myapp

というようにアプリのシンボリックリンクを ~/.pow のディレクトリに貼れば http://myapp.dev/ というホスト名で起動します。本当に zero config で動きます。37signalsのプロダクトはおもろいですね。

さらに、面白いのがRailsの開発用アプリケーションサーバなのですが、中身の実装は node.js で書かれています。注目を集めてきているサーバサイドJavaScriptですね。

僕自身はまだあんまり node.js は触ったことがなかったのですが、ちょっとpowを改良してみたくなったのでいろいろ調べてみました。何を改良したかったかというと、HTTPSでアクセスしても動くようにしたかったのです。別サービスと連携する時やhttpsのテストをするときなどに、必要に感じていたのですがなかなか簡単に開発環境でHTTPSを動作させることは面倒ですよね。powが対応したらそれは便利だろうと思ったのです。
https://github.com/37signals/pow/issues/5

といっても僕は node.js を触ったことがないのですが、とりあえずコードを見てみることにしてみました。
https://github.com/37signals/pow
libディレクトリがJavaScript本体です。しかし、これは生成されたJavaScriptなのです。CoffeeScriptというJavaScriptをよりRuby, Pythonのようにシンプルに記述して、コンパイルしてJavaScriptに変換するものが流行っているらしいです。その実態は、srcディレクトリにあります。

pow自体は、非常に面白い動作をするのですが、実際ファイルを見てみるとコードは非常にすくないです。驚きました。主要なファイルは daemon.coffee, dns_server.coffee, http_server.coffee, rack_application.coffee の4つのファイルです。なぜ、このような少ないファイルでpowは実現できているかというと、その先にライブラリがあるからです。まぁ、当然ちゃ当然ですが、とてもうまく利用しているなぁという感じです。

まずは、利用している重要なライブラリを紹介します。 node.js にはもうこんなにも有用なライブラリが揃っているのかととても驚きました。npmというRubyのgemにあたるパッケージ管理システムも存在します。

1. connect http://senchalabs.github.com/connect/
Rubyでいうと、Rackのようなものだと思います。middlewareをたくさん組み込む感じでHTTPサーバを動かせます。

2. nack http://josh.github.com/nack/
node.js で受け取ったリクエストをRubyのRackを利用して実装されたアプリに受け渡しして処理することを実現してくれます。

この2つのライブラリがあれば、powで実現していることの基本的な仕組みは実現できているということがわかるのではないでしょうか?connectでHTTPリクエストを受け取って、nackを使ってRubyを呼び出してレスポンスを返す。あと足りない部分は、シンボリックリンクを作成したら自動的にホスト名を割り当てて起動させるようにする部分です。powのコードは主にこの部分の実装になるんですね。

で、先ほど出した4つのファイルファイルですが、以下の関係になっています。

daemon.coffee => dns_server.coffee
              => http_server.coffee => rack_application.coffee

daemonが起動されて、dns_serverとhttp_servertを立ち上げます。myapp.devでローカルホストを指すようにdns_serverで処理するんですね。さらにhttp_serverにリクエストがくるようになるので、それをホスト名などからどのRubyアプリに受け渡すのかを決定しrack_applicationを呼び出すという流れです。

http_serverの実装にconnectが、rack_applicationにnackが利用されています。それぞれを拡張する形でこのように動作するように実装されています。スクリプト言語の拡張性をうまく使っていると思います。

詳細なコードリーディングの解説にはなりませんでしたが、powがどのようなことをどうやって実現しているのか解説できたのではないかと思います。まだまだ、 node.js の素人なので間違っている部分はあるかと思いますが、ご指摘いただければと思います。

[heroku][android][titanium] Heroku の Android クライアントを Titanium で作りました

タイトル通りです。iPhoneには、クライアントがある(Nezumiというやつ)のですがAndroidには探したけどありませんでした。

そこで最近興味を持っていた Titanium で作ってみることにしました。JavaScriptiPhone / Android のネイティブアプリが書けるフレームワークです。
http://developer.appcelerator.com/

なんとか、それなりに動くものができたの公開したいと思います。
https://market.android.com/details?id=net.mataki.ikanago

500円くらいで販売しようかと思っています。が、、、公開キャンペーンとしてなんとこの週末は 100円 にしておこうと思います。インストールするなら今すぐ!!!

「いやいや、そんなのすぐ作れるよ」「無料にしろよ」という方には、無料でアプリを手に入れる方法を用意しました。なんと、オープンソースにします。自分でTitaniumをインストールしてUSBケーブル差してインストールしてください。
https://github.com/mataki/ikanago

そんなの面倒でしょ?たった100円ですよ。マーケットから買ったほうが楽だと思うけどなー。今後も自動的にバージョンアップもしてくれるし。僕だったらマーケットから買うなー。

ここまで言っても無料で使いたいというあなた!一緒に開発しましょう!あなたのpull requestをお待ちしています!

アプリ名は、 ikanago にしました。神戸の名産 イカナゴの佃煮 の ikanago です。意味は特にありませんが、出身地にまつわる単語にしたかっただけでした。Herokuは日本好きなので。

ロゴはこちらで作ってもらいました。ダメもとだったのですが、申し込んだらすぐに作ってもらえましたよ!ありがとうございます。
http://tesagyo.me/

機能は、Nezumiを参考にしています。将来的にはHerokuのAddonとかにも対応して拡張したいなぁっと思っています。NewReclicのデータが一緒に見れたりとか。

iPhoneでも一応動くようにはなっていますが、変なところがあります。iPhoneでもApp Storeに公開したいと思ったのですが、デベロッパー登録が高いのでちょっと様子見です。

さぁ、Let's 購入!

[Rails][Heroku] Herokuで強制的にdb:migrateをやり直す。その2

http://d.hatena.ne.jp/shunsuk/20110322/1300794096
このブログでHerokuのデータベース(標準のpostgres)を最初からつくり直す方法として、migrationにSTEPオプションを渡す方法が紹介されています。
migrateにそんなオプションがあったとは知りませんでした。

僕は、いつも以下のやり方でやっています。

$ heroku pg:reset --db SHARED_DATABASE_URL --app manga-dojo-staging
Resetting SHARED_DATABASE_URL (DATABASE_URL)
 !    WARNING: Potentially Destructive Action
 !    This command will affect the app: manga-dojo-staging
 !    To proceed, type "manga-dojo-staging" or re-run this command with --confirm manga-dojo-staging

> manga-dojo-staging
... done
$ heroku rake db:migrate --app manga-dojo-staging
(in /app/4cf22428-1973-41f7-8768-8ffc509f1d65/home)
Migrating to CreateMangas (20110220170208)
==  CreateMangas: migrating ===================================================
-- create_table(:mangas)
   -> 0.0770s
....

HerokuではRails標準のrake db:resetは使えませんが、以下のコマンドでHerokuの標準のpostgresのDBをリセットすることができます。

$ heroku pg:reset --db SHARED_DATABASE_URL

実行後、確認のためプロンプトが表示されるのでアプリ名を入力しましょう。するとリセットが実行されます。

あとは、migrateを普通に実行するとOKです。

現在のgihyo.jpで好評(?)連載中の "Herokuで作るFacebookアプリ" の中で紹介したいと思っていたネタだったのですが、Tipsすぎでなかなかタイミングがなかったので調度良かったです。是非、連載も読んでみてね!
http://gihyo.jp/dev/serial/01/heroku

あっ、地震後初めてのブログでした。僕は元気です。

[Rails] Rails 3.0.4, 2.3.11 におけるセキュリティー対応について

本日、Rails2.3.11がリリースされましたね。Rails3.0.4と同時に。主にセキュリティーの対応のようです。
http://weblog.rubyonrails.org/2011/2/8/new-releases-2-3-11-and-3-0-4

主な内容はこちらのようです。

  1. 2.x.x, 3.0.x 系両方に影響あり
    1. mail_to の :encode=>:javascript オプションにXSSの可能性あり (CVE-2011-0446)
    2. CSRF を通り抜けられる可能性あり (CVE-2011-0447)
  2. 3.0.x 系だけに影響あり
    1. Filter Problems on Case Insensitive Filesystems (CVE-2011-0449)
    2. limit()にSQLインジェクションの可能性あり (CVE-2011-0448)

僕が対応の際に調査した項目などをまとめておきます。まず対応したのがRails2系でした。mail_toのオプションに関してXSSがある問題は、利用していないので影響はありませんでした。このオプションはあまり利用されていない気がするので、影響を受ける人はあまりいないかもしれません。

一番影響のある変更は、CSRFの問題ではないでしょうか?以下がCSRFの問題に対する公式なメーリングリストの投稿とGithubのコミットログです。

このブログでは主にここを中心に解説します。

もともとRailsでは、CSRFの対応にトークンを用いて防いでいます。その際にAjaxのXHRリクエストでは、CSRFが起こりえないという前提でトークンでの検証を行わないというコードになっていました。本来はそうなのですが、コミットログなどを参考にすると FlashJava applets を利用した場合に起こる可能性があるようなのです。今回のRailsの対応は、XHRリクエストの際もトークンで検証を行うというような変更が行われています。この変更がコミットログの以下の部分です。
https://github.com/rails/rails/commit/7e86f9b4d2b7dfa974c10ae7e6d8ef90f3d77f06#L0R88

この変更ため、Ajaxの処理の際にもトークンを送る必要がでてきました。新しく追加された csrf_meta_tag をlayoutなどで全てのページで定義して、jQueryの場合は以下のようにしてトークンを常にヘッダーに加えるようです。
https://gist.github.com/818083
csrf_meta_tag の追加部分はコミットのここあたりです。
https://github.com/rails/rails/commit/7e86f9b4d2b7dfa974c10ae7e6d8ef90f3d77f06#L2L-1

さらに、今回の変更でこれまでCSRFトークンの検証が正しく行われなかった場合にはエラーをraiseしてエラー画面を表示するという処理が行なわれていましたがこの部分も変更されました。エラー画面を表示するのではなく、ログアウト処理を行う様になっています。この理由は、まだ僕は分かっていないのです。誰か教えて!!この変更はコミットログのこの部分です。
https://github.com/rails/rails/commit/7e86f9b4d2b7dfa974c10ae7e6d8ef90f3d77f06#L0R75

この変更では、ログアウト処理は reset_session が呼ばれるだけになっています。もし、「ログイン状態を保存機能」などで reset_session だけではログアウトされない場合は、 handle_unverified_request メソッドをアプリケーションで上書きしてcookieを削除するように変更してくださいっとメーリングリストに書いているのでご注意ください。

まとめ

今回のアップデートの中のCSRFのリスクに必要な手順は以下となりそうです。
1. Railsのバージョンアップ (一時的なパッチで対応するようのパッチも作りました https://gist.github.com/818088 このファイルを config/initializers 以下に置く)
2. jQueryなどでトークンを送信しないAjaxリクエストを用いている場合は、 csrf_meta_tag をviewに組み込み、サンプルを参考にヘッダーでトークンを送信するようにする
3. ログアウト処理が reset_session だけで完了しない場合は、 handle_unverified_request を実装する

急いで調査・まとめを行ったため間違いがあるかもしれませんので、その際はご指摘いただければ幸いです。また、反映する際はご自身でご確認の上ご利用ください。

Heroku の オートスケーリングを GoogleAppEngine からやるアプリを作ったんだけど上手くいかなかったよというお話

(この記事は Ruby Advent Calendar jp:2010 の 17 日目です。前日は jugyo さんでした。)

Rubyホスティングといえば、最近は Salseforce.com に買収された Heroku や GoogleAppEngine for jruby などがPaaS的なものとしては有名ですね。

Herokuは大好きなのですが、個人的に一歩足りないと思っているところは、簡単にスケールできるようになっているのに自動でスケールしないところです。そんなところを考えて、前回ブログで NewRelicRPM という Heroku の Add-on のパフォーマンスモニタリングサービスのデータを使って、Herokuのプロセス(dyno)数を制御するというスクリプトを紹介しました。

今回は、そのスクリプトをサービス化してみようと考えて取り組んだお話です。

そのサービスを作るに当たって、やっぱりホスティング先を考えないといけません。今回のサービスは定期的にモニタリングデータを取得してdynoの数を調整するアプリになります。つまり、自由に定期Jobを行う必要があるんですね。その場合、私の好きなHerokuでやろうとすると、自由度がなく費用がかかりそうでした。Herokuの定期Jobはcronですが、一日ごと(無料)と1時間ごと($5/month)となっているからです。Rescue::Sceduleという手段もあるみたいなんですが、$36と高い。

そこで、次の候補です。GoogleAppEngine for jruby ですね!AppEngineはかなり柔軟に定期Jobをcronで設定できますし、無料で出来る幅も大きそうです。ということで、GoogleAppEngineでやってみることにしました。

ちょっと GAE for jruby を利用したときは、なかなか面倒だった記憶があるのですが、今回つくってみると意外に良い感じでした。 google-appengine のgemをインストールして以下のコマンドを叩くとアプリの基板が完成します。

$ appcfg.rb generate_app heroku-autoscale-on-gae

今回は非常にシンプルなアプリなのでSinatraで作成します。モニタリングするアプリ一覧表示する画面と登録アクション、最後にcronが叩く実際にNewRelicやHerokuにアクセスして調整を行うアクションですね。モデルもアプリだけです。非常にシンプルですね。
コードは以下にオープンソースにしました。動かして楽しんでもらえるといいと思います。

https://github.com/mataki/heroku-autoscale-on-gae

実際にローカルで動かしてもらえるとおそらく動くのではないかと思います。

$ appcfg.rb bundle .
$ dev_appserver.rb .

注意点としては、HerokuがAPIっぽいのに最近対応しているんですが、まだパスワードを平文でDBに保持しています。あと、NewRelicのAPIキーはAdd-onをインストールして以下のコマンドで表示される NEW_RELIC_LICENSE_KEY です。

$ heroku config --long

ローカルでは上手く動いた heroku-authoscale-on-gae ですが、最後に落とし穴が待っていました。なんと!!! GoogleAppEngine の本番で動作させると Heroku の API がなんと503を返し続けるのです。
そう、おそらくライバルのAppEngineからのリクエストは拒否しているんでしょう。なんということでしょう。せっかくHerokuの為に作ったのに最後の最後でHerokuに拒否されるとは。。。

そして、最後にさらに頑張って作っているさなかHeroscaleというサービスが公開されました。やりたいことは正に一緒で、手段はちょっと違いますがおそらく同じことが出来るでしょう。

https://www.heroscale.com/

かしこい皆さんは、こちらのサービスをご利用ください。

以上、これまでのRubyAdventCalenderのブログとは毛色が違いますが、「こんなん作りました」というブログでした。

[Heroku][Ruby on Rails] Heroku で オートスケーリング(auto scaling)

先日、"Heroku" のTipsを紹介します。Salseforce.com に買収されることが発表されたRuby on RailsホスティングのPaaSサービスですね。

Herokuはとても良く出来たサービスなのですが、いくつか弱点があるのかなと思っています。

  • ログが削除される
  • cron が 1時間ごと/1日ごと しか選べない
  • クラウドなのにオートスケールしない
  • ...

僕は、Ruby on Rails プログラマーですし、Herokuの考え方がとても好きなので、弱点を克服する方法を考えました。今回は、"クラウドなのにオートスケールしない"という弱点を考えました。

Herokuは、スケールは可能です。サーバプロセス数を指定することができるので、それを増やすと大規模なアクセス数があるサービスでも十分に対応可能となっているそうです。しかし、"自動"ではないので、アクセスが減ったときに自動で減らせず余分なコストが発生します。また、アクセスが増えたときに自動でプロセスを増やせず、レスポンスが遅くなってユーザに不便をかける可能性があります。

そこで、私の考えた方法はHerokuのとても優れている外部サービスとの連携(Add-onsと呼ばれる)を利用してこれを解決しました。Herokuは、NewRelicというパフォーマンスをモニタリングしてくれるサービスと連携しています。そのデータを取得し、パフォーマンスが下がったときはプロセス数を上げ、パフォーマンスが十分になったときはプロセス数を下げることで自動スケールを実現します。

以下のようなスクリプトを書いてみました。

非常に、シンプルなコードなので読んでみてもらうと分かると思いますが、上に書いたことをそのまま書いています。それぞれのサービスのgemがあるのでそれを利用するとすぐに実現できました。

使い方は、setup config部分に自動スケールしたいアプリのIDとNewRelicのAPIキーを入力だけでOKです。NewRelicのキーは、Addonsのコンソールの中から探してもらうか

heroku config --long

とアプリのDirでコマンドを打ってもらうと、NEW_RELIC_LICENSE_KEYに表示さます。

あとは、このコマンドをcronなどで定期的に実行すれば、その時のパフォーマンスに応じてプロセス数を調整し最適なものにしてくれるでしょう。

今回は、Herokuの弱点である自動スケールができないを克服する方法を紹介しました。是非ご利用ください。