CapistranoでVagrantにデプロイした際、なぜか設定したはずの環境変数が適用されなかった。それはserviceコマンドの挙動が原因だった。という話です。
経緯
おなじみRailscasts#335で紹介された方法で、いったんデプロイには成功した。
しかしその後、Raiscasts#337を参考にconfig/recipesにCapistranoの設定を分割してからVagrantで作った環境にデプロイしたところ、環境変数が読み込まれなかった。
大問題である。
環境変数には、他人には見られたくないAmazon S3のパスワードなどが入っている。asset_syncやfogを使っているので、アプリケーションにはどうしても環境変数の設定が必要。これらを本番環境で動くサーバにセットせねばならない。
選択肢として、application.ymlにS3のパスワードなどを書いて、本番環境に置くという方法もある。だができれば本番環境上で作業したくない。全部Capistranoですませたい。どうすればよいか?
余談
ちなみに、複数サーバにCapistranoでデプロイするのはとても簡単。GithubのCapistrano Wikiにやり方が書いてある。
https://github.com/capistrano/capistrano/wiki/2.x-Multistage-Extension
以前はプラグインとして提供されていたらしいcapistrano-extはすでにcapistrano内に入っている。 なので、
set :stages, %w(production vagrant)
set :default_stage, "vagrant"
require "capistrano/ext/multistage"
とするだけで、複数ステージにデプロイ可能。
config/deployディレクトリを作って
server "1xx.xxx.xxx.xx", :web, :app, :db, primary: true
set :user, "katryo"
set :port, 10022 #22から変更した
set :deploy_to, "/home/#{user}/apps/#{application}"
server "localhost", :web, :app, :db, primary: true
set :user, "vagrant"
set :port, 2222
set :deploy_to, "/home/#{user}/apps/#{application}"
ssh_options[:keys] = [
"#{ENV['HOME']}/.ssh/id_rsa",
"#{ENV['HOME']}/.vagrant.d/insecure_private_key"
]
とdeployディレクトリに各サーバの設定を別々に書けばよい。参考:
http://d.hatena.ne.jp/okinaka/20120520/1337476743
set :deploy_toが重複していて気持ち悪いけど、読み込みのタイミング上、production.rbとvagrant.rbのuserが読み込まれてからdeploy_toを設定する必要があるのでしかたなく2回同じ文を書いた。
設定したはずの環境変数がなぜ適用されない?
Capistranoでは、
ENV.update YAML.load(File.read(File.expand_path('../application.yml', __FILE__)))
set :default_environment, {
"AWS_ACCESS_KEY_ID" => ENV["AWS_ACCESS_KEY_ID"],
"AWS_SECRET_ACCESS_KEY" => ENV["AWS_SECRET_ACCESS_KEY"]
}
のように:default_environmentにsetすれば、シェルスクリプトを実行するrunの際に環境変数をセットできる。だから、開発環境の環境変数を引き継いで実行できる……はずだ。
この記事の環境変数の項でもそれについては説明した。 http://qiita.com/items/f410916d5314dad2de96
実際に、前回のデプロイでは環境変数の引き継ぎに成功した。なぜ今回に限って引き継ぎに失敗するのか?
前回の手法をもういちど試したところ、引き継ぎには成功。
前回と今回の違いはどこか? recipesにCapistranoの設定を分割したことか? 色々考え、調べたところ、原因が判明した。
原因
# storyblogはアプリケーション名
$ service unicorn_storyblog start
が原因だった。
service hoge startは環境変数をほぼ引き継がない!!!
serviceで起動した場合、環境変数はLANG, PATH, TERMの3つだけを適用する、らしい。"service /etc/init.d 違い"あたりで検索したところ、情報がいろいろ出てきた。
これらの記事を参考。
http://d.hatena.ne.jp/amari3/20111223/1324656530
http://linux.just4fun.biz/?%E9%80%86%E5%BC%95%E3%81%8DUNIX%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%2F%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9(%E3%83%87%E3%83%BC%E3%83%A2%E3%83%B3)%E3%81%AE%E8%B5%B7%E5%8B%95%E3%83%BB%E5%86%8D%E8%B5%B7%E5%8B%95%E3%83%BB%E5%81%9C%E6%AD%A2%E3%83%BB%E7%8A%B6%E6%85%8B%E5%8F%96%E5%BE%97
解決方法
なので、Raiscasts#337のCapistrano設定コードを書きかえ、
run "service unicorn_#{application} #{command}"
となっている箇所を
run "/etc/init.d/unicorn_#{application} #{command}"
と書きかえ。
nginxに関しても
run "#{sudo} service nginx #{command}"
となっているところを
run "#{sudo} /etc/init.d/nginx #{command}"
と書きかえた。
$ capistrano vagrant unicorn:stop
$ capistrano vagrant unicorn:start
としたら、見事環境変数を読み込んでくれた。やった!!!! 半日悩んだ問題が解決した。
ちなみに最後のunicorn停止と起動に関して、再起動の
$ capistrano vagrant unicorn:restart
ではダメだった。Unicornはrestartの際、環境変数を引き継いで再起動するらしいのでそれが原因……なのかも。
ちょっと違うけど、参考。/etc/init.dからの起動時に環境変数を指定する方法 http://qiita.com/items/83bb3fb5cb7f46484d4b