Railsアプリの本番環境での運用 -godとか-
perlでdaemontools + Server::Starterで面倒見てたあたりをRailsでもしっかりやりたいなぁと。
以外としっかりとやってるサンプルがウェブに落ちてなかったので、いろいろ調べました。
いろいろ検証してみたけど、最終的に以下のように落ち着きそうです。試行錯誤した中身もまとめたい気もする。
やること
- graceful restart
- Railsアプリの永続化
- 永続化してるやつの自動起動とか永続化とか
- capistranoからアプリの再起動などできるように
終着点
Railsアプリをgod + unicornで上げて、godをinit script置いてchkconfigで自動起動とかの設定。
あと、capistranoからgod監視下のアプリを再起動できるように
unicornをgodで動かす
よくあるやつ。
rails_env = ENV['RAILS_ENV'] || 'production' rails_root = ENV['RAILS_ROOT'] || '/path/to/application' God.watch do |w| w.name = 'application_name' w.interval = 30.seconds w.uid = 'app' w.gid = 'app' w.start = "bundle exec -- unicorn_rails -c #{rails_root}/config/unicorn.rb -E #{rails_env} -D" w.stop = "kill -QUIT `cat #{rails_root}/tmp/pids/unicorn.pid`" w.restart = "kill -USR2 `cat #{rails_root}/tmp/pids/unicorn.pid`" w.start_grace = 10.seconds w.restart_grace = 10.seconds w.pid_file = "#{rails_root}/tmp/pids/unicorn.pid" w.log = File.expand_path(File.join(File.dirname(__FILE__), '..', 'log', 'god.log')) w.behavior(:clean_pid_file) w.start_if do |start| start.condition(:process_running) do |c| c.interval = 5.seconds c.running = false end end w.restart_if do |restart| restart.condition(:memory_usage) do |c| c.above = 300.megabytes c.times = [3, 5] end restart.condition(:cpu_usage) do |c| c.above = 50.percent c.times = 5 end end w.lifecycle do |on| on.condition(:flapping) do |c| c.to_state = [:start, :restart] c.times = 5 c.within = 5.minutes c.transition = :unmonitored c.retry_in = 10.minutes c.retry_times = 5 c.retry_within = 2.hours end end end
init script
はこんな感じ。
/etc/init.d/godに置く。
#!/bin/bash . /etc/rc.d/init.d/functions RBENV_ROOT=/opt/rbenv RBENV_VERSION="" export RBENV_VERSION PATH=$RBENV_ROOT/shims:$RBENV_ROOT/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin RUBY_PATH=$RBENV_ROOT/shims/ruby DAEMON=$RBENV_ROOT/shims/god PID_FILE=/var/run/god.pid LOG_FILE=/var/log/god.log SCRIPT_NAME=/etc/init.d/god CONFIG_FILE_DIR=/etc/god #DEBUG_OPTIONS="--log-level debug" DEBUG_OPTIONS="" test -x $DAEMON || exit 0 RETVAL=0 god_start() { start_cmd="$DAEMON -l $LOG_FILE -P $PID_FILE $DEBUG_OPTIONS" echo $start_cmd $start_cmd || echo -en "god already running" RETVAL=$? if [ "$RETVAL" == '0' ]; then sleep 2 if [ -d $CONFIG_FILE_DIR ]; then echo "god: loading master config" $DAEMON load $CONFIG_FILE_DIR/master.conf fi fi return $RETVAL } god_stop() { stop_cmd="god terminate" echo $stop_cmd $stop_cmd || echo -en "god not running" } case "$1" in start) god_start RETVAL=$? ;; stop) god_stop RETVAL=$? ;; restart) god_stop god_start RETVAL=$? ;; status) $DAEMON status RETVAL=$? ;; *) echo "Usage: god {start|stop|restart|status}" exit 1 ;; esac exit $RETVAL
で、/etc/god/master.confに各設定のloadを書く。
ここは、/etc/god以下にconfig fileを複数用意するのもありだけど、
master.confだけ置いて中でloadする形にした。
capistranoのtask.
capistrano 3なので、lib/capistrano/tasks/god.capとかに置く感じで。
namespace :god do task :environment do set :god_config, "#{current_path}/config/god/#{fetch(:rails_env)}.rb" end task :start do on roles(:web) do execute :sudo, "/opt/rbenv/shims/god start #{fetch(:application)}" end end task :restart do on roles(:web) do execute :sudo, "/opt/rbenv/shims/god restart #{fetch(:application)}" end end task :stop do on roles(:web) do execute :sudo, "/opt/rbenv/shims/god stop #{fetch(:application)}" end end end
これでcapistrano経由でrestartとかできる。
god(というかrbenv)のpathは分離したい感じだ。
capistrano-unicornとか使うとcapistrano経由でunicornでアプリ上げれるけど、永続化とかサーバ再起動時にアプリがあがらないのとか気になってたのでちゃんと調べた。冗長なサーバ構成である程度アプリ落ちても動作する+監視で落ちたのに気づけるというところで少しは大丈夫なところもあるのかもと思いもしたけど、やっぱり気になったので。
zshの設定紹介
ふだん僕が使ってるzshの設定を紹介します。
学生時代からzsh使ってて特に気にせず使ってるのですが、新しいPCとかに移るとどんだけzshに助けられてたかを実感したりするわけです。
export WORDCHARS="*?_-.[]~=&;!#$%^(){}<>+"
単語境界にならない文字の設定。/が抜いてあるので、ファイルパスの入力中に^wで戻ると1ディレクトリ分戻れて便利。
autoload -U colors colors PS1="[%{${fg[green]}%}%t%{${fg[default]}%}]%# "
色を使えるようにする設定と、お気に入りの左prompt。時間が表示されます。
autoload -U compinit compinit
いろんな補完を有効にする設定。とりあえずこれ。
optionの補完とかもしてくれてすごく便利。
zstyle ':completion:*' matcher-list 'm:{a-z}={A-Z}'
補完するときに小文字入力でも大文字にマッチするようにする。しばらくなしでやってみたけど、やっぱりこれがないとってなった。
SAVEHIST=100000 HISTSIZE=100000 HISTFILE=~/.histfile setopt SHARE_HISTORY setopt HIST_IGNORE_DUPS setopt HIST_REDUCE_BLANKS setopt HIST_NO_STORE if [ $UID = 0 ]; then unset HISTFILE SAVEHIST=0 fi
コマンドヒストリ関連の設定。
terminal複数開いてる場合とかにヒストリ共有されるのですごい便利。
DIRSTACKSIZE=10 setopt AUTO_PUSHD setopt PUSHD_IGNORE_DUPS setopt PUSHD_SILENT
cdで異動するときに勝手にpushdしたことにする設定。popdすれば前いたディレクトリに戻れる。便利。
bindkey "^P" history-beginning-search-backward bindkey "^N" history-beginning-search-forward
ヒストリサーチのキーバインド設定。
setopt prompt_subst autoload -Uz VCS_INFO_get_data_git; VCS_INFO_get_data_git 2> /dev/null function rprompt-git-current-branch { local name st color gitdir action if "$PWD" =~ '/\.git(/.*)?$' ; then return fi name=$(basename "`git symbolic-ref HEAD 2> /dev/null`") if -z $name ; then return fi gitdir=`git rev-parse --git-dir 2> /dev/null` action=`VCS_INFO_git_getaction "$gitdir"` && action="($action)" st=`git status 2> /dev/null` if -n `echo "$st" | grep "^nothing to"` ; then color=%F{green} elif -n `echo "$st" | grep "^nothing added"` ; then color=%F{yellow} elif -n `echo "$st" | grep "^# Untracked"` ; then color=%B%F{red} else color=%F{red} fi echo "$color$name$action%f%b" } RPS1='[`rprompt-git-current-branch`]'"[%{${fg[cyan]}%}%2~%{${fg[default]}%}]"
http://d.hatena.ne.jp/uasi/20091025/1256458798
こちらの記事かな。
gitのブランチ名を作業状態に応じた色で表示します。あと、今いるディレクトリをひとつ上の階層まで表示。お気に入りのプロンプト設定その2ですね。
setopt NULL_GLOB setopt EXTENDED_GLOB
glob関連の設定。EXTENDED_GLOB設定するとTabでglobが展開されます。
setopt MENU_COMPLETE setopt IGNORE_EOF setopt NO_CLOBBER setopt RM_STAR_WAIT
MENU_COMPLETE: Tab押したときに最初の補完候補をいきなり補完する。
IGNORE_EOF: ^dでターミナルを抜けない。
NO_CLOBBER: リダイレクトですでにファイルが存在する場合に上書きせずにエラーに
RM_STAR_WAIT: rm *実行時に確認される
setopt CORRECT_ALL alias mv='nocorrect mv' alias cp='nocorrect cp' alias vi='nocorrect vi' alias git='nocorrect git' alias touch='nocorrect touch' alias mkdir='nocorrect mkdir'
ファイル名が間違ってたときとかに修正する?って聞いてくれる。ちょっとやりすぎだった気がする。
ざっとこんな感じです。
ネット上にいくらでも落ちてるよなーと思いつつも、とりあえず。
よく使うvagrant plugin
最近書けてなかったので、メモがてら、よく使っているVagrantのpluginをいくつか紹介します。
sahara
GitHub - jedi4ever/sahara: a plugin for vagrant that allows you manage a sandbox state
vagrantのboxの状態を保存して、必要に応じて巻き戻したりできるようにするpluginです。
chef試したりしてるときにけっこう便利です。
# install $ vagrant plugin install sahara # sandbox modeをonに $ vagrant sandbox on # boxの状態を保存 $ vagrant sandbox commit # 最後に保存した状態に巻き戻す $ vagrant sandbox rollback $ vagrant sandbox off
vagrant-global-status
http://www.ryuzee.com/contents/blog/6752
こちらの記事に詳しいです。いろんな案件でいくつも環境を作っているとどのプロジェクトに対応したboxが起動してるかを一発で把握するのがなかなか面倒になってきます。
そこでこれ。Vagrantfileの置かれたpathとそのVagrantfileで定義されたvmのstatusが一覧できるのですごく便利です。
$ vagrant plugin install vagrant-global-status $ vagrant global-status -a
vagrant-protect
http://www.ryuzee.com/contents/blog/6788
最後がこちら。ryuzeeさんのblogを追ってれば使えるvagrant pluginはだいたいキャッチアップできるのでは、と思ってたりします。
VirtualBoxのみ対応らしいですが、設定するとうっかりvagrant destroyを防止できます。
僕はローカルに環境作るときはchef使って構築する癖を付けてて、だいたいまっさらなところからコマンド2つ3つでアプリ動かせるようにしてるのですが、それでも1からbuildするの時間かかるし、必要な仮想マシン消しちゃうとグッとくるものがあるので、最近は使うようにしています。
$ vagrant plugin install vagrant-protect # Vagrantfile ... config.protect.enabled = true ...
まとめ
普段使ってるpluginをメモ代わりに紹介させてもらいました。
いつもすごい助かってます。
興味あってぼくもvagrant-configspecというpluginを書いてみたりもしたので、興味ある方はどうぞ。(実用的というよりは興味本位です)
Mac OS X(Mountain Lion)のErlangでwxを動かしたい(動かせてない)
Erlang/OTP trainingでobserver便利そうだったけど、手元で動かせてなかったので。
ちなみに、以下のようなわけで64bitのMac OS Xではwxは動かないらしいです。
http://erlang.org/pipermail/erlang-questions/2013-March/072687.html
以下を参考に入れてみました。
https://gist.github.com/jj1bdx/5441077
Xquartz, homebrew, kerl, gitが必要という事で、Boxen使っているのとkerlは前回入れたのでクリアということで、Xquartzをboxenで入れるところから。
$ vi /opt/boxen/repo/modules/people/manifests/ankoromochi.pp class people::ankoromochi { … package { 'XQuartz': provider => 'pkgdmg', source => 'http://xquartz.macosforge.org/downloads/SL/XQuartz-2.7.4.dmg'; } … } $ boxen
wxgtk.rbはさっきのgistにあるやつを持ってくる。
$ brew install wxgtk.rb ==> Downloading http://downloads.sourceforge.net/project/wxwindows/2.8.12/wxGTK-2.8.12.tar.gz ==> ./configure --with-libpng --with-opengl --with-libjpeg --with-libtiff --with-freetype --with-zlib --enable-unicode --prefix=/opt/boxen/homebrew/Cellar/wxgtk/2.8.12 ... ==> make install brew: superenv removed: -I/opt/X11/include -Wall -Wundef -Wno-ctor-dtor-privacy -O2 clang: error: cannot specify -o when generating multiple output files clang: error: cannot specify -o when generating multiple output files make: *** [.pch/wxprec_qadll/wx/wxprec.h.gch] Error 1 make: *** [.pch/wxprec_htmldll/wx/wxprec.h.gch] Error 1
error出たので、以下を参考に修正。
https://github.com/mxcl/homebrew/issues/22124
wxgtk.rbに--disable-precomp-headersのoptionを追加してもっかい。
無事入ったので、patchのあたったErlangをbuildする。boxen使ってるとwxgtkが入るpathが違う。
$ KERL_CONFIGURE_OPTIONS="--enable-darwin-64bit \ --disable-hipe \ --enable-kernel-poll \ --enable-threads \ --enable-smp-support \ --with-wxdir=/opt/boxen/homebrew/opt/wxgtk \ --with-wx-config=/opt/boxen/homebrew/opt/wxgtk/bin/wx-config" \ ./kerl build git https://github.com/jj1bdx/otp kr-r16b02-osx-wx kr-r16b02-osx-wx … beam/erl_bif_re.c:68:5: error: use of undeclared identifier 'erts_pcre_malloc'; did you mean 'erts_realloc'? erts_pcre_malloc = &erts_erts_pcre_malloc; ^~~~~~~~~~~~~~~~ …
と、ここでこける><
なぜか昨日は通ってた、全然関係ないErlangのbuildも通らなくなってしまった。
なんで通んなくなったんだろ・・・(まだ調べてる途中…)
今日はここまで。
Erlang/OTP training 2013に行ってきた
ふだん触ってないパラダイムの言語触るのはだいぶ楽しかったし、いい刺激になりました。
惜しいとすれば、すごい講師の方だったのでもう少し前提知識があればより貴重な情報を拾えただろうことと、@lhoguinはネットワーク系ならだいたいErlang使うって言ってたけど、会社ではなんだかんだPerl/Ruby/PHPになるので実践の機会がないとこですかね。あと、fault-tolerantなシステムの設計自体のハードルが高いです!!
Erlangは、関数型で平行プログラミングという僕にはあんまりなじみのない感じで面白かったです。あと、言語が高信頼度な分散システムを構築するための機能を提供してるってのもおもしろいですね。
講義内容は
https://gist.github.com/essen/6333394
な感じで、言語の特徴から基本的な概念の説明とか、途中から簡単な演習を挟みながら再帰やmessage passingなどについて説明していって、徐々にいろんな知識を積み上げながら、最後の方ではOTPのgen_server.erlを読むという感じでとても充実した内容でした。
けっこう細かいところまで説明していただいた感じでしたが、実践ではほぼOTP使うらしいので、OTP編やCowboyなどのWAF編なんかもあったらぜひ受けてみたいですね。
#ErlangTrainingJp でいろいろとつぶやかれてるので、追えば内容ぼんやりわかるかもしれません。
YAPCで聞いたRiakなんかはErlangのproductで、日本ではそんなに事例ないらしいけど、海外では運用されてるらしいので、ちょっと気になったり。
当日のスライド / ソースコードは以下に。
http://ninenines.eu/talks/thinking_in_erlang/thinking_in_erlang.html
https://gist.github.com/essen/6970232
あー、たのしかった。
たまにはこういう趣味の時間もいいもんだ。
Erlang/OTP training 2013に来てます
ひょんなきっかけで、Erlang/OTP trainingに参加して、Erlang触ってます。
普段使ってる言語とまったく違う感じで、すごいおもしろい。
言語自体がfault tolerantなsystemを作れるように、みたいな感じ。
key word的には
- concurrency
- error encapsulation
- fault detection
- fault identification
- live code upgrade
- stable storage
とか。
講義の内容はざっと
thinking_in_erlang.md · GitHub
こんな感じで、講師はCowboyっていう軽量HTTPサーバの作者のLoïc Hoguinさん。
なんだか、去年のErlang user of the yearらしい。
というわけで、installing Erlang on OS X 10.8.4
いろいろあるけど、詳しくは公式docment (Erlang -- Building and Installing Erlang/OTP)で、
一番簡単なのは
https://www.erlang-solutions.com/downloads/download-erlang-otp
にあるdmgからいれるのっぽい。
ぼくはここ(Linuxでkerlを使用して複数バーションのErlang/OTPを導入する - Qiita)を参考にして、kerl使っていれました。
kerl (GitHub - kerl/kerl: Easy building and installing of Erlang/OTP instances)はperlbrewみたいにbuild & installしてくれるやつ。
$ echo 'KERL_CONFIGURE_OPTIONS="--disable-hipe --enable-smp-support --enable-threads --enable-kernel-poll --enable-darwin-64bit" ' > ~/.kerlrc $ curl -O https://raw.github.com/spawngrid/kerl/master/kerl $ chmod a+x kerl $ ./kerl build R16B02 r16b02 $ ./kerl install r16b02 ~/erlang/r16b02 $ . ~/erlang/r16b02/activate
これで、Erlang使えるようになります。
% erl Erlang R16B02 (erts-5.10.3) [source] [64-bit] [smp:8:8] [async-threads:10] [kernel-poll:false] Eshell V5.10.3 (abort with ^G) 1>
Cloud Watchのカスタムメトリクスで特定のurlのhttp status codeを監視する
特定のurlのhttp status codeはこんな感じのshell scriptでとれる。
知らなかったけど、curlいろいろ出力整形できて便利だった。
$ cat /home/ec2-user/cloudwatch/http_status_check.sh #!/bin/sh curl -s $1 -o /dev/null -w "%{http_code}"
あとは、cloud watchの権限持ったユーザをIAMで作って、key/secretをcredentialsファイルとして設置したらこんな感じでcloud watchにデータ送れるのでcronにセットするなどして、そいつにアラートつくる。PATHちゃんと設定するかフルパス指定しないと、cronからmon-put-dataが見えないので注意。
$ cat /home/ec2-user/cloudwatch/custum_metrics.sh #!/bin/bash export JAVA_HOME=/usr/lib/jvm/jre export AWS_CLOUDWATCH_HOME=/opt/aws/apitools/mon export EC2_REGION=ap-northeast-1 export AWS_CREDENTIAL_FILE=/home/ec2-user/cloudwatch/credentials instanceid=i-****** status=`/home/ec2-user/cloudwatch/http_status_check.sh http://example.com` if [ $status -eq 200 ]; then Fail=0 else Fail=1 fi /opt/aws/bin/mon-put-data --namespace "Custom Metrix" --metric-name "Http Status ok" --dimensions "InstanceId=$instanceid" --value "$Fail" --unit "Count"