【AWS】EC2でのデプロイ（自動デプロイ）

# 目的
`AWS`のサーバーを利用し、Railsで作成したアプリを公開する。

# 開発環境
macOS: *Big Sur*
Rubyバージョン: *2.6.5*
Railsバージョン: *6.0.0*

# 前提

- `Webサーバー`の設定が完了している。[【AWS】EC2でのデプロイ（Webサーバーの設定）](https://qiita.com/oak1331/items/501cc3cea4411abfd26d)

# 手順

1. [はじめに](#はじめに)
1. [Gemの導入](#gemの導入)
1. [Capfileの編集](#capfileの編集)
1. [production.rbの編集](#productionrbの編集)
1. [deploy.rbの編集](#deployrbの編集)
1. [Unicorn設定ファイルの編集](#unicorn設定ファイルの編集)
1. [Nginx設定ファイルの編集](#nginx設定ファイルの編集)
1. [自動デプロイの準備](#自動デプロイの準備)
1. [自動デプロイの実行](#自動デプロイの実行)
1. [アプリ修正から自動デプロイの流れ](#アプリ修正から自動デプロイの流れ)

# はじめに

今回は`自動デプロイ`を実装していきます！
これまで手動で行っていたデプロイ作業を、ローカルのターミナルからのコマンド1つで行えるようにします。
そのために、`自動デプロイツール`と呼ばれるものを利用しますが、今回はポピュラーな`Capistrano`を導入していきます。
`Capistrano`を利用すればEC2サーバーにログインする必要もなくコマンド1つでデプロイが完了します！

# Gemの導入

それでは早速始めていきます！
まずは、`Capistrano`を利用するためのGemをインストールします。

```ruby:Gemfile
#省略

group :development, :test do
  gem 'capistrano'
  gem 'capistrano-rbenv'
  gem 'capistrano-bundler'
  gem 'capistrano-rails'
  gem 'capistrano3-unicorn'
end

#省略
```

```:ターミナル（ローカル）
% bundle install
```

```:ターミナル（ローカル）
% bundle exec cap install
```

これでインストールできました！

# Capfileの編集

続いて、Capistrano全体の設定をしている`Capfile`を編集します。

`Capfile`では、`Capistrano`関連のライブラリのうちどれを読み込むかを指定できます。
`Capistrano`の機能を提供するコードはいくつかのライブラリに分かれています。そのため、`Capistrano`を動かすにはいくつかのライブラリを読み込む必要があります。

```ruby:Capfile
require "capistrano/setup"
require "capistrano/deploy"
require 'capistrano/rbenv'
require 'capistrano/bundler'
require 'capistrano/rails/assets'
require 'capistrano/rails/migrations'
require 'capistrano3/unicorn'

Dir.glob("lib/capistrano/tasks/*.rake").each { |r| import r }
```

「require」によって引数としておかれた文字列が指すディレクトリが読み込まれ、その中にデプロイに際して必要な動作がひととおり記述されています。

# production.rbの編集

続いて、デプロイについての設定を記載するファイルを編集します。

`production.rb`は、デプロイについての設定を書くファイルです。
GitHubへの接続に必要なsshキーの指定、デプロイ先のサーバのドメイン、AWSサーバへのログインユーザー名、サーバにログインしてからデプロイのために何をするか、といった設定を記載します。
また、`deploy.rb`、`staging.rb`も同様の役割をするファイルです。

```ruby:config/deploy/production.rb
server '用意したElastic IP', user: 'ec2-user', roles: %w{app db web}
```

# deploy.rbの編集

次は、`deploy.rb`の設定です。
このファイルには、production環境、staging環境どちらにも当てはまる設定を記述することとなります。
それでは、編集していきます！

```ruby:config/deploy.rb
# capistranoのバージョンを記載。固定のバージョンを利用し続け、バージョン変更によるトラブルを防止する
lock 'Capistranoのバージョン'

# Capistranoのログの表示に利用する
set :application, 'アプリ名'

# どのリポジトリからアプリをpullするかを指定する
set :repo_url,  'git@github.com:Githubのユーザー名/レポジトリ名.git'

# バージョンが変わっても共通で参照するディレクトリを指定
set :linked_dirs, fetch(:linked_dirs, []).push('log', 'tmp/pids', 'tmp/cache', 'tmp/sockets', 'vendor/bundle', 'public/system', 'public/uploads')

set :rbenv_type, :user
set :rbenv_ruby, 'このアプリで使用しているrubyのバージョン'

# どの公開鍵を利用してデプロイするか
set :ssh_options, auth_methods: ['publickey'],
                                  keys: ['~/.ssh/キーペア名.pem'] 

# プロセス番号を記載したファイルの場所
set :unicorn_pid, -> { "#{shared_path}/tmp/pids/unicorn.pid" }

# Unicornの設定ファイルの場所
set :unicorn_config_path, -> { "#{current_path}/config/unicorn.rb" }
set :keep_releases, 5

# デプロイ処理が終わった後、Unicornを再起動するための記述
after 'deploy:publishing', 'deploy:restart'
namespace :deploy do
  task :restart do
    invoke 'unicorn:restart'
  end
end
```

「Capistranoのバージョン」は、Gemfile.lockに記載されています。

# Unicorn設定ファイルの編集

続いてUnicorn設定ファイルの編集です。
手動デプロイの時と比べ、自動デプロイ時にはRailsアプリのディレクトリが1段階深くなるため、数ヶ所変更を加える必要があります。

```ruby:config/unicorn.rb
#サーバ上でのアプリコードが設置されているディレクトリを変数に入れておく
app_path = File.expand_path('../../../', __FILE__)

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

#アプリの設置されているディレクトリを指定
working_directory "#{app_path}/current"  # 「current」を指定

#Unicornの起動に必要なファイルの設置場所を指定
pid "#{app_path}/shared/tmp/pids/unicorn.pid"  # 「shared」の中を参照するよう変更

#ポート番号を指定
listen "#{app_path}/shared/tmp/sockets/unicorn.sock"  # 「shared」の中を参照するよう変更

#エラーのログを記録するファイルを指定
stderr_path "#{app_path}/shared/log/unicorn.stderr.log"  # 「shared」の中を参照するよう変更

#通常のログを記録するファイルを指定
stdout_path "#{app_path}/shared/log/unicorn.stdout.log"  # 「shared」の中を参照するよう変更
#省略
```

# Nginx設定ファイルの編集

同じく、Nginxの設定ファイルも編集します。

```:ターミナル(EC2内で実行）
$ sudo vim /etc/nginx/conf.d/rails.conf
```

vimコマンドで編集するので、まずは「i」と打ち込んで入力モードに切り替えます。

```ruby:/etc/nginx/conf.d/rails.conf
upstream app_server {
  # Unicornと連携させるための設定
  server unix:/var/www/リポジトリ名/shared/tmp/sockets/unicorn.sock;
}

# {}で囲った部分をブロックと呼ぶ。サーバの設定ができる
server {
  # このプログラムが接続を受け付けるポート番号
  listen 80;
  # 接続を受け付けるリクエストURL ここに書いていないURLではアクセスできない
  server_name Elastic IP;

  # クライアントからアップロードされてくるファイルの容量の上限を2ギガに設定。デフォルトは1メガなので大きめにしておく
  client_max_body_size 2g;

# 接続が来た際のrootディレクトリ
  root /var/www/リポジトリ名/current/public;

# assetsファイル(CSSやJavaScriptのファイルなど)にアクセスが来た際に適用される設定
  location ^~ /assets/ {
    gzip_static on;
    expires max;
    add_header Cache-Control public;
    root /var/www/リポジトリ名/current/public;
  }

  try_files $uri/index.html $uri @unicorn;

  location @unicorn {
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_redirect off;
    proxy_pass http://app_server;
  }

  error_page 500 502 503 504 /500.html;
}
```

入力を終えたら「escキー」→「:wq」の順で実行し、保存します！
Nginxの設定を変更したら、忘れずに再読込・再起動をしましょう。

```:ターミナル（EC2内で実行）
[ec2-user@ip-172-31-25-189 ~]$ sudo systemctl reload nginx
```

```:ターミナル（EC2内で実行）
[ec2-user@ip-172-31-25-189 ~]$ sudo systemctl restart nginx
```

以上でNginx設定ファイルの編集は完了です！

# 自動デプロイの準備

ここからは自動デプロイの準備を行っていきます。
まずデータベースの状態を確認します。データベースが立ち上がっていないとデプロイが失敗するためです。

```:ターミナル（EC2内で実行）
[ec2-user@ip-172-31-25-189 ~]$ sudo systemctl status mariadb 
```

ここで`active`になっていない場合は`sudo systemctl start mariadb`を実行します。

次に、unicornのプロセスをkillします。
すでにunicornのサーバーが立ち上がっている状態で自動デプロイをすると、二重でサーバーを立ち上げることになるためです。
まずは、プロセスを確認します。

```:ターミナル（EC2内で実行）
[ec2-user@ip-172-31-23-189 www]$ cd  /var/www/開発中のアプリ
```

```:ターミナル（EC2内で実行）
[ec2-user@ip-172-31-23-189 <リポジトリ名>]$ ps aux | grep unicorn
```

続いて、プロセスをkillします。

```:ターミナル（EC2内で実行）
[ec2-user@ip-172-31-23-189 <リポジトリ名>]$ kill プロセス番号
```

以上で自動デプロイの準備は完了です！

# 自動デプロイの実行

それでは実際に、自動デプロイを実行します！

```:ターミナル（ローカル）
% bundle exec cap production deploy
```

上記コマンドを実行しエラーが表示されずに完了したら自動デプロイは完了です。

# アプリ修正から自動デプロイの流れ

アプリの修正を行った際、自動デプロイをする前にやるべき事は下記の通りです。

|  状況                           |  	自動デプロイ前にやること                                                 |
| ------------------------------ | ----------------------------------------------------------------------- |
|  VSCodeを修正した場合             |  変更点をリモートリポジトリにcommit→pushする                                     |
|  データベース関連の内容を修正した場合  |  本番環境で「rails db:drop RAILS_ENV=production」「rails db:create RAILS_ENV=production」を実行 （※実行する際、「DISABLE_DATABASE_ENVIRONMENT_CHECK=1」というオプションが必要）  |
|  Nginxを修正した場合              |  「sudo systemctl restart nginx」を実行                                    |
|  再度自動デプロイを実行する場合      |  プロセスをkillした上で「bundle exec cap production deploy」を実行              |

# 最後に

以上で、自動デプロイの実装は完了です。
では。