2012-06-03
_ サーバーをさくらの VPS(v3) に引っ越した
サーバーをリニューアルしたさくらの VPS(v3) に引っ越しました.引っ越しついでにフロントエンドを nginx にして,tDiary を unicorn で走らせるようにしています.移行ではまったのが contrib のgoogle_sitemaps.rb plugin で ENV['REQUEST_URI']と参照してるような部分があって,この部分が値を参照できずに落ちてて動かなかった.@cgi.request_uri にすればたぶんよいと思うのであとで修正したものをpushしておこう.
ついでにこれまでの Wiki 記法から GFM 記法に変えてみた.これはしばらく書きながら覚えていくかな.同じ段落でアンダースコアが 2 つあると強調表示されてしまうので,アンダースコアを表示したいときは,片方をバックスラッシュでエスケープしないといけない(\_みたいに)とか,使ってみて気がつく部分もありそう.
サーバーの環境構築までの記録は雑多なメモがあるので,整理をしてまた書くことにする.
2012-06-04
_ tDiary を unicorn で動かすようになってどのくらい早くなったか
昨日から,ここの tDiary を unicorn で動かすようにしたのですが,外部からの応答時間はどの程度変わったのか,Site24x7のレポートで確認してみました.画像は昨日から今日にかけての応答時間の推移を表したもの.
移行前の平均応答時間は 1714 ミリ秒であったものが,移行後の平均応答時間は 611 ミリ秒になりました.サーバー自体も変わってしまったので,一概に unicorn に変えたことだけではないかもしれませんが,ずいぶんと早くなったようです.
_ feedburner 経由での RSS 配信で文字化けしていますした
フロントを nginx に変えたのが関係しているのか,feedburner 経由での RSS 配信で文字化けしています.怪しそうなところで元フィードを取得したときの content-type が application/octet-stream になっていたので,application/rdf+xml を返すように nginx の設定を変更しました.
/etc/nginx/mime.types に以下を追加して,文字化けは解消できました.
application/rdf+xml rdf;
2012-06-19
_ tDiary を unicorn で動かすまでのもろもろ
サーバーの引っ越し作業を機に tDiary を Unicorn で動かすようにしたので,このサーバーでの設定について記録しておきます.サーバーの環境は以下の通り.
- さくらの VPS 1GB
- Ubuntu 12.04 Server (64bit)
- Ruby 1.9.3-p194 (shared install of rbenv)
文中でのコマンドなどは必要に応じて sudo などで実行しています.
最新の tDiary を入手する
最新の安定版は 3.1.3 ですが,リリース後に特に Rack 環境まわりの変更が充実しているので,最新版を github から clone します.ちなみにこのサーバーでは tDiary を/opt/tdiary 以下で管理し,所有者を www-data ユーザーとしています.環境構築時は作業ユーザーの権限にしておいて,最後に必要に応じて権限を変更するほうがいろいろ楽かもしれないです.
$ mkdir /opt/tdiary
$ cd /opt/tdiary
$ git clone git://github.com/tdiary/tdiary-core.git
必要なライブラリのインストール
Rack 環境で動作させるために必要なライブラリについては,Gemfile で定義されているので,bundler を利用してインストールします.特にまっさらな環境の場合,coffeescript から execjs を介して JavaScript ランタイムを実行するので,nodejs などなんらかの JavaScript ランタイムがインストールされている必要があります.ここでは Gemfile に therubyracer を追加して対応することにしました.現在は Gemfile に記述されているので,追加する必要はありません.
diff --git a/Gemfile b/Gemfile
index 9ff550f..ee45113 100644
--- a/Gemfile
+++ b/Gemfile
@@ -2,6 +2,7 @@ source :rubygems
gem 'rake'
gem 'coffee-script'
+gem 'therubyracer'
# Use rack environment
gem 'rack'
Gemfile へ therubyracer を追加したら,bundler をインストールし,その後,必要なライブラリをインストールします.インストール先は tdiary-core/.bundle 以下とし,test,development,production グループのライブラリは除外しています.production グループは主に Heroku 向けのライブラリが定義されていて,今のところ,DB も memcached も使わないので除外しています.
$ cd /opt/tdiary
$ rbevn local 1.9.3-p194
$ gem install bundler
$ rbenv rehash
$ cd tdiary-core
$ bundle install --path .bundle --without test development production
tdiary.conf を作成して rackup してみる
tDiary の設定ファイル tdiary.conf を作成します.tdiary.conf.sample をコピーして必要な部分を修正すれば基本的に問題ありません.日記データの保存先を指定する @data_path に適切なパスを指定します.書き込み先は,最終的に unicorn を実行するユーザーが書き込み権限のある場所を指定します.
$ cp tdiary.conf.sample tdiary.conf
ここまでこればひとまず rackup コマンドでアプリケーションを実行して動作の確認ができます.
$ bundle exec rackup
ブラウザでアクセスしてみて表示されればとりあえず問題なし.tdiary.conf で @index が未設定の場合は,リダイレクト先の URL がデフォルト値として"./"が指定されてアクセスできない場合がありますが,"."を消せばアクセスできます.バグのようだったので修正しました.この現象が起きるのは初回アクセス時のみのよう.
更新ページに認証をかける
認証関係は CGI の時は Web サーバーで行っていたけど,Rack 環境では OmniAuth を利用した Twitter や Github のアカウントでの認証もできるようになっています.このサーバーでは従来と同じような .htpasswd を利用した認証にしています.apache2-utils パッケージなどがインストールされていると,htpasswd コマンドを実行して,,htpasswd ファイルの作成もできますが,tDiary では .htpasswd を作成するための rake コマンドが用意されているので,わざわざインストールしてなくてもよいので便利です.
$ RACK_ENV=production bundle exec rake auth:password:create
ユーザー名とパスワードの入力をすると.htpasswd ファイルが出力されます.
Unicorn で動かす
Unicorn で実行するために,設定ファイルとして unicorn.rb を作成します.中身は,https://github.com/blog/517-unicorn を参考にしています.PID ファイルやログの出力先などは,Ubuntu で標準的に出力される場所に合わせています.また,unicorn のプロセスは master プロセスを root ユーザーで実行し,fork したプロセスは www-data ユーザーで実行するようになっています.
worker_processes 2
app_dir = File.expand_path( File.dirname( __FILE__ ) )
working_directory app_dir
preload_app true
timeout 30
listen "/tmp/tdiary.sock", :backlog => 64
listen 3000, :tcp_nopush => true
pid "/var/run/tdiary.pid"
stderr_path "/var/log/tdiary/stderr.log"
stdout_path "/var/log/tdiary/stdout.log"
before_fork do |server, worker|
old_pid = "/var/run/tdiary.pid.oldbin"
if File.exists?( old_pid ) && server.pid != old_pid
begin
Process.kill( "QUIT", File.read( old_pid ).to_i )
rescue Errno::ENOENT, Errno::ESRCH
end
end
end
after_fork do |server, worker|
begin
uid, gid = Process.euid, Process.egid
user, group = "www-data", "www-data"
target_uid = Etc.getpwnam( user ).uid
target_gid = Etc.getpwnam( group ).gid
worker.tmp.chown( target_uid, target_gid )
if uid != target_uid || gid != target_gid
Process.initgroups( user, target_gid )
Process::GID.change_privilege( target_gid )
Process::UID.change_privilege( target_uid )
end
rescue => e
STDERR.puts "couldn't change user, oh well"
raise e
end
end
unicorn をインストールし,unicorn.rb で指定したログ出力先のディレクトリを作成します.
$ gem install unicorn
$ rbenv rehash
$ sudo mkdir /var/log/tdiary
これで tDiary を unicorn で実行することが可能になります.実行ユーザーは root 権限でないと,上記の設定では PID ファイルの作成で権限がなくエラーとなるので,以下のように実行すると確認できます.
$ sudo /usr/local/rbenv/versions/1.9.3-p194/bin/unicorn -c /opt/tdiary/tdiary-core/unicorn.rb
ここから先は主に運用する際に必要な設定手順について.
tDiary をサービスとして登録する
upstart を利用して tDiary をサーバー起動時に立ち上がるように設定します./etc/init 以下に tdiary.conf ファイルを作成し,以下のような設定にしました.
#!/bin/bash
description "tDairy"
start on runlevel [2345]
stop on runlevel [016]
script
export LANG=en_US.UTF-8
exec /usr/local/rbenv/versions/1.9.3-p194/bin/unicorn -c /opt/tdiary/tdiary-core/unicorn.rb
end script
respawn
これにより service コマンドで tDiary の stop/start が行えるようになります.
$ sudo service tdiary start
ログをローテーションする
しばらく運用していくとログファイルが大きくなってくるのが気になってきたので,logrotate を利用してログのローテーションを行うようにしました./etc/logrotate.d 以下に設定ファイルを配置してローテーションさせるようにします.最初に起動したときに出力されたログファイルの所有者が root の場合,reopen に失敗するので出力するログファイルの所有者は www-data とするように指定します.また,Unicorn は USR1を送るとログファイルを reopen してくれるのでローテーション後に USR1を送るように処理を含めている.
/var/log/tdiary/*.log {
daily
missingok
rotate 52
compress
delaycompress
notifempty
create 0640 www-data adm
sharedscripts
postrotate
[ ! -f /var/run/tdiary.pid ] || kill -USR1 `cat /var/run/tdiary.pid`
endscript
}
肥大化した Unicorn プロセスを止める
運用して気になるのは fork したプロセスが肥大化している点.一定数のリクエストをさばくか,既定のメモリ使用量を超えるとプロセスを Kill する便利な unicon_killer を config.ru に追加してみました.
diff --git a/config.ru b/config.ru
index 1a169c7..299e968 100644
--- a/config.ru
+++ b/config.ru
@@ -17,6 +17,12 @@ use OmniAuth::Builder do
# provider :twitter, ENV['TWITTER_KEY'], ENV['TWITTER_SECRET']
# provider :github, ENV['GITHUB_KEY'], ENV['GITHUB_SECRET']
end
+
+# Unicorn Killer
+require 'unicorn_killer'
+use UnicornKiller::Oom, 60 * 1024
+use UnicornKiller::MaxRequests, 1000
+
map "#{base_dir}/auth" do
run TDiary::CallbackHandler.new
end
というわけで,このサーバーで tDiary が動くまでの手順でした.Unicorn で実行してしばらくたちますが,特に問題もなく安定して動作しています.レスポンスもなかなかよいので,新しく環境を作るタイミングなどで移行してみてもよいのではないでしょうか.
2012-06-20
_ tDiary を GFM スタイルで利用したときに整形済みテキストのプレビューでエラーになった
サーバー移転後から GFM スタイルで書くように変えてみたけど,複数行の整形済みテキストを利用してプレビュー表示したら以下のようなエラーが表示された.
LoadError: Could not open library 'lib.so': lib.so: cannot open shared object file: No such file or directory
/opt/tdiary/tdiary-core/vendor/bundle/ruby/1.9.1/gems/ffi-1.0.11/lib/ffi/library.rb:121:in `block in ffi_lib'
/opt/tdiary/tdiary-core/vendor/bundle/ruby/1.9.1/gems/ffi-1.0.11/lib/ffi/library.rbin `map'
/opt/tdiary/tdiary-core/vendor/bundle/ruby/1.9.1/gems/ffi-1.0.11/lib/ffi/library.rb:88:in `ffi_lib'
/opt/tdiary/tdiary-core/vendor/bundle/ruby/1.9.1/gems/rubypython-0.5.1/lib/rubypython/python.rbin `<module:Python>'
/opt/tdiary/tdiary-core/vendor/bundle/ruby/1.9.1/gems/rubypython-0.5.1/lib/rubypython/python.rb:21:in `<top (required)>'
/opt/tdiary/tdiary-core/vendor/bundle/ruby/1.9.1/gems/rubypython-0.5.1/lib/rubypython.rbin `load'
/opt/tdiary/tdiary-core/vendor/bundle/ruby/1.9.1/gems/rubypython-0.5.1/lib/rubypython.rb:261:in `reload_library'
/opt/tdiary/tdiary-core/vendor/bundle/ruby/1.9.1/gems/rubypython-0.5.1/lib/rubypython.rbin `start'
/opt/tdiary/tdiary-core/vendor/bundle/ruby/1.9.1/gems/pygments.rb-0.2.3/lib/pygments/ffi.rb:8:in `start'
/opt/tdiary/tdiary-core/vendor/bundle/ruby/1.9.1/gems/pygments.rb-0.2.3/lib/pygments/ffi.rbin `highlight'
/opt/tdiary/tdiary-core/tdiary/style/gfm_style.rb:24:in `block_code'
trace を見るに rubypython あたりでなんかおかしなよう.python は実行可能だけど,dev パッケージはインストールされていなかったので,python-dev パッケージをインストールすると上記の問題は解決できた.
解決して整形済みテキストが表示されると,highlight クラス属性を持つ div で pre が wrap されているため,highlight プラグインを有効にしていると,pre 部分の背景色が指定されてしまうという現象に遭遇した.これについては以下をスタイルシートに追加して対応した.
div.highlight {
background-color: transparent;
}
2012-06-23
_ 温めずにおいしい!夏のカレーを食べてみた
忙しいときとか面倒なときに簡単に食事できるようにレトルトのカレーを常備しておくのですが,先日,買い物にいったら温めずにおいしい!夏のカレーなるものを見かけたので,ものは試しにと買ってみた.ちなみに昨年も発売されていたらしく,好評だったらしい.
カレーは説明通り青唐辛子の辛さがいい感じにさわやかな辛さ(という表現がいいのかわからないけど…)のカレーで,本当に温めなくてもおいしく食べられた.ご飯は温かいので,一緒に食べるとちょうどいい具合の温度になる感じ.
この夏のカレーは期間限定の商品だけど,通年で販売されている温めずにおいしいカレーもあるみたい.
_ hsbt [rss がなにやら文字化けしているようです]
_ hb [Feedburner経由で配信しているのですが,そちらで文字化けしてしまっているようで,現在調査中です.]