nginxをrails6用サーバとしてCentOS7に導入してみる
シェアが高く、Apacheより高速に動作するNginxを用いてrails用サーバとして運用したいので、さくらVPSで構築してみる。今回行いたいことは下記。
- Nginxをインストールして最初の画面を起動させる
- RailsをProductionで動作させる
- PUMA(Rails用アプリケーションサーバ)とNginxを連動させる
前提条件としてはRailsアプリケーションがDevelop環境では動いている状態にあることである。Productionではまだ一度もBuildできていなくても構わない、適宜補足を入れていく。最終的にhttp://IPアドレス/でRailsアプリケーションにアクセスできるところまで進めていく。
Nginxをインストールして起動
まずはNginxをインストールするところから始めていく。yumでパッケージがあるのでそれを導入。
$sudo yum install nginx
試しに起動させてみる。
$sudo systemctl start nginx
Apache等、他サービスで既にポートが使われている場合は終了させる
エラーが出たのでログを確認。
$sudo systemctl status nginx.service
Mar 14 14:13:39 ik1-323-21947.vs.sakura.ne.jp nginx[732]: nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
Mar 14 14:13:39 ik1-323-21947.vs.sakura.ne.jp nginx[732]: nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
Address already in useとあり、既に80番ポートが使用されているため、80番ポートを使用している他のサーバを止める。ps aux
で起動プロセスを調べるとApacheが怪しそうなのでこれを止める。そして再度Nginxをスタート。
$sudo apachectl stop
$sudo systemctl start nginx
確認のため自サーバのIPにアクセスしてみると次のような画面が表示された。
一瞬Nginxか疑わしかったが、WebInspectorでResponse Headersを確認してみると下記であり、確かに起動が確認された。
Accept-Ranges: bytes
Connection: keep-alive
Content-Length: 4833
Content-Type: text/html
Date: Sat, 14 Mar 2020 05:24:23 GMT
ETag: "53762af0-12e1"
Last-Modified: Fri, 16 May 2014 15:12:48 GMT
Server: nginx/1.16.1
UNIXドメインソケットによりRailsアプリケーションサーバと通信
Nginxが立ち上がったら、次にRailsのアプリケーションサーバであるPUMAと通信を行う必要がある。この時の通信は普段Webで使っているHTTPとはことなり、同一サーバ内に限定して、よりオーバーヘッドが少なく高速化を実現できるUNIXドメインソケットによるプロセス間通信を行う。
概要としては次のような感じ
これが最終ゴールである。これを実現するために
- SocketFileの場所を決める
- PUMAとSocketFileとの通信設定
- NginxとSocketFileとの通信設定
が必要となる。
Socketファイルの場所は慣例に倣い、Rails.root/tmp/sockes/以下にpuma.sock
というファイル名で置くこととする。
PUMAでSocketを待ち受け状態にする
概要は話したので個別の設定に入る。まずはPUMA側でSocket Fileの変化をListen(待ち受け)状態にする必要がある。
pumaのconfiguration sectionによるとconfig/puma/<environment_name>.rb
があればこのconfigを優先的に読むと記述されているため、production.rb
をRails.root/config/puma/以下に作成し、そこにUnix Domain Socketの設定を記述する。
具体的にはRails.root/config/puma/production.rbを下記のように編集する。
# UNIX Domain Socket Settings
app_root_path = "#{File.expand_path("../../..", __FILE__)}" # get application root path
bind "unix://#{app_root_path}/tmp/sockets/puma.sock"
また、PUMAは複数のListenができない仕様のようなので、ポート番号3000でListenしている既存の箇所はコメントアウトしておく
# Specifies the `port` that Puma will listen on to receive requests; default is 3000.
#port ENV.fetch("PORT") { 3000 }
これでpumaをproduction環境で起動すればpuma側での設定は完了する。
rails s -e production
production環境の起動でエラーが出た場合
恐らく初めてレンタルサーバ上でgit cloneを行いRailsアプリのプロジェクトを取り込み、rails s -e production
を行うと下記のようなエラーに直面するであろう。
Missing `secret_key_base` for 'production' environment, set this string with `rails credentials:edit
Railsには簡単に可逆暗号化する仕組みがあり、それを実現するための秘密鍵が登録されていないというエラーである。
最初rails newを行うタイミングで公開鍵/秘密鍵のペアがそれぞれconfig/master.key
と、config/credentials.yml.enc
に作成されるが、それぞれがgit管理対象だと簡単に復号化が可能なため、秘密鍵に値するmaster.keyの方はgitの管理対象から外されている。
.gitignoreを確認すると、確かにその記述があることに気付く。
# Ignore master key for decrypting credentials and more.
/config/master.key
このため開発中にディレクトリからmaster.keyをコピーして本番サーバへ持ってくる必要がある。
しかし、Develop環境ではsecret_key_base
の登録は必須でないため、Rails newをしたディレクトリを削除してしまった人もいるだろう。私もその口の一人である。
このような人のために鍵のペアを再生成するコマンドが用意されている。それがrails credential:edit
である。
//ペアのいない公開鍵を削除してペアを再生成
$ rm config/credentials.yml.enc
$ rails credentials:edit
rails credentials:edit
をしてもEditorが指定されていないとエラーが吐かれた場合は~/.bashrc
に指定しておこう。
# for rails editor
export EDITOR="vim"
指定後source ~/.bashrc
で忘れずに変更を反映させ、再度実行してみよう。そしてペアが作成されたら再度rails serverを起動してみれば良い。
$ rails s -e production
=> Booting Puma
=> Rails 6.0.2 application starting in production
=> Run `rails server --help` for more startup options
Puma starting in single mode...
* Version 4.3.3 (ruby 2.6.5-p114), codename: Mysterious Traveller
* Min threads: 5, max threads: 5
* Environment: production
* Listening on unix:///home/yamanaka/webapp/infltech/tmp/sockets/puma.sock
ようやく成功した。
Nginx側でSocketにデータを送る
次にNginx側の設定を行う。目標はPUMAがListenしているunix:///home/yamanaka/webapp/infltech/tmp/sockets/puma.sock
に対してNginxに来たリクエストを送ることである。
実行ユーザをrootへ変更
最初にNginxのConfigを確認する。まずはPermissionエラーに悩まされないようにNginxの実行ユーザ設定を行う。
$ sudo su -
# vi /etc/nginx/nginx.conf
でconfigを開くと実行ユーザがnginxになっている設定がある。今回私の作成する環境ではPUMAを一般ユーザとして実行しており、nginxだとpuma.sockにアクセスできない。具体的には下記のようなエラーに遭遇する。
2020/03/14 21:04:38 [crit] 13871#0: *1 connect() to unix:///home/yamanaka/webapp/infltech/tmp/sockets/puma.sock failed (13: Permission denied) while connecting to upstream, client: 119.106.15.194, server: localhos...
このためuserをrootに変更しておく。
- user nginx;
+ user root;
userをユーザ名かあるいはnginx deploy用の専用ユーザを作成し、そのユーザを指定するという方法もある。
しかし画像のアップロード時にも同様のPermissionエラーが出力されるなどしてその後の管理がやや複雑になったので、ここでは多少セキュリティは犠牲になる可能性はあるがrootにしておいた。
既存のhttp設定をコメントアウト
次にhttpの80番ポートで待ち受ける設定が既にNginxのconfigに書かれているので、全てコメントアウトしておこう。これをしておかないと、Railsアプリ用の設定を書いて実行した時に、複数のサーバが80番をListenしているという理由でエラーになってしまう。
# server {
# listen 80 default_server;
# listen [::]:80 default_server;
# server_name _;
# root /usr/share/nginx/html;
#
# # Load configuration files for the default server block.
# include /etc/nginx/default.d/*.conf;
#
# location / {
# }
#
# error_page 404 /404.html;
# location = /40x.html {
# }
#
# error_page 500 502 503 504 /50x.html;
# location = /50x.html {
# }
# }
/etc/nginx/conf.d/myapp.confにRailsAppの設定を記述
nginx.confの下記の記述によるものであるが、Nginxでは/etc/nginx/conf.d/*.conf
を読み取り自動でConfigファイルとしてincludeしてくれる。
# Load modular configuration files from the /etc/nginx/conf.d directory.
# See http://nginx.org/en/docs/ngx_core_module.html#include
# for more information.
include /etc/nginx/conf.d/*.conf;
よってmyapp.confを自分のアプリ名に応じて作成し、そこにサーバ設定を記述していく。見た方が速いので、今回の設定をまるっと説明と共に下記に示す。
upstream puma {
# PUMAでListenしているソケットファイルの位置を指定。
server unix:///home/yamanaka/webapp/infltech/tmp/sockets/puma.sock;
}
server {
listen 80;
server_name localhost;
keepalive_timeout 30;
root /home/yamanaka/webapp/infltech/public/;
# Nginxが出力するログの位置
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
location / {
proxy_pass http://puma;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
}
# 静的ファイルはPUMAを経由せずにNginxが直接返す
location ~* \.(ico|css|gif|jpe?g|png|js|woff2|woff|ttf)(\?[0-9]+)?$ {
# キャッシュの有効期間。
expires max;
break;
}
# 5Mバイトまでのファイルをアップロードしても大丈夫。Defaultは1MB
client_max_body_size 5m;
# Error pages
error_page 500 502 503 504 /500.html;
}
基本的な設定はGitHubのNginx configuration example fileを参考にしている。これを参考にして自分のアプリに合わせた設定を記述していって欲しい。
Nginxの設定を反映
最後にnginxの設定を再読み込みすればNginxとPUMAがよろしくproxy_passを通じて連携してくれる。
nginx -s reload
nginxが起動していない場合はsystemctl start nginx
で起動しよう。
参考としてnginxの操作コマンドを簡単にまとめておく
systemctl start nginx #起動
systemctl stop nginx #停止
systemctl restart nginx #再起動
nginx -s reload #設定の再読み込み+再起動
RailsのProduction特有のエラーを修正
まずはDevelopと同様にデータベースファイルを作成する必要がある。
$ rails db:migrate RAILS_ENV=production
その後Nginxを起動させ、rails s -e production
でPUMAも起動させて自サーバーのIPにhttpでアクセスすると画面が表示される。しかし、ここではまだStyleSheetやJSが有効になっていない。
Develop環境ではサーバにアクセスがあるとWebpackのコンパイルが走っていたが、本番サーバでは悠長に待ってられないのでWebpackのプリコンパイルが事前に必要となるためである。よって下記のコマンドでコンパイル済みjs/cssファイルを作成する。
rails assets:precompile RAILS_ENV=production
その後再度PUMAを起動させれば本番環境Readyである。永続化のためにバックグラウンド起動させておこう。
rails s -e production -d
とやりたいところだが、これだとpuma.sockが生成されないというエラーを踏んでしまう。どうもrails sで指定したオプションがpumaのconfigに伝わらずproductionではない方のconfigが読まれているようである。(何故かps axu | grep puma
でプロセスを確認するとtcp://0.0.0.0:3000
で待ち受けている)
StackOverflowの例だと直接PUMAを起動することで回避できたとあるが、ややハックになるのでconfig/puma/production.rb
にてバックグラウンドでPUMAを起動する設定を書いてやる。
# Make puma start background
daemonize true
その後
$ rails s -e production
とやれば無事バックグラウンドで起動ができた。
まとめ
以上でNginxとPUMAを連携させRailsアプリをProduction環境で動作させる説明を終える。
今後、プログラミングを修正して再Deployする必要がある時には下記のコマンドを再び叩けば良い。
$ git pull origin master
$ rails assets:precompile RAILS_ENV=production
$ ps axu | grep puma #backgroundで起動しているPUMAのプロセスを探す
$ kill 〇〇 # PUMAのプロセスをKill
$ rails s -e production
git pushに連動させて自動Deployしたりなどより効率化は図ることができるが、現状それほど手間でもないので今のところはこれぐらいにしておく。
Webサイトとして公開するためには他に独自ドメインの設定とSSL化が残るが、それは別途記述予定である。