Amazon Linux 2 初期設定 & Rails5.2 + Capistrano3.11で自動デプロイ

#はじめに
EC2にRails環境を構築、Capistranoで自動デプロイするための初期設定を紹介していきます。
主に個人開発やテスト環境で扱う最低限の設定のみ記載します。
コピペで大体できるはず、、！

環境
OS : Amazon Linux 2
Webサーバー : Nginx
アプリケーションサーバー : puma
メールサーバー : postfix
DB : RDS（MySQL）

前提
macOS
AWSにてEC２、RDSを構築済み


#EC2に接続

```:ローカルPCのターミナル
"sudo ssh -i [keyペアファイル] ec2-user@[パブリックIP]"
#確認が出るので "yes"
#PCのパスワードを要求されるので、入力。
```

#初期設定

- yumをアップデート

```:SSH接続したEC2インスタンス内
yumをまとめてアップデート
$ sudo yum update -y
yum cromインストール
$ sudo yum install -y yum-cron
EPELを有効化
$ sudo amazon-linux-extras install epel

セキュリティのみ自動でパッチがあたるようにcronの設定ファイルを変更します。
$ vi /etc/yum/yum-cron.conf
変更点
update_cmd = security
apply_updates = yes
email_to = [任意]

設定したら起動及び再起動自動の自動実行を設定
$ systemctl start yum-cron
$ systemctl enable yum-cron
```

- タイムゾーンを日本時間に変更します。

```
タイムゾーンをJSTに設定
$ sudo ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime
タイムゾーンがJSTに変更されたか確認
$date
/etc/sysconfig/clockの設定変更を変更
$ sudo vi /etc/sysconfig/clock
```

```:/etc/sysconfig/clock
以下の内容を変更
ZONE="Asia/Tokyo"
UTC= true
```

- EC2インスタンスを再起動し、全てのプロセスが新しいタイムゾーンを設定を利用するようにします。

```:SSH接続したEC2インスタンス内
$ sudo reboot
```


#ユーザー追加
セキュリティ強化のため、ec2-userのアカウント名をそのまま使うのではなく、
ec2-userを停止にして、新しいユーザでログインできるようにします。

```:SSH接続したEC2インスタンス内
新規ユーザー作成
$ sudo adduser my-user
wheelグループに追加
$ sudo usermod -G wheel my-user
sudo権限を付与
$ sudo visudo
```

viが開くので、行末に「my-user ALL=(ALL) NOPASSWD: ALL」を追加

```:visudo
my-user ALL=(ALL) NOPASSWD: ALL
```

:wqで保存

ec2-userのauthorized_keysをmy-userの.sshディレクトリにコピーし、適切なパーミッションを設定します。

```:SSH接続したEC2インスタンス内
$ sudo rsync -a ~/.ssh/authorized_keys ~my-user/.ssh/
$ sudo chown -R my-user:my-user ~my-user/.ssh
$ sudo chmod 700 ~my-user/.ssh/
$ sudo chmod 600 ~my-user/.ssh/**
```

#SSH接続設定
EC2のシークレットキーをローカルPCの以下に配置
/Users/[ユーザー]/.ssh/***.pem

```:ローカルPCのターミナル
.sshディレクトリに移動
cd .ssh
シークレットキーを持っている他のユーザーもログインできるように権限を変更
$ chmod 600 ***.pem
権限が変更されているか確認
$ ls -al
-rw-------           になっていればOK
configに接続設定を記載
$ vi config
```

ローカルにショートカットを作成

```:/.ssh/config
Host [ホスト名]
 HostName [パブリックIP]
 Port 22
 User my-user
 IdentityFile ~/.ssh/***.pem
```

:wqで保存

my-userで接続できるか確認します。

```:ローカルPCのターミナル
$ ssh [ホスト名]
```

接続できたら、rootになれるか確認

```:SSH接続したEC2インスタンス内
$ sudo su -
```

#SSHのセキュリティ設定
rootとec2-userでのSSHを禁止する

```:SSH接続したEC2インスタンス内
$ sudo vi /etc/ssh/sshd_config
```

```:/etc/ssh/sshd_config
以下を追加

#ec2-userでのログインを禁止
DenyUsers ec2-user
#暗号化アルゴリズムの指定
Ciphers aes128-ctr,aes192-ctr,aes256-ctr
#鍵付きハッシュのアルゴリズム
MACs hmac-sha2-256,hmac-sha2-512

以下を変更
AuthorizedKeysFile .ssh/authorized_keys
PermitRootLogin no
```
:wqで保存

```:SSH接続したEC2インスタンス内
設定を反映
$ service sshd reload
```

#RVMとRubyをインストール

https://rvm.io/rvm/install

```:SSH接続したEC2インスタンス内
Rootに変更
$ sudo su -

RVMをインストール
# gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3
# curl -sSL https://get.rvm.io | bash

Rubyのインストールに必要なライブラリをインストール
# yum install -y gcc-c++ patch readline readline-devel zlib zlib-devel libyaml-devel libffi-devel openssl-devel make bzip2 autoconf automake libtool bison iconv-devel wget libxml2 libxml2-devel libxslt libxslt-devel
# yum install -y bzip2-devel lcms-devel libjpeg-devel libX11-devel libXt-devel libtiff-devel ghostscript-devel libXext-devel libpng-devel

ImageMagickを使うのでインストール（任意）
# yum install -y ImageMagick ImageMagick-devel

RVMを有効にするために一旦ログアウトし、もう一度SSH接続してrootになります。

Rubyをインストール
今回は2.3.8を使用
# rvm install 2.3
# rvm use 2.3.8
# ruby -v

bundllerをcapistranoが使うのでインストール
# gem install bundler
```

#MySQLをインストール
```:SSH接続したEC2インスタンス内
mariaDB削除
$ sudo yum remove mariadb-libs
$ sudo rm -rf /var/lib/mysql

mysqlインストール
$ sudo yum install mysql

Amazon RDSヘ接続
$ mysql -h {ENDPOINT} -P 3306 -u {Username} –p
パスワードが要求されるので、RDSで設定したパスワードを入力
```

#posfixのインストール
```:SSH接続したEC2インスタンス内
$ sudo yum install postfix
設定ファイルを変更
$ vi /etc/postfix/main.cf

適宜各要素を以下のように変更
inet_interfaces = all
mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain
home_mailbox = Maildir/
smtpd_banner = $myhostname ESMTP unknown
allow_min_user = yes

自動起動設定
$ systemctl enable postfix
$ systemctl start postfix
```

# Nginxをインストール
```:SSH接続したEC2インスタンス内
$ sudo yum -y install nginx

NginxでSSL通信する場合は鍵を配置
$ vi /etc/nginx/conf.d/server.key
サーバー証明書、中間証明書を配置
$ vi /etc/nginx/conf.d/server_geotrustca.crt

自動起動設定
$ systemctl start nginx
$ systemctl enable nginx

設定ファイルの文法チェック
$ nginx -t
```


#redisのインストール
```:SSH接続したEC2インスタンス内
$ rpm -ivh http://rpms.famillecollet.com/enterprise/remi-release-7.rpm
$ yum install --enablerepo=epel,remi redis
$ systemctl enable redis
$ systemctl start redis
```


# RailsディレクトリとShared内ファイルの作成

Capistranoのディレクトリ構成に合わせます。

```:SSH接続したEC2インスタンス内
sharedディレクトリを作成
mkdir -p /usr/local/rails/[デプロイするアプリ名]/shared/
mkdir -p /usr/local/rails/[デプロイするアプリ名]/shared/public/assets/
mkdir -p /usr/local/rails/[デプロイするアプリ名]/shared/config

※Webpackerを使う場合、アセットパイプラインでエラーが出る場合は空ファイルを配置
touch /usr/local/rails/[デプロイするアプリ名]/shared/public/assets/.manifest.json
touch /usr/local/rails/[デプロイするアプリ名]/shared/public/assets/.sprockets-manifest.json

secret_key_baseを.envで管理（デモ環境なのでmaster.keyは使わない）
vi /usr/local/rails/[デプロイするアプリ名]/shared/.env
```

RuntimeErrorになるので、rake secretで生成した文字列を貼り付けます。

```
bundle exec rake secret
ff9906beb21fd77ca11aa433d42b7caa720f37641a27bf1617e67894a4fca2c64526af874d1ef1cfac4372cabdf18127110b31bb766c46ef35dbab62736b04e3
```


```shared/.env
SECRET_KEY_BASE: ff9906beb21fd77ca11aa433d42b7caa720f37641a27bf1617e67894a4fca2c64526af874d1ef1cfac4372cabdf18127110b31bb766c46ef35dbab62736b04e3
```

```:SSH接続したEC2インスタンス内
ファイルの所有者を変更
chown -R my-user:my-user /usr/local/rails
```

※AWS認証情報を扱う場合は参考にしてください（CLI設定がおすすめ）
https://dev.classmethod.jp/cloud/aws/how-to-configure-aws-cli/


#node.jsとyarnを使う場合はインストール
```:SSH接続したEC2インスタンス内
node.jsのインストール
$ curl --silent --location https://rpm.nodesource.com/setup_8.x | sudo bash -
sudo yum install nodejs

yarnのインストール
$ curl --silent --location https://dl.yarnpkg.com/rpm/yarn.repo | sudo tee /etc/yum.repos.d/yarn.repo
$ sudo rpm --import https://dl.yarnpkg.com/rpm/pubkey.gpg
$ sudo yum install yarn
```

#Gitの設定

```:SSH接続したEC2インスタンス内
gitをインストール
$ sudo yum -y install git

.sshに移動
$ cd /home/my-user/.ssh
.ssh ディレクトリがない場合は作成し，パーミッションを700に設定
$ mkdir .ssh
$ chmod 700 .ssh

キーペアの登録
$ ssh-keygen
id_rsa　id_rsa.pub　が生成される

~/.ssh/id_rsa.pubの内容をクリップボードにコピー、GitHubに登録
$ pbcopy < ~/.ssh/id_rsa.pub

authorized_keysに変更
$ mv id_rsa.pub authorized_keys

接続設定を記載
$ vi ~/.ssh/config
```

```:~/.ssh/config
Host GitHub
  HostName github.com
  User git
  IdentityFile ~/.ssh/id_rsa
```

```:SSH接続したEC2インスタンス内
パーミッションを変更
$ chown -R my-user:my-user /home/my-user/.ssh
$ chmod 700 /home/my-user/.ssh/
$ chmod 600 /home/my-user/.ssh/authorized_keys
$ chmod 600 /home/my-user/.ssh/id_rsa
$ chmod 600 /home/my-user/.ssh/config
```

#Capistranoでのデプロイ

参考にさせていただきました。
https://qiita.com/naoki_mochizuki/items/657aca7531b8948d267b

pumaの設定のみ追記しました。

```config/deploy.rb
#Capistranoのバージョンを固定
lock '3.11.0'

#アプリケーション名
set :application, "[アプリ名]"
#レポジトリURL
set :repo_url, "ssh://git@github.com:****/[アプリ名].git"
#gitでバージョン管理方法
set :scm, :git
#対象ブランチ masterに固定（デフォルトはmaster）
set :branch, '[ブランチ名]'
#デプロイ先ディレクトリ フルパスで指定
set :deploy_to, "/usr/local/rails/[アプリ名]"
#sharedに入るものを指定
set :linked_dirs, %w{bin log tmp/pids tmp/cache tmp/sockets bundle}
#rvmのパス
set :default_env, { rvm_bin_path: '~/.rvm/bin' }
# sudo に必要
set :pty, true
#1回分のreleasesを保持する（デフォルトは5）
set :keep_releases, 1
#puma設定
set :puma_threds,  [4, 16]
set :puma_workers, 0
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.error.log"
set :puma_error_log,  "#{release_path}/log/puma.access.log"
set :puma_preload_app, true
set :puma_worker_timeout, nil
set :puma_init_active_record, true

SSHKit.config.command_map[:rake] = 'bundle exec rake'

namespace :deploy do

  #puma再起動タスク
  desc "Restart Puma"
  task :restart do
    on roles(:app), in: :sequence, wait: 5 do
      invoke 'puma:restart'
    end
  end

  after :finishing, 'deploy:cleanup'

end

```

```:Capfile
require "capistrano/setup"
require "capistrano/deploy"
require "capistrano/scm/git"
install_plugin Capistrano::SCM::Git
require "capistrano/rvm"
# require "capistrano/rbenv"
require "capistrano/bundler"
require "capistrano/rails/assets"
require "capistrano/rails/migrations"

# Load custom tasks from `lib/capistrano/tasks` if you have any defined
Dir.glob("lib/capistrano/tasks/*.rake").each { |r| import r }
```

```config/deploy/staging.rb
EC2サーバーのIP、EC2サーバーにログインするユーザー名、サーバーのロールを記述
server 'パブリックIP', user: 'my-user', roles: %w{app db web} 

デプロイするサーバーにsshログインする鍵の情報を記述
set :ssh_options, keys: '~/.ssh/***.pem'
```


#おまけ　logのローテーション

```:SSH接続したEC2インスタンス内
$ sudo vi /etc/logrotate.d/[アプリ名]
```


```/etc/logrotate.d/[アプリ名]
/usr/local/rails/[アプリ名]/shared/log/*.log {
daily                #ログを毎日ローテーションする
missingok            #ログファイルが存在しなくてもエラーを出さずに処理を続行
rotate 10            #10世代分古いログを残す
dateext              #日次ローテート＋日付文字列
compress             #ローテーションしたログをgzipで圧縮
sharedscripts        #複数指定したログファイルに対し、postrotateで記述したコマンドを実行
su my-user my-user   #rotateするユーザを指定

postrotate
puma_pid=/usr/local/rails/[アプリ名]/shared/tmp/pids/puma.pid
test -e $puma_pid && kill -USR2 $(cat $puma_pid) || true
endscript
}
```

```:SSH接続したEC2インスタンス内
エラーが出ていないか確認
$ logrotate -d /etc/logrotate.d/[アプリ名]
```