monoの開発ブログ

home

最近のRubyなWebアプリの構成

24 Jun 2012

私が運用しているWebアプリ (Perfume Headlineとかこのブログとか) で使っているライブラリなどを紹介します。つまり、最近とは最近流行りという意味ではなく、あくまで最近私が使っているという意味に過ぎません。あしからず。

紹介するのは以下のものです。こうやって並べるとほんとにたくさんのソフトウェアに支えられていることがわかりますね。感謝しながら使いたいですね。

Webアプリケーションフレームワーク

小規模なWebアプリのつもりだったのでSinatraを採用し、現在も使い続けています。が、何だかんだいっていろいろほしくなってくるので、周辺ライブラリの揃っているRailsのほうが楽な気がします。

データベース

サイトの性質上書き込みはほぼ無いのでSQLiteを使っています。ORマッパはDataMapperです。ActiveRecordは単独でも使えるので、Railsを使わない場合でもActiveRecordを使った方がよかった気がします。

テンプレートエンジン

HTMLはHaml、CSSはSassから生成しています。さらに、Sass向けのCSSフレームワークであるCompassを利用しています。CSS3の先行実装を利用する場合、ベンダープレフィックス付きのプロパティを何個も書くのは非現実的です。そのために正しく記述していないサイトが蔓延ってしまったわけですが、Compassを使えば1行の記述から必要なプロパティをすべて生成してくれます。現段階でCSS3を利用するのであれば、CSSの手書きはやめましょう。

例えば、linear-gradientを利用する場合、次のように記述するだけでOKです。

$experimental-support-for-svg: true; // SVGを生成する
@include background-image(linear-gradient(left, #f00, #ff0 20%, #0f0 40%, #0ff 60%, #00f 80%, #f0f));

この記述から以下のようなCSSが生成されます。

background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iNTAlIiB4Mj0iMTAwJSIgeTI9IjUwJSI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2ZmMDAwMCIvPjxzdG9wIG9mZnNldD0iMjAlIiBzdG9wLWNvbG9yPSIjZmZmZjAwIi8+PHN0b3Agb2Zmc2V0PSI0MCUiIHN0b3AtY29sb3I9IiMwMGZmMDAiLz48c3RvcCBvZmZzZXQ9IjYwJSIgc3RvcC1jb2xvcj0iIzAwZmZmZiIvPjxzdG9wIG9mZnNldD0iODAlIiBzdG9wLWNvbG9yPSIjMDAwMGZmIi8+PHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSIjZmYwMGZmIi8+PC9saW5lYXJHcmFkaWVudD48L2RlZnM+PHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0idXJsKCNncmFkKSIgLz48L3N2Zz4g');
background-size: 100%;
background-image: -webkit-gradient(linear, 0% 50%, 100% 50%, color-stop(0%, #ff0000), color-stop(20%, #ffff00), color-stop(40%, #00ff00), color-stop(60%, #00ffff), color-stop(80%, #0000ff), color-stop(100%, #ff00ff));
background-image: -webkit-linear-gradient(left, #ff0000,#ffff00 20%,#00ff00 40%,#00ffff 60%,#0000ff 80%,#ff00ff);
background-image: -moz-linear-gradient(left, #ff0000,#ffff00 20%,#00ff00 40%,#00ffff 60%,#0000ff 80%,#ff00ff);
background-image: -o-linear-gradient(left, #ff0000,#ffff00 20%,#00ff00 40%,#00ffff 60%,#0000ff 80%,#ff00ff);
background-image: -ms-linear-gradient(left, #ff0000,#ffff00 20%,#00ff00 40%,#00ffff 60%,#0000ff 80%,#ff00ff);

アセット管理

複数のCSSやJavaScriptをまとめたり、キャッシュを防ぐためにファイル名にハッシュ値を含ませたりするためにspeocketsを利用しています。sprockets-helpersとsprockets-sassも併用すると便利です。

管理用コマンド

データベースのマイグレーション・アップグレードやクローラの実行などはThorを利用してコマンド化しています。

バッチ

cronを利用するとデプロイが面倒なので、rufus-schedulerを利用してアプリケーション内にバッチの実行機能を組み込んでいます。以下のようなプログラムを作成して実行するだけでcronのようにスケジュールに従って処理を行えます。

require 'rufus/scheduler'

scheduler = Rufus::Scheduler.start_new

scheduler.in '10m' do
  system '...'
end

scheduler.every '5m' do
  system '...'
end

scheduler.at 'Tue Jun 19 21:37:17 +0900 2012' do
  system '...'
end

scheduler.cron '0 * * * *' do
  system '...'
end

scheduler.join

プロセス起動

foremanを利用して必要なプロセスをまとめて起動できるようにしています。Procfileに以下のように記述しておけば、 foreman start コマンド一発でWebサーバとバッチのスケジューラを起動できます。

web: bundle exec unicorn -c unicorn.conf -p $PORT -E production
job: bundle exec ruby schedule.rb

死活監視

アプリケーションの死活監視にはdaemontoolsを利用しています。foremanを利用した場合、foremanのプロセスに子プロセスがぶら下がり、子プロセスのどれかが終了するとforemanも含めてすべてのプロセスが終了するため、foremanのプロセスを監視して自動的に再起動するようにします。

#!/bin/sh
exec 2>&1
exec setuidgid user zsh -c 'cd /path/to/app; exec foreman start'

デプロイ

複雑な準備も必要ないので、今のところデプロイはgit pullするだけで済ませています。daemontoolsやnginx、Varnishなどの設定はすべて手動で行っています。サーバは1台だけなので特に困っていないですが、まじめにやるならcapistranoとかを使うんでしょうかね。

Webサーバ

本番用にはUnicorn、開発用にはPowを利用しています。特にこだわりはないです。Powはファイルを変更したときに手動で再起動する必要がありますが、guard-powを利用すると自動化できるので便利です。

キャッシュサーバ

キャッシュサーバとしてVarnishを利用しています。ユーザによってHTMLを吐き分ける必要がないため、アプリケーションではなく外側でキャッシュしてしまうのが楽だろうというのと、nginxだとキャッシュの削除を柔軟に行いづらいというのがVarnishを利用している理由です。

リバースプロキシ

1台のVPSに複数のアプリを突っ込んでいるので、Nginxで振り分けています。VPSだとメモリ的な意味でNginxが使いやすいです。Varnishさえあれば十分だと思われるかもしれませんが、複数のサイトの中には静的なファイルのみのサイトも含まれているので、それらを処理するためにNginxを利用しています。

テスト

テストにはRSpec、フィクスチャの定義にはfactory_girlを利用しています。テストの実行にはguard-rspecを利用しています。