前回の記事でミニマムな Rack アプリケーションを動かしてみました。 Rails も Rack ベースのアプリケーションですが、通常は rails server
コマンドでアプリケーションを起動します。 Rails のアプリでも config.ru
は持っているので、 rackup
でも起動できるはずですが、 rails server
コマンドで起動するのと何が違うのかを少し調べてみました。
ちなみに前回の記事はこちらです。
まずはそれぞれで動かしてみる
とりあえず実際に動かしてみてその違いを見てみます。まずは rails server
コマンドで起動してみます。
$ rails s => Booting Puma => Rails 5.2.2 application starting in development => Run `rails server -h` for more startup options Puma starting in single mode... * Version 3.12.0 (ruby 2.6.0-p0), codename: Llamas in Pajamas * Min threads: 5, max threads: 5 * Environment: development * Listening on tcp://localhost:8080 Use Ctrl-C to stop
tcp://localhost:8080
でアプリケーションが起動しますので、こちらにブラウザ等でアクセスすると下記のようなログが出力されます。
Started GET "/" for 220.211.52.113 at 2019-04-14 13:32:54 +0000 Cannot render console from 220.211.52.113! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255 Processing by Rails::WelcomeController#index as HTML Rendering /home/ec2-user/.rbenv/versions/2.6.0/lib/ruby/gems/2.6.0/gems/railties-5.2.2/lib/rails/templates/rails/welcome/index.html.erb Rendered /home/ec2-user/.rbenv/versions/2.6.0/lib/ruby/gems/2.6.0/gems/railties-5.2.2/lib/rails/templates/rails/welcome/index.html.erb (2.2ms) Completed 200 OK in 18ms (Views: 6.2ms | ActiveRecord: 0.0ms)
今度は rackup
で起動してみます。
$ rackup Puma starting in single mode... * Version 3.12.0 (ruby 2.6.0-p0), codename: Llamas in Pajamas * Min threads: 5, max threads: 5 * Environment: development * Listening on tcp://localhost:9292 Use Ctrl-C to stop
rails server
で起動した時と比べると、最初の3行が出力されていません。また、ポートも 8080 ではなく 9292 で起動されています。
tcp://localhost:9292
にアクセスするとログは下記のような出力になります。
127.0.0.1 - - [14/Apr/2019:13:36:22 +0000] "GET / HTTP/1.1" 200 - 0.1098
シンプルな Web サーバのログが出力されるだけで、 rails server
の時に出力されていた Rails に関するログは出力されていません。
処理内容の差分
それでは起動時に実際にどのような処理が行われているのか、コードから確認してみたいと思います。下記 Rails ガイドのページで初期化プロセスが紹介されていますので、これを参考に確認してみます。
rails server
コマンドで起動した場合も最終的に config.ru から Rack ベースの Rails アプリケーションが起動されることになるのですが、 rackup
と違って Rails の環境を色々整えた上でアプリケーションが起動されることになります。
詳細なステップについては上記サイトで紹介されているので、ここで一つ一つの紹介は割愛しますが、主に違う点は下記の点かと思います。
Bundler の読み込みと設定が行われる
rails コマンドの別名の拡張が行われる
PORT番号などの設定が行われる
Logger の設定が行われる
上記が行われた上で config.ru から Rails アプリケーションが起動します。それぞれについて簡単に見てみたいと思います。
Bundler の読み込みと設定
起動シーケンスの最初の方で、 config/boot.rb
が実行されます。内容は下記のようになっています。
# config/boot.rb ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) require 'bundler/setup' # Set up gems listed in the Gemfile.
これによって Gemfile のパスが取得され、 bundler/setup が require されています。
rails コマンドの別名の拡張
続いて railties/lib/rails/commands.rb
の内容が実行され、 rails コマンドの別名の拡張が行われます。これによって例えば rails s
が rails server
として解釈できるようになります。ここまではまだ rails
コマンドの内容になります。
# railties/lib/rails/commands.rb # frozen_string_literal: true require "rails/command" aliases = { "g" => "generate", "d" => "destroy", "c" => "console", "s" => "server", "db" => "dbconsole", "r" => "runner", "t" => "test" } command = ARGV.shift command = aliases[command] || command Rails::Command.invoke command, ARGV
PORT 番号などの設定
ここからは rails server
コマンドの内容になり、railties/lib/rails/commands/server/server_command.rb
内で Rack::Server を継承した Rails::Server が定義されています。 Rails::Server は初期化時にオプションを受け取るようになっていて、この中で PORT 番号などの設定が行われています。
module Rails class Server < ::Rack::Server class Options def parse!(args) Rails::Command::ServerCommand.new([], args).server_options end end def initialize(options = nil) @default_options = options || {} super(@default_options) set_environment end
rails server
コマンドでは Rails::Server を初期化する際にオプションを渡しています。 rackup
で起動するとデフォルトでは 9292 番ですが、ここではデフォルトが 3000 番ポートとして設定され、さらに今回のケースでは PORT 環境変数で 8080 番ポートが指定されているので、起動時には 8080 番ポートが使用されます。
module Command class ServerCommand < Base # :nodoc: include EnvironmentArgument # Hard-coding a bunch of handlers here as we don't have a public way of # querying them from the Rack::Handler registry. RACK_SERVERS = %w(cgi fastcgi webrick lsws scgi thin puma unicorn) DEFAULT_PORT = 3000 DEFAULT_PID_PATH = "tmp/pids/server.pid"
def perform extract_environment_option_from_argument set_application_directory! prepare_restart Rails::Server.new(server_options).tap do |server| # Require application after server sets environment to propagate # the --environment option. require APP_PATH Dir.chdir(Rails.application.root) if server.serveable? print_boot_information(server.server, server.served_url) after_stop_callback = -> { say "Exiting" unless options[:daemon] } server.start(after_stop_callback) else say rack_server_suggestion(using) end end end
$ echo $PORT 8080
Logger の設定
続けて同じく Rails::Server クラスの log_to_stdout メソッドで ActiveSupport::Logger のインスタンスの作成とアサインが行われます。これによって Web サーバのログだけでなく Rails に関係するログが出力されるようになります。
def log_to_stdout wrapped_app # touch the app so the logger is set up console = ActiveSupport::Logger.new(STDOUT) console.formatter = Rails.logger.formatter console.level = Rails.logger.level unless ActiveSupport::Logger.logger_outputs_to?(Rails.logger, STDOUT) Rails.logger.extend(ActiveSupport::Logger.broadcast(console)) end end
まとめ
rails server
と rackup
の違いは私も今までよくわかっていなかったので、今回整理できて参考になりました。今回は差分のところだけに絞ったので、 Rails アプリの起動シーケンスについてはざっくり割愛してしまいましたが、今後時間があったら Rack ミドルウェアの関連や、実際にリクエストがあった時の挙動なども見てみたいと思います。