自動でのデプロイ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3457284/2ee83391-e055-9cb9-4db5-dd10116a3c90.png)
１：デプロイの自動化
数ある自動デプロイツールの中でも、もっともポピュラーなCapistranoを導入して、デプロイを自動化する。

２：自動デプロイツールのメリット
自動デプロイツールを利用することによって、デプロイ時に必要なコマンド操作が1回で済むようになります。これにより、手動デプロイする場合に起こりがちな下記の問題を解消できます。

手動デプロイで起こりがちな問題
・コマンドの打ち間違い、手順の間違いが発生する可能性がある
・手順が多く、煩わしい

３：Capistranoの導入準備
まずは、Capistranoを利用するためのGemをインストールします。
①Gemfileを以下のように編集します。

「group :development, :test do ~ end」がすでに記述されている場合は、「gem ~」の部分だけを追記します。
Gemfile

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

②ターミナル（ローカル）：アプリケーションのディレクトリで実行します。
% bundle install
⇩
gemを読み込めたら、下記のコマンドを打ちます。

③ターミナル（ローカル）：アプリケーションのディレクトリで実行します。
% bundle exec cap install

４：Capfileを下記のように編集します。Capfile→Gemfileの２つ上にあるファイル。

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 }

５：production.rbの一番下に、下記のように編集します。
config/deploy/production.rb
server '用意したElastic IP', user: 'ec2-user', roles: %w{app db web}
↓
例：server '11.366.64.450', user: 'ec2-user', roles: %w{app db web}

6:deploy.rbを下記のように編集します。
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のバージョン' #カリキュラム通りに進めた場合、’2.6.5’ です

# どの公開鍵を利用してデプロイするか
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に記載されています。以下のように書かれている場合は「3.８.0」です。
【例】Gemfile.lock

capistrano (3.8.0)

７：Unicornの設定ファイルを下記のように編集します。
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の設定ファイルを編集します。
ターミナル(EC2内で実行）
$ sudo vim /etc/nginx/conf.d/rails.conf

９：/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-user@ip-172-31-25-189 ~]$ sudo systemctl restart nginx

９：データベースの起動を確認します。
データベースが立ち上がっていないとデプロイが失敗します。以下のコマンドでデータベースの状態を確認しておきます。

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

※以下のように、「active」と表示されれば起動しています。

ターミナル（EC2）
● mariadb.service - MariaDB database server
   Loaded: loaded (/usr/lib/systemd/system/mariadb.service; disabled; vendor preset: disabled)
   Active: active (running) since 金 2020-07-17 03:46:51 UTC; 8s ago

→万が一、「active」になっていない場合はsudo systemctl start mariadbを実行します。

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

ターミナル（EC2内で実行）
# 上記の例だと「17877」
[ec2-user@ip-172-31-23-189 <リポジトリ名>]$ kill プロセス番号

１２：ローカルでの修正をすべてmasterにpushします。
ローカルでのコードの変更が、すべてmainブランチにpushされていることを確認しておきます。

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

１３：自動デプロイを実行しましょう
ローカルのターミナルに戻り、以下のコマンドで自動デプロイを実行します。

ターミナル（ローカル）：アプリケーションのディレクトリで実行します。
% bundle exec cap production deploy

※はじめての自動デプロイでは「bundler install」のタスクで時間がかかることがあります。5分経っても動かない場合は「EC2インスタンスの再起動」を実行します。
 
EC2インスタンスの再起動後は、必ず「データベース」「Nginx」の再起動も行います。
ターミナル（EC2内で実行）
[ec2-user@ip-172-31-25-189 ~]$ sudo systemctl restart mariadb 
[ec2-user@ip-172-31-25-189 ~]$ sudo systemctl restart nginx

１４：エラーの例
①Bundler::GemNotFound: Could not find date-3.3.3 in any of the sourcesについて、外部サイトで検索すると
エラーの原因
2022/12/3にrailsの必須gemの一つであるmailが2.7.1から2.8にアップデートされたことで、
このgemバージョンで使用するdateというgemが、EC2側で取得できておらずエラーが発生しているとのことです。

そして、解決方法について、
①Gemfileの末尾に「gem 'mail', '2.7.1'」を追記。（mailのバージョンを下げる指定をすることで、dateのgemを取得しないようにする為。）
*Gemfileに「gem 'date', '~> 3.3', '>= 3.3.3'」を記述した場合は、gem 'date'の記述を削除。
②Gemfile.lockの削除方法
↓


VSコードから「Gemfile.lock」をファイルごと削除すれば大丈夫。
理由：
Gemfile.lockはbundle installを実行すると生成される履歴ファイルであるため、削除を行ってからbundle installしないとpgをインストールした過去の履歴が残ってしまい、エラーが解消されない可能性があります。

■pgのgemを完全に削除する手順
１：Gemfileで、「gem 'pg'」の記述を削除する
２：VScodeからGemfile.lockを右クリックで削除、ローカル環境のfurimaアプリでbundle installをおこなう
３：GitHubにコミットとプッシュ
４：EC2環境でgit pull origin master（※デフォルトブランチがmainの場合はgit pull origin main）
５：EC2環境内のアプリケーションでbundle install


③ローカルでbundle install
④コミットプッシュ
⑤EC2内で自動デプロイ実行(% bundle exec cap production deploy)