CentOS8にNginxとRails、Unicornを導入する方法

投稿者: | 2020-09-20

前回はNginxにRails、Passengerを導入する方法について説明しました。今回はPassengerと同様にアプリケーションサーバとして使用されるUnicornを導入する方法について説明します。

実施環境

OS CentOS8 最小のインストール
Nginx 1.18.0
Ruby 2.7.1
Rails 6.0.3.2
yarn 1.22.5
Node.js 14.9.0
Unicorn 5.7.0

事前準備

初めにRuby用のユーザーの作成、必要なコマンド、develのインストールを行います。

・Rails用ユーザー・railsを作成しパスワードを設定する
# useradd rails
# passwd rails

・git等のコマンドをインストールする
# dnf groupinstall -y "Development Tools"

・Ruby、Railsのインストールに必要なものをインストールする
# dnf -y install openssl-devel readline-devel zlib-devel sqlite-devel libcurl-devel 

インストール後、NginxとUnicornの連携後の「403 Forbidden」を回避するためにSELinuxを無効にします。

・SELinuxを無効にする
# vi /etc/selinux/config

■config
-----------------------------------------------------------
# This file controls the state of SELinux on the system.
# SELINUX= can take one of these three values:
#     enforcing - SELinux security policy is enforced.
#     permissive - SELinux prints warnings instead of enforcing.
#     disabled - No SELinux policy is loaded.
SELINUX=disabled   # enforcingをdisabledにする
# SELINUXTYPE= can take one of these three values:
#     targeted - Targeted processes are protected,
#     minimum - Modification of targeted policy. Only selected processes are protected. 
#     mls - Multi Level Security protection.
SELINUXTYPE=targeted
-----------------------------------------------------------

・SELinux反映のため、端末を再起動する
# reboot

Nginxのインストール

事前準備後、Nginxのインストールをします。今回はリポジトリから安定版をインストールします。

・yum.repo.d配下にNginx用のリポジトリファイルを作成する
# vi /etc/yum.repos.d/nginx.repo

■nginx.repo
-----------------------------------------------------------
[nginx-stable]
name=nginx stable repo
baseurl=http://nginx.org/packages/centos/$releasever/$basearch/ 
gpgcheck=1
enabled=1
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true
-----------------------------------------------------------

・Nginxをインストールする
# dnf -y install nginx

・Nginxのバージョンを確認する
# nginx -v
nginx version: nginx/1.18.0

Nginxインストール後、FirewallのHTTPポートを開放します。

・FirewallのHTTPポートを開放
# firewall-cmd --add-service=http --zone=public --permanent
# firewall-cmd --reload

Firewall設定後、Nginxを起動し自動起動の設定を行います。

・Nginxを起動し自動起動の設定をする
# systemctl start nginx
# systemctl enable nginx

Rubyのインストール

Nginxのインストール後、rbenvからRubyをインストールします。Rubyのインストールはrailsユーザーで行います。

初めにrbenv、ruby-buildをgithubからダウンロードし環境変数を設定します。

・Ruby用ユーザーへ切り替え
$ su - rails

・rbenvをダウンロードする
$ git clone https://github.com/sstephenson/rbenv.git ~/.rbenv

・ruby-buildをダウンロードする
$ git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build 

$ vi ~/.bash_profile
------------------------------------------------------------
# User specific environment and startup programs
# rbenv setting
export PATH="$HOME/.rbenv/bin:$HOME/.rbenv/versions/2.7.1/bin:$PATH"
eval "$(rbenv init -)"
------------------------------------------------------------

・.bash_profileを反映する
$ source ~/.bash_profile

rbenvの設定完了後、Rubyをインストールします。今回はRuby 2.7.1をインストールします。

・Rubyのインストール、今回はバージョン2.7.1をインストールする
$ rbenv install 2.7.1

・Rubyのバージョンを切り替える
$ rbenv global 2.7.1

・Rubyのバージョン確認
$ ruby -v
ruby 2.7.1p83 (2020-03-31 revision a0c7c23c9c) [x86_64-linux]

Node.jsとyarnのインストール

Rubyのインストール後、Rails用にNode.jsとyarnをインストールします。

・Node.jsの最新版をyumリポジトリに追加
# curl --silent --location https://rpm.nodesource.com/setup_14.x | bash -

・yumリポジトリに安定版yarnのリポジトリを追加
# curl --silent --location https://dl.yarnpkg.com/rpm/yarn.repo | sudo tee /etc/yum.repos.d/yarn.repo 

・Node.jsとyarnをインストールする
# dnf -y install yarn nodejs

・yarnとNode.jsのバージョンを確認
# node -v
v14.9.0

# yarn -v
1.22.5

bundlerとRails、Unicornのインストール

Node.jsとyarnのインストール後、RailsとUnicornをインストールします。今回はbundlerを使用しRailsとUnicornをインストールします。RailsとUnicornはRuby用ユーザー・railsでインストールを行うため、事前にRuby用ユーザーに切り替えておいてください。

・bundlerをインストールする
$ gem install bundler

・アプリケーション用のディレクトリの作成
$ mkdir -p ~/projects/sample_app && cd ~/projects/sample_app

・Gemfileを作成する
$ bundle init

・RailsのインストールのためGemfileを編集する
$ vi Gemfile

■Gemfile
------------------------------------------------------------
# frozen_string_literal: true

source "https://rubygems.org"

git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }

# gem "rails"
gem 'rails'
------------------------------------------------------------

・Railsをvendor/bundleにインストールする
$ bundle install --path vendor/bundle

・Railsのサンプルアプリケーションを作成する
$ bundle exec rails new .

※Gemfileを上書きするかどうか聞かれるため、Yを入力しEnterを押す
...
Overwrite /home/rails/projects/sample_app/Gemfile? (enter "h" for help) [Ynaqdh] Y 
...

・UnicornをインストールするためGemfileを編集する
$ vi Gemfile

■Gemfile
-----------------------------------------------------------
    ---省略---
gem 'unicorn'
gem 'uglifier'
gem 'coffee-rails'
    ---省略---
-----------------------------------------------------------

・Unicornをvendor/bundleにインストールする
$ bundle install --path vendor/bundle

Unicornのインストール後、今回表示させるサンプルアプリケーションを作成します。

・サンプルアプリケーション、データベースの作成
$ bundle exec rails generate scaffold board title:string text:string
$ rake db:migrate

・アプリケーションのルーティングを設定する
$ vi ~/projects/sample_app/config/routes.rb

■routes.rb
-----------------------------------------------------------
Rails.application.routes.draw do
  resources :boards
  # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
  root "boards#index"
end
-----------------------------------------------------------

アプリケーション作成後、Firewallで3000ポートを一時的に開放し、サンプルアプリケーションが表示できるか検証します。

・Firewallの3000ポートを開放する
# firewall-cmd --add-port=3000/tcp --zone=public --permanent
# firewall-cmd --reload

・Railsサーバを起動する
$ bundle exec rails server -b <サーバのIPアドレス>

ポート開放後、ブラウザで「http://<サーバのIPアドレス>:3000」にアクセスし、アプリケーションが表示されれば成功です。

サンプルアプリケーションの検証後、3000ポートを閉じておきます。

・Firewallの3000ポートを閉じる
# firewall-cmd --remove-port=3000/tcp --zone=public --permanent
# firewall-cmd --reload

unicorn.rbの設定

サンプルアプリケーション作成後、Unicornの設定ファイルのunicorn.rbを新規作成します。

・unicorn.rbを作成する
$ vi ~/projects/sample_app/config/unicorn.rb

■unicorn.rb
-----------------------------------------------------------
# -*- coding: utf-8 -*-
worker_processes Integer(ENV["WEB_CONCURRENCY"] || 3)
timeout 15
preload_app true

# Unicornのsock、pidファイルのパスを指定
listen "/home/rails/projects/sample_app/tmp/unicorn.sock"
pid "/home/rails/projects/sample_app/tmp/unicorn.pid"

before_fork do |server, worker|
  Signal.trap 'TERM' do
    puts 'Unicorn master intercepting TERM and sending myself QUIT  instead'
    Process.kill 'QUIT', Process.pid
  end

  defined?(ActiveRecord::Base) and
    ActiveRecord::Base.connection.disconnect!
end

after_fork do |server, worker|
  Signal.trap 'TERM' do
    puts 'Unicorn worker intercepting TERM and doing nothing. Wait for master to send QUIT' 
  end

  defined?(ActiveRecord::Base) and
    ActiveRecord::Base.establish_connection
end

# Unicornのログのパスを指定
stderr_path File.expand_path('log/unicorn.log', ENV['RAILS_ROOT'])
stdout_path File.expand_path('log/unicorn.log', ENV['RAILS_ROOT'])
-----------------------------------------------------------

Unicornの起動・停止用テンプレートファイルの作成

unicorn.rb作成後、Unicornの起動・停止用のテンプレートファイルを作成します。手動で起動を行うことも可能ですが、Unicornの管理を簡単にするため、今回はテンプレートファイルを作成します。

テンプレートファイルの内容は、以下の記事を参考にしました。

・Rails 4.2 + Unicorn + Nginx でアプリケーションサーバの構築

https://qiita.com/Salinger/items/5350b23f8b4e0dcdbe23

・テンプレートファイルの作成
$ bundle exec rails generate task unicorn

・テンプレートファイルの編集
$ vi ~/projects/sample_app/lib/tasks/unicorn.rake

■unicorn.rake
-----------------------------------------------------------
namespace :unicorn do
  ##
  # Tasks
  ##
  desc "Start unicorn for development env."
  task(:start) {
    config = Rails.root.join('config', 'unicorn.rb')
    sh "bundle exec unicorn_rails -c #{config} -E development -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

  def unicorn_signal signal
    Process.kill signal, unicorn_pid
  end

  def unicorn_pid
    begin
      File.read("/home/rails/projects/sample_app/tmp/unicorn.pid").to_i
    rescue Errno::ENOENT
      raise "Unicorn doesn't seem to be running"
    end
  end

end
-----------------------------------------------------------

テンプレートファイル作成後、Unicornを起動します。

・Unicornの起動
$ bundle exec rake unicorn:start

今回は起動した状態としておきますが、Unicornを停止する場合は以下のコマンドを入力します。

・Unicornの停止
$ bundle exec rake unicorn:stop

Nginxの設定

Unicornのテンプレートファイル作成後、NginxでUnicornを使用する設定を行います。今回はnginx.confファイルに設定を行わず、/etc/nginx/conf.d配下にsample_app.confというファイルを作成し設定を行います。

・default.confからsample_app.confを作成し、default.confをリネームする
# cp /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/sample_app.conf
# mv /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf.bak

・sample_app.confを編集する
# vi /etc/nginx/conf.d/sample_app.conf

■sample_app.conf
-----------------------------------------------------------
upstream unicorn {
  server unix:/home/rails/projects/sample_app/tmp/unicorn.sock;
}

server {
  listen 80;
  server_name localhost;

  access_log /var/log/nginx/sample_app_access.log;
  error_log /var/log/nginx/sample_app_error.log;

  root /home/rails/projects/sample_app/public;

  client_max_body_size 100m;
  error_page 404 /404.html;
  error_page 500 502 503 504 /500.html;
  try_files $uri/index.html $uri @unicorn;

  location @unicorn {
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_pass http://unicorn;
  }
}
-----------------------------------------------------------

sample_app.confの編集後、設定ファイルのチェックを行い、問題なければNginxを再起動します。

・Nginxの設定ファイルをチェックする
# nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

・Nginxを再起動する
# systemctl restart nginx

Nginxの再起動後、今回は/home/rails配下にRuby、Unicorn等を配置しているため、「403 Forbidden」を回避するため/home/railsディレクトリにその他ユーザーの実行権を付与します。

・/home/railsディレクトリにその他ユーザーの実行権を付与する
# chmod o+x /home/rails

動作確認

Nginxの再起動後、ブラウザで「http://<サーバのIPアドレス>」にアクセスし、先程のサンプルアプリケーションが表示されれば成功です。