怖話.jpをHerokuからさくらVPS 512に移行したのでパフォーマンスを測ってみました。(怖話.jpのアプリに大きく依存しています。また日本向けのサービスなので手元から計測したためネットワーク的にHerokuは不利です。)

  • Heroku
    • kowabana.jp
  • さくら
    • oulu.fjord.jp

さくらVPSではnginx + unicorn + mysqlを使っています。

Apache Bench

% ab -n 1000 -c 100 http://kowabana.jp/
Requests per second:    6.12 [#/sec] (mean)
Time per request:       163.526 [ms] (mean, across all concurrent requests)
% ab -n 1000 -c 100 http://oulu.fjord.jp/
Requests per second:    38.29 [#/sec] (mean)
Time per request:       26.114 [ms] (mean, across all concurrent requests)

Chrome Developer ToolsでのDOMContent event fired

トップページをスーパーリロードした場合。

  • Heroku
    • 1.69s
  • さくら
    • 540ms

結果

人気サイトじゃないのでabの結果はそれほど気にしてませんが、DOMContent event firedはパラパラと残りのパーツが表示されるのは別として、ユーザーにとって「ページが表示された」と感じる実際の時間に近いと思います。それが3倍違うというのは結構な違いだなと思います。確かに体感速度でも速くなったように思います。

怖話.jpをHerokuからさくらVPS 512に移行することにしました。理由は下記です。

  • DB容量が無料の範囲を大幅に超えている。(一応容量オーバーしていても使える)
  • slug(リポジトリ)のサイズが制限(100MB)を超えた。(これはオーバーしているとpushできない)
  • ファイルアップロードが必要になった。(S3を使えば対応は可能)
  • 回線が遅い。(us westとかなので仕方が無い)

Amazon EC2にしなかった理由はまだ人気が無いのでお金をかけられないからです。もし売上が月数十万になったら移行します。

DB移行

HerokuのDBはPostgreSQLです。さくらVPS 512ではMySQLを使うことにしました。

現在のDB容量は37.4MB。レコード数は約2万件ぐらいです。heroku db:pullをしたら1時間たっても終わる気配がなかったので次の方法を取ることにしました。

  • herokuからローカルにpg_dumpを持ってくる。
  • ローカルのpostgresにpg_restore。
  • ローカルのmysqlにtapsサーバーを立てる。
  • ローカルのpostgresからtapsサーバーへpush。
  • ローカルのmysqlからmysqldump。
  • さくらVPSのmysqlにrestore。

大したデータ量じゃないのに、要はネット経由でtapsするととてつもなく時間がかかるのです。

「これ絶対もっと楽な方法あるよなあ」

などと思いながら作業しました。

% heroku plugins:install git://github.com/ddollar/heroku-sql-console.git
% heroku sql
SQL> select * from unk;

It's not psql. It's only send sql and return results.

Heroku-ja Meetup #1 : ATND

Heroku-ja Meetup #1 - 写真 - Google+

昨日行われたHeroku-ja Meetup #1でPaaS向けCMS LokkaというLTをさせていただきました。

正にHeroku三昧の時間で楽しかったです。会場のリクルート メディアテクノロジーラボ MTL Cafeも設備が整っていて、発表するのにとてもやり易かったです。Herokuオフィスの雰囲気や皆さんがどうやってHerokuを使っているのかというお話がとても為になりました。

Lokkaについてもruby1.9.2対応してくださった@tyabeさん(Herokuのデフォルトが1.9.2に変わって焦ったけど@tyabeさんのお陰でLokkaが動いてて良かった)に会えたり、@yagi_さんと次何実装するか話したり、Herokuユーザーの方々に宣伝できたりで行って良かったです。

RubyKaigi2011終わったらまたLokkathonやりたいです。

主催の@junyaさん、キャンセル待ちから運営側に回ることで滑り込むという裏技を使いつつ、ピザでみんな手がベタベタになるのに気を効かせて速攻ティッシュを買ってくるなどの名運営っぷりを見せた@rono23さんなどなど、皆さんありがとうございました。

HerokuのCedar StackはClojureに対応してる。要はLeiningenに対応してる。(RubyのBundlerに対応してるのと同じようなもん)

project.cljを見て必要なもんをpush時にインストールしてくれる。

;; lib/unk/core.clj
(ns unk.core
  (:use compojure.core
        ring.adapter.jetty))

(defroutes app
  (GET "/" _ "unk"))

(defn -main []
  (let [port (Integer/parseInt (get (System/getenv) "PORT" "8080"))]
    (run-jetty app {:port port})))
;; project.clj
(defproject unk "1.0.0-SNAPSHOT"
  :description "FIXME: write description"
  :dependencies [[org.clojure/clojure "1.2.1"]
                 [compojure "0.6.3"]            
                 [ring/ring-jetty-adapter "0.3.8"]])
# Procfile:
web: lein run -m unk.core
$ heroku apps:create --stack cedar
$ git push heroku master
$ heroku apps:open

http://sharp-wind-970.herokuapp.com/

OMG!

環境変数$PORTを使う必要がある部分でちょっと躓きました。

Herokuの新しいCedar Stackを使う。

今までと大きく違うのはforemanを使ったprocess modelが導入されたこと。要はwebアプリもcronジョブもバックグラウンドタスクもみんな宇宙船UNIX号の仲間たち、foremanで平等に扱おうよということ。具体的にはProcfileにprocessの設定を書いてforeman経由で動かす。

# Gemfile:
source :rubygems
gem 'rack'
# config.ru:
require 'rubygems'
require 'rack'

class App
  def call(env)
    [200, {'Content-Type' => 'text/plain'}, ['hello']]
  end
end

run App.new
# Procfile:
web: bundle exec rackup -p $PORT

こんな感じ。

$ gem install foreman
$ foreman start

ローカルでもforemanで試せる。

$ heroku apps:create --stack cedar
$ git push heroku master
$ heroku apps:open

http://floating-dawn-850.herokuapp.com/

いいね!

process typeを自分で定義する

unixのprocessなら何でもありなので適当に定義できる。

# Procfile:
web: bundle exec rackup -p $PORT
unk: echo unk; sleep 1000
% foreman start
02:23:42 web.1     | started with pid 77070
02:23:42 unk.1     | started with pid 77071
02:23:42 unk.1     | unk
02:23:43 web.1     | [2011-06-23 02:23:43] INFO  WEBrick 1.3.1
02:23:43 web.1     | [2011-06-23 02:23:43] INFO  ruby 1.8.7 (2011-02-18) [i686-darwin10.7.0]
02:23:43 web.1     | [2011-06-23 02:23:43] INFO  WEBrick::HTTPServer#start: pid=77072 port=5000

こいつは自由だぜ。

# Gemfile:
gem 'barista'
gem 'therubyracer-heroku'
$ rails g barista:install

coffeeをrailsに提供するからバリスタってか?heroku上ではコンパイルするためのjs実装が無いのでエラーが出る(ローカルではnodejsを使ってます)。therubyracerはv8のrubyバインディング。therubyracer-herokuはそれをherokuで動かすためのもの(何やってるのかはわからない。見てない)。

ttfのwebfontをWebFont Loaderで読み込む時に"fontがapplication/octet-streamになってるぞ"みたいなnoticeが出る。

ローカル(WEBrick)では出てなかったので気付かなかったがHeroku(nginx + thin)では出てる。

キモイので自前のnginxがあるサーバーにfontを移して/etc/nginx/mime.typesに下記を追加。

application/x-font-ttf    ttf;

ブラウザのDeveloper ToolsのNetworkでcontent-typeが楽に確認できて便利。

怖話

殆ど同じと言って良いくらい使い勝手がHerokuと似ているPaaSのDuostackでもLokkaが動くようになりました。

% gem install duostack
% git clone git://github.com/komagata/lokka.git
% lokka
% duostack create foobarbuz
% git push duostack master
% open http://foobarbuz.duostack.net

Duostackでも動くようにしたと言っても実際は2行足しただけだった。

# Gemfile
group :production do	
   gem 'dm-postgres-adapter', '1.1.0'	
+  gem 'dm-mysql-adapter', '1.1.0'	
end
# config.yml
production:
-  dsn: <%= ENV['DATABASE_URL'] %>
+  dsn: <%= ENV['DATABASE_URL'] || ENV['DUOSTACK_DB_MYSQL'] %>
development:
   dsn: sqlite3://<%= root %>/db/development.sqlite3

Commit f62862d491e5b2dce9afde9c82e6e2c7640ac60f to komagata/lokka - GitHub

これはcoolだ。

HerokuではpostgresだけどDuostackではmysqlで動きます。

% git push heroku master
% git push duostack master

こんな感じで同じローカルリポジトリで二つのPaaSを使い分けるという荒業も出来る。両方AWSベースらしいから障害耐性という面では意味無いけど。

$ curl -o latest.dump `heroku pgbackups:url`

所謂上記で取れるdumpファイルはPostgreSQLマニュアルで言うところの”非プレインテキスト形式のアーカイブ”なのでpg_restoreじゃないとリストア出来ない。不便に思うけど、pgbackupsというサービスを作る事を考えたら柔軟なデータ形式よりも、同じDBだったらroleとか全て取りこぼし無く復旧してくれそうな形式になっているのは納得出来る話だ。(ちょこっとcommentsテーブルだけ取りたいなんて今回の自分の用途にとってはうざいが・・・)

$ pg_restore -d foo foo.dump

roleは違うけどデータは取れた。

$ heroku pgbackups:capture --expire

個数制限があるのでこれで古いのを消しつつ新しいのをバックアップしてくれる。楽だなあ。