プログラム

PadrinoアプリでNewrelic

New relic

New relicはパフォーマンス監視サービスでRackアプリケーションの場合、用意されたnewrelic_rpmというGemをインストールするのみでNew relicが用意するダッシュボードでページが表示されるまでのパフォーマンスの詳細な内訳(Ruby自体の処理時間、DBが要した処理時間やスロークエリ等)を知ることができます。
アカウント作成は無償でもできて、無償の場合、1アプリケーションの直近30分のパフォーマンスを知ることができます。
今回はSqaleで動かしているPadrinoアプリのパフォーマンス監視を行うまでの手順を整理します。

アカウントを作成する

Newrelicを開くとトップページに "Create Your Free Account!" のリンクがあるのでここからアカウントを作成します。

newrelic.yml をダウンロードする

アカウントを作成してログインするとnewrelic用の設定ファイル newrelic.yml をダウンロードできるのでダウンロードしてPadrinoプロジェクト下のconfigディレクトリに配置します。

Gemfileにnewrelic用のライブラリ追加

Padrinoアプリで利用しているGemfileにnewrelic用のクライアントをインストールするための2行を追加。

# Padrino Stable Gem
gem 'padrino', '0.10.7'
# for newrelic
gem 'padrino-rpm', :git => "https://github.com/Asquera/padrino-rpm.git"
gem 'newrelic_rpm'

2013.2.24追記

  • ここでポイントは gem 'padrino'〜 の下にnewrelic用のgemを追加することです
  • padrino読む前に追加するとエラーになります。

Unicorn用の設定追記

Sqaleでは標準でアプリケーションサーバunicornなのでこちらを参考にして起動時の設定ファイルに追記します。

# config/boot.rb
::NewRelic::Agent.after_fork(:force_reconnect => true)  # Unicorn用に追加
Padrino.load!

実行結果

以上の設定を行ってSqaleへデプロイします。
そうすると正常に稼働すれば、数分後にnew relicのダッシュボードにパフォーマンス情報が表示されます。

また、組み込んだPadrinoアプリ上の log/newrelic_agent.log からも確認できます。

sqale@impas-hideack-1:~$ tail -f current/log/newrelic_agent.log
[02/10/13 17:13:15 +0900 impas-hideack-1 (24017)] INFO : Connected to NewRelic Service at collector-5.newrelic.com:80
[02/10/13 17:13:15 +0900 impas-hideack-1 (24017)] INFO : Reporting performance data every 60 seconds.
[02/10/13 18:13:03 +0900 impas-hideack-1 (24175)] INFO : Dispatcher: sinatra
[02/10/13 18:13:03 +0900 impas-hideack-1 (24175)] INFO : Application: Impas
[02/10/13 18:13:03 +0900 impas-hideack-1 (24175)] INFO : New Relic Ruby Agent 3.3.5 Initialized: pid = 24175
[02/10/13 18:13:03 +0900 impas-hideack-1 (24175)] INFO : Agent Log at /home/sqale/current/log/newrelic_agent.log
[02/10/13 18:13:13 +0900 impas-hideack-1 (24175)] INFO : Resolved collector.newrelic.com to 204.93.223.153
[02/10/13 18:13:14 +0900 impas-hideack-1 (24175)] INFO : Resolved collector-5.newrelic.com to 204.93.223.139
[02/10/13 18:13:14 +0900 impas-hideack-1 (24175)] INFO : Connected to NewRelic Service at collector-5.newrelic.com:80
[02/10/13 18:13:14 +0900 impas-hideack-1 (24175)] INFO : Reporting performance data every 60 seconds.

機能を追加した際等々の折にアプリケーション側のパフォーマンス変化を気にしたい場合などに気楽に確認できるため便利です。

ストーリーボードのバックエンド等々のお話

ストーリーボードをSqale(http://sqale.jp )へ運用を移管したのは以前簡単にここにも書きましたが、一度どういった技術を使っているのかまとめがてら整理してみます。

実装

現在、実装周りでは、

などを利用しています。

Youtubeの動画を連続して再生することができるYoutubeプレイヤー REMP(http://remp.rockf.es/ )のころからのスタイルでサーバ再度側は基本的にAPIが主体となる様な設計になっており、WebUI上からの操作をJSが吸収して、サーバサイドのAPIを叩き、その応答をJSが再びUIへ反映させるといった構成になっています。
私が担当したサーバサイド側の実装では、大きく分けて、

  • 最低限必要なページの生成
  • 編集されたプレゼンテーションドキュメントの保管
  • プレゼンテーションに利用する画像の保管
  • プレゼンテーション操作(ページめくり等)が行われた際のページめくり情報のWebsocket配信

等を行なっています。

プレゼンテーション自体のデータ(=Markdown記述)は現在Sqaleでは共用MySQLサーバは提供されていますが、今回はMongoDBを利用しているため、ここのみ外出ししたMongoDBに接続し保管しています。
また、プレゼンテーション用にアップロードされた画像はヘテムル(http://heteml.jp )へAmazonS3互換の動きをするfS3を導入してアップロードされた画像をストーリーボードのサーバーサイドプログラム側からS3プロトコルで画像をストアしています。

更にストーリーボードの一つの特徴であるページめくり等を同じ時間にプレゼンテーションページを閲覧しているユーザにページめくり操作をマルチキャストしたり、閲覧中のプレゼンテーションに対してリアルタイムで"いいね!"をすることができる機能を実現するためにWebsocketを導入していますが、ここは現在Websocket ASPサービスであるPUSHERを使っています*1

監視サービス

監視サービスですが、Pingdomを使っています。
基本有償サービスですが、1サイトのみであれば無償で利用できます。

HTTP疎通の死活監視を行なってくれるので、最低限の状況は把握できるので役になっています。

利用状況解析

お約束感がありますが、mixpanelgoogle analyticsを使っています。
RubygemsmixpanelというGemがあるので、これを利用してストーリーボード上のAPIが呼び出される度にログを記録する様にしています。
基本、ストーリーボード上で何らかの操作が行われるとストーリボードのサーバサイドAPIが叩かれるので、ページめくりAPIが多いとどこかのプレゼンテーションの発表に利用されたかな。とか利用状況がわかったりします。

こんなかんじです

以上大雑把ではありますが、ストーリーボードの実装概要をまとめてみました。
今後も機能をどんどん足していくし(と思うし)、新しい技術も今後積極的に入れていきたいのでまたある程度溜まったら記事を書こうかと思います。

*1:いつか自前で実装するといつつ、すごく気楽に利用できるので使い続けてます…

Padrinoでpryを使う

Padrinoでpadrino console使うと標準ではirbが立ち上がりますが、これをPryに切り替えたい場合、Gemfileに以下の内容を追加するだけですぐに使えました。

#Gemfileに以下の2行を追加
gem "pry"
gem "pry-padrino"

試してみる。

$ padrino console
=> Loading development console (Padrino v.0.10.6)
=> Loading Application Stbd
=> Loading Application Admin
[1] pry(main)>

Mongoidでsumする話

SQL的に

select sum(file_size) from Pictures where user_id=10;

といって書く様な処理をmongoidでさせるのどうすればいいのかと調べたのでメモ。
愚直に、

size = Picture.where(user_id: 10).sum(:file_size)

でよいのですね。そのまんま。

Padrinoでormをmongoidに指定した場合のGemfileにmongoが無い…?

Padrino (Padrino v. 0.10.6) でORMをmongoidに指定した場合

$ padrino g project projectname -t rspec -e erb -d mongoid
$ padrino start
/usr/local/Cellar/ruby/1.9.3-p125/lib/ruby/gems/1.9.1/gems/bundler-1.2.0.pre/lib/bundler/runtime.rb:68:in `require': cannot load such file -- mongo (LoadError)
(以下略)

となって、エラーになる。
padrinoコマンドで自動生成されたGemfileを見ると

source :rubygems
# Server requirements
# gem 'thin' # or mongrel
# gem 'trinidad', :platform => 'jruby'
# Project requirements
gem 'rake'
gem 'sinatra-flash', :require => 'sinatra/flash'
# Component requirements
gem 'erubis', "~> 2.7.0"
gem 'mongoid'
gem 'bson_ext', :require => "mongo"
(略)

となっていて、gem 'mongo' が無いので、ここに

gem 'mongo'

足すと問題解決するんだけど、なんで含まれないんだ…?

Padrinoで追加したRakeタスクをProduction環境として実行する

padrinoで作っていたrakeタスクを...

$ padrino rake taskname

といった具合に実行しようとするとdevelopで実行されていてrakeタスクの中で呼んでいたmongoidがdevelopを向いていて本番用のデータベースを向いて実行されない。
よくよく考えると環境変数設定してないからだな。ということで、

$ PADRINO_ENV=production padrino rake taskname

とかけば、本番用のデータベースに向いた状態でタスクが実行される。

Padrinoで任意のrakeタスクを追加する

Padrinoでは、rakeタスクを実行したい場合、特にRakefileを配置しなくてもrakeコマンドを付与することで実行することができる。

$ padrino rake (実行したいrakeタスク)

rakeタスク自体は、Padrinoの場合、

  • ~/lib/tasks
  • ~/tasks
  • ~/test
  • ~/spec

に配置されれば、自動的に再起探索されて実行される。
また、Padrinoのプロジェクトで利用しているmongoid等で作られたmodelなどはそのまま使えるので例えば、

# ~/tasks/test.rake 
#encoding: utf-8
task :default => "test"
desc 'rakeタスクのテスト'
task :test do
oneuser = User.find(:first, :conditions=>{:user_id=>'123'})
p oneuser
end

とすれば、

$ padrino rake test
=> Executing Rake test ...
#<User _id: 500a5fbf7fae1d624f000001, _type: nil, created_at: 2012-07-21 07:52:31 UTC, updated_at: 2012-07-21 07:52:31 UTC, user_id: 123, name: "hideack">

といった具合に流用できる。

Padrino事始め

いままでREMPでは、フレームワークSinatraを使ってAPI等々の実装を行なってきたのですが、機能を足すたびに記述が長くなってしまい、またview等を追加する場合にもスケルトン生成を主導手動で行わければならないといった点があるので、やはり少し大きめのアプリケーションになるとRailsなのかな。と思っていたのですが、その前にSinatraを拡張したPadrino(http://www.padrinorb.com/)を使ってみることにしました。

導入

お約束ですが、Rubygemsですぐにインストールできます。

% sudo gem install padrino

次に作成するアプリケーションのプロジェクトを生成します。(ここがSinatraに無いところ)
Padrinoでは、好きなORMやテンプレートエンジンを利用することができるので、例えば、ORMにmongoid、テンプレートエンジンにerbを使いたければ、プロジェクトディレクトリを掘る階層でpadrinoコマンドを実行します。

% padrino gen project testapp -d mongoid -e erb
create
create  .gitignore
create  config.ru
create  config/apps.rb
(中略)
=================================================================
testapp is ready for development!
=================================================================
$ cd ./testapp
$ bundle install
=================================================================

で、次にコマンド実行時の末尾にあるとおり、bundle install でプロジェクトに必要なGemをインストールします。

% cd testapp
% bundle install
Fetching gem metadata from http://rubygems.org/.........
Fetching gem metadata from http://rubygems.org/..
Using rake (0.9.2.2)
Using i18n (0.6.0)
Using multi_json (1.3.6)
Using activesupport (3.2.5)
(中略)
Using sinatra-flash (0.3.0)
Your bundle is complete! Use `bundle show [gemname]` to see where a bundled gem is installed.

この段階でのディレクトリ構成を見ると

.
├── Gemfile
├── Gemfile.lock
├── app
│   ├── app.rb
│   ├── controllers
│   ├── helpers
│   └── views
│   └── layouts
├── config
│   ├── apps.rb
│   ├── boot.rb
│   └── database.rb
├── config.ru
├── public
│   ├── favicon.ico
│   ├── images
│   ├── javascripts
│   └── stylesheets
└── tmp

といった具合になっています。(Sinatraだと手動であれこれ掘らないとダメだったのですが楽…)
ひとまず動かしてみます。現状、mongoidをORMに指定したのでlocalhostのMongoDBに接続しに行くのでこれを止めます。

# config/database.rb
# Connection.new takes host, port
host = 'localhost'
port = Mongo::Connection::DEFAULT_PORT
database_name = case Padrino.env
when :development then 'testapp_development'
when :production  then 'testapp_production'
when :test        then 'testapp_test'
end
# 以下コメントアウト
#Mongoid.database = Mongo::Connection.new(host, port).db(database_name)

Sinatra流儀にひとまずapp.rbに直接 / へアクセスされた際の挙動を追加してみます。
単純にHello返すのみ。

# app/app.rb
class Testapp < Padrino::Application
register Padrino::Rendering
register Padrino::Mailer
register Padrino::Helpers
enable :sessions
get '/' do
"Hello, Padrino!"
end

ここまでで既に動くはずなので、rackupしてみます。

% rackup
[2012-06-10 14:33:47] INFO  WEBrick 1.3.1
[2012-06-10 14:33:47] INFO  ruby 1.9.3 (2012-02-16) [x86_64-darwin11.3.0]
[2012-06-10 14:33:47] INFO  WEBrick::HTTPServer#start: pid=36264 port=9292

といった具合にWEBrickが動き始めるので、ブラウザで http://localhost:9292 へアクセスすると表示を確認できます。
またコンソールを確認すると、ログ出力も確認できます。

[2012-06-10 14:33:47] INFO  WEBrick::HTTPServer#start: pid=36264 port=9292
DEBUG -      GET (0.0079ms) / - 200 OK
127.0.0.1 - - [10/Jun/2012 14:34:42] "GET / HTTP/1.1" 200 15 0.0175
127.0.0.1 - - [10/Jun/2012 14:34:42] "GET /favicon.ico HTTP/1.1" 200 - 0.0081

ひとまず動いたことだけは確認できました。
このままでは、Sinatraとほとんど変わりませんが、画面数を増やしていく場合にコントローラを分離して記述ができたりするのでそのあたりは後で書く…。
Sinatraの様にシンプルでありながら、Railsの様なMVCパタンも入れられるところが自分好みな感じがします。(触り始めて数時間の感想)