💎

Rails で独自環境変数に APP_ENV を使ってはいけない

に公開

TL;DR

  • puma.rb の最新テンプレートでは environmentRAILS_ENV から読み込む設定が冗長なものとして削除された
  • Puma では RAILS_ENV よりも APP_ENV を優先して読み込む
  • Puma が読み込んだ環境名は RACK_ENV として設定される
  • RAILS_ENV を指定せず APP_ENV のみ指定した状態で Rails サーバーを起動すると、 Rails.envAPP_ENV の値になり意図しない挙動をした

ここから本文

リーナー開発チームの黒曜(@kokuyouwind)です。

リーナーでは Rails を使って Web アプリケーションを開発しており、なるべく最新の Rails バージョンや設定テンプレートに追従しています。

今日はそんな中で踏んだ RAILS_ENVAPP_ENV の挙動についてまとめます。

APP_ENV を独自の環境切り替えに使っていた

Rails において、 RAILS_ENV は基本的に development, test, production のいずれかの値に設定します。

独自設定でこれ以外の値を足すこともできますが、一般にはあまり推奨されないとされています。以下の neko314 さんの記事がわかりやすいです。

https://neko314.hatenablog.com/entry/2020/11/21/231326

一方で、リーナーでは本番環境の他に検証用の環境としてステージング環境などがあり、これらの設定を切り替える必要があります。

そこで RAILS_ENV = development は開発者のローカル開発時の設定に寄せ、クラウドにデプロイされる本番環境・ステージング環境・開発環境はいずれも RAILS_ENV = production にしたうえで、独自の APP_ENVleaner_dev, leaner_stg, leaner_prd とすることで読み込む設定を切り替えるようにしていました。[1]

実装としてはConfig gem で読み込むパスを APP_ENV に応じて追加する形にしています。

# APP_ENVに応じたConfigを読み込む
Settings.add_source!(Rails.root.join("config/settings/app/#{ENV.fetch('APP_ENV')}.yml").to_s)
Settings.reload!

例えば RAILS_ENV = production かつ APP_ENV = leaner_stg であれば、 config/settings/production.rb に加えて config/settings/app/leaner_stg.yml を読み込みます。

APP_ENV を指定したらローカル環境が起動しなくなった

あるときローカル環境でも少し設定を変えた状態で起動したくなり、 APP_ENVlocal.fork を指定したところ、なぜか rails server が失敗するようになってしまいました。

ログを見ると、 puma が environment: local で起動しようとしている…?

Puma starting in single mode...
* Environment: local.fork

なんで…? と思い原因を調べてみたところ、 Puma の設定が原因でした。

puma.rb の environment 指定が消えた

以前の Rails が生成する puma.rb では、 environmentRAILS_ENV の値を設定し、存在しない場合は development をデフォルトにする設定が入っていました。

が、これが冗長だとして削除するコミットが Rails v8.1.1 に含まれています。

https://github.com/rails/rails/commit/cda8c70c059a94ed5891f0c6c61783aadfdced32#diff-e25cab4832f078ca2f9dffa8918f895c8474f8086878e4ced9f5795131fc2de2L30-L32

@@ -27,19 +27,6 @@
- # Specifies the `environment` that Puma will run in.
- rails_env = ENV.fetch("RAILS_ENV", "development")
- environment rails_env

リーナーでも、このテンプレート変更に追従し puma.rb から environment の指定を削除していました。

Puma では RAILS_ENV よりも APP_ENV を優先する

では、 environment の指定がない場合に RAILS_ENV を読み込む処理はどうなっているのでしょう。

lib/puma/configuration.rb を見てみましょう。

https://github.com/puma/puma/blob/v7.1.0/lib/puma/configuration.rb#L253

あれ、 RAILS_ENV 以外に APP_ENVRACK_ENV も見ている…? なんなら APP_ENV の優先度が一番高い…?

これだと本番でも RAILS_ENV より APP_ENV が優先されてヤバいのでは…?

Rails では RAILS_ENV を優先する

Puma の environment の値がどう使われるのか、もう少し追ってみましょう。

この値は Puma::Launcher.set_rack_environmentRACK_ENV にも設定されます。

https://github.com/puma/puma/blob/v7.1.0/lib/puma/launcher.rb#L361-L364

そうなると Rails.env にも影響するかも、と思ったのですが、こちらは RAILS_ENV を優先して読み込むため RAILS_ENV が設定されていれば問題なさそうです。

https://github.com/rails/rails/blob/v8.1.1/railties/lib/rails.rb#L74-L76

ほな大丈夫かー。

問題が発生するケース

はい、大丈夫じゃないですね。

まずコードで見て分かる通り、 RAILS_ENV が設定されておらず APP_ENV のみが設定されているケースでは APP_ENV の値が Rails.env に設定されてしまいます。

特に、ローカル環境では起動コマンドに応じて developmenttest が切り替わってほしいため、 RAILS_ENV を設定しないほうが便利です。この環境で APP_ENV を設定すると意図せず Rails.env にも設定されてしまう問題があります。

冒頭の「APP_ENV を指定したらローカル環境が起動しなくなった」というのは、このケースを踏んでしまったわけですね。

学び

  • APP_ENV 環境変数を独自目的に使っている場合は puma.rb から environment 指定を消してはいけない
  • そもそも APP_ENV のように一般的な名前の環境変数を独自目的に使ってはいけない

今回の問題は puma.rb の変更に起因するものでしたが、そもそも APP_ENV は Sinatra などでも環境指定に使われており、独自目的に使うのは避けるべきでした。

これに限らず、アプリケーション独自の環境変数は他と被らないよう prefix した命名規約にするのが良いですね。

リーナーでも取り急ぎは puma.rbenvironment 指定を戻して対応しつつ、環境変数名を LEANER_APP_ENV のように独自であることがわかりやすいものに変える予定です。

あまりないケースだと思いますが、もし APP_ENV など一般的な名前の変数を独自目的に使っている方がいたら他山の石にしていただけると嬉しいです。

宣伝

こういうしょーもない失敗の話など、気軽に雑談しましょう!

https://pitta.me/matches/HTxgLMvyqyBw

「今年こんなことを頑張ったよ!」というドヤ LT 会をやるので東京や名古屋近郊の方はぜひお越しくださいー。

https://leanertechnologies.connpass.com/event/376577/

脚注
  1. RAILS_ENV=production, APP_ENV=staging だと本番かステージか紛らわしいので、 APP_ENV の命名規約を RAILS_ENV と変えています。 ↩︎

リーナーテックブログ

Discussion