サルでもできる!? Rails6 のアプリをAWS EC2にデプロイするまでの全手順【前半】（VPC, RDS, EC2, Capistrano）

【ご案内（2021/5/17）】 Puma のバージョン 5 (Systemd での起動)に対応しました！ 

この記事では，画像投稿機能の付いた `Rails 6` のアプリを `AWS EC2` にデプロイするまでの全過程を解説します。

Railsアプリのデプロイに関する記事はたくさん見かけますが，初学者にとっては

「具体的に何をすればよいかが分からない:sweat:」

記事がほとんどで，ましてや「独自ドメインの設定方法」や「投稿画像を独自ドメインでCloudFrontから配信する方法」まで網羅的に解説を行っている記事は見当たらない状態です。

そこで，「<font color="red">**Rails初学者が初めてAWSにデプロイする際に本当に参考となる記事**</font>」を目指して書かせていただきました。

「`Heroku` にデプロイしたことはあるけれど，`AWS` にデプロイしたことがない」という方を対象としますので，`AWS` の操作は，直感的に操作できる `AWS マネジメントコンソール` で行います。

`AWS CLI` や `Terraform` などのツールは使用しませんので，あらかじめご了承下さい。

章 | タイトル
--- | ---
 | 【前半】 <-- こちら
1章 | はじめに
2章 | VPC
3章 | RDS
4章 | EC2
5章 | サーバー構築
6章 | デプロイ(Capistrano)
 | [【後半】](https://qiita.com/take18k_tech/items/2efd778c8dd9aae9496e)
7章 | 独自ドメイン
8章 | HTTPS化(ACM, ALB)
9章 | デプロイ関連事項
10章 | S3
11章 | CloudFront
12章 | 削除方法

## 1. はじめに

### 1.1 注意事項

この記事における注意事項を列記します。

#### 料金

`AWS` は有料のサービスで，いわゆる従量課金制です（サービスの使用量に応じて料金が決まります）。

この記事の構成で `AWS` を利用した場合，ポートフォリオのようにあまりアクセスが来ないアプリであっても，（無料利用枠を使用する前提で）月におよそ3000円前後かかります。

`RDS`, `ALB` などを使用せず， `EC2` にデータベースシステムをインストールする構成にすればもっと安く済ませることができますが，ポートフォリオのアピール材料としては弱くなるでしょう。

なお，エラーが発生した際に原因を特定しやすくするため，`AWS` へのデプロイはアプリの `完成後ではなく`，`開発初期の段階` から行うことをお勧めします。

ただ，料金の問題がありますので，アプリの開発初期は `Heroku` にデプロイし，仕上げの段階で `AWS` にデプロイするという手法もありでしょう。

#### Railsアプリの条件

AWSにデプロイ予定のアプリが，以下の条件を満たすことを前提とします。

- アプリの Rails のバージョンが `6` であること

- データベースは `MySQL 5.7` もしくは `PostgreSQL` を使用

- 画像投稿機能がある場合は `CarrierWave` を使用

`6.1` に画像投稿機能の付いたサンプルアプリを作成する方法を紹介しておりますので，必要がございましたらご利用下さい。

#### 動作確認時の環境

- Ruby 2.7.3
- Rails 6.1.3.2
- Puma 5.3.1

#### 構成図

以下のような一般的な構成を目指します。（料金の都合上，冗長化は行いません）

![1_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431142/a447ff8c-456b-5269-49b9-a558c0c6dd72.png)

#### サーバー環境

- 【Web サーバー】 Nginx
- 【アプリケーションサーバー】 Puma

#### 使用する AWS のサービス

- VPC(Amazon Virtual Private Cloud)
- RDS(Amazon Relational Database Service)
- EC2(Amazon Elastic Compute Cloud)
- Route 53
- ACM(Certificate Manager)
- ALB(Application Load Balancer)
- S3(Amazon Simple Storage Service)
- IAM(AWS Identity and Access Management)
- CloudFront

#### 基礎知識の解説

この記事は，「画像投稿機能の付いた Rails アプリを AWS EC2 にデプロイし，独自ドメインでアクセスできるようにする」ことを主目的としております。

最低限度知っておくべき内容は触れますが，インフラの基礎知識は解説しません。

AWS に必要な基礎知識を学べる本はたくさんありますし，Udemyにも丁寧な解説動画が存在します。デプロイの完了後でもよいですので，是非学習されることをお勧めします。

#### 注意点

理解が不十分な状態で進められる場合は， `絶対にミスをしない` ように気を付けて下さい。1つのミスで最初からやり直すことになり，数時間の作業が無駄になる可能性があります。

#### コメントについて

記事内の改善点・間違いなどございましたら，遠慮なくご指摘下さい。可能な限りで記事にも反映させていただきます。

Qiita上の質問には原則お答えする時間が取れませんので，あらかじめ了解いただいた上でご質問下さい。

#### メモ

作業中にパスワードなど各自設定が異なる部分が複数存在します。メモ帳に以下を貼り付け，決まり次第埋めていくようにされるとよいでしょう。

```none:メモ
【アプリ名】



【RDS】

●マスターユーザー名



●マスターパスワード


●エンドポイント


【EC2】

●Elastic IP


●sshでサインインするときのコマンド

ssh アプリ名

●ユーザー名



●パスワード

【ドメイン】

●ドメイン名


●ネームサーバー情報

【S3】

●バケット名


●リージョン


【IAM】

●ユーザー名


●アクセスキーID


●シークレットアクセスキー


【CloudFront】

static.ドメイン名

```

まずは，AWSで使用する `アプリ名` を決め，上記にメモして下さい。ただし，「aws_sample_app」のように `スネークケース` 表記であることを前提とします。（アルファベット大文字は使わないで下さい）

「（AWSで使用する）アプリ名」は，「Railsのアプリ名」「GitHubのリポジトリ名」と合わせる方が混乱しづらいと思いますが，同じでなくても問題ありません。

以下， `アプリ名` と記載している箇所は，全てこのメモ通りにして下さい。

### 1.2 AWSアカウントの作成・初期設定

要点のみ記載します。

- AWSアカウントの作成
  - https://portal.aws.amazon.com/billing/signup
  - メールアドレス・パスワードは超重要です。絶対に忘れないようにし，また他人に知られないように注意しましょう。不正利用されると高額の料金を請求される可能性があります。
  - パスワードは，大文字・小文字・数字・記号を含む16文字以上の強固なものを設定しましょう。
  - サポートプランは特に用事がなければ「ベーシックプラン」でOK

- 「セキュリティステータス」に全てチェックが入るように設定を進めましょう。

【お勧め参考記事】 https://qiita.com/tmknom/items/303db2d1d928db720888

- ルートアカウントの MFA を有効化
  - セキュリティ向上のため2段階認証を導入
- 個々の IAM ユーザーの作成
  - 最初に発行される「ルートアカウント」は，セキュリティの都合上，今後サインインに使用しないようにします
  - 普段のサインインに使用する `IAM` ユーザーを作成
  - 「パスワードのリセットが必要」は不要なのでチェックを外しましょう
  - 今後のサインインに必要となる「アカウントID」「ユーザー名」「パスワード」を忘れないようにしましょう
- グループを使用してアクセス許可を割り当て
  - ほぼ全ての権限を持つ「AdministratorAccess」のポリシーを持つグループを作成し，作成した `IAM` ユーザーを追加
- IAMパスワードポリシーの適用

最後に「請求アラーム」を作成し，課金状況を確認できるようにしておくことをお勧めします。

【お勧め参考記事】 https://www.kakiyoro.com/archives/2198

## 2. VPC

- [(公式) Amazon VPC](https://aws.amazon.com/jp/vpc/)

`Amazon VPC` とは `Amazon Virtual Private Cloud` の略称で，ユーザー専用のプライベートなネットワーク空間を構築できるサービスです。

RailsアプリをAWSで動作させるために，Webサーバーを配置する `EC2` とデータベースシステムを動かす `RDS` を使用しますが，これらを配置するには先に `VPC` を設定する必要があります。

`Amazon VPC` は追加料金なしで使用できます。（参考：[https://docs.aws.amazon.com/ja_jp/vpc/latest/userguide/what-is-amazon-vpc.html](https://docs.aws.amazon.com/ja_jp/vpc/latest/userguide/what-is-amazon-vpc.html)）

### 2.1 VPC の作成

2章では以下の部分を作成していきます。

![2_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431142/7cf020c7-cc23-b4fd-ba41-d7f7367be79d.png)

【注意】 メニューバー右側の真ん中に「米国」などと表示されている場合は，「東京（アジアパシフィック(東京) ap-northeast-1）」に変更して下さい

![2_1a.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431142/8272d282-77bd-6745-78f7-c09c933eda1a.png)

- 画面左上の「サービス」を開き、検索欄に「VPC」と入力し、「VPC」を選択


![2_1b.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431142/a5587960-bde9-1ec7-ec1a-654973c0fe68.png)

【注意】 以下，左上の「New VPC Experience」をONにしていることを前提とします。

- 画面左のメニューバー（もしくは，ダッシュボード内の）の「VPC」をクリック

![2_1c.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431142/46219e49-ca11-47ac-0b24-b875ac45fe54.png)

- 「VPCを作成」ボタンをクリック

キー | 値
--- | ---
名前タグ | アプリ名_vpc
IPv4 CIDR ブロック | 10.0.0.0/16

【注意】1章で注意しましたとおり，「アプリ名」の箇所はあらかじめ `メモ` しておいた「アプリ名」を使用して下さい

- 他はデフォルトのままで「VPCを作成」ボタンをクリック

![2_1d.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431142/ba930631-d2ba-e9ed-22d7-a674ae951b33.png)

### 2.2 サブネット の作成

VPCの中に，「インターネットから直接通信できる `EC2用` のサブネット」と「インターネットから直接通信できない `RDS用` のサブネット」を2つずつ作成していきましょう。（許可する接続方法は後に設定します）

- 画面左のメニューバーから「サブネット」を選択

#### EC2 用のサブネット

`HTTPS化` する際に `ALB` を使用するため，異なる `アベイラビリティーゾーン` に属するサブネットを `2つ` 用意する必要があります。

- 「サブネットの作成」ボタンをクリック

キー | 値
--- | ---
VPC | アプリ名_vpc ※リストから選択

![2_2a.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431142/e2700ecc-0c7b-d6a6-22bb-ffb1e165f019.png)

キー | 値
--- | ---
名前タグ | アプリ名\_public\_1a\_subnet
アベイラビリティーゾーン | ap-northeast-1a ※リストから選択
IPv4 CIDR ブロック | 10.0.0.0/24

![2_2b.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431142/ee706655-3058-1cc4-2e44-84087dbd6f22.png)

- 「新しいサブネットを追加」ボタンをクリック

キー | 値
--- | ---
名前タグ | アプリ名\_public\_1c\_subnet
アベイラビリティーゾーン | ap-northeast-1c ※リストから選択
IPv4 CIDR ブロック | 10.0.1.0/24

※ まだ作成しますので，「サブネットを作成」ボタンをクリックしないこと！

#### RDS 用のサブネット

`RDS` を利用するには異なる `アベイラビリティーゾーン` に属するサブネットを `2つ` 用意する必要があります。

- 「新しいサブネットを追加」ボタンをクリック

キー | 値
--- | ---
名前タグ | アプリ名\_private\_1a\_subnet
アベイラビリティーゾーン | ap-northeast-1a
IPv4 CIDR ブロック | 10.0.10.0/24

- 「新しいサブネットを追加」ボタンをクリック

キー | 値
--- | ---
名前タグ | アプリ名\_private\_1c\_subnet
アベイラビリティーゾーン | ap-northeast-1c
IPv4 CIDR ブロック | 10.0.11.0/24

- 「サブネットを作成」ボタンをクリック

- 下図の状態になっていることを確認しておきましょう

![2_2c.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431142/6eb29464-1131-c219-448d-54ae6d0cf902.png)

### 2.3 インターネットゲートウェイ の作成

`VPC` 内から `インターネット` に接続するための「出入口」に相当する `インターネットゲートウェイ` を作成しましょう。

- 画面左のメニューバーから「インターネットゲートウェイ」を選択
- 「インターネットゲートウェイの作成」ボタンをクリック

キー | 値
--- | ---
名前タグ | アプリ名_gateway

![2_3a.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431142/52fed266-0d31-8851-3712-104e2a6460cf.png)

- 他はデフォルトのままで「インターネットゲートウェイの作成」ボタンをクリック
- フラッシュで表示されている「VPCへアタッチ」をクリック
  - （「アクション」を選択し、「VPCにアタッチ」を選択してもOK）

キー | 値
--- | ---
使用可能なVPC | アプリ名_vpc

- 「インターネットゲートウェイのアタッチ」ボタンをクリック

![2_3b.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431142/5b82897c-8153-f974-cad4-b331d3a90da7.png)

### 2.4 ルートテーブル の作成

`インターネットゲートウェイ` をアタッチしただけでは，サブネットからインターネットにアクセスすることはできません。
`インターネットゲートウェイ` へ転送する経路である `ルートテーブル` を設定する必要があります。

- 画面左のメニューバーから「ルートテーブル」を選択
- 「ルートテーブルの作成」ボタンをクリック

キー | 値
--- | ---
名前タグ | アプリ名_table
VPC | アプリ名_vpc

![2_4a.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431142/c09d1a8a-48ef-3bd9-0ad6-42dbd0c8c85b.png)

- 「作成」ボタンをクリック
- 「閉じる」ボタンをクリック

- 作成したルートテーブルを選択し，「アクション」ボタンをクリックし，「ルートの編集」を選択

![2_4b.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431142/120326b7-98b4-2f5c-4f7a-9b312d8b4302.png)

- 「ルートの追加」ボタンをクリック
  - `local` を消さないこと！

キー | 値
--- | ---
送信先 | 0.0.0.0/0
ターゲット | 「Internet Gateway」を選択し，先ほど作成した「アプリ名_gateway」を選択

![2_4c.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431142/7bc72fa3-2d5c-6e48-4ad0-7b2e5952a0c1.png)

![2_4d.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431142/28a0f0ce-0188-227f-3ab6-28ab62114fa9.png)

- 「ルートの保存」をクリック
- 「閉じる」ボタンをクリック

- （作成したルートテーブルを選択したまま）「アクション」ボタンをクリックし，「サブネットの関連付けの編集」を選択
- 「アプリ名\_public\_1a\_subnet」「アプリ名\_public\_1c\_subnet」のみを選択し，「保存」ボタンをクリック

![2_4e.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431142/40ce7faa-ecb0-e02d-6062-d02d5901019d.png)

### 2.5 セキュリティグループ の作成

インスタンスごとに許可する通信を設定するため， `セキュリティグループ` を作成しておきましょう。

（ `EC2` や `RDS` に適用する作業は，後で行います）

#### EC2 用のセキュリティグループ

【構成内容】 クライアント --(HTTP or SSH)--> `EC2`

- 画面左のメニューバーの「セキュリティグループ」を選択

![2_5a.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431142/90eeb629-02a3-459f-0259-9bf6c726a621.png)

- 「セキュリティグループを作成」ボタンをクリック

キー | 値
--- | ---
セキュリティグループ名 | アプリ名\_ec2\_security\_group
説明 | アプリ名\_ec2\_security\_group
VPC | アプリ名_vpc

「インバウンドルール」の「ルールを追加」を2回クリック

キー | 値
--- | ---
タイプ | HTTP ※「HTTPS」ではありません！
プロトコル | TCP ※自動選択
ポート範囲 | 80 ※自動選択
ソース | 任意の場所 （0.0.0.0/0 と ::/0 が表示）
説明 |  ※空白でOK

キー | 値
--- | ---
タイプ | SSH
プロトコル | TCP ※自動選択
ポート範囲 | 22 ※自動選択
ソース | マイIP （自分のグローバルIPが表示）
説明 |  ※空白でOK

![2_5b.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431142/2cc6e351-44fb-a5a0-3937-1511a3e9299e.png)

【注意】ソースを `マイIP` とすることで，SSH接続できるIPアドレスを制限できます。ただし，自宅以外で `EC2` にアクセスする可能性がある場合は，他のIPアドレスも登録が必要です。（意味が分からない場合は， `SSH` のソースも `任意の場所` に設定して下さい）

#### RDS 用のセキュリティグループ

【構成内容】 `EC2` --(TCP)--> `RDS`

データベースは `EC2` からのリクエストのみを受け付けるように設定しておきます。

画面左のメニューバーの「セキュリティグループ」を選択し，「セキュリティグループを作成」ボタンをクリック

キー | 値
--- | ---
セキュリティグループ名 | アプリ名\_db\_security\_group
説明 | アプリ名\_db\_security\_group
VPC | アプリ名_vpc

「インバウンドルール」の「ルールを追加」をクリック

【注意】アプリで使用するデータベースシステムを確認し，「いずれか一方」のルールを追加して下さい

- `MySQL` の場合

キー | 値
--- | ---
タイプ | MySQL/Aurora
プロトコル | TCP ※自動選択
ポート範囲 | 3306 ※自動選択
ソース | カスタム : アプリ名\_ec2\_security\_group
説明 |  ※空白でOK

![2_5c.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431142/5198f34f-0780-5f47-2cbc-4b8a469eb36c.png)

「セキュリティグループを作成」をクリック

- `PostgreSQL` の場合

キー | 値
--- | ---
タイプ | PostgreSQL
プロトコル | TCP ※自動選択
ポート範囲 | 5432 ※自動選択
ソース | カスタム : アプリ名\_ec2\_security\_group
説明 |  ※空白でOK

![2_5d.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431142/bb3a85c6-ebaa-94b9-fce4-75a730b04ac7.png)

「セキュリティグループを作成」をクリック

## 3. RDS

- [(公式) Amazon RDS](https://aws.amazon.com/jp/rds/)

`Amazon RDS` とは `Amazon Relational Database Service` の略称で，`MySQL` や `PostgreSQL` のようなデータベース管理システム（`DBMS`）を最適な動作条件で利用できるサービスです。

`RDS` は運用負荷が低く非常に便利なのですが，`コストが高い` ため注意が必要です。

AWSアカウント作成後，1年間は一定の条件で無料となりますが，不要になった時点で削除することをお勧めします。

### 3.1. サブネットグループの作成

`2.2` で作成した `RDS` 用のサブネット2つをグループ化しましょう。

- 画面左上の「サービス」を開き、検索欄に「RDS」と入力し、「RDS」を選択

- 画面左のメニューバーの「サブネットグループ」を選択し，右上の「DB サブネットグループを作成」ボタンをクリック

- サブネットグループの詳細

キー | 値
--- | ---
名前 | アプリ名\_subnet\_group\_db
説明 | アプリ名\_subnet\_group\_db
VPC | アプリ名\_vpc

- サブネットの追加
  - 異なる `アベイラビリティーゾーン` に属するサブネットを2つ以上選択する必要があります
  - `マルチ AZ` 構成時に必要となるため，今回のように `マルチ AZ` を使用しない場合にも必須となります

キー | 値
--- | ---
アベイラビリティーゾーン | ap-northeast-1a, ap-northeast-1c
サブネット | 10.0.10.0/24, 10.0.11.0/24 の2つを選択

![3_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431142/31fdca87-d051-9504-cbb5-91d7f83ad2c7.png)

「作成」ボタンをチェック

### 3.2 パラメータグループの作成(MySQL限定)

【注意】`PostgreSQL` の場合はスキップして下さい

`RDS` の `MySQL` のデフォルト文字コードは `latin1` であり，日本語に対応していません。

文字コードを変更するためのパラメータグループを先に作成しておきましょう。

（1度作成すれば使い回しが可能です）

- 画面左のメニューバーの「パラメータグループ」を選択

「パラメータグループの作成」ボタンをクリック

- パラメータグループの詳細

キー | 値
--- | ---
パラメータグループファミリー | mysql5.7
グループ名 | mysql57-supported-in-ja
説明 | parameter group for mysql5.7 supported in ja

- 作成した「mysql57-supported-in-ja」をクリック

- 「パラメータの編集」ボタンをクリック

- 検索窓にキーをコピペして値を変更

キー | 値
--- | ---
character_set_client | utf8
character_set_connection | utf8
character_set_database | utf8mb4
character_set_results | utf8
character_set_server | utf8mb4
skip-character-set-client-handshake | 1

- 「変更の保存」ボタンをクリック

### 3.3 DBインスタンスの作成

画面左のメニューバーの「データベース」を選択

「データベースを作成」ボタンをクリック

「MySQL」の場合と「PostgreSQL」の場合で設定が異なりますので，いずれか片方を実行して下さい。

#### 3.3.1 MySQLの場合

- データベース作成方法を選択

「標準作成」を選択

- エンジンのオプション

キー | 値
--- | ---
エンジンのタイプ | MySQL
バージョン | MySQL 5.7.* (*の箇所は最新のものを推奨)

- テンプレート

「無料利用枠」を選択

![3_3ma.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431142/3b7d7b2d-1bd3-ccb9-a864-0c0755e97971.png)

- 設定

【注意】ここの「アプリ名」の `_` は ハイフン `-` とすること
【注意】マスターユーザー名・パスワードをメモしておくこと

キー | 値
--- | ---
DB インスタンス識別子 | アプリ名-db
マスターユーザー名 | root
マスターパスワード | 任意

- DB インスタンスサイズ

db.t2.micro（デフォルト）

![3_3mb.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431142/ba8da289-0b92-48e6-6278-7a6f47c548e4.png)

- ストレージ

※デフォルト設定で

- 可用性と耐久性

※デフォルト設定で

![3_3mc.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431142/b2ffd9cf-6792-e82f-41c0-662427caf617.png)

- 接続

「アプリ名_vpc」を選択

- 「追加の接続設定」のドロップダウンをクリック

キー | 値
--- | ---
サブネットグループ | アプリ名\_subnet\_group\_db ※2.1で作成
セキュリティグループ | アプリ名\_db\_security\_group ※1.5で作成, default は外す
アベイラビリティーゾーン | ap-northeast-1a

![3_3md.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431142/4c70104d-82b0-abd0-97c2-1f1871b02de6.png)

- データベース認証

※デフォルト設定で

- 「追加設定」をクリック

- データベースの選択肢

キー | 値
--- | ---
最初のデータベース名 | アプリ名_production
DB パラメータグループ | mysql57-supported-in-ja
オプショングループ | ※デフォルト設定

- バッグアップ以降

※デフォルト設定で

![3_3me.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431142/0689949d-8b2d-5d85-ec11-180eef24b231.png)

![3_3f.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431142/db6a488f-cc84-5570-8a35-5463c82a6ff1.png)

- 「データベースの作成」ボタンをクリック

#### 3.3.2 PostgreSQLの場合

まず，ターミナルでローカルで使用している `PostgreSQL` のバージョンを確認しておきましょう。

```
psql -V
```

- データベース作成方法を選択

「標準作成」を選択

- エンジンのオプション

`PostgreSQL` のバージョンは，上記で確認したローカルのバージョンに合わせた方が無難です。

メジャーバージョンを合わせれば十分なので，例えばローカルが `12.3` ならば，12.* (*の箇所は最新のもの) をお勧めします。

（2021年5月時点ではバージョン `13` は無料利用枠の対象外となっております。ローカルのバージョンが `13` の場合でも，無料利用枠のために `12` を選択されてもよいでしょう）

キー | 値
--- | ---
エンジンのタイプ | PostgreSQL
バージョン | ※上記を参考に設定

- テンプレート

「無料利用枠」を選択

![3_3pa.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431142/53e526cf-0fe0-be7e-a2a1-8a7feffa9973.png)

- 設定

【注意】ここの「アプリ名」の `_` は ハイフン `-` とすること
【注意】マスターユーザー名・パスワードをメモしておくこと

キー | 値
--- | ---
DB インスタンス識別子 | アプリ名-db
マスターユーザー名 | postgres
マスターパスワード | 任意

- DB インスタンスサイズ

db.t2.micro（デフォルト）

![3_3pb.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431142/50ee9fee-83d9-639d-1f20-35ca2e60ee74.png)

- ストレージ

※デフォルト設定で

- 可用性と耐久性

※デフォルト設定で

![3_3pc.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431142/ac52166a-c167-0c09-5d9e-a94b5ab6ada7.png)

- 接続

「アプリ名_vpc」を選択

- 「追加の接続設定」のドロップダウンをクリック

キー | 値
--- | ---
サブネットグループ | アプリ名\_subnet\_group\_db ※2.1で作成
セキュリティグループ | アプリ名\_db\_security\_group ※1.5で作成, default は外す
アベイラビリティーゾーン | ap-northeast-1a

![3_3pd.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431142/23d58058-48c8-e3ac-6d9f-5652e5fbd23e.png)

- データベース認証

※デフォルト設定で

- 「追加設定」をクリック

- データベースの選択肢

キー | 値
--- | ---
最初のデータベース名 | アプリ名_production
DB パラメータグループ | ※デフォルト設定
オプショングループ | ※デフォルト設定

- バッグアップ以降

※デフォルト設定で

![3_3pe.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431142/f2ab6868-3281-18e9-f0f1-66f7c3590e67.png)

![3_3pf.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431142/5a8a2ab9-7e38-65d4-e590-b4a790f0e11e.png)

- 「データベースの作成」ボタンをクリック

### 3.4 エンドポイントの確認

- アプリ名を選択

- 「接続とセキュリティ」の「エンドポイント」をメモ
  - データベースが作成されるまで待つ必要がありますので，別タブで次の `EC2` の設定に進めるとよいでしょう

![3_4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431142/bd176c35-261a-4d9c-1920-d543de8e90e6.png)

## 4. EC2

- [(公式) Amazon EC2](https://aws.amazon.com/jp/ec2/)

`Amazon EC2` とは `Amazon Elastic Compute Cloud` の略称で，コンピューティング性能をクラウド内で提供するサービスです。サーバーに必要なものをクラウド内で借りることができます。

Railsのアプリをこの `EC2` にデプロイし，ブラウザからアクセスできるように準備を行います。

EC2 も運用に料金がかかりますので，不要になったインスタンスなどは削除するようにしましょう。

### 4.1 インスタンスの作成

- 画面左上の「サービス」を開き、検索欄に「EC2」と入力し、「EC2」を選択

- 画面左のメニューバーの「インスタンス」を選択し，「インスタンスを起動」ボタンをクリック

#### ステップ 1: Amazon マシンイメージ (AMI)

「無料利用枠の対象」タグの付いた「Amazon Linux 2 AMI (HVM), SSD Volume Type」「64ビット (x86)」の「選択」ボタンをクリック

![4_1a.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431142/45f4932f-22ba-3947-3cfa-14b444924df7.png)

#### ステップ 2: インスタンスタイプの選択

- 「無料利用枠の対象」タグの付いた「t2.micro」が選択されていることを確認した上で，「次のステップ」ボタンをクリック
  - 「確認と作成」ボタンをクリックしないこと。クリックした場合は，「戻る」ボタンをクリックしてステップ3まで移動しましょう

![4_1b.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431142/d745d635-d652-f086-7aff-a09fa4d5d8fd.png)

#### ステップ 3: インスタンスの詳細の設定

キー | 値
--- | ---
ネットワーク | アプリ名\_vpc
サブネット | アプリ名\_public\_1a\_subnet

他はデフォルト設定のまま「次のステップ」ボタンをクリック

![4_1c.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431142/f240212d-c9f2-e3f2-25ca-c0073b81dd38.png)

#### ステップ 4: ストレージの追加

「次のステップ」ボタンをクリック

#### ステップ 5: タグの追加

「タグの追加」ボタンをクリック

キー | 値
--- | ---
Name | 「アプリ名_instance」を選択

![4_1d.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431142/1e9a0ba0-f1ef-f62f-17c9-380ba97d86d2.png)

「次のステップ」ボタンをクリック

#### ステップ 6: セキュリティグループの設定

「既存のセキュリティグループを選択する」にチェック

「アプリ名\_ec2\_security\_group」にチェック

![4_1e.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431142/d43db9be-fb9c-ad7b-d219-eebefbf1222d.png)

「確認と作成」ボタンをクリック

#### ステップ 7: インスタンス作成の確認

- セキュリティグループの「名前」が「アプリ名\_ec2\_security\_group」になっていることを確認した上で，「起動」ボタンをクリック

#### 既存のキーペアを選択するか、新しいキーペアを作成します。

- 「新しいキーペアの作成」を選択

- 「キーペア名」は「アプリ名」を入力
  - `メモ` しているアプリ名と全く同じにして下さい（ハイフン `-` を使用せず，アンダースコア `_` を使用）

- 「キーペアのダウンロード」を選択
  - `アプリ名.pem` をダウンロードできます。後に `EC2` のログインで利用します

![4_1f.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431142/fce9e582-bd11-2960-4480-29e190ebc498.png)

「インスタンスの作成」ボタンをクリック

#### 作成ステータス

「インスタンスの表示」ボタンをクリック

### 4.2 Elastic IPの作成、紐付け

インターネットに接続するには `グローバルIPアドレス` が必須です。

現状ではこの `グローバルIPアドレス` は固定化されておらず `EC2インスタンス` を再起動した際に変更されてしまいます。（URLが変化してしまう！）

`Elastic IP` 設定することで，`グローバルIPアドレス` を固定化することができます。

`Elastic IP` はこれから行う `EC2インスタンス` との関連付けを行っている状態では無料ですが，関連付けた `EC2インスタンス` を削除したり停止させると料金が発生するようになります。

`EC2インスタンス` を削除する際は `Elastic IP` の開放を忘れないように注意しましょう。
#### Elastic IPの作成

- 画面左のメニューバーの「Elastic IP」を選択

- 「Elastic IP アドレスの割り当て」ボタンをクリック

- 「割り当て」ボタンをクリック

#### Elastic IPの紐付け

- 先ほど割り当てた `Elastic IP` を選択した状態で、「アクション」ドロップダウンを選択し、「Elastic IP アドレスの関連付け」をクリック

![4_2a.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431142/fe81be8d-e397-a0ea-d6f6-a06c5c710cd6.png)

ここで，表示されている `Elastic IP` は必ずメモしておいて下さい。

キー | 値
--- | ---
インスタンス | アプリ名_instance
プライベート IP アドレス | ※クリックして表示されるIPを選択
再関連付け | チェックを入れる

![4_2b.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431142/23daa3c1-ccb8-3580-8aa7-327cebf195e4.png)

- 「関連付ける」ボタンをクリック

### 4.3 EC2へのログイン

#### アプリ名を定義（ローカル環境）

作業ミスを減らすため，ターミナルで頻出する `アプリ名` を一時的にグローバル変数として定義しておきましょう。

`アプリ名` の箇所は，各自がメモしているものに置き換えて実行して下さい。

```zsh:ターミナル
APP_NAME=アプリ名
echo $APP_NAME
```

```zsh:(例)
APP_NAME=aws_sample_app
echo $APP_NAME
```

最後のコマンドで，「アプリ名」が正しく表示されていることを確認した上で，以下を実行して下さい。

【補足】間違った場合は再度上記を実行し直してから進めて下さい。また，コマンドは原則コピペして下さい。1文字間違っただけで大きく時間をロスする可能性があります。

```zsh:ターミナル
# zsh の場合
echo "export APP_NAME=$APP_NAME" >> ~/.zshrc
source ~/.zshrc

# bash の場合
echo "export APP_NAME=$APP_NAME" >> ~/.bash_profile
source ~/.bash_profile
```

【備考】 使用しているシェルスクリプトが `zsh` か `bash` か分からない場合は `echo $SHELL` を実行して確認して下さい

![4_3a.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431142/401f06ed-e17b-f2f0-e1fb-2a7262afeee6.png)

#### キーペアの移動・アクセス権の付与

【参考】 https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/ec2-key-pairs.html

4.1 でダウンロードしたキーペア `アプリ名.pem` は `.ssh` ディレクトリに移動し，利用できる状態にしておきましょう。

```zsh:ターミナル
cd
mkdir .ssh
mv Downloads/$APP_NAME.pem .ssh
chmod 400 .ssh/$APP_NAME.pem
ls -l .ssh/$APP_NAME.pem
```

最後のコマンドで `-r-------- (略) .ssh/アプリ名.pem` が表示されていればOKです。

【補足】`mv` コマンド実行時に `No such file or directory` が表示された場合は，Google Chrome の下に現れている `アプリ名.pem` のドロップダウンメニューから，キーペアの場所を確認して，直接，ホームディレクトリの `.ssh` ディレクトリに移動させて下さい。キーペアのファイル名が `アプリ名.pem` になっているかどうかも確認しましょう。

#### EC2 インスタンスの状態を確認

- `EC2` の画面左のメニューバーの「インスタンス」をクリックし，作成した EC2インスタンスの「インスタンスの状態」が `実行中` となっているか確認して下さい
  - `実行中` になっていない場合はしばらく待ってからブラウザをリロードして下さい

【補足】4.2で `Elastic IP` をメモし忘れた人は，作成した EC2インスタンス を選択し， `Elastic IP` をメモして下さい

![4_3b.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431142/e0fb8f7f-f725-9869-9b3b-774091f963f8.png)

#### ssh接続の設定ファイルを作成

`EC2インタスタンス` にログインするには，「ホスト名」「ユーザー名」「AWSからダウンロードしたキーペア（秘密鍵）」が必要になります。

これらをログインの度に毎回記述するのは面倒なので，設定ファイルに記載し，使える状態にしましょう。

```zsh:ローカル環境のターミナル
vi ~/.ssh/config
```

`Vim` エディタでファイルが開かれますので，以下を記述して下さい。（デフォルトで `ec2-user` という名前のユーザーが用意されています）

```yml:ローカル環境のターミナル(~/.ssh/config)
Host アプリ名
  Hostname ElasticIPを記入
  User ec2-user
  IdentityFile ~/.ssh/アプリ名.pem
```

【参考】 「アプリ名」が `aws_sample_app`, 「Elastic IP」が `172.217.161.46` の場合は，次の形式となります。

```yml:(例)
Host aws_sample_app
  Hostname 172.217.161.46
  User ec2-user
  IdentityFile ~/.ssh/aws_sample_app.pem
```

#### 【補足】 Vimの基本

一般のテキストエディタは，ファイルを開いてすぐに文字が入力できます。 `Vim` は `インサートモード` に変更しなければキー入力ができません。

ここは初学者が非常に混乱しやすいところですので，くれぐれもご注意下さい。

以下は最低限覚えておきましょう。

- `Vim` の起動時は「ノーマルモード」
- 文字をキー入力したいときは `i` を入力し，「インサートモード」に変更する必要がある
  - 「かな」入力モードでは `i` を入力しても効果が出ないので，「英数」入力モードに切り替えてから行うこと
- 「ノーマルモード」への移行は，`esc`
- 保存終了は「ノーマルモード」にしてから `:wq`
- （保存せずに）強制終了は「ノーマルモード」にしてから `:q!`

#### EC2にログイン

それでは， `EC2インタスタンス` にログインしてみましょう。

```zsh:ターミナル
ssh アプリ名
```

`Are you sure you want to continue connecting (yes/no/[fingerprint])` と表示されたら，`yes` と入力してEnterキーを押しましょう。

これで， `EC2インタスタンス` にログインできます。

---

【備考】ここでSSH接続できない場合は，以下の可能性があります。

- 先ほど作成した `~/.ssh/config` にミスがある

- 4.3で，キーペアが指定箇所に移動できていない or パーミッションを適切に変更できていない（`ls -l ~/.ssh/$APP_NAME.pem` で確認）

- `Elastic IP` をメモし間違えている

- 2.5で作成した「EC2 用のセキュリティグループ（アプリ名\_ec2\_security\_group）」の設定を間違えている
  - SSHのソースが `マイIP` の場合は，IPアドレスが変わっていないかも確認しましょう（不明な場合は `任意の場所` に変更）

- 4.1で設定したEC2 用のセキュリティグループが「アプリ名\_ec2\_security\_group」になっていない

- 2.4で作成したルートテーブルの「サブネットの関連付け」を間違えている

---

### 4.4 EC2の初期設定

[（参考）Amazon Linux インスタンスでユーザーアカウントを管理する](https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/managing-users.html)

---

#### 要注意

今後，EC2にログインした状態の `サーバー環境のターミナル` と，通常の `ローカル環境のターミナル` のどちらで操作するかを「必ず」確認して下さい。1度でも間違えると，エラー解決に大きく時間を取られる可能性があります。

なお，一定時間が経過しますと，EC2からログアウトしますので，その点にも注意しましょう。

---

#### 新規ユーザーアカウントの作成

今後 `EC2` での作業や `Capistrano` で利用するユーザーを作成しましょう。作業を楽にするため，しばらくパスワード無しで sudo コマンドを使える状態にしておきます。

EC2にログインした状態の `サーバー環境のターミナル` で以下を実行しましょう。

「ユーザー名」の箇所は自由に決め， `メモ` しておいて下さい。

```bash:サーバー環境のターミナル
APP_USER=ユーザー名
echo $APP_USER
```

最後のコマンドで，「ユーザー名」が正しく表示されていることを確認した上で，以下を実行して下さい。

```bash:サーバー環境のターミナル
sudo adduser $APP_USER
echo "$APP_USER ALL=(ALL) NOPASSWD: ALL" | sudo EDITOR='tee -a' visudo > /dev/null
sudo su - $APP_USER
sudo ls
```

最初の表示が `ec2-user` から「自分の決めたユーザー名」に変更され，最後のコマンドでパスワードを求められなければOKです。

【補足】2番目の操作は `sudo visudo` 実行後，一番最後の行に `ユーザー名 ALL=(ALL) NOPASSWD: ALL` を追加する操作です

#### アプリ名を定義（サーバー環境）

作業ミスを減らすため，サーバー環境でも「アプリ名」をグローバル変数として定義しておきましょう。

```bash:サーバー環境のターミナル
APP_NAME=アプリ名
echo $APP_NAME
```

最後のコマンドで，「アプリ名」が正しく表示されていることを確認した上で，以下を実行して下さい。

（間違っている場合は再度上記を実行し直してから進めて下さい）

```bash:サーバー環境のターミナル
echo "export APP_NAME=$APP_NAME" >> ~/.bash_profile
source ~/.bash_profile
```

#### 新規ユーザーでssh接続するための設定

新規ユーザーでssh接続できるようにするには，`アプリ名.pem` （秘密鍵）に対応する `公開鍵` を `EC2` の `~/.ssh/authorized_keys` に配置し，適切な権限を与えておく必要があります。

```bash:サーバー環境のターミナル
mkdir .ssh -m 700
vi .ssh/authorized_keys
```

サーバー側でVimエディタが開かれます。

ここで，新しく `ローカル` 環境のターミナルを用意して下さい。（ `command + n` もしくは `command + t` ）

そして， `ローカル` 環境のターミナルで `秘密鍵(アプリ名.pem)` から `公開鍵` をコピーして下さい。

```zsh:ローカル環境のターミナル
ssh-keygen -y -f ~/.ssh/$APP_NAME.pem | pbcopy
```

（コマンドを実行するだけでコピーされますので，`command + c` は不要です。もし `pbcopy: command not found` が表示される場合は ` | pbcopy` を削って実行し，直接コピーして下さい）

その後，サーバー環境のターミナルに戻って以下の操作を行ってください。

```bash:サーバー環境のターミナル
# 公開鍵(ssh-rsa *****)を貼り付け，保存終了
chmod 600 .ssh/authorized_keys
exit
exit
```

これで，新しく作成したユーザーでもログインができるようになりました。

ログイン時のユーザー名を変更しましょう。

```zsh:ローカル環境のターミナル
vi ~/.ssh/config
```

- 「ユーザー名」を `ec2-user` から `メモしたユーザー名` に変更しましょう。

```yml:ローカル環境のターミナル(~/.ssh/config)
Host アプリ名
  Hostname ElasticIP
  User ユーザー名 ★ここを変更★
  IdentityFile ~/.ssh/アプリ名.pem
```

```yml:(ユーザー名が take の場合)
Host aws_sample_app
  Hostname 172.217.161.46
  User take
  IdentityFile ~/.ssh/aws_sample_app.pem
```

### 4.5 今後のEC2のログイン方法

再度，以下のコマンドで `EC2インスタンス` にログインしてみましょう。

```zsh:ローカル環境のターミナル
ssh アプリ名
```

`ec2-user` ではなく各自が決めたユーザー名でログインできるはずです。

デフォルトユーザーは必要なくなりましたので，削除しておきましょう。

```bash:サーバー環境のターミナル
sudo userdel -r ec2-user
```

## 5. サーバー構築

Railsアプリを実行できるようにするための環境構築を行っていきます。

`EC2インスタンス` にログインしていない場合は，次でログインして下さい。

```zsh:ローカル環境のターミナル
ssh アプリ名
```

### 5.1 Nginx の設定

以下の順序で実行していきます。

- サーバーをアップデート
- `Nginx` のインストール
- `Nginx` の起動とインスタンス起動時自動起動の設定

```bash:サーバー環境のターミナル
sudo yum -y update
sudo amazon-linux-extras install -y nginx1
sudo systemctl start nginx
sudo systemctl enable nginx
```

ブラウザに `Elastic IP` を入力してアクセスしましょう。

`Welcome to nginx on Amazon Linux!` が表示されればOKです。

【補足】ここでアクセスできない場合は，以下の可能性があります

- 2.5で作成した「EC2 用のセキュリティグループ（アプリ名\_ec2\_security\_group）」の設定を間違えている
  - 「タイプが HTTP, ソースが `0.0.0.0/0` と `::/0`」のものが含まれているかを確認。「HTTP」を「HTTPS」にしているミスを見かけます
- 4.1で設定したEC2 用のセキュリティグループが「アプリ名\_ec2\_security\_group」になっていない

### 5.2 必要なプラグインのインストール

- 各種プラグインのインストール
  - 1行のコマンドです

```bash:サーバー環境のターミナル
sudo yum -y 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
```

【注意】上記はあくまで「画像投稿可能なアプリ」のデプロイを想定したものです。アプリに導入されている gem や ライブラリ次第では，依存関係にある他のプラグインもインストールしておく必要があります。

### 5.3 データベースにログイン

まず，`EC2` にプリインストールされている `MariaDB` を削除しておきましょう。

```bash:サーバー環境のターミナル
sudo yum -y remove mariadb-libs
```

#### MySQLの場合

以下の順序で実行していきます。

- `MySQL` のリポジトリを追加
- `MySQL` の5.7がインストールされるように変更
- `MySQL` をインストール

```bash:サーバー環境のターミナル
sudo yum localinstall -y https://dev.mysql.com/get/mysql80-community-release-el7-3.noarch.rpm
sudo yum-config-manager --disable mysql80-community
sudo yum-config-manager --enable mysql57-community
sudo yum -y install mysql-community-client mysql-server mysql-devel
```

次のコマンドでRDSの `MySQL` にログインできます。

```bash:サーバー環境のターミナル
mysql -h エンドポイント -u root -p
# RDS の パスワード を入力
```

`mysql>` という表示が出た後に次を実行して下さい。

```bash:サーバー環境のターミナル(mysql)
show databases;
# 表示されたデータベース一覧に，「アプリ名_production」が存在していればOK
exit
```

【参考】「エンドポイント」は `2.4` で確認したものです。確認できていない場合は `RDS` の「データベース」をクリックし，アプリを選択して出てくる「エンドポイント」を確認しましょう。

---

【補足】もし，データベース一覧に，「アプリ名_production」が存在しない場合は，`exit;` で終了する前に以下を実行してデータベースを作成して下さい。

```bash:サーバー環境のターミナル(mysql)
# 「アプリ名_production」が存在しない場合のみ，次を実行して下さい
CREATE DATABASE アプリ名_production;
```

---

#### 【参考】 Sequel Aceで MySQL に接続する方法

`Sequel Ace` をインストールしていない場合は，App Store からインストールしましょう。

- SSHのタブを選択

キー | 値
--- | ---
MySQL Host | RDSのエンドポイントを記載
Username | root
Password | RDSのパスワードを記載
Database | アプリ名_production
Port | 3306
SSH Host | Elastic IPを記載
SSH User | EC2のユーザー名を記載
SSH Password | 右側の鍵マークボタンをクリックし「~/.ssh/アプリ名.pem」を選択

【参考】 `.ssh` は隠しファイルです。隠しファイルを表示するショートカットキーは `command + shift + .` です。

![5_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431142/869bff2f-16aa-bca3-aa9b-4347f9d9700d.png)

#### PostgreSQLの場合

単純にインストールするとバージョンが低すぎるので，`amazon-linux-extras` リポジトリから `PostgreSQL` のバージョン11を入れることにしましょう。

```bash:サーバー環境のターミナル
sudo amazon-linux-extras install -y postgresql11
yum list | grep postgresql
sudo yum -y install postgresql-devel
```

---

【補足】PostgreSQLのバージョン12以上を強引に入れることも可能ですが，`postgresql-devel` のインストールが難しいのでお勧めしません。なお，インストール可能な最新バージョンは， `amazon-linux-extras | grep postgresql` で確認できます。

---

次のコマンドでRDSの `PostgreSQL` にログインできます。

```bash:サーバー環境のターミナル
psql -h エンドポイント -U postgres
# RDS の パスワード を入力
```

`postgres=>` という表示が出た後に次を実行して下さい。

```bash:サーバー環境のターミナル(PostgreSQL)
\l
# 表示されたデータベース一覧に，「アプリ名_production」が存在していればOK
exit;
```

【補足】もし，データベース一覧に，「アプリ名_production」が存在しない場合は，`exit;` で終了する前に以下を実行してデータベースを作成して下さい。

```bash:サーバー環境のターミナル(PostgreSQL)
# 「アプリ名_production」が存在しない場合のみ，次を実行
CREATE DATABASE アプリ名_production;
```

#### 【参考】 Postico で PostgreSQL に接続する方法

[https://eggerapps.at/postico](https://eggerapps.at/postico)

インストールしていない場合は，上記サイトの `Download` ボタンをクリックし，zipファイルを解凍して出てくる `Postico` をアプリケーションに移動した上で，実行して下さい。

- `Postico` を起動し，左下の「New Favorite」をクリック

- `Options` のドロップダウンを開き， `Connect via SSH` をクリック

キー | 値
--- | ---
Nickname | ※任意 (アプリ名_production など)
Host | RDS のエンドポイントを記載
Port | ※不要
User | postgres
Password | RDSのパスワードを記載
Database | アプリ名_production
SSH Host | Elastic IPを記載
User | EC2のユーザー名を記載
Password | ※不要
Private Key | 「Choose」をクリックし，「~/.ssh/アプリ名.pem」を選択

「Connect」を選択し，再度「Connect」を選択

![5_3b.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431142/f9cb601d-9804-47a9-ae36-57a8be71057e.png)

### 5.4 Node, Yarn のインストール

- `Node` のインストール
  - 【参考】 https://docs.aws.amazon.com/ja_jp/sdk-for-javascript/v2/developer-guide/setting-up-node-on-ec2-instance.html
  - Rails が使用している `node-sass` が現時点では[バージョン14以下にしか対応していない](https://github.com/sass/node-sass)ため， バージョン14をインストール

```bash:サーバー環境のターミナル
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.34.0/install.sh | bash
. ~/.nvm/nvm.sh

# インストール可能なバージョンを確認
nvm ls-remote | grep v14

# バージョン14のいずれかをインストール
# (例) nvm install v14.16.1

node -e "console.log('Running Node.js ' + process.version)"
```

最後のコマンドで `Running Node.js バージョン` が表示されればOKです。

- `Yarn` のインストール

```bash:サーバー環境のターミナル
curl -o- -L https://yarnpkg.com/install.sh | bash
```

### 5.5 Rubyのインストール

- `rbenv` のインストール

```bash:サーバー環境のターミナル
git clone https://github.com/sstephenson/rbenv.git ~/.rbenv
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile
echo 'eval "$(rbenv init -)"' >> ~/.bash_profile
source ~/.bash_profile
```

- `ruby-build` のインストール

```bash:サーバー環境のターミナル
git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build
```

- `Ruby` のインストール

まず，デプロイするアプリの `Ruby` のバージョンを確認して下さい。

（`ローカル環境のターミナル` でアプリのディレクトリまで移動し `ruby -v` で確認できます）

以下は，バージョンが `2.7.3` の場合の例です。バージョンはアプリに「必ず」合わせて下さい。

```bash:サーバー環境のターミナル
rbenv install 2.7.3
rbenv global 2.7.3
rbenv rehash
```

`Ruby` のインストールには時間がかかります。

- `Bundler` のインストール

```bash:サーバー環境のターミナル
gem install bundler
```

完了後に以下を実行し，全てバージョンが表示されればOKです。

```bash:サーバー環境のターミナル
rbenv -v
ruby -v
yarn -v
bundler -v
```

### 5.6 デプロイ先ディレクトリの設定

（デフォルト設定の場合） `Capistrano` によるアプリのデプロイ先は `/var/www/アプリ名` ディレクトリになります。

そこで，このディレクトリをあらかじめ作成しておき，必要な権限を与えておきましょう。

```bash:サーバー環境のターミナル
sudo mkdir -p /var/www/$APP_NAME
sudo chown `whoami`:`whoami` /var/www/$APP_NAME
ls -l /var/www
```

最後のコマンドで `drwxr-xr-x 2 ユーザー名 ユーザー名 日付 アプリ名` が表示されればOKです。

### 5.7 Git の初期設定

次に，サーバー環境で `GitHub` の操作ができるようにしていきましょう。

まずは，サーバー環境の `Git` の初期設定を行いましょう。ここでは，ローカル環境と同じ「ユーザー名」「メールアドレス」を登録することとしておきます。

まず， Git に登録した名前とメールアドレスを確認しましょう。

```zsh:ローカル環境のターミナル
cat ~/.gitconfig
```

次の箇所をコピーして下さい。

```none:出力内容の一部
[user]
	name = ~~~~~
  email = ~~~~~@~~~.~~~
```

- `.gitconfig` ファイルの生成

```bash:サーバー環境のターミナル
cd
vi .gitconfig
```

- Vimが開いたら先ほどコピーしたものをペーストしましょう。

### 5.8 GitHub連携

サーバー環境に GitHub へのアクセス権限を持たせるため，必要な公開鍵・秘密鍵を生成しましょう。

```bash:サーバー環境のターミナル
cd .ssh
ssh-keygen -t rsa -f "${APP_NAME}_git_rsa" -N ""
```

- これで，以下のファイルが作成されます
  - 公開鍵（GitHubに配置する鍵） `アプリ名_git_rsa.pub`
  - 秘密鍵（EC2に残しておく鍵） `アプリ名_git_rsa`

- `config` ファイルの生成

```bash:サーバー環境のターミナル
vi config
```

- Vimでファイルが開かれたら次を追加
  - 「アプリ名」の箇所は各自の設定に合わせること

```none:~/.ssh/config
Host github github.com
  Hostname github.com
  User git
  IdentityFile ~/.ssh/アプリ名_git_rsa
```

- GitHubに公開鍵を登録

以下を実行し，値をコピーしておきましょう。

```bash:サーバー環境のターミナル(~/.ssh)
cat "${APP_NAME}_git_rsa.pub"
```

![5_8a.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431142/5f2a745e-cd35-025a-75d7-6af6f44108f8.png)

1. ブラウザでGitHubにアクセス
2. 右上のサムネイル画像をクリック
3. 「Settings」をクリック
4. 左メニューバーから「SSH and GPG keys」をクリック
5. 「SSH Keys」の「New SSH key」をクリック

![5_8b.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431142/5bccdb67-03d0-8069-ff99-4559bb35bfcf.png)
![5_8c.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431142/d41d4853-f56a-ef3d-82fe-309cbccfa907.png)

キー | 値
--- | ---
Title | アプリ名\_git\_rsa.pub
Key | サーバー環境のターミナルからコピーした値をペースト

![5_8d.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431142/1c084abb-4c5a-f7fb-d86d-e92eea903cb7.png)

「Add SSH key」ボタンをクリック

- サーバーから GitHub に接続できるか確認

```bash:サーバー環境のターミナル(~/.ssh)
chmod 600 config
ssh github
```

`yes` を入力し，次の赤枠の内容が出力されればOKです。

![5_8e.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431142/7c39160a-e770-2cf2-64c5-6bd75d6273be.png)

## 6. デプロイ(Capistrano)

`Capistrano` を使い，1コマンドで Rails アプリを `AWS EC2` にデプロイできる状態にしていきましょう。

サンプルのアプリをデプロイされたい場合は `6.1` から進めて下さい。オリジナルのアプリをデプロイされる場合は `6.2` から進めて下さい。

なお，オリジナルアプリのデプロイについては，以下の条件を改めて確認しておいて下さい。

- Rails `6` であること
- アプリで使用するデータベースシステムの指定が `RDS` と一致していること
  - 他のデータベースを指定している場合は `config/database.yml` を修正して下さい。
  - `rails db:system:change --to=mysql` もしくは `rails db:system:change --to=postgresql` コマンドが便利です
- アプリで使用する gem やライブラリ次第では `EC2` に他にもあらかじめインストールしておく必要がある可能性があります

### 6.1 Railsのサンプルアプリを作成

【注】 「サンプルのアプリ」で AWS のデプロイに挑戦されたい方以外はスキップして下さい。

`CarrierWave` を用いた画像投稿機能を持つアプリを簡単に実装しましょう。

#### アプリの作成(MySQLの場合)

```zsh:ターミナル
rails new aws_sample_app -d mysql -C -M -T --skip-active-storage
```

#### アプリの作成(PostgreSQLの場合)

```zsh:ターミナル
rails new aws_sample_app -d postgresql -C -M -T --skip-active-storage
```

※ 以下は MySQL, PostgreSQL 共通です

```zsh:ターミナル
cd aws_sample_app
git add .
git commit -m "init"
bundle remove jbuilder
bundle add carrierwave
rails g scaffold Post title:string image:string --skip-assets --skip-helper
rails db:migrate:reset
rails g uploader Image
echo "/public/uploads/" >> .gitignore
```

- トップページを設定

```config/routes.rb
Rails.application.routes.draw do
  # ********** 以下を追加 **********
  root to: 'posts#index'
  # ********** 以上を追加 **********
  resources :posts
end
```

- 画像投稿機能を付けるための準備

```app/models/post.rb
class Post < ApplicationRecord
  # ********** 以下を追加 **********
  mount_uploader :image, ImageUploader
  # ********** 以上を追加 **********
end
```

- 投稿する画像の選択ボタンを作成

```erb:app/views/posts/_form.html.erb
<!-- 略 -->
  <div class="field">
    <%= form.label :image %>
  <!-- ********** 「<%= form.text_field :image %>」を変更 ********** -->
    <%= form.file_field :image, accept: "image/png,image/jpeg,image/gif" %>
  <!-- ********** 以上を変更 ********** -->
  </div>
<!-- 略 -->
```

- 投稿詳細ページで，投稿した画像が表示されるように設定

```erb:app/views/posts/show.html.erb
<p id="notice"><%= notice %></p>

<p>
  <strong>Title:</strong>
  <%= @post.title %>
</p>

<!-- ********** 以下を編集 ********** -->
<% if @post.image? %>
  <p>
    <%= image_tag @post.image.url %>
  </p>
<% end %>
<!-- ********** 以上を編集 ********** -->

<%= link_to 'Edit', edit_post_path(@post) %> |
<%= link_to 'Back', posts_path %>
```

動作確認を行った上で，GitHubにリポジトリを作成し，add, commit を行い, GitHub に push しましょう。

さらに，画像を `AWS S3` に保存できるようにすべきですが，ひとまず画像投稿は可能ですのでデプロイ完了後の10章・11章で設定を行うこととしましょう。

### 6.2. データベースの設定

※以下は自作アプリの場合の場合も実行して下さい。必要があれば作業前にブランチを切るようにしましょう。

ファイル名 | ファイルのイメージ
--- | ---
config/master.key | 鍵
config/credentials.yml.enc | 錠付きファイル

まず，本番環境用の `config/database.yml` を修正しておきましょう。

`RDS` のパスワードとエンドポイントは `GitHub` から見られないようにするため， `credentials.yml.enc` に記載し，これを読み込むように設定することとします。

（ `dotenv-rails` を導入し `.env` にパスワードなどを記載する方法もあります）

#### MySQLの場合

```config/database.yml
production:
  <<: *default
  # ***** 以下を修正 *****
  database: アプリ名_production
  username: root
  password: <%= Rails.application.credentials.db[:password] %>
  host: <%= Rails.application.credentials.db[:hostname] %>
```

#### PostgreSQLの場合

```config/database.yml
production:
  <<: *default
  # ***** 以下を修正 *****
  database: アプリ名_production
  username: postgres
  password: <%= Rails.application.credentials.db[:password] %>
  host: <%= Rails.application.credentials.db[:hostname] %>
```

※ 以下は MySQL, PostgreSQL 共通です

- `database.yml` で読み込むパスワードとエンドポイントを `credentials.yml.enc` に記載しましょう
  - `yaml` 形式ですので，インデント幅にはくれぐれも注意して下さい
  - 例えば，`password:`の後に「半角スペース1個」がないだけでエラーになります

```zsh:ローカル環境のターミナル
EDITOR=vi rails credentials:edit
```

```config/credentials.yml.enc
db:
  password: RDSのパスワード
  hostname: RDSのエンドポイント
```

さらに，本番環境で `master.key` が存在しない場合はエラーが出るように設定しておきます。

```config/environments/production.rb
# 次のコメントアウトを解除
config.require_master_key = true
```

念のため以下を実行し，パスワードとエンドポイントが取得できることを確認しておいた方がよいでしょう。

```zsh:ローカル環境のターミナル
rails c
# コンソール起動後
Rails.application.credentials.db[:password]
Rails.application.credentials.db[:hostname]
# RDSのパスワード, エンドポイントが表示されることを確認後
exit
```

確認ができれば，add, commit を行い, GitHub に push しましょう。

master(main) ブランチ以外で作業をされている場合は，プルリクを出し，GitHubの master(main) ブランチにマージするところまでは行ってください。

### 6.3. Capistrano

1コマンドで Rails アプリをデプロイできるよう `Capistrano` を導入しましょう。

- [Capistrano::Rails](https://github.com/capistrano/rails)
- [Capistrano::rbenv](https://github.com/capistrano/rbenv)
- [Capistrano::Rbenv::Vars](https://github.com/zshannon/capistrano-rbenv-vars)
- [Capistrano::Puma](https://github.com/seuros/capistrano-puma)

```none:Gemfile
group :development do
  # 略
  # ***** 以下を追加 *****
  gem "capistrano", "~> 3.10", require: false
  gem "capistrano-rails", "~> 1.6", require: false
  gem 'capistrano-rbenv', '~> 2.2'
  gem 'capistrano-rbenv-vars', '~> 0.1'
  gem 'capistrano3-puma'
  # ***** 以上を追加 *****
end
```

```zsh:ローカル環境のターミナル
bundle install
bundle exec cap install STAGES=production
```

`Capfile` の `# require "capistrano/passenger"` の下に以下を追加

```rb:Capfile
require 'capistrano/rbenv'
require 'capistrano/bundler'
require 'capistrano/rails/assets'
require 'capistrano/rails/migrations'
require 'capistrano/puma'
install_plugin Capistrano::Puma
install_plugin Capistrano::Puma::Systemd
install_plugin Capistrano::Puma::Nginx
```

- デプロイの設定ファイルを修正・追記しましょう
  - 「アプリ名」「GitHubリポジトリURL」は各自のものに置き換えて下さい
  - 「GitHubリポジトリURL」は `HTTPS` でなく `SSH` のURLをコピペして下さい

```config/deploy.rb
# config valid for current version and patch releases of Capistrano
lock "~> 3.14.1"

# ***** 以下を修正 *****
set :application, "アプリ名"
set :repo_url, "GitHubのリポジトリURL"
set :rbenv_ruby, File.read('.ruby-version').strip
set :branch, ENV['BRANCH'] || "master"

# Nginxの設定ファイル名と置き場所を修正
set :nginx_config_name, "#{fetch(:application)}.conf"
set :nginx_sites_enabled_path, "/etc/nginx/conf.d"

append :linked_files, "config/master.key"
append :linked_dirs, "log", "tmp/pids", "tmp/cache", "tmp/sockets", "node_modules"
# ***** 以上を追加 *****
```

- `production.rb` の一番上に，EC2サーバーにログインするユーザー名、サーバーのロールを記述
  - 「ElatsticIP」「アプリ名」「ユーザー名」は各自のものに置き換えること

```config/deploy/production.rb
server "ElatsticIP", user: "ユーザー名", roles: %w{app db web}

set :ssh_options, {
  keys: %w(~/.ssh/アプリ名.pem),
  forward_agent: true,
  auth_methods: %w(publickey),
}
```

#### master.key の配置と Rails アプリのデプロイ

準備が整いましたので，Railsアプリのデプロイを行いましょう。

初回の実行では `master.key` がないためデプロイに失敗しますが， `master.key` を配置するために必要なディレクトリが生成されます。

デプロイ失敗後に `scp` コマンドで `master.key` をサーバー側に追加し，再度実行すればデプロイに成功するでしょう。

```zsh:ローカル環境のターミナル
# Railsアプリのルートディレクトリに移動してから
bundle exec cap production deploy

# 「ERROR linked file /var/www/アプリ名/shared/config/master.key does not exist on ElasticIP」が出ればOK

scp config/master.key $APP_NAME:/var/www/$APP_NAME/shared/config
bundle exec cap production puma:config
bundle exec cap production puma:systemd:config puma:systemd:enable
```

【注意】 ブランチが master でない場合は， `BRANCH=HEAD` を付けた `bundle exec cap production deploy BRANCH=HEAD` を実行して下さい

最後のコマンドで， `/etc/systemd/system/puma_アプリ名_production.service` が作成されます。このファイルの `$HOME` を実際のパスに変更しないとエラーが出るため，修正して下さい。

```bash:サーバー環境のターミナル
echo $HOME

# ここで表示されるパス「/home/ユーザー名」をコピー

cd /etc/systemd/system/
vi puma_${APP_NAME}_production.service

# 開かれたファイルの ExecStart=$HOME の $HOME を先ほどコピーしたものに置き換えて保存終了

sudo systemctl daemon-reload
sudo systemctl enable puma_${APP_NAME}_production.service
```

これで準備が完了です。再度デプロイしてみましょう！

```zsh:ローカル環境のターミナル
bundle exec cap production deploy
```

【備考】ここでデプロイに失敗した場合は，まずエラー文を確認しましょう。いろいろなケースがありますが，アプリで使用されている gem 次第では，先に依存関係にあるプラグインを EC2 にインストールしておく必要があります。

#### Nginx の設定

`Nginx` 用の設定ファイルを追加するため，先に必要なディレクトリをサーバー側で作成しておきましょう。

```bash:サーバー環境のターミナル
cd /etc/nginx
sudo mkdir sites-available
```

`ローカル環境` のターミナルで，Railsアプリのルートディレクトリまで移動した後，以下を実行しましょう。
これで `Nginx` 用の設定が生成されます。

```zsh:ローカル環境のターミナル
# Railsアプリのルートディレクトリに移動してから
bundle exec cap production puma:nginx_config
```

これで，サーバー側の `/etc/nginx/conf.d` に設定ファイル `アプリ名.conf` が入ります。

`Nginx` を再起動しましょう。

```bash:サーバー環境のターミナル(/etc/nginx)
sudo service nginx restart
```

ブラウザのアドレスバーに `Elastic IP` を入力してアクセスし， `スーパーリロード` して下さい。（ショートカットキーは `command + shift + r`）

アプリが表示されればOKです！

【補足】アプリが表示されない場合は，以下を試して下さい

- ローカル環境のターミナルから `curl -I ElasticIP` を実行したとき，最初に `HTTP/1.1 301 Moved Permanently` が出力される場合
  - `config/environments/production.rb` の `config.force_ssl = true` をコメントアウトして下さい
- `9.4` にログの確認方法を記載していますので，確かめてみましょう!!

---

これでひとまずデプロイは完了です！……が，他にもすべきことがあります。

- [【後半へ進む！】](https://qiita.com/take18k_tech/items/2efd778c8dd9aae9496e) 

「無事デプロイに成功した！!」という方は是非 <font color="red">**LGTM**</font> もお願いいたします！