【Rails&React】サーバへの実行環境設定手順

今回はRails&Reactのサーバ実行環境構築手順を忘れないように書き留めておきたいと思います。

前提

OS: CentOS 7.9
Node.js
Ruby 2.7.6
Rails 6
PostgreSQL 12
プログラムはGithubからコピー

手順

サーバを新規にセットアップした際の準備手順

この手順は、サーバを新しく作った際に、ベースになる手順。
2回目以降のアプリリリースでは不要。

1.yumを最新化

sudo yum update -y

2.Postgresqlサーバ インストール&起動

(参考)https://postgresweb.com/install-postgresql12-centos-7

(1)リポジトリパッケージをインストール
sudo yum -y install https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm
(2)インストール
sudo yum -y install postgresql12-server
(3)サービス自動起動設定
systemctl enable postgresql-12
(4)データベースクラスタ作成
su - postgres
/usr/pgsql-12/bin/initdb -E UTF8 --locale=C -W
exit
※ここで補足

以下の記述(postgresql-12-setup)でやると簡単だということがいろいろな記事で書いてありましたが、暗号方式が解決できなくなったので、今回はこの方法はやめて、上記の手順でやり直した。(今回は勉強不足のままスルー)

/usr/pgsql-12/bin/postgresql-12-setup initdb
(5)起動
systemctl start postgresql-12
(6)状態確認

以下のコマンドで、Active : active( running )となっていることを確認する

systemctl status postgresql-12
(7)データベース一覧確認
su - postgres
psql -l
exit

3.必要なパッケージをインストール

最低限必要となるパッケージをインストールします。

(参考)https://pikawaka.com/rails/ec2_deploy

sudo yum -y install gcc-c++ make patch git curl zlib-devel openssl-devel ImageMagick-devel readline-devel libcurl-devel libffi-devel libicu-devel libxml2-devel libxslt-devel

4.Node.jsをインストール

(参考)https://docs.aws.amazon.com/ja_jp/sdk-for-javascript/v2/developer-guide/setting-up-node-on-ec2-instance.html

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash

. ~/.nvm/nvm.sh

// 安定版をイストール
nvm install --lts

node -e "console.log('Running Node.js ' + process.version)"

// バージョンを表示することでインストールされたことを確認
node -v
npm -v
nvm --version

5.yarnをインストール

Reactで必要となるのでインストール。

(参考)https://qiita.com/i-f/items/af88bec7438abbc83f0b

curl -o- -L https://yarnpkg.com/install.sh | bash
※適用のためターミナル再ログイン

source ~/.bashrc

yarn -v

6.Rubyをインストール

(参考)https://qiita.com/Ekodhikodhi/items/01eab1b2b5785163e684

git clone https://github.com/sstephenson/rbenv.git ~/.rbenv

echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile

echo 'eval "$(rbenv init -)"' >> ~/.bash_profile

source ~/.bash_profile

rbenv -v

git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build

rbenv rehash

// インストール可能なバージョンを確認
rbenv install -l

// 今回は2.7.6をインストール
rbenv install 2.7.6

rbenv rehash

rbenv global 2.7.6

ruby -v

7.NGINXをインストール

(参考)https://hombre-nuevo.com/linux/vps0026/

(1)インストール
sudo vi /etc/yum.repos.d/nginx.repo

// 以下追記
[nginx]
name=nginx repo
baseurl=http://nginx.org/packages/centos/7/$basearch/
gpgcheck=0
enabled=1

// インストール
sudo yum install nginx

// バージョン確認
nginx -v
(2)NGINXサービス設定と起動
// 自動起動設定
sudo systemctl enable nginx

// 起動
sudo systemctl start nginx

// 起動確認
sudo systemctl status nginx
(3)ファイアウォールの設定

CentOS7の初期状態は「firewalld」が有効になっている

// 現在の開放ポートを確認
firewall-cmd --list-all

// http(80番ポート)とhttps(443番ポート)を許可
firewall-cmd --add-service=http --permanent
firewall-cmd --add-service=https --permanent

// firewalld再起動
firewall-cmd —-reload
(4)NGINXの設定

一旦、NGINXを停止て、設定ファイル作成

// Nginx停止
sudo service nginx stop
NGINXの設定方法

NGINXでバーチャルホスト(サイト)を作る方法は2通りある。

1)/etc/nginx/conf.d/ の配下に〜.confというファイルを作る方法
2)/etc/nginx/ の配下にsites-availableとsites-enabledの2つのディレクトリを作成し、前者に〜.confを作成、後者にそのシンボリックリンクを作る方法

いずれの場合も、保守のしやすさを考えると、サイト毎にconfファイルを分けるのが一般的だと思います。

1)はNGINXのデフォルトの方法で、nginx.confに最初から include /etc/nginx/conf.d/*.conf; と記述がある通り、conf.d配下にあるconfファイルでバーチャルホストを作る仕様。

2)はnginx.confに include /etc/nginx/sites-enabled/*; を追記して行う方法で、メリットとしてはシンボリックリンクの作成/削除でバーチャルホストの有効/無効をコントロールできる(コントロールしやすい)というところがあります。
1)の方法だと、サイトを無効化するのに、confファイルを削除しないといけないので、サイト復活が面倒になります。

今回は2)の方法で行います。

フォルダ作成

cd /etc/nginx
sudo mkdir sites-available
sudo mkdir sites-enabled

nginx.confに追記

// nginx.confを編集
vi /etc/nginx/nginx.conf

// 以下の1行を追記
 include /etc/nginx/sites-enabled/*; 

実際のアプリケーション用のNGINX個別設定は後述

アプリケーション環境固有の手順

ここからは、アプリリリース毎で実施する手順。

1.PostgreSQLのユーザ作成

// ログイン
psql -h localhost -p {ポート番号} -U {管理者ユーザ名}

// ユーザ作成
create user {アプリ用ユーザ名} with password '{アプリ用ユーザのパスワード}' createdb createrole;

// ユーザ確認
select * from pg_user;

// DBを抜ける
¥q

注意点としては、ここで作成したユーザ名、パスワードはRailsAPIのDB情報と同期する。(同じ情報で設定する)

2.アプリケーション配置

cd /var
sudo mkdir www
cd www
mkdir {アプリケーションのルートディレクトリ}
cd {アプリケーションのルートディレクトリ}

// githubからapi、frontそれぞれのアプリケーションをclone
git clone https: ~~~~

// ディレクトリ名をリネーム(今回はapiとfrontに命名)
mv {git clone したディレクトリ名} api
mv {git clone したディレクトリ名} front

3.apiのセットアップ

(1)必要ライブラリインストール(production向け)
cd api
bundle install --without test development

※ここでエラーが発生(pgでbundle失敗)
PostgreSQLのクライアントライブラリが不足しているので追加でインストール

sudo yum install postgresql-libs
sudo yum install postgresql-devel

※ここでエラーが発生(pg)
An error occurred while installing pg (1.4.5), and Bundler cannot continue.のメッセージ
Gemfileでpgのバージョンを指定

# 運用環境向け変更
# gem 'pg', '~> 1.1'
gem 'pg', '~> 1.2.3'
(2)SECRET_KEY_BASE編集(暗号キーを環境変数参照に変更)

api/config/credentials.yml.encを編集して保存(viで編集、またはVSCodeからSSH接続して編集)

secret_key_base:<%= ENV["SECRET_KEY_BASE"] %>

Rails環境の暗号キーを取得
apiにカレントが当たった状態で以下実行。

bin/rails secret
// コードが表示されるので、それをコピーしておく。

// 環境編集に上記コードを書き込みして保存
sudo vi /etc/environment

SECRET_KEY_BASE='{ここに上記コードを貼り付け}'

一旦ログアウト・ログイン

(3)DB作成、テーブル作成
bin/rails db:create RAILS_ENV=production
bin/rails db:migrate RAILS_ENV=production

注意点はRailsAPIのDB情報は環境に合わせて設定した上で実行すること

(4)Unicornインストール

Gemfileに追記(viで編集、またはVSCodeからSSH接続して編集)

group :production do
  gem 'unicorn', '5.4.1'
end

パッケージインストール

cd api
bundle install --without test development

api/config/unicorn.rb を作成(viで編集、またはVSCodeからSSH接続して編集)
以下サンプル。ポイントはリスナーは今回socketで対応する。

app_path = File.expand_path('../../', __FILE__)

#アプリケーションサーバの性能を決定する
worker_processes 1

#アプリケーションの設置されているディレクトリを指定
working_directory app_path

#Unicornの起動に必要なファイルの設置場所を指定
pid "#{app_path}/tmp/pids/unicorn.pid"

#ポート番号を指定
listen "#{app_path}/tmp/sockets/unicorn.sock"
#listen 3000

#エラーのログを記録するファイルを指定
stderr_path "#{app_path}/log/unicorn.stderr.log"

#通常のログを記録するファイルを指定
stdout_path "#{app_path}/log/unicorn.stdout.log"

#Railsアプリケーションの応答を待つ上限時間を設定
timeout 60

#以下は応用的な設定なので説明は割愛

preload_app true
GC.respond_to?(:copy_on_write_friendly=) && GC.copy_on_write_friendly = true

check_client_connection false

run_once = true

before_fork do |server, worker|
  defined?(ActiveRecord::Base) &&
    ActiveRecord::Base.connection.disconnect!

  if run_once
    run_once = false # prevent from firing again
  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

socket用ディレクトリ作成
unicornのリスナーをportではなくsocketとしたので、socketの格納先を作成

cd api/tmp
mkdir sockets

unicorn用起動/停止スクリプト作成

cd api
rails g task unicorn

※ここでエラー発生 エラーメッセージ:cannot load such file -- listen (LoadError)
Gemfileを見ると、「gem 'listen', '~> 3.3'」がdevelopment限定になっていた。(Railsのデフォルト?)
これをpuroductionでもインストールされるように変更して、bundle install --without test developmentして、rails g task unicornを再実施。

// before
group :development do
  gem 'listen', '~> 3.3'
  # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
  gem 'spring'
end

// after
gem 'listen', '~> 3.3'
group :development do
  # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
  gem 'spring'
end

api/lib/tasksに「unicorn.rake」ファイルが作られるので編集(viで編集、またはVSCodeからSSH接続して編集)
以下サンプル。

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("/var/www/{アプリケーションのルートディレクトリ}/api/tmp/pids/unicorn.pid").to_i
     rescue Errno::ENOENT
       raise "Unicorn does not seem to be running"
     end
   end

end

unicorn起動

rails unicorn:start
※unicorn -c /var/www/{アプリケーションのルードディレクトリ}/api/config/unicorn.rb -E production -D とだけ表示されればOK

※ここでエラー発生
「check stderr log」とあるので、api/log/unicorn.stderr.logを調べる。
「devise/mailer.rb to define constant Devise::Mailer」とある…。
api/config/application.rbの「require "action_mailer/railtie"」が必要らしいが、どうもコメントブロックされているので、コメントブロックを解除。
再度、rails unicorn:startでうまく動いた。
(参考)https://teratail.com/questions/304109

念のため、unicornの状態確認

ps -ef | grep unicorn | grep -v grep
※数行プロセスが表示されればOK

参考までにunicornの停止方法

rails unicorn:stop

4.frontのセットアップ

Nodeパッケージをインストールしてビルド

cd /var/www/{アプリケーションのルートディレクトリ}/front
npm install
npm run build

5.エラーページを作成

この後のNGINX設定で関連付けするのに、ログ出力先とエラーページ格納先のディレクトリを作成する。
また、エラーページもあらかじめ作成しておく。

cd /var/www/{アプリケーションのルードディレクトリ}
mkdir nginx_log
mkdir error_page
echo 404 Not Found > /var/www/{アプリケーションのルードディレクトリ}/error_page/404.html
echo 500 Internal Server Error > /var/www/{アプリケーションのルードディレクトリ}/error_page/500.html

6.NGINXでサイト設定

(1)設定ファイル(confファイル)作成

/etc/nginx/sites-availableにアプリケーション名.confを作成

cd /etc/nginx/sites-available
vi {アプリケーション名}.conf

以下サンプル

 upstream api {
   server unix:/var/www/{アプリケーション名}/api/tmp/sockets/unicorn.sock;
 }

 server {
   listen 80;
   server_name {サーバ名 or IP};
   return 301 https://$host$request_uri;
 }

 server {
   listen 443 ssl;
   server_name {サーバ名 or IP};
   ssl_certificate /{任意のパス}/*****.crt;
   ssl_password_file /{任意のパス}/*****.passfile;
   ssl_certificate_key /{任意のパス}/*****.key;

   ssl_protocols TLSv1.2 TLSv1.3;
   ssl_ciphers 'ECDH !aNULL !eNULL !SSLv2 !SSLv3';
   ssl_prefer_server_ciphers off;
   ssl_session_cache shared:SSL:10m;
   ssl_session_timeout 10m;

   access_log /var/www/{アプリケーションのルートディレクトリ}/nginx_log/access.log;
   error_log  /var/www/{アプリケーションのルートディレクトリ}/nginx_log/error.log;

   location / {
     root /var/www/{アプリケーションのルートディレクトリ}/front/build;
     try_files $uri $uri/index.html @api;
   }

   error_page 404 /404.html;
   location = /404.html {
     root /var/www/{アプリケーションのルートディレクトリ}/error_page;
     internal;
   }

   error_page 500 502 503 504 /500.html;
   location = /500.html {
     root /var/www/{アプリケーションのルートディレクトリ}/error_page;
     internal;
   }

   location @api {
     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://api;
     proxy_intercept_errors on;
   }
 }

今回はhttps化の構成で記述。
以下、ポイント

  • httpのURL接続はhttpsにリダイレクト)
  • APIへのプロキシ設定付
(2)シンボリックリンク作成

/etc/nginx/sites-enabledに上記(1)のシンボリックリンクを作成

cd /etc/nginx/sites-enabled
ln -s /etc/nginx/sites-available/{アプリケーション名}.conf /etc/nginx/sites-enabled/{アプリケーション名}.conf
(3)NGINX再起動

設定有効化のために再起動

// 設定のチェック
sudo nginx -t
※「syntax is ok」「test is successful」が出ればOK

// 停止
sudo service nginx stop

// 起動
sudo systemctl start nginx

最後に

以上の手順で、Webアプリケーションの公開ができたと思います。

もし、動かない点があれば、フロントエンドはNGINXの設定(もしくは起動状態)を確認、バックエンドはNGINXとUnicornの設定(もしくは起動状態)を確認すれば、問題の解決はできると思います。

今回は某VPSでの環境構築でしたが、以前にAWSのEC2で実践済みだったのもあり、ほぼほぼノウハウの流用はできました。

おわり

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です