やりたい事
- パスワードとかSECRET_KEY_BASEとか、環境変数に設定するのがオススメの方法なのでそれに従う。
- ローカルサーバーであれば、ログインしているシェルで環境変数を設定しておけば良いが、capistranoでdeployする場合でも環境変数にうまく設定しておきたい。
もうちょっと追加要件
- sshでサーバーにログインした場合でも、環境変数が設定されていてほしい。アプリケーション側で問題が発生した時にサーバー上でrailsの再起動などの作業を行う可能性があるので。
- 将来的にrails以外のアプリケーションも動くかもしれないので、gemに依存した作りにしたくない。
実装
①環境変数を記載したymlファイルをサーバー上に配置する
配置はchefやansibleなどの構成管理ツールでやると良い。ファイルのパーミッションはアプリケーションユーザーに対して読み取り権限だけにしておく事
$ cat /path/to/secret/yml/env.yml
DB_PASSWORD: your_password
SECRET_KEY_BASE: ramdomstrings
②sshログイン時のシェルで環境変数を読み込ませる
/etc/profile.d/以下に専用のscriptを書いておく。これでシェルログイン時に環境変数がセットされる。
中身は、ymlを読んでexportするだけ。
$ cat /etc/profile.d/env_vars.sh
secret_file='/path/to/secret/yml/env.yml'
IFS_SAVE=$IFS
IFS=$'\n'
for line in `cat ${secret_file}`
do
key=`echo $line | sed -e "s/^\([^ ]*\): \([^ ]*\)$/\1/"`
value=`echo $line | sed -e "s/^\([^ ]*\): \([^ ]*\)$/\2/"`
export $key=$value
done
IFS=$IFS_SAVE
③capistrano-rbenvを利用してdeploy時にも環境変数を反映させる
capistranoはSSHKitを利用して、ssh経由でコマンドを実行するので/etc/profile.d/以下のscriptは読んでくれない。そこで、capistrano-rbenvというgemのrbenv_prefixを利用する。
rbenv/lib/capistrano/tasks/rbenv.rakeを一部抜粋。7行目で全てのコマンドの前に、rbenv_prefixが設置されている事が分かる。
task :map_bins do
SSHKit.config.default_env.merge!({ rbenv_root: fetch(:rbenv_path), rbenv_version: fetch(:rbenv_ruby) })
rbenv_prefix = fetch(:rbenv_prefix, proc { "#{fetch(:rbenv_path)}/bin/rbenv exec" })
SSHKit.config.command_map[:rbenv] = "#{fetch(:rbenv_path)}/bin/rbenv"
fetch(:rbenv_map_bins).each do |command|
SSHKit.config.command_map.prefix[command.to_sym].unshift(rbenv_prefix)
end
end
end
という事で、rbenv_prefixで、/etc/profile.d/env_vars.shを読ませる事にする。
capistranoのconfigファイル内で以下を設定。
set :stage, :staging
set :rbenv_prefix, "source /etc/profile.d/env_vars.sh && RAILS_ENV=#{fetch(:stage)} ~/.rbenv/bin/rbenv exec"
stageは設定しないと正しく動かなかったので設置。
おわり
以上でめでたくcapistaranoで環境変数を設定しつつdeployする事が出来ました。
あまりrails wayを把握してないのでもっといい方法があるよ!って方がいましたらコメントなど頂けると嬉しいです!