Edited at

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

More than 1 year has passed since last update.


はじめに

プライベートでサービスを立ち上げるときには、今まではいつもherokuを使っていました。herokuはすべてがサービス化されていてすごい楽なんですけど、反面、コスパが悪くて、ちゃんとプライベートのサービスやるには向かねーなあって思ってました。

そんなわけで重い腰を上げて今回からは自分でVPSを使って環境構築してみることにしました。自分はサーバ設定まわりがとても苦手で、かつ、まとまった情報もあまりネットになく、けっこう苦労したので、普段qiitaとか書かないのですが、せっかくなのでまとめておきます。

CentOS7 + Ruby on Rails 5 + Pumaで動作。Capistranoでのデプロイができるというところまで。


おおまかな流れ


  • Railsを動作させる@ローカルMac


    • プロジェクト作成

    • MySQLユーザ作成

    • git, github初期設定



  • VPS初期設定


    • ユーザ作成

    • firewall設定

    • swap領域作成



  • MySQLインストール

  • Rubyインストール


    • rbenv

    • ruby

    • bundler



  • Nginxインストール

  • Capistranoでデプロイ


Railsを動作させる@ローカルMac

まずはローカルでrailsプロジェクトが動くようにします。最終的にローカルからCapistranoでdeployを行います。RubyやMySQLなどはインストールされているものとします。


プロジェクト作成

$ rails new アプリ名 -d mysql


MySQLユーザの作成

$ mysql -u root

> CREATE USER 'DBユーザ名'@'localhost' IDENTIFIED BY 'パスワード';
> GRANT ALL PRIVILEGES ON DB名.* TO 'DBユーザ名'@'localhost';
> FLUSH PRIVILEGES;
> exit;

DBユーザ名、パスワード、DB名はあとでdatabase.ymlに書く必要があるのでメモっておく。


git, githubの設定

gitを初期し、githubへアップします。

$ cd アプリ名

$ git init

.gitignoreを編集して不要なファイルがgit管理されないようにしましょう。

/.bundle

/vendor/bundler/
/log/*.log
/tmp
/uploads/tmp/
/config/settings.local.yml
/config/settings/*.local.yml
/config/environments/*.local.yml
/config/secrets.yml
/config/database.yml

.pryrc
.DS_Store

コミットしてプッシュします

$ git add .

$ git commit -m "first commit"
$ git remote add origin git@github.com:eiei19/アプリ名とか.git
$ git push -u origin master


database.yml と sercrets.ymlの編集

こんな感じになるはずです。


databese.yml

default: &default

adapter: mysql2
encoding: utf8
pool: 5
host: 127.0.0.1

development:
<<: *default
database: DB名
username: DBユーザ名
password: DBパスワード

test:
<<: *default
database: dos_test

production:
<<: *default
database: DB名(production)
username: DBユーザ名(production)
password: DBパスワード(production)



sercrets.yml

development:

secret_key_base: xxx

test:
secret_key_base: xxx

production:
secret_key_base: xxx


Production環境のDBパスワードなどは、あとでVPS側で設定するときに決めます。


railsを動かす


DBの作成と起動

$ bin/rake db:create

$ bin/rails server

http://localhost:3000 にアクセスしてRailsのWelcome画面が表示されればOK。


適当にコントローラーとか作る


app/controllers/posts_controller.rb

class PostsController < ApplicationController

def index
end
end


app/views/posts/index.html.erb

<h1>Hello World</h1>



config/routes.rb

Rails.application.routes.draw do

get '/posts', to: 'posts#index'
end

http://localhost:3000/posts にアクセスしてHello Worldが表示されればOK。commit&pushしておきましょう。


VPS初期設定

https://vultr.com/

今回はvultrというVPSサービスを使ってみました。よくわかってないですが、コンパネから一発でVPSを立ち上げることができて、snapshotとかもとれて、Tokyoリージョンもあります。ミニマムで$5/monthからと値段もお手頃。


SSHの設定

サーバをCentOS7で立ち上げます。まずはssh周りを設定します。

$ root@IPアドレス

-- ここからVPS --

# adduser ei
# passwd ei
# gpasswd -a ei wheel
# mkdir /home/ei/.ssh
# vi /home/ei/.ssh/authorized_keys
> 公開鍵を貼り付ける
# chmod 700 /home/ei/.ssh/authorized_keys
# chown -R ei:ei /home/ei/.ssh/

-- ローカルで違うターミナルを開く --

$ ssh ei@IPアドレス #接続できることを確認する

-- ここからVPS --

$ sudo vi /etc/ssh/sshd_config

> 以下を変更
PermitRootLogin no
PasswordAuthentication no

$ sudo systemctl reload sshd

これでrootログインとパスワードログインができなくなった。


Timezone設定

$ sudo ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime

$ date


Firewall設定

次にiptablesによる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をあける。
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [48:7196]
-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

$ sudo systemctl reload iptables


swap領域の確保

初期設定だとswap領域が確保されていないので、メモリを使いすぎると、使いすぎてるプロセスがキルされます。それだと困るのでswap領域を確保しておきます。

# dd if=/dev/zero of=/swapfile bs=1M count=1024

# mkswap /swapfile
# swapon /swapfile
# vi /etc/fstab

> 以下追記

/swapfile swap swap defaults 0 0

ここまでできたら自動マウントされるか試すためにサーバを再起動する。topコマンドswap領域が確保されてればOK。


MySQLインストール

つぎはミドルウェアとかを入れていきます。まずは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 -y install mysql-community-server mysql-devel
# systemctl enable mysqld.service
# systemctl start mysqld.service
# cat /var/log/mysqld.log | grep "A temporary password"

> 表示されるパスワードを使ってインストールする

# mysql_secure_installation

> 新しいパスワードを決める。質問は全部YESでOK.

# vi /etc/my.cnf

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


ユーザ作成

# mysql -u root -p

> CREATE USER 'DBユーザ名'@'localhost' IDENTIFIED BY 'パスワード';
> GRANT ALL PRIVILEGES ON DB名.* TO 'DBユーザ名'@'localhost';
> FLUSH PRIVILEGES;
> exit;


Rubyインストール

Rubyをインストールします。今回はrbenvを使います。

# 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.3.0
# rbenv global 2.3.0
# rbenv rehash
# gem install bundler --no-ri --no-rdoc

最後にnodejsをいれておく。railsで必要。
# yum -y install nodejs


Nginxインストール

# vi /etc/yum.repos.d/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


Capistranoでデプロイ


デプロイ準備

デプロイ用のディレクトリとユーザを作成します。

# mkdir /var/www

# adduser www
# chown -R www:www /var/www
# chmod -R 770 /var/www/
# adduser deploy
# gpasswd -a deploy www
# gpasswd -a nginx www
# mkdir /home/deploy/.ssh
# vi /home/deploy/.ssh/authorized_keys
> デプロイ用ユーザの公開鍵を貼り付ける。最初のユーザとは変えておく。macでは、ssh-keygen -t rsa でキーペアを作れる。
# chown -R deploy:deploy /home/deploy/.ssh
# chmod -R 700 /home/deploy/.ssh

$ ssh -i ~/.ssh/さっき貼り付けた公開鍵とペアの秘密鍵 deploy@IPアドレス

> ログインできるか確認する


Capistrano設定

あともう一息です。ローカルのRailsプロジェクトに戻ります。gemファイルに追記。

group :development do

gem 'capistrano', require: false
gem 'capistrano-rails', require: false
gem 'capistrano-bundler', require: false
gem 'capistrano3-puma', require: false
gem 'capistrano-rbenv', require: false
end

$ bundle install --path vendor/bundle

$ bundle exec cap install


Capfile.

require 'capistrano/setup'

require 'capistrano/deploy'
require 'capistrano/rails'
require 'capistrano/rbenv'
require 'capistrano/bundler'
require 'capistrano/puma'


deploy.rb

server 'IPアドレス', port: 22, roles: [:app, :web, :db], primary: true

set :repo_url, 'git@github.com:eiei19/アプリ名とか.git'
set :application, 'アプリ名'
set :user, 'deploy'
set :ssh_options, {
forward_agent: true,
user: fetch(:user),
keys: %w(~/.ssh/deployユーザの秘密鍵)
}
set :puma_threads, [4, 16]
set :puma_workers, 0
set :pty, true
set :use_sudo, false
set :stage, :production
set :deploy_via, :remote_cache
set :deploy_to, "/var/www/#{fetch(:application)}"
set :puma_bind, "unix://#{shared_path}/tmp/sockets/#{fetch(:application)}-puma.sock"
set :puma_state, "#{shared_path}/tmp/pids/puma.state"
set :puma_pid, "#{shared_path}/tmp/pids/puma.pid"
set :puma_access_log, "#{release_path}/log/puma.access.log"
set :puma_error_log, "#{release_path}/log/puma.error.log"
set :puma_preload_app, true
set :puma_worker_timeout, nil
set :puma_init_active_record, true
set :rbenv_type, :system
set :rbenv_ruby, '2.3.0'
set :linked_dirs, fetch(:linked_dirs, []).push(
'log',
'tmp/pids',
'tmp/cache',
'tmp/sockets',
'vendor/bundle',
'public/system',
'public/uploads'
)
set :linked_files, fetch(:linked_files, []).push(
'config/database.yml',
'config/secrets.yml'
)

namespace :puma do
desc 'Create Directories for Puma Pids and Socket'
task :make_dirs do
on roles(:app) do
execute "mkdir #{shared_path}/tmp/sockets -p"
execute "mkdir #{shared_path}/tmp/pids -p"
end
end
before :start, :make_dirs
end

namespace :deploy do
desc "Make sure local git is in sync with remote."
task :check_revision do
on roles(:app) do
unless `git rev-parse HEAD` == `git rev-parse origin/master`
puts "WARNING: HEAD is not the same as origin/master"
puts "Run `git push` to sync changes."
exit
end
end
end

desc 'Initial Deploy'
task :initial do
on roles(:app) do
before 'deploy:restart', 'puma:start'
invoke 'deploy'
end
end

desc 'Restart application'
task :restart do
on roles(:app), in: :sequence, wait: 5 do
invoke 'puma:restart'
end
end

before :starting, :check_revision
after :finishing, :compile_assets
after :finishing, :cleanup
end


$ cap production deploy:initial

database.ymlがないとか言われるはずなので、サーバにdeployユーザでログインして配置します。ホントはここも自動化するべき。


nginx設定

最後にnginxを設定して終了。

/etc/nginx/conf.d/xxxx.conf とか置く。

upstream アプリ名 {

server unix:/var/www/アプリ名/shared/tmp/sockets/アプリ名-puma.sock fail_timeout=0;
}

server {
listen 80;
server_name サーバのIPもしくはドメイン;
root /var/www/アプリ名/current/public;

location ~ ^/assets/ {
root /var/www/アプリ名/current/public;
}

try_files $uri/index.html $uri @アプリ名;

location / {
proxy_pass http://アプリ名;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
}

error_page 500 502 503 504 /500.html;
client_max_body_size 4G;
keepalive_timeout 10;
}