AWS に Rails のアプリをデプロイする方法 ~本番環境を構築する~
link です。
この記事は前回 AWS に Rails のアプリをデプロイする方法 ~ Rails アプリを起動するまで の続きになります。
前回は Rails アプリを起動してアクセスするまでをやりました。
今回は実際に外部公開する Rails アプリの本番環境を EC2 インスタンス上で構築する手順を紹介します。
想定環境
- Windows 11
- Amazon Linux 2023
- Ruby 3.2
- Ruby on Rails 7
本番環境構築について
今回、本番環境を構築するにあたって Unicorn+nginx で Rails アプリを立ち上げます。
Rails アプリの起動は rails s
で行えます。
しかし rails s
で立ち上がる Rack アプリサーバーは負荷を分散させるための機能がありません。
そのため、実運用していく上で、大勢の人がサイトを見たときなどにとても重くなってしまいます。
そこで Unicorn+nginx を使ってアクセス時の負荷を分散できるようにして Web アプリを起動させます。
Unicorn と nginx について
nginx
nginx はクライアントからのリクエストを受け、何らかの処理を行う Web サーバーです。
nginx は master
と worker
の 2 プロセスに分かれています。
master
プロセスがアプリのソースを保持し、 worker
プロセス群が実際のリクエストを処理するようになっています。
1 つの worker
プロセスが複数のリクエストを処理できるようになっているため、大量のリクエストを処理できることが特徴です。
しかし、 Rails は Rack アプリケーションであるため、 nginx から直接リクエストを受け取ることができません。
そこで Unicorn を間に挟んで nginx から Rails アプリにリクエストを流せるようにします。
Unicorn
Unicorn は Rack アプリケーション用の Web サーバーです。
Unicorn は送信されてきた多数のリクエストをさばき、分散して Rack アプリケーションに伝達するという機能を持ちます。
nginx と同様、 master
と worker
の 2 プロセスに分かれています。
worker
プロセスは 1 つのリクエストを 1 つのプロセスで処理します。
リクエスト処理の流れは以下のようになっています。
- nginx からリクエストが Unicorn に渡される
- Unicorn は Rack をとおして Rails アプリケーションのルーターに処理を渡す
- Rails アプリケーションの結果を Unicorn が受け取る
- Unicorn はこれを Web サーバーに渡し、最終的にクライアントへと渡る
Unicorn のインストールと設定
ローカルの Rails のプロジェクトフォルダ内の Gemfile
に Unicorn を追加します。
group :production do
gem 'unicorn'
end
config/unicorn.rb
を作成して以下の内容を記述します。
# Rails のルートパスを求める
app_path = File.expand_path('../../', __FILE__)
# Unicorn は複数のワーカーで起動するのでワーカー数を定義
worker_processes 1
# Unicorn の起動コマンドを実行するディレクトリを指定します
working_directory app_path
# プロセスの停止などに必要なPIDファイルの保存先を指定
pid "#{app_path}/tmp/pids/unicorn.pid"
# ポートを設定
listen "#{app_path}/tmp/sockets/unicorn.sock"
# Unicorn のエラーログと通常ログの位置を指定
stderr_path "#{app_path}/log/unicorn.stderr.log"
stdout_path "#{app_path}/log/unicorn.stdout.log"
# 接続タイムアウト時間
timeout 60
# Unicorn の再起動時にダウンタイムなしで再起動を行う
preload_app true
check_client_connection false
run_once = true
# USR2 シグナルを受けると古いプロセスを止める
before_fork do |server, worker|
defined?(ActiveRecord::Base) &&
ActiveRecord::Base.connection.disconnect!
if run_once
run_once = false
end
old_pid = "#{server.config[:pid]}.oldbin"
if File.exist?(old_pid) && server.pid != old_pid
begin
sig = (worker.nr + 1) >= server.worker_processes ? :QUIT : :TTOU
Process.kill(sig, File.read(old_pid).to_i)
rescue Errno::ENOENT, Errno::ESRCH => e
logger.error e
end
end
end
after_fork do |_server, _worker|
defined?(ActiveRecord::Base) && ActiveRecord::Base.establish_connection
end
続いて、 Unicorn の起動・停止スクリプトを作成します。
$ rails g task unicorn
これで lib/tasks/unicorn.rake
が生成されるので中身を以下のように書き換えます。
namespace :unicorn do
# Tasks
desc "Start unicorn"
task(:start) {
config = Rails.root.join('config', 'unicorn.rb')
sh "unicorn -c #{config} -E production -D"
}
desc "Stop unicorn"
task(:stop) {
unicorn_signal :QUIT
}
desc "Restart unicorn with USR2"
task(:restart) {
unicorn_signal :USR2
}
desc "Increment number of worker processes"
task(:increment) {
unicorn_signal :TTIN
}
desc "Decrement number of worker processes"
task(:decrement) {
unicorn_signal :TTOU
}
desc "Unicorn pstree (depends on pstree command)"
task(:pstree) do
sh "pstree '#{unicorn_pid}'"
end
# Helpers
def unicorn_signal signal
Process.kill signal, unicorn_pid
end
def unicorn_pid
begin
File.read("/home/vagrant/myapp/tmp/unicorn.pid").to_i
rescue Errno::ENOENT
raise "Unicorn does not seem to be running"
end
end
end
EC2 インスタンスにログインして環境変数 SECRET_KEY_BASE
を設定します。
~/.bash_profile
に以下の内容を追記します。
$ export SECRET_KEY_BASE=`bundle exec rake secret`
これで Unicorn を使う準備は完了です。
最後に、変更を commit してリモートリポジトリに push したあと、 EC2 インスタンス内のプロジェクトフォルダで git pull
して変更を反映します。
nginx のインストールと設定
EC2 インスタンスに nginx をインストールします。
$ sudo yum -y install nginx
/etc/nginx/conf.d/rails.conf
を作成して以下の内容を記述します。
# Unicorn と連携させるための設定
upstream app_server {
server unix:/var/www/AwsRails/tmp/sockets/unicorn.sock;
}
server {
# このプログラムが接続を受け付けるポート番号
listen 80;
# 接続を受け付けるリクエスト URL
server_name EC2 インスタンスの IP アドレス;
# クライアントからアップロードされてくるファイルの容量の上限
client_max_body_size 2g;
# 接続が来た際の root ディレクトリ
root /var/www/AwsRails/public;
# assets ファイル (CSS や JavaScript のファイルなど)にアクセスが来た際に適用される設定
location ^~ /assets/ {
gzip_static on;
expires max;
add_header Cache-Control public;
}
try_files $uri/index.html $uri @unicorn;
# Unicorn のリバースプロキシ
location @unicorn {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://app_server;
}
error_page 500 502 503 504 /500.html;
}
今のままで POST メソッドを実行すると nginx が Rails アプリがあるディレクトリへのアクセス権限を持っていないため 403 が発生してしまいます。
そこで /var/lib/nginx
の権限を変更して POST メソッドを実行できるようにします。
$ cd /var/lib
$ sudo chmod -R 775 nginx
nginx のインストールと設定はこれで完了です。
本番環境でのアプリ起動
Rails アプリを起動させます。
Unicorn インストール時に作成したスクリプトで Unicorn を使って Rails アプリを起動します。
$ rake unicorn:start
unicorn -c /home/{ユーザ名}/AwsRails/config/unicorn.rb -E production -D
の 1 行だけ表示されれば起動成功です。
アプリを停止させるときは rake unicorn:stop
を実行します。
http://EC2 インスタンスの IP アドレス
にアクセスして、 Web アプリが利用できることを確認しましょう。
これでアクセス負荷を分散させつつ、 Web アプリを動作させられるようになったはずです。
参考サイト
- Unicorn と Nginx の概要と違い - Qiita
- 【CentOS 7】nginx + Unicorn で Rails アプリケーションを本番環境で立ち上げる方法
- 独学向け Rails アプリを AWS にデプロイする方法まとめ【入門】 - Qiita
- なぜ rails の本番環境では Unicorn,nginx を使うのか? ~ Rack,Unicorn,nginx の連携について ~【Ruby On Rails で web サービス運営】 - Qiita
まとめ
今回は実際に外部公開する Rails アプリの本番環境を EC2 インスタンス上で構築する手順を紹介しました。
AWS に Rails のアプリをデプロイする方法の紹介は以上になりますが、 セキュリティの関係上、実際に公開するには SSL 証明書を取得して HTTPS でアクセスできるようにする必要があります。
そちらの手順についてはおいおい解説したいと思います。
それではまた、別の記事でお会いしましょう。