CentOS7.6 RubyonRails6 mysql8でconohaVPSに公開するまで

Railsのリポジトリがある前提です
バージョン



```
centos
7.6
mysql
8.0.17

$ ruby -v
2.6.3
$ rails -v 
6.0.0.rc1

$ node -v
v12.8.0

```

大まかな手順

```
1. conohaにVPS登録
2. vpsサーバの各種設定
3. Rubyインストール
4. mysqlインストール・設定
5. nginxインストール・設定
6. CapistranoとUnicornのインストール・設定
7.
8.
```






#1. conohaに登録

↓ こちらから登録すると1000円分のクーポンがもらえるらしいです
https://www.conoha.jp/referral/?token=IWGrST5H2QhYV.qMLTsPTHRLvaa1fDkrP7CpxS7_2efeNKRbFC8-O6O


私はCentos7.6でメモリ2GB1750円のvpsプランを選択しました。


=>⚠️ # はroot権限という意味です

#2. サーバーの設定

```
ログイン
$ ssh root@123.43.567.89

# yum update

localeを変更する
# localectl set-locale LANG=ja_JP.utf8
# localectl
# cat /etc/locale.conf
# source /etc/locale.conf #反映


作業ユーザー登録
# useradd username
# passwd username
# usermod -G wheel username
# visudo

↓ファイルが立ち上がる
## Allows people in group wheel to run all commands
%wheel  ALL=(ALL)       ALL

上の行の先頭のコメントを外して保存
```

一旦、サーバーから抜けて作成したユーザーでログインできるか確認

```
$ ssh hogehuga@123.45.678.000
ログイン成功！！
```

#### 鍵認証の設定
Mac側で鍵のペアを生成(秘密鍵と公開鍵)
VPS側で公開鍵をMac側で秘密鍵を使ってマッチさせる仕組み
鍵を生成前にまずはVPS側で公開鍵の保管場所を作成する

```
$ mkdir ~/.ssh     # ディレクトリ作成
$ chmod 700 ~/.ssh #パーミッション設定
```
MAC側(localhost)で作業

```
$ ssh-keygen -y rsa -v # 鍵が作成される(id_rsaとid_rsa.pub)
$ chmod 600 ~/.ssh/id_rsa.pub # パーミッション付与
```

公開鍵をVPSに転送

```
$ scp ~/.ssh/id_rsa.pub hogehuga@123.45.678.000:~/.ssh/authorized_keys
```
vpsに入って確認する

```
$ ls -la .ssh
=> authorized_keysが入っていれば良い
```

### セキュリティを高めるsshの設定

```
# vim /etc/ssh/sshd_config
で以下のように変更

PubkeyAuthentication yes
Port変更
Port 55555
rootユーザーでのログインを禁止。
PermitRootLogin no
パスワードログインを禁止。yesからnoへ変更
PasswordAuthentication no
公開鍵の場所を設定
AuthorizedKeysFile      .ssh/authorized_keys
```

sshdを再起動。編集を反映させる

```
# service sshd restart
```

### iptablesをインストール

```
# yum install iptables-services
# vim /etc/sysconfig/iptables

下を削除
# -A INPUT -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT
下を追加
-A INPUT -m state --state NEW -m tcp -p tcp --dport 55555 -j ACCEPT
```
### iptablesの設定を反映させる再起動

```
# service iptables restart
```

configファイルも作っておく


```
# vi ~/.ssh/config

Host conoha_deploy_app
  HostName 123.45.67.89 # ip_adress
  User hogehuga
  Port 55555
  IdentityFile  ~/.ssh/id_rsa # deployの時にも使います

ssh-agentを登録しておく
$ eval `ssh-agent`
$ ssh-add ~/.ssh/id_rsa

ローカルから接続できているか試す
$ ssh conoha_deploy_app
```

# 3. Ruby インストール

サーバーでの作業が続きます
### まずはgitをインストール!

なるべく新しいのが良いと思ったので2.7.4です。
少し手間ですが、以下のコマンドを実行しました。
参考
https://ex1.m-yabe.com/archives/3546

```
# yum install curl-devel expat-devel gettext-devel openssl-devel zlib-devel perl-ExtUtils-MakeMaker
#  cd /usr/local/src
#  wget  https://www.kernel.org/pub/software/scm/git/git-2.7.4.tar.gz
#  tar zxvf git-2.7.4.tar.gz
#  cd git-2.7.4
#  ./configure --prefix=/usr/local/
#  rpm -qa|grep gcc
#  yum -y install gcc
#  ./configure --prefix=/usr/local/
#  make prefix=/usr/local all
#  make prefix=/usr/local install
# git version
=> git version 2.7.4
```

### rbenvインストール

rbenvはrubyのバージョン管理ツールです

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

### ruby-buildインストール

ruby-burildはrbenv installを実行するためのrbenvのプラグインです。
rbenvとセットでインストール

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

###依存パッケージのインストール

rubyをインストールするための関連するパッケージです。これがないと、rbenv installの際にコケます。

```
$  sudo yum -y install bzip2 gcc openssl-devel readline-devel zlib-devel
```

###rbenv読み込み設定
```
$ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile
# rbenvの初期化
$ echo 'eval "$(rbenv init -)"' >> ~/.bash_profile
# bashの再読込
$ source ~/.bash_profile
# バージョン確認　バージョンが出ればOK
$ rbenv -v
```

### Rubyインストール

```
$ rbenv install 2.6.3
$ rbenv global 2.6.3
$ rbenv rehash
# バージョン確認
$ ruby -v
2.6.3
```

### bundlerインストール

```
$ gem install bundle
```
### node.jsインストール

```
railsでnode.jsが必要

$ sudo yum install nodejs npm --enablerepo=epel
yarnもインストール
$ sudo npm install -g yarn
```

# 4. MySQL8のインストール

```
sudo rpm -Uvh https://dev.mysql.com/get/mysql80-community-release-el7-3.noarch.rpm
sudo yum install --enablerepo=mysql80-community mysql-community-server
sudo yum clean all
mysqld --version
systemctl enable mysqld.service　自動起動設定
systemctl start mysqld.service   起動
sudo grep 'temporary password' /var/log/mysqld.log => 最初のログインで使用する初期設定用のパスワードが表示されます
mysql -u root -p 
mysql_secure_installation　＝＞passwordを変更する

```



Mysql 8のmy.cnf設定


デフォルトでも構いません
https://qiita.com/nanorkyo/items/94a80683c6753f61316a

```/etc/my.cnf

リモートで
sudo vim /etc/my.cnf


自分はこんな感じです↓

[mysqld]
default-authentication-plugin=mysql_native_password
default-password-lifetime       = 0

datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock

log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid

[mysqld]
character-set-server            = utf8mb4
collation-server                = utf8mb4_bin
open-files-limit                = 65536
max-connections                 = 10000
table-open-cache                = 2000

[client]
default-character-set           = utf8mb4

```

リスタートして
 
```
sudo systemctl restart mysqld.service
```

ログインする

```

Mysqlログイン
mysql -u root -p

> create user 'username'@'123.4.56.78' identified by 'password';

> grant all privileges on *.* to 'hogehuga'@'123.4.56.78' with grant option;
> flush privileges;

$ mysql -u hogehuga -h 123.4.56.78 -p

> create database app_production;
> exit;

```

#5. nginx（webサーバー）の設定

デプロイするアプリの置き場所をVPSに用意する

```
$ sudo mkdir -p /var/www/アプリケーション名
```
nginxをインストールするための準備

```
# cd /etc/yum.repos.d
# touch nginx.repo
```

ファイルの中身を書き加えましょう。

管理者権限になっていることに注意

```
# vi nginx.repo
```
nginx.repoファイルの書き方は以下のような感じ(サンプル)

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

nginxをインストール

```
インストールされるリポジトリ情報を確認
# yum info nginx

インストール
# yum install nginx
```

設定ファイルをバックアップ

```
# sudo cp /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/local.conf
# mv /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf.bk
```

```/etc/nginx/conf.d/local.conf

#各種ログのディレクトリ設定
  error_log  /var/www/app_name/current/log/nginx.error.log;
  access_log /var/www/app_name/current/log/nginx.access.log;
#処理を受け取る最大許容量
  client_max_body_size 2G;

upstream unicorn {
 server unix:/var/www/deploy_app/shared/tmp/sockets/unicorn.sock; ### 変更箇所 ちょー大事、後ほど登場するユニコーンと紐づく
}


server {
    listen       80; # ポート番号
    server_name  123.654.87.9;
#次のリクエストが来るまでの待ち時間（秒
    keepalive_timeout 5;


    location / {
        root   /var/www/app_name/current/public;
    }
    #キャッシュのディレクトリ
    try_files $uri/index.html $uri.html $uri @app;
    location @unicorn {
      # HTTP headers
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header Host $http_host;
      proxy_redirect off;
      proxy_pass http://unicorn;
    }
    error_page   500 502 503 504  /500.html;
    location = /500.html {
        root   /var/www/app_name/current/public;
    }
}
```

設定ファイルを編集

```
$ sudo vi /etc/sysconfig/iptables
```

-A INPUT -m state --state NEW -m tcp -p tcp --dport 80 -j ACCEPT

を追記

```/etc/sysconfig/iptables
# sample configuration for iptables service
# you can edit this manually or use system-config-firewall
# please do not ask us to add additional ports/services to this default configuration
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
-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 12345 -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 80 -j ACCEPT

-A INPUT -j REJECT --reject-with icmp-host-prohibited
-A FORWARD -j REJECT --reject-with icmp-host-prohibited
COMMIT
```

みたいな感じになってます


再起動。

```
$ sudo service iptables restart
```

開放されてるか確認

```
$ sudo iptables -nL
こんな感じで表示されます
ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:80
```

サーバー再起動の際にもNginxが自動的に起動するよう設定

```
chkconfig nginx on
```

Nginxの設定が完了

#6. CapistranoとUnicornの設定

Gemfileにインストール作業で使用する物を記述

```Gemfile

ruby '2.6.3'
gem 'mysql2'
・・・・その他中略・・・
group :development do
  # Access an IRB console on exception pages or by using <%= console %> anywhere in the code.
  gem 'web-console', '>= 3.3.0'
  gem 'listen', '>= 3.0.5', '< 3.2'
  # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
  gem 'spring'
  gem 'spring-watcher-listen', '~> 2.0.0'
  # 以下追記
  gem 'capistrano'
  gem 'capistrano-bundler'
  gem 'capistrano-rails'
  gem 'capistrano-rbenv'
  gem 'capistrano-nvm', require: false
  gem 'capistrano-yarn'
  gem 'capistrano3/unicorn'
end

group :production, :staging do
 # 以下追記
 gem 'unicorn'
end


```

```
$ bundle install
```

Capistranoを設定していく

ファイルを生成するコマンド

```
$ cap install
```

capistranoの設定ファイルを編集

```
# capistranoの基本動作を設定
require "capistrano/setup"   
require "capistrano/deploy"  
require "capistrano/scm/git" 
install_plugin Capistrano::SCM::Git


require "capistrano/rails" 
require "capistrano/rails/assets"
require "capistrano/rails/migrations" 
# rbenvの設定
require "capistrano/rbenv" #ok
require "capistrano/bundler"
require "pry" # 全然必要ないけどデバッグで便利でした...
require 'capistrano/yarn'
require 'capistrano3/unicorn'


Dir.glob("lib/capistrano/tasks/*.rake").each { |r| import r }
```
リモートのサーバー情報書いていく

```config/deploy/production.rb
set :stage, :production
set :branch, 'master' ## 必要があれば変更

server '21.43.65.7', user: 'hoge', roles: %w{app db web}, port: 12345

set :ssh_options, {
  port: 12345, #### 変更
  keys: [File.expand_path('~/.ssh/id_rsa')], # リモートサーバー用秘密鍵があるところを指定
  forward_agent: true,
  auth_methods: %w(publickey)
```


アプリケーション名や
gitのリポジトリなど書いていきます

```config/deploy.rb

# config valid for current version and patch releases of Capistrano
lock "~> 3.11.0"

set :application, "deploy_app"
set :repo_url, "git@github.com:user_name/deploy_app.git" デプロイするgitリポジトリURL

set :deploy_to, '/var/www/deploy_app' # デプロイ先

set :rbenv_type, :user  # rbenvをシステムにインストールした or ユーザーローカルにインストールした
set :rbenv_ruby, '2.6.3' # サーバで利用するrubyのバージョンを指定
set :rbenv_path, '/home/username/.rbenv'
set :rbenv_prefix, "RBENV_ROOT=#{fetch(:rbenv_path)} RBENV_VERSION=#{fetch(:rbenv_ruby)} #{fetch(:rbenv_path)}/bin/rbenv exec"

# リリースフォルダをいくつまで保持するか？
set :keep_releases, 5
set :deploy_via, :remote_cache

set :log_level, :debug # capistranoの出力ログの制御
set :pty, true # sudoを使用するのに必要

set :yarn_flags, "--ignore-engines --prefer-offline --production --no-progress"
set :yarn_roles, :all
set :yarn_env_variables, {}

# Shared に入るものを指定
set :linked_files, %w{config/database.yml config/secrets.yml} # シンボリックリンクを貼るファイル
set :linked_dirs,  %w{log tmp/pids tmp/sockets tmp/cache vender/bundle } # sharedにシンボリックリンクを張るディレクトリ指定

after 'deploy:publishing', 'deploy:restart'
namespace :deploy do


 desc 'Restart application'
  # アプリ再起動を行うタスク
  task :restart do
    on roles(:app), in: :sequence, wait: 5 do
      execute :mkdir, '-p', release_path.join('tmp')
      execute :touch, release_path.join('tmp/restart.txt')
    end
  end
  # linked_files で使用するファイルをアップロードするタスク
  # deployが行われる前にコマンドにて実行する
  desc 'upload important files'
  task :upload do
    on roles(:app) do |host|
      execute :mkdir, '-p', "#{shared_path}/config"
      upload!('config/database.yml',"#{shared_path}/config/database.yml")
      upload!('config/secrets.yml',"#{shared_path}/config/secrets.yml")
    end
  end

  # webサーバー再起動時にキャッシュを削除する
  after :restart, :clear_cache do
   on roles(:web), in: :groups, limit: 3, wait: 10 do
    #Here we can do anything such as:
    within release_path do
      execute :rm, '-rf', release_path.join('tmp/cache')
    end
   end
  end
  # Flow の before, after のタイミングで上記タスクを実行
  before :started,   'deploy:upload'
  after  :finishing, 'deploy:cleanup'

  #unicorn 再起動タスク
  desc 'Restart application'
  task :restart do
    invoke 'unicorn:restart' # lib/capustrano/tasks/unicorn.rake 内処理を呼び出す
  end
end


```


認証で使うsecrets.ymlを生成します
ローカルで以下のコマンドを実施

```
$ rails secret | pbcopy
```
ファイルに貼り付ける

```config/secrets.yml
production:
  secret_key_base: #####ここに貼り付けてください
```

リモートサーバにて

```
必要なディレクトリを作成しておく

$ sudo mkdir -p /var/www/deploy_app/shared/config
$ sudo  chown -R 作業ユーザー名 www

```

Uniconの設定をします

ローカルで

vim lib/capistrano/tasks/unicorn.cap

```
コピペでいいです

namespace :unicorn do
  task :environment do
    set :unicorn_pid, "#{current_path}/tmp/pids/unicorn.pid" # config/unicorn/production.rbないのpidと同じ
    set :unicorn_config, "#{current_path}/config/unicorn/production.rb"
  end

  def start_unicorn
    # フォルダが存在した場合のみ実行する
    within current_path do
    execute :bundle, :exec, :unicorn, "-c #{fetch(:unicorn_config)} -E #{fetch(:rails_env)} -D"
    end
  end
  # unicorn停止
  def stop_unicorn
    execute :kill, "-s QUIT $(< #{fetch(:unicorn_pid)})"
  end
  # unicorn再起動
  def reload_unicorn
    execute :kill, "-s USR2 $(< #{fetch(:unicorn_pid)})" 
  end
  # unicorn 強制終了
  def force_stop_unicorn
    execute :kill, "$(< #{fetch(:unicorn_pid)})"
  end
  # unicorn をスタートさせる
  desc "Start unicorn server"
  task :start => :environment do
    on roles(:app) do
      start_unicorn
    end
  end
  # unicorn停止させる
  desc "Stop unicorn server gracefully"
  task :stop => :environment do
    on roles(:app) do
      stop_unicorn
    end
  end
  # 
  desc "Restart unicorn server gracefully"
  task :restart => :environment do # TODO
    on roles(:app) do
      if test("[ -f #{fetch(:unicorn_pid)} ]")
        reload_unicorn
      else
        start_unicorn
      end
    end
  end
  # unicornを強制終了させるTask
  desc "Stop unicorn server immediately"
  task :force_stop => :environment do # TODO
    on roles(:app) do
      force_stop_unicorn
    end
  end
end
```

unicornの設定ファイルを書いていく
ここがうまく紐づかないとリリース後にrailsが動かなかったりします
気をつけてください

```config/unicorn/production.rb

# -*- coding: utf-8 -*-
worker_processes Integer(ENV["WEB_CONCURRENCY"] || 3)
timeout 30

# ホットデプロイをするか？
preload_app true # 更新時ダウンタイム無し
current_path = '/var/www/deploy_app/shared'
app_path = '/var/www/deploy_app/current'
working_directory "#{app_path}"

# nginxと連携するための設定
# リクエストを受け取る ポート番号を指定
listen "/var/www/deploy_app/shared/tmp/sockets/unicorn.sock" # nginxと同じ

# PIDの管理ディレクトリ
pid "#{current_path}/tmp/pids/unicorn.pid"

stdout_path "#{app_path}/log/unicorn.log"
stderr_path "#{app_path}/log/unicorn.log"


# ログの出力パス
stderr_path File.expand_path('log/unicorn.log', ENV['RAILS_ROOT'])
stdout_path File.expand_path('log/unicorn.log', ENV['RAILS_ROOT'])

puts "カレントパス#{ current_path }"
puts ""

# フォーク前に行うことを定義
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
```

database.ymlの設定

```config.database.yml
default: &default
  adapter:  mysql2
  encoding:  utf8mb4
  charset:   utf8mb4
  collation: utf8mb4_unicode_ci
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root
  password: 
  host: localhost
  socket: /tmp/mysql.sock

development:
  <<: *default
  database: deploy_app_development

test:
  <<: *default
  database: deploy_app_test

production:
  <<: *default
  adapter: mysql2
  database: deploy_app_production
  username: username
  password: PassWord
  host: 12.34.765.89
```


# 7 リモートとgithubの連携

ローカルとサーバーの鍵認証は先にしておりますが、
リモートサーバーとgithubを連携するための
鍵認証の設定です

**リモートサーバーで作業すること**

```
$ cd ~/.ssh
$ ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/home/hogehuga/.ssh/id_rsa): Enter 
Enter passphrase (empty for no passphrase): Enter
Enter same passphrase again: Eneter

$ cat id_rsa.pub
# 出力をコピーして、以下のgithubのコンソールリンクより公開鍵を登録
-> https://github.com/settings/keys


# 設定ファイルを作成
$ sudo vi ~/.ssh/config
# 以下を記述
Host github
  Hostname github.com
  User git
  IdentityFile ~/.ssh/id_rsa 

ssh-agentの設定(これやらないとpermission error出ます)
$ eval `ssh-agent` 
$ ssh-add ~/.ssh/id_rsa

$ ssh -vT github で確認 # vはデバッグオプションで便利 
#以下のような文章が出力されればOK
Hi Hogehuga! You've successfully authenticated, but GitHub does not provide shell access.
```

gitにプッシュする

```
$ git add .
$ git commit -m "deploy"
$ git git push origin master
```

長かったですが、デプロイしてみましょう
ローカルから

```
$ bundle exec cap production deploy:upload # database.yml、secrets.ymlをアップロードする
$ bundle exec cap production deploy:check # deploy可能かのチェック
$ bundle exec cap production deploy
```


すんなりいくとは思いませんが、
エラーが起きた場合にはnginxのログやunicornのログなどを頼りにしつつ、問題を切り分けてコツコツ進んでいくのが良いと思います。

ファイルの権限/設定のパスの整合性/各種システムの起動し忘れ/などなどです



雑なメモ

```
nginx基本コマンド
起動
$ sudo systemctl start nginx
停止
$ sudo systemctl stop nginx
再起動
$ sudo systemctl restart nginx
再起動しても設定ファイルが反映されない場合など
$ sudo nginx -s reload
状態の確認
$ sudo systemctl status nginx


Mysqlコマンド
ログイン
mysql -u username -h 123.45.67.89 -p

・起動
sudo service mysqld start
・停止
sudo service mysqld stop
・再起動
sudo service mysqld restart


Iptablesコマンド

systemctl start iptables
systemctl enable iptables
systemctl status iptables

sshdコマンド
systemctl restart sshd
systemctl status sshd




Unicornコマンド



```