Rails では、config/environments/*.rb
に、実行環境ごとに異なる設定を書けます。
rails new
で新規作成すると、以下の3種類が生成されています。
-
production.rb
(本番環境) -
development.rb
(開発環境) -
test.rb
(CI環境)
どの環境としてアプリを起動するかは、環境変数 RAILS_ENV
で指定できます。
そして、config/environments/*.rb
は、独自に追加することができるので、
ステージング環境(本番デプロイ前にテストする環境)の設定を
staging.rb
に書いている。・・・というプロジェクトは多いと思いますが、私は staging.rb はアンチパターン だと思います。
なぜ、アンチパターンだと思うのか?
テストは本番と同じコード・設定・環境でするのが基本です。本番と違うものを動かして問題がなかったとしても、それはテストにはならないのです。当たり前ですが。
そのため staging.rb
は production.rb
は同一の内容でなければならないはずです。
しかし、別ファイルにしている時点で、同一内容か保証し続けるのは難しいのです。
コードレビューなどでチェックすることも考えられますが、きっと起きます。
「ステージング環境では問題ないのに本番環境では起動すらできない。実は production.rb を修正し忘れていていた」事件が。
代わりにどうすればいいか?
ステージング環境でも production.rb を使いましょう!同じ設定を使うのだから、必ず本番環境と同じ動作になります。
もちろん、ステージング環境と本番環境ではホスト名やAWSアカウントなどが異なることも多く、その差異を何らかの方法で吸収しなければなりませんが、大きく分けて、
- 差異をインフラ層で吸収する
- 環境変数で設定する
の、2種類の方法があります。
1. 差異をインフラ層で吸収する
例えば、本番とステージングでSMTPサーバーのホスト名が異なるなら、ステージング環境の /etc/hosts
に以下のような設定を追加します。
# /etc/hosts
#
# 本番環境のSMTPサーバーのホスト名が smtp.xyz.com で、
# ステージング環境のSMTPサーバーのIPが192.168.2.18のとき、
# ステージング環境のRailsサーバーに以下のような設定を追加。
192.168.2.18 smtp.xyz.com
こうすることで、このサーバー内では(アプリケーションのコードは変えていないのに) smtp.xyz.com で本番ではなくステージング環境のSMTPサーバーにアクセスするようになります。サーバーの台数が増えていくと/etc/hosts
よりDNSで管理した方がいいかもしれません。
もちろん、インフラ層が原因で別の問題が起きる可能性も生じます。例えば「/etc/hosts のIPを間違えて別のサーバーに接続しちゃった!」とか。しかし、経験上、インフラの設定ミスが起きる頻度は、アプリの不具合よりずっと低い。
ただし、今後のDocker化などを見据えると「2. 環境変数で設定する」の方が発展性があるかもしれません。
2. 環境変数で設定する
例えば、本番とステージングでSMTPサーバーのホスト名が異なるなら、それぞれ SMTP_HOST
という環境変数にホスト名を指定しておくようにします。
# config/environments/production.rb
Rails.application.config do
...
# 本番とステージングで、SMTPサーバーのホストが異なる場合の例:
config.action_mailer.smtp_settings = {
address: (ENV['SMTP_HOST'] || raise '環境変数 SMTP_HOST が定義されていません'),
port: 2525
}
...
end
なお、環境変数を使う方法には、アプリのDocker化がしやすくなるおまけのメリットがあります。
従来のオンプレミス環境では、サーバーのホスト名やポート番号は、基本的に変化しないものでした。しかし、Kubenetesなどの環境では、ホスト名やポート番号は動的に変化するので、外部からアプリに設定を注入する必要があります。その注入方法としては環境変数を使うものが多いようです。
また、SaaS開発の方法論である The Twelve-Factor App でも、「設定を環境変数に格納する」と明確に指定されています。
staging.rb が許容される場合
ごく小規模な、他のサーバーとほとんど通信しないようなアプリで、Docker化の予定も無いということであれば、staging.rb を書くのが手っ取り早いかもしれません。
しかし、私としては最初から production.rb と環境変数を使うことをオススメします。