CapistranoでUnicorn + nginx in Ubuntuにデプロイしたら環境変数が適用されない問題解決メモ


CapistranoでVagrantにデプロイした際、なぜか設定したはずの環境変数が適用されなかった。それはserviceコマンドの挙動が原因だった。という話です。

経緯

おなじみRailscasts#335で紹介された方法で、いったんデプロイには成功した。

しかしその後、Raiscasts#337を参考にconfig/recipesにCapistranoの設定を分割してからVagrantで作った環境にデプロイしたところ、環境変数が読み込まれなかった。

大問題である。

環境変数には、他人には見られたくないAmazon S3のパスワードなどが入っている。asset_syncfogを使っているので、アプリケーションにはどうしても環境変数の設定が必要。これらを本番環境で動くサーバにセットせねばならない。

選択肢として、application.ymlにS3のパスワードなどを書いて、本番環境に置くという方法もある。だができれば本番環境上で作業したくない。全部Capistranoですませたい。どうすればよいか?

余談

ちなみに、複数サーバにCapistranoでデプロイするのはとても簡単。GithubのCapistrano Wikiにやり方が書いてある。
https://github.com/capistrano/capistrano/wiki/2.x-Multistage-Extension
以前はプラグインとして提供されていたらしいcapistrano-extはすでにcapistrano内に入っている。 なので、

deploy.rb
set :stages, %w(production vagrant)
set :default_stage, "vagrant"
require "capistrano/ext/multistage"

とするだけで、複数ステージにデプロイ可能。

config/deployディレクトリを作って

config/deploy/production.rb
server "1xx.xxx.xxx.xx", :web, :app, :db, primary: true
set :user, "katryo"
set :port, 10022 #22から変更した
set :deploy_to, "/home/#{user}/apps/#{application}"
config/deploy/vagrant.rb
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では、

config/deploy.rb
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設定コードを書きかえ、

config/recipes/unicorn.rb
run "service unicorn_#{application} #{command}"

となっている箇所を

config/recipes/unicorn.rb
run "/etc/init.d/unicorn_#{application} #{command}"

と書きかえ。

nginxに関しても

config/recipes/nginx.rb
run "#{sudo} service nginx #{command}"

となっているところを

config/recipes/nginx.rb
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