🪤

Railsをアップグレードしたら、本番環境が簡易アプリケーションサーバで稼働するようになった

に公開

Railsをアップグレードして一息ついていた頃のこと

とあるアプリケーションのRailsバージョンを7.0.8.7 → 7.1.5.2にアップグレードし、リリースしました。
リリース後しばらく目立った問題も出ていなかったのですが、一部のリクエストに対してのみレスポンスが返せなくなっているらしいことが数日後に発覚しました。

原因調査

当初はエラーになるリクエストの条件が判然としなかったのですが、どうやらクエリパラメータが大量に付与されるリクエストに対してのみレスポンスが返せていないらしいということが見えてきました。
ということで実際に問題のリクエストを再現してみると、サーバ側で以下のログが出力されていました。RequestURITooLargeとのことなので、推測は当たっていたようです。

ERROR WEBrick::HTTPStatus::RequestURITooLarge

WEBrick is 何

https://github.com/ruby/webrick

WEBrick is an HTTP server toolkit that can be configured as an HTTPS server, a proxy server, and a virtual-host server.

WEBrick is suitable for use in testing and for development.

主に開発やテスト用途での利用が想定されている、Rubyに標準で含まれるWebサーバのライブラリです。

このアプリはpumaで動いていたはずなんだけど...

上記のログが出力されているということは、どうやらWEBrickがアプリケーションサーバとして動いているということのようです。


よく見たら起動ログにちゃんと出てたね...

WEBrickにはリクエストURIの長さに制限があり、2083バイトを超える場合にWEBrick::HTTPStatus::RequestURITooLargeエラーが発生するので辻褄は合ったのですが、このアプリケーションでは通常pumaを利用していたはず...🤔

取り急ぎの対応

ひとまずは何かしらの理由でpumaで起動できず、代わりにWEBrickが起動してしまっているのだろうと思って確認してみると、Rails7.1.5.2にした際にpumaのバージョンを上げる必要があったようなのですがこの対応ができていませんでした。
Rails7.0.8.7下で利用していたpuma 6.4.3を6.6.1にアップグレードしたところ、無事pumaで起動するようになり問題が解消しました。

Rails起動時のアプリケーションサーバ決定ロジックを見てみる

問題が解決したのはいいのですが、なぜpumaが利用できない場合にWEBrickが起動してしまうのかRailsのソースコードを追ってみました。
(アプリケーションサーバが使えないならエラー落ちしてくれるものだと思うじゃないですか)

Railsでは以下のserver_optionsメソッドでアプリケーションサーバの指定をserver: options[:using]としてRackup::Serverに渡しています。明示的な指定がない場合はnilになります。
https://github.com/rails/rails/blob/7-1-stable/railties/lib/rails/commands/server/server_command.rb#L153-L156

Rackup側では以下の箇所でアプリケーションサーバを決定しようとします。Handler.getは引数がnilの場合にはnilを返すので、options[:server]nilの場合はHandler.defaultが利用されることになります。
https://github.com/rack/rackup/blob/main/lib/rackup/server.rb#L344-L346
https://github.com/rack/rackup/blob/main/lib/rackup/handler.rb#L40-L41

Handler.defaultが核心部分で、環境変数の指定がない場合は91行目の分岐に入って puma, falcon, webrick の順で利用可能なアプリケーションサーバを探しに行きます。
pumaやfalconが利用できなかったとしても、WEBrickが見つかればWEBrickが返されて起動するというわけです。
https://github.com/rack/rackup/blob/main/lib/rackup/handler.rb#L61-L93

対策

rails serverコマンドの-uオプションで明示的にアプリケーションサーバを指定できるので、起動コマンドに追記するのが確実です。
-uオプションでの指定があれば、そのアプリケーションサーバを利用できない場合には起動時にエラーになってくれます。
今回のアプリケーションでは起動時に明示的な指定が無かったので、pumaのバージョンアップと合わせて起動コマンドも修正しました。

rails s # こうではなく
rails s -u puma # こうする 

まとめ

  • Railsアップグレード時には、アプリケーションサーバ(puma等)の互換性も確認が必要
    • そもそもRailsアップグレード時にgemは極力最新化しておいたほうがいい
      • (今回は「まあいけるやろ」みたいな気持ちと、さすがにアプリケーションサーバがダメなら起動失敗するだろうという思い込みで痛い目を見ました)
  • Rackupはアプリケーションサーバをフォールバック起動するため、意図しない構成で起動してしまう可能性がある
    • rails s -u puma のように -u オプションで明示的に指定することで予防できる
paiza

Discussion