Rails Tutorialの知識から【ポートフォリオ】を作って勉強する話 #18 EC2環境構築, Nginx+Puma+Capistrano編

# こんな人におすすめ
* プログラミング初心者でポートフォリオの作り方が分からない
* Rails Tutorialをやってみたが理解することが難しい

前回：[#17 VPC環境構築編](https://qiita.com/aokyo17/items/405d8eda3b6108c7af05)
番外：[#17.5 MySQL導入編](https://qiita.com/aokyo17/items/36c8231798bd2d46690c)
次回：[#18.5 環境変数, Gmail送信設定編](https://qiita.com/aokyo17/items/98e8e48d309a506db4e8)

この記事は、**動画を観た時間を記録するアプリのポートフォリオ**です。
今回は**RailsポートフォリオのデプロイのためにEC2を構築**します。
**クライアントにCloud9で使っているEC2、ホストに前回構築したEC2**を使います。
なお、**VPC構築が完了した前提**で進めます（VPC構築は[#17](https://qiita.com/aokyo17/items/405d8eda3b6108c7af05)）。

# 今回の流れ

1. 完成のイメージを理解する
* EC2そのものを設定する
* 各パッケージをインストールする
* GitHubと連携し、ポートフォリオをクローンする
* RDSを接続する
* Nginxをインストール、設定する
* Pumaを設定する
* Capistranoを導入する
* その他の設定をする
* アプリケーションをデプロイする

# 完成のイメージを理解する
前回のVPCに引き続き、今回はEC2内を構築します。
はじめに、EC2そのものの設定をします。

アプリケーションをデプロイするには、いくつか必要なパッケージがあります。
それらのパッケージを、パッケージ管理できるyumでダウンロードします。

EC2にポートフォリオを配置するには、GitHubを使います。
VPCに構築したRDSを接続する必要もあります。

webサーバーにNginx、アプリケーションサーバーにPumaを使います。
Nginxは、webアプリケーションへのリクエストを処理するために必要です。
Pumaは、webアプリケーションを動かすために必要です。
[Rails開発におけるwebサーバーとアプリケーションサーバーの違い（翻訳）](https://qiita.com/jnchito/items/3884f9a2ccc057f8f3a3)

デプロイの自動化するためには、Capistranoを使います。

それでは、EC2内の環境を構築していきます。

# EC2そのものを設定する
まずは、EC2そのものの設定を行います。
ここでの手順は以下の通りです。

* yumをアップデートする
* ユーザーを登録する
* SSHの設定をする
* タイムゾーンと文字コードを変更する

## yumをアップデートする
パッケージをインストールする前に、yum自体のアップデートを行います。
yumは、AmazonLinux2起動時にデフォルトでインストールされています。

```shell:server
$ sudo yum update
```

## ユーザーを登録する
まずは新たにルート権限を持つ、EC2ユーザーを登録します。
以下は、ユーザーの登録、パスワードの登録、ルート権限の編集を行う手順です。

```shell:server
$ sudo adduser hoge
（adduser以降は好きな名前を入力）
$ sudo passwd hoge
$ sudo visudo
```

```vim:
## Allow root to run any commands anywhere
root           ALL=(ALL)       ALL
hoge           ALL=(ALL)       ALL
```

rootの下にユーザーを同じように追加できたらOKです。
後は、作成したユーザーに切り替えます。

```shell:server
$ sudo su - hoge
```

これでユーザーの登録は完了です。

## SSHの設定をする
クライアントの公開鍵をサーバーに預けて、作成したEC2ユーザーに接続できるようにします。
まずはクライアント側で秘密鍵・公開鍵を作成します。
以下はクライアント側の作業です。

```shell:Cloud9
$ cd ~/.ssh
$ ssh-keygen -t rsa
Enter file in which to save the key ():lantern_key_rsa（自分のアプリ名）
Enter passphrase (empty for no passphrase):何もせずエンター
Enter same passphrase again:何もせずエンター
$ vim config
```

次に作成した鍵の使用用途を設定します。

```config:~/.ssh/config
Host lantern_key_rsa
  Hostname 設定したElastic IP
  Port 22
  User 作成したユーザー名
  IdentityFile ~/.ssh/latnern_key_rsa（自分のアプリ名）
```

```shell:Cloud9
$ cat lantern_key_rsa.pub
中身をコピーする
```

最後に、サーバー側で公開鍵を登録します。
以下はサーバー側の作業です。

```shell:server
$ mkdir .ssh
$ chmod 700 .ssh
$ cd .ssh
$ vim authorized_keys
```

```:~/.ssh/authorized_keys
コピーした公開鍵を貼り付ける
```

```shell:server
$ chmod 600 authorized_keys
$ exit
$ exit
```

クライアント側でログインできるか確認します。

```shell:Cloud9
$ ssh lantern_key_rsa
```

以上で鍵の設定は完了です。

## タイムゾーンと文字コードを変更する
日本での使用を最適化するよう、EC2を設定します。

```shell:server
$ sudo vi /etc/sysconfig/clock
```

```vim:/etc/sysconfig/clock
ZONE="Asia/Tokyo"
UTC=true
```

```shell:server
$ sudo vi /etc/sysconfig/i18n
```

```vim:/etc/sysconfig/i18n
LANG="ja_JP.UTF-8"
```

以上でタイムゾーンと文字コードを変更は完了です。

# 各パッケージをインストールする
デプロイに必要なパッケージをインストールします。
以下をインストールしていきます。

* Gitなど
* MySQL
* Node.js
* Ruby
* Bundler

## Gitなど
Gitなどをインストールします。
インストールするものには以下が含まれています。

* Git
* C#とC++のコンパイラ
* コンパイルの必要があるgemライブラリ

```shell:server
$ sudo yum install git make gcc-c++ patch openssl-devel libyaml-devel libffi-devel libicu-devel libxml2 libxslt libxml2-devel libxslt-devel zlib-devel readline-devel ImageMagick ImageMagick-devel epel-release
```

## MySQL
サーバー側で使用するデータベース、MySQLをインストールします。

```shell:server
$ sudo yum install http://dev.mysql.com/get/mysql80-community-release-el7-3.noarch.rpm
$ sudo yum install mysql mysql-devel mysql-server mysql-utilities
```

参考になりました↓
[MySQL 8.0 を yum でインストール&設定メモ](https://blog.apar.jp/linux/9868/)
[MySQL Yum Repository](https://dev.mysql.com/downloads/repo/yum/)

## Node.js
Railsの一部のコンパイルにはNode.jsが使われています。
Node.jsを使うために、以下をインストールします。

* EPEL（yumで使える追加リポジトリ）
* npm（Node.js内のパッケージマネージャー）
* n（npmで動くNode.js自体のパッケージマネージャー）

```shell:server
$ sudo amazon-linux-extras install epel
$ sudo yum install nodejs npm
$ sudo npm install -g n
$ sudo n stable
$ node -v
```

Node.jsを調べるとよく出てくるnvm、npm、nの各違いについてざっくり紹介します。

* nvm → Node.js自体のバージョン管理
* npm → Node.jsで作られたパッケージの管理
* n → npmで動くNode.js自体のバージョン管理

参考になりました↓
[CentOS 7に最新版のNode.jsをインストールしてみよう！](http://exrecord.net/how-to-install-latest-nodejs)
[nvm と n の違い](https://obel.hatenablog.jp/entry/20171121/1511248779)

## Ruby
RailsはRubyで動くのでインストールします。
Rubyを使うために以下をインストールします。

* rbenv（Rubyのパッケージマネージャー）
* ruby-build（rbenvのプラグイン、rbenvとセットで使う）

```shell:server
$ git clone https://github.com/rbenv/rbenv.git ~/.rbenv
$ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile
$ echo 'eval "$(rbenv init -)"' >> ~/.bash_profile
$ source .bash_profile
$ git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build
$ rbenv rehash
$ rbenv install -v 2.6.3
$ rbenv global 2.6.3
$ rbenv rehash
```

Gitからrbenvをクローンした後、パスを通したり「色々」しています。
その後ruby-buildをクローンして、rubyをインストールしています。

パスを通すことについては、**参考記事などから理解しておきましょう。**
「色々」については、深掘りしなくて結構です。

参考になりました↓
[rbenv（公式）](https://github.com/rbenv/rbenv)
[rbenv + ruby-build はどうやって動いているのか](https://takatoshiono.hatenablog.com/entry/2015/01/09/012040)
[Pathを通すとは、環境変数とは](https://qiita.com/fuwamaki/items/3d8af42cf7abee760a81)

## Bundler
サーバー側でもBundlerを使用するので、インストールします。

```shell:server
$ rbenv exec gem install bundler
$ rbenv rehash
```

以上でひとまずのパッケージをインストールしました。
次に進みます。

# GitHubと連携し、ポートフォリオをクローンする
サーバー側にポートフォリオを配置します。
GitHubと連携し、ポートフォリオをクローンします。
ここでの作業は以下の通りです。

* Git、GitHubの設定ファイルをつくる
* ポートフォリオ用のディレクトリをつくる
* GitHubとSSH接続する鍵をつくる
* GitHubに公開鍵をアップする
* GitHubに接続、クローンする

## Git、GitHubの設定ファイルをつくる
Git、GitHubに関する設定を記述します。

```shell:server
$ vi .gitconfig
```

```vim:.gitconfig
[user]
  name = Gitに登録したユーザー名
  email = Gitに登録したメールアドレス

[color]
  ui = true

[url "github:"]
    InsteadOf = https://github.com/
    InsteadOf = git@github.com:
```

## ポートフォリオ用のディレクトリをつくる
ポートフォリオを配置するディレクトリを作ります。
webアプリケーションの配置には/var/wwwが用いられます。

```shell:server
$ cd /
$ sudo chown hoge var
（hogeはEC2で作成したユーザー名）
$ cd var
$ sudo mkdir www
$ sudo chown hoge www 
$ cd www
$ sudo mkdir rails
$ sudo chown hoge rails
```

## GitHubとSSH接続する鍵をつくる
本来、サーバー側に鍵を置くのは危険です。
後にこの鍵をクライアント側で管理しますが、とりあえずの接続を試みます。

```shell:server
$ cd ~
$ cd chmod 700 .ssh
$ cd .ssh
$ ssh-keygen -t rsa
Enter file in which to save the key (/home/hoge/.ssh/id_rsa): aws_git_rsa
$ vi config
```

```vim:~/.ssh/config
Host github
  Hostname github.com
  User git
  IdentityFile ~/.ssh/aws_git_rsa
```

```shell:server
$ cat aws_git_rsa.pub
表示された公開鍵をコピー
```

## GitHubに公開鍵をアップする
作った公開鍵をGitHubにアップします。

[GitHub](https://github.co.jp/)にログイン
 → 右上アイコンから『Settings』 → 『SSH and GPG keys』 → 『New SSH key』

```
Title：hoge
Key：コピーした公開鍵
『Add SSH key』
```

参考になりました↓
[GitHubでssh接続する手順~公開鍵・秘密鍵の生成から~](https://qiita.com/shizuma/items/2b2f873a0034839e47ce)

## GitHubに接続、クローンする
SSHを用いてGitHubに接続し、ポートフォリオをクローンします。

```shell:server
$ chmod 600 config
$ ssh -T github
Are you sure you want to continue connecting (yes/no)? yes
```

ブラウザで[GitHub](https://github.co.jp/)を開く
リポジトリ『自分のアプリ名』 → 『Clone or download』 → 『Use SSH』 → コピー

```shell:server
$ cd /var/www/rails
$ git clone コピーしたものをペースト
$ cd /var/www/rails
$ cd ls
```

ここに自分が作成したアプリのファイルがあれば成功です。

# RDSを接続する
EC2からRDSに接続します。

AWSにログイン → AWSマネジメントコンソール『RDS』
画面左のダッシュボード『データベース』 → 『自分のアプリ名』 → 下部タブ『接続とセキュリティ』のエンドポイントをコピー

**通常の場合**

```shell:server
$ mysql -h コピーしたエンドポイント -P 3306 -u root -p
Enter password:MySQLのユーザーのパスワード（設定していない場合は何もせずエンター）
```

```sql:mysql
mysql> show databases;
```

**[#17.5](https://qiita.com/aokyo17/items/36c8231798bd2d46690c)の手順でMySQLを導入した場合**

```shell:server
$ mysql -h コピーしたエンドポイント -P 3306 -u ①任意の名前 -p
Enter password:②任意のパスワード
```

```sql:mysql
mysql> show databases;
```

ここに自分のアプリ名のDBが表示されたら成功です。

参考になりました↓
[0から始めるAWS入門④：RDS編](https://qiita.com/hiroshik1985/items/6643b7323183f82297b2)
[EC2からRDS(MySQL)に接続できない。セキュリティグループの設定について・・・](https://qiita.com/nao-otsu/items/8cabd447d73b621cd07d)

# Nginxをインストール、設定する
webサーバーであるNginxのインストール、設定をします。

```shell:server
$ sudo yum install nginx
$ cd /etc/nginx
$ mkdir conf.d
$ cd conf.d
$ sudo vi lantern.conf（自分のアプリ名）
```

```nginx:/etc/nginx/conf.d/lantern.conf
# ログに関するファイルの場所
error_log  /var/log/nginx.error.log;
access_log /var/log/nginx.access.log;
# クライアントから送られるボディの上限
client_max_body_size 2G;

# Pumaとやり取りするための通信路
upstream app_server {
  server unix:///var/www/lantern/shared/tmp/sockets/puma.sock fail_timeout=0; # 自分のアプリ名
}

# サーバーに関する情報（バーチャルホストに使用される）
server {
# ポート
  listen 80;
# 処理するサーバー名
  server_name サーバー側のIP（Elastic IP）;
# ドキュメントルートの設定
  root /var/www/lantern/current/public; # 自分のアプリ名

# http://〇〇/でアクセスした時、下記のファイルを左から探すようにする
  try_files $uri/index.html $uri.html $uri @app;

# 内部リダイレクトの処理
  location @app {
# バックエンドサーバーに送信するヘッダーを定義し直す
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
# プロキシのパス（おそらくPumaに渡す）
    proxy_pass http://app_server;
  }

# エラー時のページ表示先
  error_page 500 502 503 504 /500.html;
  location = /500.html {
    root /var/www/lantern/current/public; # 自分のアプリ名
  }
}
```

Nginxをインストールすると、ある程度の処理がデフォルトで記述されます。
公式ドキュメントなどを見つつ、それを参考に設定を行います。

とりわけ大事な部分はこちらです。

```nginx
upstream app_server {
  server unix:///var/www/lantern/shared/tmp/sockets/puma.sock;
}
```

アプリケーションサーバーにPumaを使うので、ソケットを設定します。
これによりPumaとの連携が可能です。

参考になりました↓
[documentation | nginx（公式）](https://nginx.org/en/docs/)

# Pumaを設定する
アプリケーションサーバーであるPumaを設定します。
しかしPumaは後述のCapistranoで管理するので、ここでの設定は不要です。

# Capistranoを導入する
デプロイを自動化する、Capistranoを導入します。
ここでの手順は以下の通りです。

* Gemを追加し、ファイルを生成する
* Capfileを編集する
* config/deploy/production.rbを編集する
* config/deploy.rbを編集する

## Gemを追加し、ファイルを生成する
Gemに'capistrano'を追加します。
それ以外は、Capistranoのタスクを追加させるために必要なGemです。
以下は、クライアント側での作業です。

```rb:Gemfile

group :development, :test do
+ gem 'capistrano'
+ gem 'capistrano-bundler'
+ gem 'capistrano-rails'
+ gem 'capistrano-rbenv'
+ gem 'capistrano-rbenv-vars'
+ gem 'capistrano3-puma'
end
```

cap installで、Capistranoのファイルを生成します。

```shell:Cloud9
$ bundle install
$ cap install
```

## Capfileを編集する
これから自分の環境に合わせてCapistranoのファイルを編集します。
まずは、タスクの読み込みを行うCapfileです。
タスクとは、Capistranoで処理を行うまとまりです。

```rb:Capfile
require 'capistrano/setup'
require 'capistrano/deploy'
require 'capistrano/rbenv'
require 'capistrano/bundler'
require 'capistrano/rails/assets'
require 'capistrano/rails/migrations'
require 'capistrano/scm/git'
require 'capistrano/puma'
require 'capistrano/console'

install_plugin Capistrano::SCM::Git
install_plugin Capistrano::Puma

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

参考になりました↓
[Capistrano3のデプロイフレームワークの使い方](https://qiita.com/yuku_t/items/01c0ec4389db143e27f5)
[Capistrano3におけるRailsのデプロイタスクの内部実装](https://qiita.com/yuku_t/items/78f32d6e5d21aee4e745)

## config/deploy/production.rbを編集する
環境ごとの設定を記述します。

```rb:config/deploy/production.rb
server "サーバー側EC2のIP", user: "サーバー側のユーザー名", roles: %w{app db web}

set :ssh_options, {
  keys: %w(~/.ssh/lantern_key_2_rsa),
  forward_agent: true
}
```

## config/deploy.rbを編集する
タスクを記述します。
と言っても、実はほとんどの処理がCapfileによって行われています。

```rb:config/deploy.rb
# Capistranoのバージョン指定
lock "~> 3.11.2"

# Capistranoでの必須の設定
set :application, "自分のアプリ名"
set :repo_url, "git@github.com:githubのユーザー名/アプリ名.git"

# Pumaに関する設定（後述）
# ソケットの場所、Nginxとのやり取りに必要
set :puma_bind, "unix://#{shared_path}/tmp/sockets/puma.sock"
# サーバー状態を表すファイルの場所
set :puma_state, "#{shared_path}/tmp/pids/puma.state"
# プロセスを表すファイルの場所
set :puma_pid, "#{shared_path}/tmp/pids/puma.pid"
# ログの場所
set :puma_access_log, "#{shared_path}/log/puma.error.log"
set :puma_error_log, "#{shared_path}/log/puma.access.log"

# タスクでsudoなどを行う際に必要
set :pty, true
# シンボリックリンクのファイルを指定、具体的にはsharedに入るファイル
append :linked_files, "config/master.key"
# シンボリックリンクのディレクトリを生成
append :linked_dirs, "log", "tmp/pids", "tmp/cache", "tmp/sockets", "public/system", "public/uploads", "public/storage"
# 環境変数の設定
set :default_env, { path: "/usr/local/rbenv/shims:/usr/local/rbenv/bin:$PATH" }
# 保存しておく過去分のアプリ数
set :keep_releases, 3

namespace :deploy do
  desc 'Create database'
  task :db_create do
    on roles(:db) do |host|
      with rails_env: fetch(:rails_env) do
        within current_path do
          execute :bundle, :exec, :rails, 'db:create'
        end
      end
    end
  end
  
  desc 'Restart application'
  task :restart do
    on roles(:app), in: :sequence, wait: 5 do
      invoke 'puma:restart'
    end
  end
end
```

Capistranoの構文について解説します。

```rb:
# 変数をセットします
set :キー, "値"
# 値を呼び出します
fetch(:キー)
# タスクを追加します
namespace :hoge do
  task :fuga do
  end
end
```

Capistranoはshell内でこのように使います。

```shell:
# デプロイを行う場合
$ cap production deploy
# deployのうち、タスクdb_createを行う場合
$ cap production deploy:db_create
# 例）hogeのタスク、fugaを行う場合
$ cap production hoge:fuga
```

Pumaの設定について解説します。

capistrano3-pumaによって、CapistranoでPumaを動かすことが可能になります。
本来Pumaの設定は、config/puma.rbに記載されています。
しかし、config/deploy.rbでこの辺りの設定が補われます。

shared_pathについて解説します。

Capistranoでは、1回のデプロイごとにreleasesにまとめられます。
そのうち、最新のものをcurrentとしてデプロイしています。
そして、その中の共有できるファイルはsharedにまとめられています。
そのパスをそれぞれrelease_path、current_path、shared_pathと表現できます。


参考になりました↓
[capistranoで設定される変数の値一覧の表示方法](https://ja.stackoverflow.com/questions/19184/capistrano%E3%81%A7%E8%A8%AD%E5%AE%9A%E3%81%95%E3%82%8C%E3%82%8B%E5%A4%89%E6%95%B0%E3%81%AE%E5%80%A4%E4%B8%80%E8%A6%A7%E3%81%AE%E8%A1%A8%E7%A4%BA%E6%96%B9%E6%B3%95)
[What does deploy:initial do in Capistrano task](https://stackoverflow.com/questions/46482003/what-does-deployinitial-do-in-capistrano-task)

**備考：Capistranoで使われる変数一覧**

```
［設定が必須の変数］
:application
The name of the application.
:repo_url
URL to the repository.

［設定が任意の変数］
:deploy_to
default: -> { "/var/www/#{fetch(:application)}" }
:scm
default: :git
:branch
default: 'master'
:svn_username
When using :svn, provides the username for authentication.
:svn_password
When using :svn, provides the password for authentication.
:svn_revision
When using :svn, set the specific revision number you want to deploy.
:repo_path
default: -> { "#{fetch(:deploy_to)}/repo" }
:repo_tree
default: None. The whole repository is normally deployed.
:linked_files
default: []
:linked_dirs
default: []
:default_env
default: {}
:keep_releases
default: 5
:tmp_dir
default: '/tmp'
:local_user
default: -> { ENV["USER"] || ENV["LOGNAME"] || ENV["USERNAME"] }
:pty
default: false
:log_level
default: :debug
:format
default: :airbrussh
:shared_directory
default: shared
:releases_directory
default: releases
:current_directory
default: current
```

その他、変数はこちら↓
[Configuration | Capistrano（公式）](https://capistranorb.com/documentation/getting-started/configuration/)
[パスに関する変数 | Capistrano（公式）](https://github.com/capistrano/capistrano/blob/master/lib/capistrano/dsl/paths.rb)
[capistrano-puma（GitHub）](https://github.com/seuros/capistrano-puma)

# その他の設定をする
その他デプロイに必要な設定をします。
ここでの手順は以下の通りです。

* 本番環境時のシークレットキーを有効にする
* サーバーに秘密鍵を置かないようにする

## 本番環境時のシークレットキーを有効にする
秘匿情報の管理のために、シークレットキーを有効にします。
まずは本番環境でのシークレットキーを必須にします。

```rb:config/environments/production.rb
# 中略
# Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"]
# or in config/master.key. This key is used to decrypt credentials (and other encrypted files).
config.require_master_key = true
```

次に、サーバー側にmaster.keyを設置します。

```shell:Cloud9
$ scp -i ~/.ssh/lantern_key_2_rsa config/master.key ユーザー名@IPアドレス:/var/www/lantern_app/shared/config/
```

参考になりました↓
[Rails5.2から導入されたcredentials.yml.encを極める](https://qiita.com/yuuuking/items/53a37a2e998972be32b8)

## サーバーに秘密鍵を置かないようにする
今のところGitHubの秘密鍵をホストサーバーに置いてしまっています。
セキュリティ上よくないので、クライアント側で管理します。
具体的にはssh-agentを用いて、サーバーからでもGitHubが使えるようにします。

### GitHub用の鍵をつくる
再びGitHub用の鍵を作ります。先ほどと手順は同じです。

```shell:shell（Cloud9）
$ cd ~/.ssh
$ ssh-keygen -t rsa
Enter file in which to save the key ():ec2_git_rsa
Enter passphrase (empty for no passphrase):何もせずエンター
Enter same passphrase again:何もせずエンター
```

公開鍵の方はコピーしておきます。

### GitHubに公開鍵を登録する
ここも先ほどと手順は同じです。

GitHubにログイン
→ 右上アイコンから『Settings』 → 『SSH and GPG keys』 → 『New SSH key』

```
Title：hoge
Key：コピーした公開鍵
『Add SSH key』
```

### SSHの設定をする
SSHのconfigを触ります。
クライアント側とサーバー側どちらも設定を変更します。

#### クライアント

```shell:Cloud9
$ vim ~/.ssh/config
```

```config:~/.ssh/config
Host lantern_key_2_rsa
  Hostname 設定したElastic IP
  Port 22
  User 作成したユーザー名
  IdentityFile ~/.ssh/lantern_key_rsa

Host ec2_git_rsa
  Hostname github.com
  User git
  IdentityFile ~/.ssh/ec2_git_rsa
```

#### ホストサーバー

```shell:server
$ vim ~/.ssh/config
```

```config:~/.ssh/config
Host github
  Hostname github.com
  User git
  ForwardAgent yes
```

ForwardAgentをyesにすることで、クライアント側の鍵を流用することができます。

### ssh-agentに鍵を加える＋自動化する
ssh-agentに鍵を加えます。
ここに鍵を加えることで、サーバーからGitHubに繋ぐことができます。

しかしssh-agentは登録した鍵がログインごとにリセットされます。
よってログイン時に自動で鍵を加えるようにします。

```bash:~/.bash_profile
echo "Add EC2 and GitHub keys to ssh-agent"
eval `ssh-agent`

HOST_KEY='lantern_key_2_rsa'
GIT_KEY='ec2_git_rsa'

ssh-add $HOME/.ssh/$HOST_KEY
ssh-add $HOME/.ssh/$GIT_KEY
```

ログアウト時にssh-agentを停止させる処理も加えます。

```~/.bash_logout
eval `ssh-agent -k`
```

### サーバーからGitHubに接続できるか確認する
それでは本当に接続できる確認します。

```shell:Cloud9
$ ssh -A lantern_key_rsa
```

GitHubへの接続には-Tオプションを使用します。
これによってクライアント側の鍵を流用します。

```shell:server
$ ssh -T github
Hi ユーザー名! You've successfully authenticated, but GitHub does not provide shell access.
```

### サーバーの秘密鍵などを削除する
あとはサーバー側の秘密鍵を削除します。
GitHubの古い公開鍵も必要ないので、こちらも削除します。

```
省略
```

# アプリケーションをデプロイする
後はデプロイするだけです。

```shell:Cloud9
$ cap production deploy
```

ブラウザでEC2のIPを検索し、アプリケーションが表示されたら成功です。
（しかしエラーなしでここまで来るのは奇跡です。解決策は後述します。）

# トラブルシューティング
自動デプロイする際、様々なエラーが発生します。
一例ですが、私が見舞ったトラブルの解決策を書き残します。

## Permission denied (publickey).
主にSSH接続に失敗するとこのエラーが発生します。

### 解決策1：ssh-agentを確認する
ssh-agentに鍵が追加されていないから認証できないケースです。

```shell:Cloud9
$ ssh-add -l
The agent has no identities.
```

このような文章が出てきた場合、キーが追加されていません。
キーを追加しましょう。

```
$ ssh-add キーのパス
```

参考になりました↓
[SSH で Permission Denied となる傾向と対策](https://qiita.com/youcune/items/2f427979403771f2e03a)

### 解決策2：サーバーの容量を確認、削除する
容量がいっぱいになって起動がうまくいかなくなったケースです。
適宜、削除します。

```shell:server
$ cd /var/www/アプリ名/releases
$ ls
2020XXXXXXXXXX 2020XXXXXXXXXX 2020XXXXXXXXXX（...他）
$ rm -rf 2020XXXXXXXXXX （...他）
```

## Your Ruby version is X.X.X, but your Gemfile specified X.X.X
クライアント側とサーバー側でのバージョンの不一致です。

### 解決策1：クライアント側とサーバー側のバージョンを合わせる
それぞれのRubyのバージョンを合わせます。
Cloud9の場合、Rubyのパッケージ管理にはrvmが使われているはずです。

```shell:Cloud9
$ ruby -v
$ rvm list
# バージョンを違っていた場合、インストールする
$ rvm install X.X.X
$ rvm use X.X.X
$ ruby -v
```

サーバー側は、rbenvを使っています。

```shell:server
$ rbenv versions
# バージョンを違っていた場合、インストールする
$ rbenv install X.X.X
$ rbenv global X.X.X
$ ruby -v
```

バージョンが切り替わっていない場合、一度再起動してみましょう。

### 解決策2：Gemfileやconfig/deploy.rbを確認する
ファイルでのバージョン指定が間違っている可能性があります。
確認してみましょう。

```
Gemfile、config/deploy.rbのバージョンを確認する
```

## ActiveRecord::NoDatabaseError: Unknown database 'アプリ名_production'
データベースが作られていないために発生しています。

### 解決策1：AWS側の設定を整える
クライアント側からRDSに接続できない場合、AWS側の設定を疑います。

**VPC**
AWSにログイン → AWSマネジメントコンソール『VPC』
画面左のダッシュボード『VPC』 → 作成したVPCを選択 → 『アクション』 → 『DNSホスト名の編集』

```
DNSホスト名：有効化にチェック
『保存』
```

**セキュリティグループ**
画面左のダッシュボード『セキュリティグループ』 → 作成したセキュリティグループを選択 → 下部タブ『インバウンドのルール』 → 『ルールの編集』

```
タイプ：MySQL/Aurora
ソース：カスタム、0.0.0.0/0
『ルールの保存』
```

**RDS**
AWSマネジメントコンソール『RDS』
画面左のダッシュボード『データベース』 → 作成したデータベースを選択 → 『変更』

```
パブリックアクセシビリティ：はい
『次へ』
『DBインスタンスの変更』
```

参考になりました↓
[AWS Aurora でDB作成時に「 Cannot create a publicly accessible DBInstance. The specified VPC does not support DNS resolution, DNS hostnames, or both. Update the VPC and then try again」とエラーになった際の対処法](https://sikakugetnikki.blog.fc2.com/blog-entry-109.html)
[\[備忘録\] RDSで外部サーバからアクセス許可する方法](https://qiita.com/Takafumi_Ueki/items/f01824a5b52f29025026)

### 解決策2：プロダクション用のデータベースを作る
デプロイ前に本番用のデータベースを作成します。

```shell:Cloud9
$ rails db:create RAILS_ENV=production
```

## 500 Internal Server Error
cap production deployは済んだものの、何らかの理由で処理が完了しない場合に起こります。

### 解決策：シンボリックリンクのフォルダを確認する
正しくシンボリックリンクが設定されていない場合があります。
例えば以下のように、フォルダを追加します。

```rb:config/deploy.rb
append :linked_dirs, "log", "tmp/pids", "tmp/cache", "tmp/sockets", "public/system", "public/upload"
```

## Aws::Sigv4::Errors::MissingCredentialsError: Cannot load \`Rails.config.active_storage.service`:
pumaのエラーログから、Credentialsのエラーです。
Rails5.2以上をお使いの場合、後述の参考記事を一読すると良いでしょう。
とりあえず解決策を載せておきます。

### 解決策：Credentialsを確認する
```shell:Cloud9
$ EDITOR=vim rails credentials:edit
```

```yml:credentials
# 以下のコメントアウトを外す
aws:
  access_key_id: XXXX
  secret_access_key: XXXX
```

あとは先述の『本番環境時のシークレットキーを有効にする』をご確認ください。

参考になりました↓
[Rails5.2から導入されたcredentials.yml.encを極める](https://qiita.com/yuuuking/items/53a37a2e998972be32b8)
[unicornの.socketファイルがうまく生成されません](https://ja.stackoverflow.com/questions/2609/unicorn%E3%81%AE-socket%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%8C%E3%81%86%E3%81%BE%E3%81%8F%E7%94%9F%E6%88%90%E3%81%95%E3%82%8C%E3%81%BE%E3%81%9B%E3%82%93)

## Nginxを再起動していない
Nginxを再起動しないため、設定が更新されていない場合があります。
設定し直す度に、再起動を行うことをおすすめします。

### 解決策：Nginxを再起動、動作を確認する
Nginxでよく使うものを列挙しました。
Nginxの動作確認や、再起動の手順を載せます。

```shell:server
# Nginxを停止する
$ sudo systemctl stop nginx
# Nginxを起動する
$ sudo systemctl start nginx
# Nginxの動作確認
$ sudo systemctl status nginx.service
# Nginxのプロセスが使用しているポートを確認する
$ sudo lsof -i | grep nginx
# Nginxの実行中プロセスを確認する
$ ps aux | grep nginx
# Nginxを停止する（何らかの理由で停止できない場合はこちらを使う）
$ sudo kill -9 XXXX
```

## ブラウザで表示されない、301になる
「NginxもPumaも正常に動いている、ログも問題ない」
にも関わらずブラウザで表示されない場合は、Rails側の問題かもしれません。
chromeから「このサイトにアクセスできません」と出る場合、curlで確認します。

```shell:shell
$ curl -I http://ホスト側のIPアドレス
HTTP/1.1 301 Moved Permanently
Server: nginx/1.16.1
Date: Wed, 01 Jan 2020 00:00:00 GMT
Content-Type: text/html
Connection: keep-alive
Location: https://ホスト側のIPアドレス/
```

### 解決策：config.force_ssl = falseにする
デフォルトではsslを強制しています。
これをfalseにして、httpでの通信ができるようにします。

```rb:config/environment/production.rb（Cloud9）
config.force_ssl = false
```

参考になりました↓
[(デプロイ編②)世界一丁寧なAWS解説。EC2を利用して、RailsアプリをAWSにあげるまで](https://qiita.com/naoki_mochizuki/items/5a1757d222806cbe0cd1)

---
前回：[#17 VPC環境構築編](https://qiita.com/aokyo17/items/405d8eda3b6108c7af05)
番外：[#17.5 MySQL導入編](https://qiita.com/aokyo17/items/36c8231798bd2d46690c)
次回：[#18.5 環境変数, Gmail送信設定編](https://qiita.com/aokyo17/items/98e8e48d309a506db4e8)