LoginSignup
6
6

More than 1 year has passed since last update.

Vultr VPSにCentOS7, Ruby on Rails 6, Puma, Capistrano3でのProduction環境デプロイ

Last updated at Posted at 2020-03-10

はじめに

Amazon EC2は良いけど高いので、Railsアプリを動かすのに安いVPSを探していたところ、VultrというVPSを見つけました。月額3.50ドルから、と格安で使えますし、東京リージョン(こちらは月額$5から)もあります。

激安VPSのVultr紹介者からのリンク経由でアカウント登録すると$100のクレジットをもらえるので、早速作ってみました。このキャンペーンはいつ終わるか分からないので、興味のある方は以下のリンクからアカウント登録してください。あなたはクレジットをもらえるし、私にも多少のクレジットが入るらしいので、Win-Winです。

このリンクで$100もらえます

スクリーンショット 2020-03-09 午後7.03.25.png

私もリファラー経由でアカウントを作ったので$100もらえました。これで最初にあれやこれや試すのは無料でできます。
スクリーンショット 2020-03-09 午後7.17.41.png

使った環境

サーバOS: CentOS7 (Kernel 3.10.0-1062.12.1.el7.x86_64)
ローカルOS: macOS Catalina Version 10.15.3

Ruby 2.5.3 on rbenv
Rails 6.0.2.1
puma 4.3.3
bundler 2.1.2
Capistrano 3.12.0
MySQL 5.7.29

1. Vultr VPSインスタンス作成

(1)Products→Instances→+でインスタンス追加画面を出します。
スクリーンショット 2020-03-09 午後5.54.09.png

(2)VPSをメニューから選びます。
今回は一番安い$3.50のプランにしたかったのでアメリカ New YorkをLocationに選びました。

スクリーンショット 2020-03-09 午後6.00.44.png

(3)OSは情報を検索しやすいCentOS7にしました。

スクリーンショット 2020-03-09 午後6.01.28.png

(4)IP V6はたぶん使わないのですが、無料なので選んでおきました。

スクリーンショット 2020-03-09 午後6.03.41.png

(5)Start UpスクリプトにはGitHubのSSH公開鍵を読み込むスクリプトを仕込みました。これは後でCapistranoでデプロイする時にGitHubからコードを持って来るのに必要となります。

スクリーンショット 2020-03-09 午後6.04.53.png

(6)SSH公開鍵を仕込んでおくと後で楽です。

スクリーンショット 2020-03-09 午後6.06.45.png

(7)SSH鍵の作り方
暗号強度の強いed25519で作りました。以下はMacの例です。

$ ssh-keygen -t ed25519 -N "" -f ~/.ssh/vultr -C あなたのメルアド
Generating public/private ed25519 key pair.
Your identification has been saved in /Users/あなた/.ssh/vultr.
Your public key has been saved in /Users/あなた/.ssh/vultr.pub.

念の為、ssh-addしておきます。
$ ssh-add
Identity added: /Users/あなた/.ssh/id_rsa (/Users/あなた/.ssh/id_rsa)

(8)インスタンス作成
ここまで来たらDeploy Nowポチッと押します。

スクリーンショット 2020-03-09 午後6.09.10.png

インスタンスにOSインストールが終わるまで数分待ちます。

スクリーンショット 2020-03-09 午後6.09.46.png

OSインストールが終わってStatusRunningになればインスタンス生成は完了です。

2. ログインアカウントの作成

(1)OSインストールが終わってStatusRunningになったら・・・をクリックしてServer Details画面を出します。
スクリーンショット 2020-03-09 午後6.12.22.png

(2)画面右上のView Consoleアイコンをクリックしてコンソールを出します。
スクリーンショット 2020-03-09 午後6.16.18.png
スクリーンショット 2020-03-09 午後6.16.55.png

(3)コンソールが出たらServer Details画面のrootパスワードでrootログインします。
スクリーンショット 2020-03-09 午後6.17.14.png
スクリーンショット 2020-03-09 午後6.19.36.png

(4)後でCapistranoで使うアカウントを追加します。

Vultrのコンソール
# adduser deploy
# passwd deploy
# gpasswd -a deploy wheel
# mkdir /home/deploy/.ssh
# chmod 700 /home/deploy/.ssh
# vi /home/deploy/.ssh/authorized_keys
> 公開鍵を貼り付ける(Macで作った/Users/あなた/.ssh/vultr.pubの中身を貼り付けます。)

# chmod 600 /home/deploy/.ssh/authorized_keys
# chown -R deploy:deploy /home/deploy/.ssh/

(5)SSHでログインできるか確認する。

Macのターミナル
$ ssh -i ~/.ssh/vultr deploy@あなたのVPSIPアドレス
deploy@あなたのVPSIPアドレス's password: さっき作ったdeployアカウントのパスワード
Last login: Mon Mar  9 22:25:20 2020 from あなたのLocalマシンのFQDN
-bash: warning: setlocale: LC_CTYPE: cannot change locale (UTF-8): No such file or directory
[deploy@vultr ~]$  ログインできました。

3. SSHの基本設定

いちいちパスワードを入れるのは面倒だし、Capistranoでパスワード入力をスキップできるようにsshd構成を変更します。

(1)ローカルマシンからSSHログイン

Macのターミナル
$ ssh -i ~/.ssh/vultr deploy@あなたのVPSIPアドレス

(2)VPSにログイン後の作業

VPSにログインして作業
$ sudo vi /etc/ssh/sshd_config

> 以下を変更
PermitRootLogin no         # rootのリモートログインを不許可
PasswordAuthentication no  # パスワード無しでSSHログイン
ClientAliveInterval 10     # SSHがタイムアウトしないように10秒毎にAlive確認
ClientAliveCountMax 6      # 上記を最大6回(つまり60秒)繰り返す

> 変更を反映
$ sudo systemctl reload sshd

(3)ローカルマシンからパスワード無しでSSHログイン

Macのターミナル
$ ssh -i ~/.ssh/vultr deploy@あなたのVPSIPアドレス

もし、間違ってリモートからSSHできなくなってしまった時はView Consoleアイコンをクリックしてコンソールを出してrootでログインし、復旧させる。

4. sudoの設定

deployユーザーにパスワードなしでsudoコマンドを実行できるように設定しました。お好みで設定しても、しなくても良いと思います。

(1)visudoコマンドでsudo権限を付与します。

visudoを実行
$ sudo visudo
visudo
## Allow root to run any commands anywhere
root    ALL=(ALL)       ALL
deploy  ALL=(ALL)       ALL  <=この行を追加

## Same thing without a password
# %wheel        ALL=(ALL)       NOPASSWD: ALL
%deploy         ALL=(ALL)       NOPASSWD: ALL  <=この行を追加

deployグループのユーザーはパスワード無しでsudoできます。

一応確認
[deploy@vultr ~]$ sudo whoami
root

パスワードを聞かれずにwhoamiできました。

5. Firewallの設定

$ sudo yum remove -y firewalld
$ sudo yum install -y iptables-services
$ sudo systemctl start iptables
$ sudo systemctl enable iptables
$ sudo /usr/libexec/iptables/iptables.init save
$ sudo vi /etc/sysconfig/iptables

> Web用に80と433をあける。
# Generated by iptables-save v1.4.21 on Tue Mar 10 01:44:41 2020
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [17:1954]
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
-A INPUT -p icmp -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT
-A INPUT -p tcp -m state --state NEW -m tcp --dport 80 -j ACCEPT  <=この行を追加
-A INPUT -p tcp -m state --state NEW -m tcp --dport 443 -j ACCEPT  <=この行を追加
-A INPUT -j REJECT --reject-with icmp-host-prohibited
-A FORWARD -j REJECT --reject-with icmp-host-prohibited
COMMIT
# Completed on Tue Mar 10 01:44:41 2020

> 上記の変更を反映させます。
$ sudo systemctl reload iptables

6. Swap領域の設定

今回作成したインスタンスはメモリが0.5GBと少なく、メモリを多く必要とするRailsではメモリ不足になる可能性が高いので、Swap領域を設定します。ページングが多発するとパフォーマンスが悪くなりますが、その際はVultrのプランをUpgradeすれば良いです。(ただし、一度UpgradeするとDowngradeはできません。) 今回は4GBと多めにSwap領域を確保します。

rootにsuして実行
[deploy@vultr ~]$ su
Password: 
[root@vultr deploy]# dd if=/dev/zero of=/swapfile bs=1M count=4096
4096+0 records in
4096+0 records out
4294967296 bytes (4.3 GB) copied, 8.98244 s, 478 MB/s
[root@vultr deploy]# mkswap /swapfile
Setting up swapspace version 1, size = 4194300 KiB
no label, UUID=5b48c0e8-9f76-4cb1-8826-1063a95e51eb
[root@vultr deploy]# swapon /swapfile
swapon: /swapfile: insecure permissions 0644, 0600 suggested.
# vi /etc/fstab 

> 以下を一番下に追加
/swapfile swap swap defaults 0 0

> /etc/fstabの変更を保存したら、自動マウントされるか試すためにサーバを再起動する。
[root@vultr deploy]# reboot

再起動後、topコマンドでSwap領域を確認。

[deploy@vultr ~]$ top

top - 01:55:14 up 0 min,  1 user,  load average: 0.00, 0.00, 0.00
Tasks:  68 total,   2 running,  66 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.3 us,  0.0 sy,  0.0 ni, 99.7 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :   498648 total,   351784 free,    59388 used,    87476 buff/cache
KiB Swap:  4194300 total,  4194300 free,        0 used.   417552 avail Mem 

7. MySQLインストールと構成

DBはお好きなものを使えば良いと思いますが、私のローカルMacはMySQL5.7が入っているので同じバージョンを入れました。MySQL8が2倍速いという話もあり、試したのですがRails6だとmysqlクライアントを動かすのが素直に行かず、面倒臭いので5.7にしました。

(1)MySQLインストール

DefaultでMariaDBが入っているのでアンインストールしてからMySQLをインストールします。

インストール作業
# yum -y remove mariadb-libs
# rm -rf /var/lib/mysql/
# yum -y localinstall http://dev.mysql.com/get/mysql57-community-release-el7-8.noarch.rpm

> バージョンを確認
# yum info mysql-community-server
Failed to set locale, defaulting to C
Loaded plugins: fastestmirror
Loading mirror speeds from cached hostfile
 * base: centos.mirror.constant.com
 * extras: centos.mirror.constant.com
 * updates: centos.mirror.constant.com
mysql-connectors-community                                                                          | 2.5 kB  00:00:00     
mysql-tools-community                                                                               | 2.5 kB  00:00:00     
mysql57-community                                                                                   | 2.5 kB  00:00:00     
(1/3): mysql-connectors-community/x86_64/primary_db                                                 |  53 kB  00:00:00     
(2/3): mysql57-community/x86_64/primary_db                                                          | 198 kB  00:00:00     
(3/3): mysql-tools-community/x86_64/primary_db                                                      |  69 kB  00:00:00     
Available Packages
Name        : mysql-community-server
Arch        : x86_64
Version     : 5.7.29  <=5.7の現時点での最新版
Release     : 1.el7
Size        : 175 M
Repo        : mysql57-community/x86_64
Summary     : A very fast and reliable SQL database server
URL         : http://www.mysql.com/

> インストールしてmysqldの起動と自動起動有効化
# yum -y install mysql-community-server mysql-devel
# systemctl enable mysqld.service
# systemctl start mysqld.service

(2)MySQL構成

Config作業
# cat /var/log/mysqld.log | grep "A temporary password"

> 表示されるパスワードをメモ
> 上記のMySQL rootのパスワードを使ってインストール。いろいろ聞かれるが、基本的に全部YesOK

# mysql_secure_installation

# vi /etc/my.cnf

> 以下追記
character-set-server = utf8
default_password_lifetime = 0

(3)MySQLユーザー作成

Railsで使うDBを作り、DBユーザーを作成してGrantします。

DB&ユーザー作成
# mysql -u root -p

mysql> CREATE DATABASE あなたのアプリ名_production;
mysql> CREATE USER 'DBユーザ名'@'localhost' IDENTIFIED BY 'パスワード';
mysql> GRANT ALL PRIVILEGES ON あなたのアプリ名_production.* TO 'DBユーザ名'@'localhost';
mysql> FLUSH PRIVILEGES;
mysql> exit;

8. Rubyインストールと基本構成

Rubyもいろいろバージョンがあるが、新し過ぎるバージョンはRails6側の各種Gemが未対応だったりとバージョン依存関係が面倒臭くなるので、Mac上のRailsアプリで動作が確認できている2.5.3を使うことにした。もちろん、rbenvも使う。

Rubyのインストール(rbenv install 2.5.3)のところは安いプランだとパフォーマンスが悪いので結構時間がかかる。インストールには時間の余裕を持ってやった方が良い。また、途中でSSHがタイムアウトしないように前述のタイムアウトの設定を確認してからやること。

(1)Rubyのインストール

# yum -y install git gcc-c++ glibc-headers openssl-devel readline libyaml-devel readline-devel zlib zlib-devel bzip2
# cd /usr/local
# git clone git://github.com/sstephenson/rbenv.git rbenv
# git clone git://github.com/sstephenson/ruby-build.git rbenv/plugins/ruby-build

# vi /etc/profile.d/rbenv.sh

> 以下を記述してパスを通しておく。
export RBENV_ROOT="/usr/local/rbenv"
export PATH="${RBENV_ROOT}/bin:${PATH}"
eval "$(rbenv init --no-rehash -)"

# source /etc/profile.d/rbenv.sh
# rbenv install 2.5.3  <=これが時間がかかる。
# rbenv global 2.5.3
# rbenv rehash

(2)bundlerのインストール

ローカルのbundlerと同じバージョンをインストールする。

ローカルのbundlerのバージョン確認。私の環境は2.1.2であった。

ローカルのMacターミナル
$ gem list bundler

*** LOCAL GEMS ***

bundler (2.1.2, default: 1.17.3)
capistrano-bundler (1.6.0)

bundlerインストール

Vultrターミナル
# gem install bundler -v 2.1.2

Railsで必要となるNodejsをインストールしておく。

Vultrターミナル
# vi /etc/environment

> 次の2行を追加
LANG=en_US.utf-8
LC_ALL=en_US.utf-8

> EPELリポジトリからNode.jsをインストール
# yum install epel-release
# yum install nodejs npm
# npm install -g n
# n stable  #=> 安定版インストール

  installing : node-v12.16.1
       mkdir : /usr/local/n/versions/node/12.16.1
       fetch : https://nodejs.org/dist/v12.16.1/node-v12.16.1-linux-x64.tar.xz
   installed : v12.16.1 to /usr/local/bin/node
      active : v6.17.1 at /bin/node

> nodejsのバージョン確認
# node -v
v6.17.1

Yarnもインストールしておく。

# npm install -g yarn
/usr/bin/yarn -> /usr/lib/node_modules/yarn/bin/yarn.js
/usr/bin/yarnpkg -> /usr/lib/node_modules/yarn/bin/yarn.js
/usr/lib
└── yarn@1.22.4 

9. Nginxのインストール

Vultrターミナル
> リポジトリの準備
# vi /etc/yum.repos.d/nginx.repo

> nginx.repoは空のはずなので、以下を追加。
[nginx]
name=nginx repo
baseurl=http://nginx.org/packages/mainline/centos/7/$basearch/
gpgcheck=0
enabled=1

> インストール実行
# yum -y update
# yum -y install nginx
# systemctl start nginx
# systemctl enable nginx

プロセス確認
# ps aux|grep nginx
root     16535  0.0  0.1  46456   972 ?        Ss   02:48   0:00 nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.conf
nginx    16536  0.0  0.3  46848  1664 ?        S    02:48   0:00 nginx: worker process

動作確認のため、ブラウザでVultr VPSのIPアドレスにアクセス。以下の画面が出ればNginxのインストールとFirewallの変更は成功している。

スクリーンショット 2020-03-09 午後11.08.19.png

10. 新しくRailsアプリをRubyMineで生成

今回Vultrで動かすアプリをローカルのMac側で新規に生成して準備しておく。説明は不要と思うが、作ったアプリは以下の画面の通り。

Rails 6.0.2.1
Ruby 2.5.3

(1) rails new

スクリーンショット 2020-03-14 午後5.00.42.png

スクリーンショット 2020-03-14 午後5.01.35.png

(2) bundle install

Rails 6にしてから依存関係は微妙に面倒臭くなった。Rails newしたらGemをクリーンインストールできるか確認してからアプリを作った方がいいね。Gemのインストール時は明示的にPathを指定した方が良い。

$ bundle clean --force
$ bundle install --path vendor/bundle

An error occurred while installing mysql2 (0.5.3), and Bundler cannot continue.
Make sure that `gem install mysql2 -v '0.5.3' --source 'https://rubygems.org/'` succeeds before bundling.

In Gemfile:
  mysql2

macOS Catalina V10.15.3 ではお馴染みのエラー。対処方法は次の通り。

$ brew info openssl@1.1
openssl@1.1: stable 1.1.1d (bottled) [keg-only]
...
For compilers to find openssl@1.1 you may need to set:
  export LDFLAGS="-L/usr/local/opt/openssl@1.1/lib"
  export CPPFLAGS="-I/usr/local/opt/openssl@1.1/include"
...

上記ののpathをbundle configで設定してからbundle installを再実行。

$ bundle config --local build.mysql2 "--with-cppflags=-I/usr/local/opt/openssl@1.1/include"
$ bundle config --local build.mysql2 "--with-ldflags=-L/usr/local/opt/openssl@1.1/lib"
$ bundle install --path vendor/bundle

(3) database.ymlを修正

ローカルのMacのMySQLに接続するようdatabase.ymlを修正。

config/database.yml
default: &default
  adapter: mysql2
  encoding: utf8mb4
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: <%= ENV.fetch('DATABASE_USER', '') %>
  password: <%= ENV.fetch('DATABASE_PASSWORD', '') %>
  host: <%= ENV.fetch('DATABASE_HOST', '') %>

development:
  <<: *default
  database: Capistrano3ToVultr_development

test:
  <<: *default
  database: Capistrano3ToVultr_test

production:
  <<: *default
  database: Capistrano3ToVultr_production
  username: Capistrano3ToVultr
  password: <%= ENV['CAPISTRANO3TOVULTR_DATABASE_PASSWORD'] %>

(4) msgpackをインストール

rakeを使う前にmsgpackをインストール

$ gem install msgpack

(5) db:create & migration

DB作成してマイグレーション

LocalのMacターミナル
$ rake db:create
Created database 'Capistrano3ToVultr_development'
Created database 'Capistrano3ToVultr_test'
$ rake db:migrate

(6) webpackerをインストール

$ rails webpacker:install
✨  Done in 10.47s.
Webpacker successfully installed 🎉 🍰

(7) Railsアプリを起動してブラウザで確認。

スクリーンショット 2020-03-14 午後5.58.51.png

11. RailsアプリをGitHubにPush

(1) GitHubに新規にリポジトリを作成

スクリーンショット 2020-03-14 午後6.05.08.png

リポジトリを作成すると次のような画面になる。

スクリーンショット 2020-03-14 午後6.09.08.png

(2) RailsアプリのディレクトリでGitを初期化

上記の画面のcreate a new repository on the command lineに従って初期化する。

$ cd アプリのroot directory
$ git init
Reinitialized existing Git repository in /Users/あなたのRailsディレクトリ/Capistrano3ToVultr/.git/

(3) .gitignoreにGitHubにPushしたくないファイルを指定

私の.gitignoreの例
# See https://help.github.com/articles/ignoring-files for more about ignoring files.
#
# If you find yourself ignoring temporary files generated by your text editor
# or operating system, you probably want to add a global ignore instead:
#   git config --global core.excludesfile '~/.gitignore_global'

# Ignore bundler config.
/.bundle
/vendor/bundler/

# Ignore all logfiles and tempfiles.
/log/*
/tmp/*
!/log/.keep
!/tmp/.keep

# Ignore uploaded files in development.
/storage/*
!/storage/.keep

/public/assets
.byebug_history

# Ignore master key for decrypting credentials and more.
/config/master.key
/config/settings.local.yml
/config/settings/*.local.yml
/config/environments/*.local.yml

/public/packs
/public/packs-test
/node_modules
/yarn-error.log
yarn-debug.log*
.yarn-integrity

.pryrc
.DS_Store

(4) Git Commit & Push to GitHub

GitHubにpushして入れる。

pushしてGitHubに入れる
$ cd アプリのroot directory
$ git add .
$ git commit -m "first commit"  <=好きなコメントでOK
$ git remote add origin git@github.com:あなたのリポジトリ名.git
$ git push -u origin master

Push後、GitHubの該当リポジトリに以下のようにファイルが入っていればOK。
スクリーンショット 2020-03-09 午後11.51.12.png

(5) GitHubにデプロイ用ユーザーのSSH公開鍵を登録

Setting > Deploy keys > Add deploy keysの順にクリック。

スクリーンショット 2020-03-14 午後7.15.03.png

以下の画面になったら、ローカルMacの/Users/あなた/.ssh/vultr.pubの中身を入れてAdd keyする。

スクリーンショット 2020-03-14 午後7.18.08.png

12. Capistrano3を構成する

上記で新規に作ったRailsアプリのConfigを次のように変更。

(1) Capistrano関連Gemのインストール

Gemfile
> 以下を追加
# Use Capistrano for deployment
group :development do
  gem 'capistrano'
  gem 'ed25519'  <=参考記事4には無いが、SSH鍵でed25519を使ったので指定。
  gem 'bcrypt_pbkdf'
  gem 'capistrano-rbenv'
  gem 'capistrano-bundler'
  gem 'capistrano-rails'
  gem 'capistrano3-puma'
end
ローカルMacターミナル
$ bundle install --path vendor/bundle
...
Bundle complete! 21 Gemfile dependencies, 77 gems now installed.
Bundled gems are installed into `./vendor/bundle`
Post-install message from capistrano3-puma:

    All plugins need to be explicitly installed with install_plugin.
    Please see README.md

(2) Capistranoの構成ファイルの生成

前のステップでbundle installしたらcap installを実行してCapfileを生成する。VultrにはProduction環境だけ入れるので、Productionの分しか作らない。

ローカルMacターミナル
$ bundle exec cap install STAGES=production
mkdir -p config/deploy
create config/deploy.rb
create config/deploy/production.rb
mkdir -p lib/capistrano/tasks
create Capfile
Capified

ここで、{Rails root}/Capfileconfig/deploy.rbが生成されている事を確認する。

(3) Capistranoの構成

上記で生成されたCapfiledeploy.rbを編集する。

Capfile
# Load DSL and set up stages
require "capistrano/setup"

# Include default deployment tasks
require "capistrano/deploy"

# Load the SCM plugin appropriate to your project:
#
# require "capistrano/scm/hg"
# install_plugin Capistrano::SCM::Hg
# or
# require "capistrano/scm/svn"
# install_plugin Capistrano::SCM::Svn
# or
require "capistrano/scm/git"
install_plugin Capistrano::SCM::Git

# Include tasks from other gems included in your Gemfile
#
# For documentation on these, see for example:
#
#   https://github.com/capistrano/rvm
#   https://github.com/capistrano/rbenv
#   https://github.com/capistrano/chruby
#   https://github.com/capistrano/bundler
#   https://github.com/capistrano/rails
#   https://github.com/capistrano/passenger
#
# require "capistrano/rvm"
require "capistrano/rbenv"
# require "capistrano/chruby"
require "capistrano/bundler"
require "capistrano/rails/assets"
require "capistrano/rails/migrations"
# require "capistrano/passenger"
require 'capistrano/puma'
install_plugin Capistrano::Puma
install_plugin Capistrano::Puma::Nginx

# Load custom tasks from `lib/capistrano/tasks` if you have any defined
Dir.glob("lib/capistrano/tasks/*.rake").each { |r| import r }
config/deploy/production.rb
server 'VultrサーバーのIPアドレス', user: "deploy", roles: %w{app db web}
config/deploy.rb
# config valid for current version and patch releases of Capistrano
lock "~> 3.12.0"

server 'VultrサーバーのIPアドレス', port: 22, roles: [:app, :web, :db], primary: true
set :application,     "あなたのアプリ名"
set :repo_url,        "git@github.com:あなたのGitHubリポジトリ名.git"
set :user,            'deploy'
set :ssh_options,     {
  forward_agent: true,
  user: fetch(:user),
  keys: %w(~/.ssh/vultr)
}

# Default branch is :master
# ask :branch, `git rev-parse --abbrev-ref HEAD`.chomp

# Default deploy_to directory is /var/www/my_app_name
set :deploy_to, "/var/www/#{fetch(:application)}"

# Default value for :format is :airbrussh.
# set :format, :airbrussh

# You can configure the Airbrussh format using :format_options.
# These are the defaults.
# set :format_options, command_output: true, log_file: "log/capistrano.log", color: :auto, truncate: :auto

# Default value for :pty is false
# set :pty, true

# Default value for :linked_files is []
# append :linked_files, "config/database.yml"

# Default value for linked_dirs is []
# append :linked_dirs, "log", "tmp/pids", "tmp/cache", "tmp/sockets", "public/system"

# Default value for default_env is {}
# set :default_env, { path: "/opt/ruby/bin:$PATH" }

# Default value for local_user is ENV['USER']
# set :local_user, -> { `git config user.name`.chomp }

# Default value for keep_releases is 5
# set :keep_releases, 5

# Uncomment the following to require manually verifying the host key before first deploy.
# set :ssh_options, verify_host_key: :secure

# rbenv
set :rbenv_type, :system
set :rbenv_ruby, File.read('.ruby-version').strip
set :rbenv_prefix, "RBENV_ROOT=#{fetch(:rbenv_path)} #{fetch(:rbenv_path)}/bin/rbenv exec"

# bundler
append :linked_dirs, '.bundle'
set :bundle_jobs, 2  <=一番低いスペックのVultrプランなので2にした。Default4

append :linked_files, "config/master.key"
append :linked_dirs, "log", "tmp/pids", "tmp/cache", "tmp/sockets"

Capistranoはdeployユーザー権限で実行するので、Vultrに作ったVPSの/home/deploy/.bash_profileを編集しておく。

/home/deploy/.bash_profile
# .bash_profile

# Get the aliases and functions
if [ -f ~/.bashrc ]; then
	. ~/.bashrc
fi

# User specific environment and startup programs

PATH=$PATH:$HOME/.local/bin:$HOME/bin

export PATH

export EDITOR=vi
export DATABASE_USER="上記で作ったDBユーザー名"
export DATABASE_PASSWORD="上記で作ったDBユーザーのパスワード"

(4) Vultr VPSにアプリのデプロイ先ディレクトリを準備

deploy.rbでデプロイ先をset :deploy_to, "/var/www/#{fetch(:application)}"としているので、VPSに/var/www/Capistrano3ToVultrディレクトリを作成する。

$ ssh -i ~/.ssh/vultr deploy@Vultr VPSのIPアドレス

以下はVultr側
$ sudo su
# mkdir /var/www
# mkdir /var/www/Capistrano3ToVultr
# mkdir /var/www/Capistrano3ToVultr/shared
# mkdir /var/www/Capistrano3ToVultr/shared/config
# chown -R deploy.deploy /var/www

(5) Vultr VPSにSharedディレクトリに入れるファイルをコピーしておく。

以下でrailsがリリース間で共有するリソースを定義しており、でデプロイ時に自動的に作られるが、ファイルは作られない。config/master.key は予めサーバのshared以下にコピーしておく。

config/deploy.rb
append :linked_files, "config/master.key"
append :linked_dirs, "log", "tmp/pids", "tmp/cache", "tmp/sockets"
$ scp -i ~/.ssh/vultr config/master.key deploy@VPSのIPアドレス:/var/www/Capistrano3ToVultr/shared/config/
master.key             100%   32     2.7KB/s   00:00

参考記事4に明確には書いてないけど必要だった事

  • deployユーザーはパスワード無しでSSHできる事が必要。そうでないとbundle exec cap production puma:nginx_configでコケる。
  • SSH鍵を複数のGitHubリポジトリで使い回しはできない。

(4) Capistranoでデプロイ実行

cap production deployでデプロイを実行するとGitHubからファイルを取ってきて指定した/var/www/アプリ名ディレクトリにデプロイされる。デプロイ前にGitHubにPushしておくこと。

ローカルMacのRailsルートディレクトリで実行
$ bundle exec cap production deploy
00:00 git:wrapper
      01 mkdir -p /tmp
途中省略

デプロイが成功すればreleasesディレクトリにタイムスタンプがディレクトリ名になり、その下にファイルがデプロイされ、/var/www/Capistrano3ToVultr/currentにリンクされる。

Vultrにログインしてデプロイを確認
[deploy@vultr releases]$ pwd
/var/www/Capistrano3ToVultr/releases
[deploy@vultr releases]$ ls -l
total 4
drwxrwxr-x 12 deploy deploy 4096 Mar 15 00:15 20200315001550

12. Nginx と Puma の連携

分かってしまえばどうと言うことはないのだが、ここでハマった。CapistranoもNginxも正常動作しているように見えた(エラーは何も出なかった)のだが、Railsの画面が出ず、Nginxの初期画面しか出なかった。原因はディレクトリやファイルのパーミッション設定とNginxとPumaの各プロセスの実行ユーザーの指定だった。これに気づくまで時間を要してしまったので、細かく書こうと思う。

(1) Config

以下は動作している各Configである。Defaultと変えたところだけコメントを付けた。ディレクトリやファイルのパーミッション、ユーザー、グループに注意。

/var/wwwのパーミッション
drwxr-xr-x   3 deploy deploy  4096 Mar  7 22:09 www
/var/www/Capistrano3のパーミッション(Capistrano3は今回作ったRailsアプリ名)
drwxr-xr-x 5 deploy deploy 4096 Mar  8 04:55 Capistrano3
/var/www/Capistrano3/shared/puma.rb
#!/usr/bin/env puma

directory '/var/www/Capistrano3/current'
rackup "/var/www/Capistrano3/current/config.ru"
environment 'production'

tag ''

pidfile "/var/www/Capistrano3/shared/tmp/pids/puma.pid"
state_path "/var/www/Capistrano3/shared/tmp/pids/puma.state"
stdout_redirect '/var/www/Capistrano3/shared/log/puma_access.log', '/var/www/Capistrano3/shared/log/puma_error.log', true

threads 0,16

bind 'unix:///var/www/Capistrano3/shared/tmp/sockets/puma.sock'

workers 0

restart_command 'bundle exec puma'

prune_bundler

on_restart do
  puts 'Refreshing Gemfile'
  ENV["BUNDLE_GEMFILE"] = ""
end
/etc/nginx/nginx.conf
#user  nginx;
user  deploy; <=重要。NginxPumaは同じユーザーでプロセスを起動する。
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  65;

    #gzip  on;

    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*; <=RailsアプリのConfigをここでインクルードしている。
}
以下のディレクトリを手動作成
drwxr-xr-x 2 root root 4096 Mar  8 16:58 sites-available
drwxr-xr-x 2 root root 4096 Mar  8 16:58 sites-enabled

[deploy@vultr sites-available]$ pwd
/etc/nginx/sites-available
[deploy@vultr sites-available]$ ls -l
total 4
-rw-r--r-- 1 deploy deploy 1614 Mar  8 17:18 Capistrano3_production

[deploy@vultr sites-enabled]$ pwd
/etc/nginx/sites-enabled
[deploy@vultr sites-enabled]$ ls -l
total 0
lrwxrwxrwx 1 root root 49 Mar  8 03:39 Capistrano3_production -> /etc/nginx/sites-available/Capistrano3_production  <=リンクを貼っただけ
/etc/nginx/sites-available/Capistrano3_production
upstream puma_Capistrano3_production { 
  server unix:/var/www/Capistrano3/shared/tmp/sockets/puma.sock fail_timeout=0;
}

server {
  listen 80;
  server_name あなたのVultrサーバーのIPアドレス;
  root /var/www/Capistrano3/current/public;
  try_files $uri/index.html $uri @puma_Capistrano3_production;

  client_max_body_size 4G;
  keepalive_timeout 10;

  error_page 500 502 504 /500.html;
  error_page 503 @503;

  location @puma_Capistrano3_production {
    proxy_http_version 1.1;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $host;
    proxy_redirect off;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "Upgrade";
    proxy_set_header X-Forwarded-Proto http;
    proxy_pass http://puma_Capistrano3_production;
    # limit_req zone=one;
    access_log /var/www/Capistrano3/shared/log/nginx.access.log;
    error_log /var/www/Capistrano3/shared/log/nginx.error.log;

    # Basic認証
    auth_basic "Restricted"; <=テスト用のサーバーなのでBasic認証を付けた
    auth_basic_user_file /etc/nginx/sites-available/.htpasswd; <=テスト用のサーバーなのでBasic認証を付けた
  }

  location ^~ /assets/ {
    gzip_static on;
    expires max;
    add_header Cache-Control public;
  }

  location = /50x.html {
    root html;
  }

  location = /404.html {
    root html;
  }

  location @503 {
    error_page 405 = /system/maintenance.html;
    if (-f $document_root/system/maintenance.html) {
      rewrite ^(.*)$ /system/maintenance.html break;
    }
    rewrite ^(.*)$ /503.html break;
  }

  if ($request_method !~ ^(GET|HEAD|PUT|PATCH|POST|DELETE|OPTIONS)$ ){
    return 405;
  }

  if (-f $document_root/system/maintenance.html) {
    return 503;
  }
}

(2) Railsアプリの動作確認

Puma起動

ローカルのMacからcapコマンドでPuma起動
$ bundle exec cap production puma:status
00:00 puma:status
      WARN  Puma not running
$ bundle exec cap production puma:start
00:00 puma:start
      using conf file /var/www/Capistrano3/shared/puma.rb
      01 RBENV_ROOT=/usr/local/rbenv /usr/local/rbenv/bin/rbenv exec bundle exec puma -C /var/www/Capistrano3/shared/puma.rb --daemon
      01 Puma starting in single mode...
      01 * Version 4.3.3 (ruby 2.5.3-p105), codename: Mysterious Traveller
      01 * Min threads: 0, max threads: 16
      01 * Environment: production
      01 * Daemonizing...
     01 deploy@あなたのサーバーIPアドレス 0.940s
$ bundle exec cap production puma:status
00:00 puma:status
      01 RBENV_ROOT=/usr/local/rbenv /usr/local/rbenv/bin/rbenv exec bundle exec pumactl -S /var/www/Capistrano3/shared/tmp/pids/puma.state -F /var/www/Capistrano3/shared/puma.rb status
      01 Puma is started
     01 deploy@あなたのサーバーIPアドレス 0.576s

Railsアプリ画面確認

とりあえずrails gで作った画面が表示された。
スクリーンショット 2020-03-10 午前0.56.23.png

終わりに

今後、本番のRailsアプリをデプロイする時にはサーバーを二重化してVultrのロードバランサを使って耐障害性を高めるなどやりたいと思う。MySQLの二重化とか、TokyoサーバーとNew Yorkサーバーでのクラスタリングとか遅延がある中でどこまで実運用に耐えられるのか試したい。一定規模のユーザーが増えて、収益が十分確保できればAWSに移行するだろうが、それまでは月々のRunning Costをいかに安く抑えるかが重要なのでVultrは強い味方と言えると思う。ただし、インフラ関係のスキルは必要だが。

今後やりたいこと

  • SMTPの構成
  • バックアップ&リストア検証
  • ロードバランサーで二重化
  • MySQLクラスタリング

参考記事一覧

  1. CentOS7, Ruby on Rails 5, PumaでProducrion環境構築@VPS + Capistranoでのデプロイ
6
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
6