【中編】Rails+Nuxt+MySQL+Dockerで作ったWEBサービスをECS・ECR・CircleCIで自動テスト・デプロイしてterraform化する

#この記事を書くにあたって
AWSについての知識が全くなかった状態からポートフォリオを作成し、個人的にAWSへのデプロイと、CircleCIの自動デプロイが一番難しかったので、備忘録・これからポートフォリオを作るよ・これからAWS使うよって方向けの記事があれば良いなと思い書きました。

私が作ったポートフォリオの概要は
[【AWS?Docker?】ポートフォリオで必要な知識を自分なりに分かりやすくまとめる【terraform?CircleCI?】](https://qiita.com/Shogo1222/items/8b8fc4c65632844a453f)

この記事の前身となる記事は
[【前編】Rails+Nuxt+MySQL+Dockerで作ったWEBサービスをECS・ECR・CircleCIで自動テスト・デプロイしてterraform化する](https://qiita.com/Shogo1222/items/d90b3fa01a2dca14aeeb)

是非ご覧いただけると有り難いです。

第一弾：[【前編】Rails+Nuxt+MySQL+Dockerで作ったWEBサービスをECS・ECR・CircleCIで自動本番デプロイしてterraform化する](https://qiita.com/Shogo1222/items/d90b3fa01a2dca14aeeb)
第二弾：本記事
第三弾:【後編】Rails+Nuxt+MySQL+Dockerで作ったWEBサービスをECS・ECR・CircleCIで自動本番デプロイしてterraform化する

#この記事で目指すゴール

- VPC・IGW・サブネット・ルートテーブル・セキュリティグループ等のネットワークの基本的な設定が出来る。
- Route53・ACM・ALBを使って常時SSL通信が出来るWEBサービスが構築出来る。
- ECS・ECRを使って本番環境にデプロイ出来る。
- CircleCIを使ってmasterへプッシュすると同時に自動デプロイする事が出来る。

#前提
- AWSのアカウント・IAMアカウント・キーペアを取得済み（ぐぐったらすぐ出来るはず）
- aws-cliをインストール済み
- 使用リージョンは東京（ap-northeast-1）
- 前回の記事まで実装を進めている。<br>
[【前編】Rails+Nuxt+MySQL+Dockerで作ったWEBサービスをECS・ECR・CircleCIで自動テスト・デプロイしてterraform化する](https://qiita.com/Shogo1222/items/d90b3fa01a2dca14aeeb)

#完成品
[qiita-sample-app](https://github.com/Shogo1222/qiita-sample-app)

#注意
この記事で出来る事は普通に**AWSの無料枠を超えて有料**になります。
また、お名前.comでドメインを取得するため、そこにも**お金がかかります。**
勉強代だと思って割り切れる方のみお読み下さい。
とりあえずECSとECRを使って、疎結合なウェブサービスを公開してみたい！という方向けです。

#ネットワークの構築
まずはネットワークをAWSの画面上でぽちぽち作っていきます。
張り切っていきましょう！

##VPC作成

![VPC.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235522/89b4f986-a97d-eab5-a266-19aafc52f2f5.png)

###豆知識
CIDRブロックとはClassless Inter-Domain Routingの略です。
IPアドレスの表記方法の一つ。簡単に言うと、VPCの範囲のことです。

ちなみにここでは`/16`のような表示のことです。
CIDR表記では、`/16`、`/24`、`32`といった表記がされます。

`/16`というのは、第3オクテッドまでを指定出来ます。
例えば、10.20.0.0の`0.0`
この部分を指定出来ます。

`/24`というのは、第4オクテッドまでを指定出来ます。
例えば、10.20.0.0の最後の`0`
この部分を指定出来ます。

`/32`というのは、特定のIPアドレスを指定出来ます。

VPCを作成する際、中にサブネットやAZでIPを確保せざるを得ない上に、変更が効かないため、
`/16`を指定して利用可能なIPを多く確保することが多いです。

##サブネット作成

ここでは、AZを**ap-northeast-1aとap-northeast-1c**を使用します。
2つのAZを利用する理由は、後々にALBを利用するからです。

サブネット＞サブネット作成より、以下のサブネットを作成して下さい。
![サブネット.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235522/5cc74ad1-1d57-fc15-9e50-e0cd8b7fe2fc.png)

|name|CIDRブロック|Region/AZ|
|:--|:--|:--|
|qiita-sample-front-subnet-1a|10.20.1.0/24|ap-northeast-1a|
|qiita-sample-front-subnet-1c|10.20.2.0/24|ap-northeast-1c|
|qiita-sample-back-subnet-1a|10.20.3.0/24|ap-northeast-1a|
|qiita-sample-back-subnet-1c|10.20.4.0/24|ap-northeast-1c|
|qiita-sample-rds-subnet-1a|10.20.5.0/24|ap-northeast-1a|
|qiita-sample-rds-subnet-1c|10.20.6.0/24|ap-northeast-1c|

##ルートテーブルの作成
ルートテーブル＞ルートテーブル作成より、以下のルートテーブルを作成して下さい。

![ルートテーブル.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235522/ee42cf3b-6725-8161-8f3b-c30a91efae75.png)

|name|
|:--|
|qiita-sample-front-route-table|
|qiita-sample-back-route-table|
|qiita-sample-rds-route-table|

##IGWの作成

インターネットゲートウェイ＞インターネットゲートウェイの作成より、以下のインターネットゲートウェイを作成してください。

![igw.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235522/733b61a4-441d-c2a9-be2a-20756a802331.png)

VPCとIGWの関連付けをします。

![vpctoigw.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235522/d22b8d93-683f-44a8-7eab-455808cf69b2.png)

|name|
|:--|
|qiita-sample-igw|

##サブネットとルートテーブルの関連付け

- ルートテーブルの編集タブを選択し、RDS以外のルートテーブルに対し、0.0.0.0に先ほど作ったIGWを関連付け
- サブネット関連付けタブを選択し、以下の表のように関連付け

![ルートテーブルの編集.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235522/48e93aad-10a3-b37e-9255-2e6a23867cb4.png)

|サブネット|ルートテーブル|
|:--|:--|
|qiita-sample-front-subnet-1a|qiita-sample-front-route-table|
|qiita-sample-front-subnet-1c|qiita-sample-front-route-table|
|qiita-sample-back-subnet-1a|qiita-sample-back-route-table|
|qiita-sample-back-subnet-1c|qiita-sample-back-route-table|
|qiita-sample-rds-subnet-1a|qiita-sample-rds-route-table|
|qiita-sample-rds-subnet-1c|qiita-sample-rds-route-table|

##セキュリティグループの作成
下記の表のセキュリティグループを作成

|セキュリティグループ名|タイプ|プロトコル|ポート範囲|ソース|
|:--|:--|:--|:--|:--|
|qiita-sample-front-sg|HTTP|TCP|80|0.0.0.0/0|
|同上|SSH|TCP|22|ご自身のIP|
|同上|HTTPS|TCP|443|0.0.0.0/0|
|同上|カスタム|TCP|3000|0.0.0.0/0|
|qiita-sample-back-sg|カスタム|TCP|3000|0.0.0.0/0|
|同上|HTTPS|TCP|443|0.0.0.0/0|
|qiita-sample-rds-sg|MYSQL/Aurora|TCP|3306|qiita-sample-back-sg|

ネットワークはここまでで終わり。

#Route53の作成（ここから有料です。）

##ドメインの取得
[お名前.com](https://www.onamae.com/)より、ドメインの取得をします。
この記事を参照している方のお好きなドメイン（できれば1円とかのやつ）を取得して下さい。

お名前.comでのドメイン取得方法については、[【ドメイン取得】お名前.comとawsでドメイン取得](https://qiita.com/nakanishi03/items/25278fb4dfad60ebfac4)を参照すると良いです。

**※ドメイン自動更新設定はオフにしておきましょう！（1年ごとにお金かかっちゃう）**

今回、私の環境では、以下のドメインを取得しました。

|ドメイン名|役割|
|:--|:--|
|qiita-sample.work|front|
|qiita-sample-api.work|back|

##ホストゾーンの作成

Route53より、ホストゾーンの作成をします。

![Route53　ホストゾーン.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235522/6c64ccec-b4a0-8284-58e0-25016de388b4.png)

ホストゾーン詳細より、NSレコードの**値/トラフィックのルーティング先**をコピーして下さい。

##お名前.comのネームサーバにNSレコードの値を登録

![Route53 NS設定.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235522/65d66497-b4e8-37fb-c4d4-03627cb96885.png)

反映まで結構時間かかります。（1日とか最大でかかるかも）

```
dig qiita-sample.work(取得したドメイン) +short NS
```
をterminalから打ち、無事登録したNSレコードが表示されていれば反映されています。

##ACMを使ってドメインに証明書を発行する
ACMより、証明書のリクエストをします。
パブリック証明書のリクエストを選択し、証明書をリクエストします。

|ドメイン名|
|:--|
|qiita-sample.work|
|*.qiita-sample.work|

以上のドメイン名を記入し、DNS検証を選択します。
同様に、qiita-sample-api.workにも同様の処理をします。

![ACM request.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235522/34021701-9c92-2078-889a-239b34ff9fb8.png)

すると、検証画面にいくので、そこで**Route53でのレコード作成**をします。（上記のドメイン全てにレコードの作成をして下さい。）
しばらくすると検証保留中から、発行済みステータスとなります。
ずっと検証保留中になるのは、だいたいNSサーバの設定が遅れている可能性があります。
digコマンドで確認してみましょう。

#RDSの作成
Route53・ドメインネームサーバの設定にやや時間がかかるので、ここでRDSをサクッと作成しておきましょう。

###サブネットグループの作成
![RDS サブネットグループ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235522/b4719147-21b3-2b73-4f5a-874a341d5ec5.png)

###RDSの作成・設定

以下の設定を参考に、RDS＞データベースの作成よりRDSの作成をして下さい。

|設定||備考|
|:--|:--|:--|
|エンジンのタイプ|MySQL||
|バージョン|5.7.30|今回は5.7|
|テンプレート|開発/テスト|無料枠でも良いのですが、今回は勉強のため|
|DB インスタンス識別子|qiita-sample-rds|なんでもOK|
|マスターユーザー名|**|何でもOK|
|マスターパスワード|**|何でもOK・流出させないように|
|DB インスタンスクラス情報|db.t2.micro|最安のものにしておきます|
|ストレージタイプ|汎用SSD|
|ストレージ割り当て|20|20が最小です|
|Virtual Private Cloud (VPC)|先ほど作ったVPC|
|サブネットグループ|先ほど作ったサブネットグループ|
|既存の VPC セキュリティグループ|先ほど作ったセキュリティグループ|
|データベース認証|パスワード認証|
|最初のデータベース名|production|
|DB パラメータグループ|default|今回はdefaultにします。（自分で作ってもOK!）|
|オプショングループ|default|今回はdefaultにします。（自分で作ってもOK!）|

しばらくすると利用可能ステータスとなるので、そうしたらRDS作成完了です。

##database.yml、credentials.yml.encの修正

```yml:./back/config/database.yml
production:
  <<:         *default
  host:       <%= Rails.application.credentials.rds[:host] %>
  database:   <%= Rails.application.credentials.rds[:database] %>
  username:   <%= Rails.application.credentials.rds[:username] %>
  password:   <%= Rails.application.credentials.rds[:password] %>
```

```termnal:./
docker-compose run -e EDITOR="vi" back rails credentials:edit 
```
すると、vim画面が現れるので、以下の設定を記入して下さい。

```
rds:
  host: {エンドポイント}
  database: {databaseの名前（初期で作ったもの）}
  username: {設定したユーザーネーム}
  password: {設定したパスワード}
```

#ロードバランサーの作成

EC2>ロードバランサーの作成より、ロードバランサーを作成しましょう。

##ロードバランサーの設定
**front**
![ロードバランサー の設定.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235522/4a7b95ee-dcbf-4bd5-a18a-f96d7abdb685.png)

**back**
![ロードバランサー の設定back.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235522/865c4e6b-9288-3c14-2b69-5c519604f7d0.png)
アベイラビリティーゾーンには、先ほど作ったVPCとサブネットを登録しましょう。

##セキュリティ設定の構成

![ロードバランサー  セキュリティ設定の構成.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235522/27a120de-e47b-07ad-78c6-502022dbfcd2.png)

先ほど作った証明書を選択しましょう。
まだ認証されていなかったら、少し待ってね。

##セキュリティグループの設定

先ほど作ったセキュリティーグループをアタッチして下さい。

##ルーティングの設定

新しくターゲットグループを作成します。
今回frontでは、ECSで生成されるEC2内で**host mode**で起動するため
ターゲットをインスタンスに設定し、ポートを3000で受け付けるようにします。
backでは、ECSのインスタンス内のコンテナで**awsvpc mode**で起動するため、ターゲットをipにしています。これは、コンテナのプライベートIPを指します。

**front**
![ロードバランサー ルーティング.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235522/6660b768-8bc6-335b-90e7-468fb60601b2.png)

**back**
![ロードバランサー ルーティングback.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235522/8498fa83-2025-6ed6-4604-bbeb55bf1a21.png)

##ターゲットの登録
後述しますが、ECSのサービス起動時に自動的にターゲットが選択されるため、心配ご無用です。
そのままロードバランサーを作成しましょう！

##Route53にAレコードを追加
Route53＞ホストゾーンより、Aレコードの追加をし、以下の設定にしてください。
frontとback、同様に処理して下さい。

![ALB Route53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235522/420d2b59-aa6b-4c62-cb53-5db03e4ff2dd.png)


#ECRにリポジトリを作成・イメージのプッシュ

##frontのAPIの向きを本番用に変えておきましょう。
```js:./front/plugins/axios.js
import axios from "axios"

export default axios.create({
  //baseURL: "http://localhost:3000"
  baseURL: "https://www.qiita-sample-api.work"
})
```
**※aws cliを使えるようにしておきましょう！**
[macOS に AWS CLI バージョン 1 をインストールする](https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/install-macos.html#awscli-install-osx-path)
[AWS CLIのインストール](https://qiita.com/yuyj109/items/3163a84480da4c8f402c)

Amazon ECRより、リポジトリを作成。リポジトリ名を記入してそのまま作成して下さい。
リポジトリを選択し、プッシュコマンドを押すとリポジトリにプッシュするためのコマンドが表示されるため、frontとback共にリポジトリを作成してローカル環境で実行して下さい。

これらのコマンドを実行すると、ディレクトリ上にあるDockerfileを元にイメージを作成します。
ローカル環境でdocker-compose buildをした時と同じ状態になります。
ここで作成し、ECRにプッシュしたイメージを使って、ECS上で読み込み、起動させます。

ここで注意したいのが、**frontとback、それぞれどちらのリポジトリにプッシュするのか確認した上でDockerfileがあるディレクトリに移動してから実行して下さい。**
![ecr画面.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235522/c6f19321-d26b-1f60-ddc8-ca66ebce134d.png)

#ECSの作成・設定
##全体像
ECSでは、
`クラスター>サービス>タスク`
という粒度になっています。
クラスターは論理的な枠組み、箱です。
サービスはクラスター上で起動するオートスケーリング＋ALBのような存在です。
タスクの数を自動で管理したり、ネットワーキングの設定をします。
タスクはDockerイメージを実行する単位だと考えて下さい。

サービスは、タスク定義を元にタスクを生成していきます。
無理やり例えると、たい焼きの鋳型がサービス、たい焼き自体がタスクのような感じです。
クラスタはたい焼き屋さんの屋台。
![building_taiyaki.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235522/393777d9-2557-0b30-21a2-b6297cd88e20.png)


##ECSクラスターの作成

![ECS作成1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235522/b066f77c-15a7-bc44-b3e8-322cea594553.png)

今回はEC2 Linux+ネットワーキングを選択

![ECS作成2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235522/31b042db-827e-50b5-9da6-59a690aaacb8.png)

ネットワーキングは先ほど作成したVPC・サブネット（front）を選択します。
また、Auto assign public IPは、Enabledを選択。
ssh接続出来るよう、キーペアを設定しておきましょう。

[Amazon EC2 キーペアと Linux インスタンス](https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/ec2-key-pairs.html)

Cluster作成後、以下のようにEC2インスタンスが起動されたら成功です。
うまいことEC2インスタンスが紐づけされない場合、セキュリティグループやルートテーブルが問題な事が多いです。
![ECS作成3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235522/c6fee6f7-7d7e-fa8b-79df-6cb95cdff959.png)

##タスク定義の作成
ECSより、新しいタスク定義の作成を選択

EC2を選択
![ECS タスク定義2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235522/574d7a0e-a157-fb8a-6dbe-c8c5c7af5321.png)

**front**

|設定||備考|
|:--|:--|:--|
|タスク定義名|qiita-sample-front|何でもOK|
|タスクロール||ECSを実行する権限をもつロール|
|ネットワークモード|ホスト||
|タスク実行ロール||ECSを実行する権限をもつロール|
|タスクメモリ (MiB)|700|インスタンスのサイズ内で納めること|
|タスク CPU (単位)|256|インスタンスのサイズ内で納めること|

![ECS タスク定義1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235522/cb9d7b5b-e389-a4ce-6fd5-0ffe2a291e68.png)

**back**

|設定||備考|
|:--|:--|:--|
|タスク定義名|qiita-sample-back|何でもOK|
|タスクロール||ECSを実行する権限をもつロール|
|ネットワークモード|aws-vpc||
|タスク実行ロール||ECSを実行する権限をもつロール|
|タスクメモリ (MiB)|400|インスタンスのサイズ内で納めること|
|タスク CPU (単位)|128|インスタンスのサイズ内で納めること|

![ECS タスク定義3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235522/cf731b68-478b-8962-ce54-db663ad35906.png)

##サービスの作成
クラスターのサービスタグより、サービスの作成をします。

**front**

|設定||備考|
|:--|:--|:--|
|起動タイプ|EC2||
|タスク定義|先ほど作成したもの||
|サービス名|qiita-front|何でもOK|
|サービスタイプ|REPLICA||
|タスクの数|1|1~以上ならなんでもOK|

ロードバランシングはApplication Load Balancerを選択し、先ほど作成したALBとターゲットを選択

**back**

|設定||備考|
|:--|:--|:--|
|起動タイプ|EC2||
|タスク定義|先ほど作成したもの||
|サービス名|qiita-back|何でもOK|
|サービスタイプ|REPLICA||
|タスクの数|1|1~以上ならなんでもOK|

ネットワーク構成では、先ほど作成したVPCとbackのサブネット・セキュリティグループを選択
ロードバランシングはApplication Load Balancerを選択し、先ほど作成したALBとターゲットを選択

##ECSサービスの起動
上記のように作成されると、自動的にタスクが一つ追加されているはずです。
以下のようになっていればOK!無事イメージからEC2内部でコンテナが起動されているはずです。
![ECS タスク.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235522/16a32a7b-b458-ae3d-7663-484a8acc1a70.png)

#db:migrate
タスク定義を作ってタスクを実行しても良いのですが、今回はEC2インスタンスの中にssh接続し、その上でdockerコンテナ内に入り込み`rails db:migrate`してみます。
本来DBを作成しなければならないのですが（db:create）、今回はRDSを作成時に初期のDBにproductionと設定し、作成済みのため、必要ありません。

[pemファイルDLしといてね](https://qiita.com/Shogo1222/items/dcbc1e50f6fc83f48b44#ecs%E3%82%AF%E3%83%A9%E3%82%B9%E3%82%BF%E3%83%BC%E3%81%AE%E4%BD%9C%E6%88%90)

```terminal
ssh -i {pemファイルのパス} ec2-user@{インスタンスのpublicIP}


   __|  __|  __|
   _|  (   \__ \   Amazon Linux 2 (ECS Optimized)
 ____|\___|____/

For documentation, visit http://aws.amazon.com/documentation/ecs
```

```terminal:EC2インスタンス内
docker ps
```
で、現在起動しているコンテナが表示されます。
railsを起動しているコンテナのCONTAINER IDを指定して、以下コマンドを打つと、Dockerコンテナ内部に入れます。

```terminal:EC2インスタンス内
docker exec -it {CONTAINER ID} sh
```

ここで、環境を指定してdb:migrateしましょう！

```terminal:Dockerコンテナ内
rails db:migrate RAILS_ENV=production
```

#起動確認
長々とお疲れ様でした！

ttps://www.{自分で設定したfrontのドメイン}/users
にアクセスし、以下の画面のようになり、ユーザーをローカル同様登録できればOKです。
ttps://www.{自分で設定したbackのドメイン}/users
にアクセスし、登録したユーザーのjsonが返ってきていればOKです。

![qiita-sample画像.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235522/c24abcf6-c3eb-c222-aa33-fa9fcf43d6d7.png)

#CircleCIでの自動デプロイ
ラスト、CircleCIでmasterにpushしたら、自動的にタスク定義を更新し、ECSのサービスを更新、タスクをアップデート出来るようにしていきましょう。

##全体の流れ
```
ローカルからgithubへpush
→githubからCircleCI起動
→CircleCI上でdocker buildを実行
→CircleCI上でECRにイメージをpush
→ECSのサービスを更新・・・
```
となります。
###豆知識
ローカルでbuildする時との違いは、環境変数の有無です。

CircleCIで実行されたbuildは、**gitリポジトリのソースコードを参照**しており、さらに**仮想マシンの中で実行**されています。
Railsの実行に必要な環境変数がgitリポジトリ上に存在しない場合は環境変数を参照出来ずに失敗します。
ですが、CircleCIには仮想マシン上に環境変数を設定出来ます。

##環境変数の設定
Project Settings>Environment Variables

![circleci environments.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235522/b4da5d85-3c3a-a1d3-0e5d-3ffcb43f274e.png)

```
AWS_ACCESS_KEY_ID // AWSのアクセスキー
AWS_SECRET_ACCESS_KEY // AWSのシークレットアクセスキー
AWS_ACCOUNT_ID //以下URL参照
AWS_ECR_ACCOUNT_URL // ECRのURL 例:{アカウントID}.dkr.ecr.{リージョン}.amazonaws.com
AWS_REGION // リージョン

RAILS_MASTER_KEY // ./back/config/以下にあるmaster.keyの値
AWS_RESOURCE_NAME_PREFIX // 自身で作成したサービスのprefix（今回だとqiita-sample）
CLUSTER_NAME // 先ほど作成したClusterの名称
REPO_NAME_FRONT // frontのECRリポジトリ名称
REPO_NAME_BACK // backのECRリポジトリ名称
FAMILY_NAME_FRONT // frontのタスク定義の名称 
FAMILY_NAME_BACK // backのタスク定義の名称
SERVICE_NAME_FRONT // 先ほど作成したfrontのECSサービスの名称
SERVICE_NAME_BACK // 先ほど作成したfrontのECSサービスの名称
```

[AWS アカウント ID の確認](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/console_account-alias.html#FindingYourAWSId)

##config.ymlの設定

```yml:./front/.circleci/config.yml
version:                                  2.1
orbs:
  aws-ecr:                                circleci/aws-ecr@6.2.0
  aws-ecs:                                circleci/aws-ecs@1.2.0

# 実行するjob
jobs:
  # buildするjob
  build:
    machine:
      image:                              circleci/classic:edge
    steps:
      - checkout
      - run:
          name:                           docker-compose build
          command:                        docker-compose build
  # testするjob
  test:
    machine:
      image:                              circleci/classic:edge
    steps:
      - checkout
      - run:
          name:                           docker-compose up -d
          command:                        docker-compose up -d
      - run:                              sleep 30
      - run:
          name:                           docker-compose run back rails db:create RAILS_ENV=test
          command:                        docker-compose run back rails db:create RAILS_ENV=test
      - run:
          name:                           docker-compose run back rails db:migrate RAILS_ENV=test
          command:                        docker-compose run back rails db:migrate RAILS_ENV=test
      - run:
          name:                           docker-compose run back bundle exec rspec spec
          command:                        docker-compose run back bundle exec rspec spec
      - run:
          name:                           docker-compose down
          command:                        docker-compose down

# 順番を制御するworkflow
workflows:
  build_and_test_and_deploy:
    jobs:
      - build
      - test:
          requires:
            - build
      - aws-ecr/build-and-push-image:
            name:                         'build-and-push-back'
            account-url:                  AWS_ECR_ACCOUNT_URL
            region:                       AWS_REGION
            repo:                         ${REPO_NAME_BACK}
            tag:                          "${CIRCLE_SHA1}"
            path:                         './back'
            dockerfile:                   back/Dockerfile.pro
            extra-build-args:             '--build-arg RAILS_MASTER_KEY=$RAILS_MASTER_KEY'
            requires:
              - test
            filters:
              branches:
                only:
                  - master
      - aws-ecr/build-and-push-image:
            name:                         'build-and-push-front'
            account-url:                  AWS_ECR_ACCOUNT_URL
            region:                       AWS_REGION
            repo:                         ${REPO_NAME_FRONT}
            tag:                          "${CIRCLE_SHA1}"
            path:                         './front'
            dockerfile:                   front/Dockerfile.pro
            requires:
              - test
            filters:
              branches:
                only:
                  - master
      - aws-ecs/deploy-service-update:
            family:                       ${FAMILY_NAME_BACK}
            service-name:                 ${SERVICE_NAME_BACK}
            cluster-name:                 ${CLUSTER_NAME}
            #"container="はTask Definitionで設定したコンテナ名になるので、注意してください。
            container-image-name-updates: 'container=${AWS_RESOURCE_NAME_PREFIX}-back,image-and-tag=${AWS_ECR_ACCOUNT_URL}/${REPO_NAME_BACK}:${CIRCLE_SHA1}'
            requires:
              - build-and-push-back
            filters:
              branches:
                only:
                  - master
      - aws-ecs/deploy-service-update:
            family:                       ${FAMILY_NAME_FRONT}
            service-name:                 ${SERVICE_NAME_FRONT}
            cluster-name:                 ${CLUSTER_NAME}
　　　　　　　 #"container="はTask Definitionで設定したコンテナ名になるので、注意してください。
            container-image-name-updates: 'container=${AWS_RESOURCE_NAME_PREFIX}-front,image-and-tag=${AWS_ECR_ACCOUNT_URL}/${REPO_NAME_FRONT}:${CIRCLE_SHA1}'
            requires:
              - build-and-push-front
            filters:
              branches:
                only:
                  - master

```

##本番用のDockerfileを作成

```dockerfile:./back.Dockerfile.pro
# イメージの指定
FROM ruby:2.6.3-alpine3.10

# 必要パッケージのダウンロード
ENV RUNTIME_PACKAGES="linux-headers libxml2-dev make gcc libc-dev nodejs tzdata mysql-dev mysql-client yarn" \
    DEV_PACKAGES="build-base curl-dev" \
    HOME="/app" \
    LANG=C.UTF-8 \
    TZ=Asia/Tokyo

# 作業ディレクトリに移動
WORKDIR ${HOME}

# ホスト（自分のパソコンにあるファイル）から必要ファイルをDocker上にコピー
ADD Gemfile ${HOME}/Gemfile
ADD Gemfile.lock ${HOME}/Gemfile.lock

RUN apk update && \
    apk upgrade && \
    apk add --update --no-cache ${RUNTIME_PACKAGES} && \
    apk add --update --virtual build-dependencies --no-cache ${DEV_PACKAGES} && \
    bundle install -j4 && \
    apk del build-dependencies && \
    rm -rf /usr/local/bundle/cache/* \
    /usr/local/share/.cache/* \
    /var/cache/* \
    /tmp/* \
    /usr/lib/mysqld* \
    /usr/bin/mysql*

# ホスト（自分のパソコンにあるファイル）から必要ファイルをDocker上にコピー
ADD . ${HOME}

# ポート3000番をあける
EXPOSE 3000

# コマンドを実行
CMD ["bundle", "exec", "rails", "s", "puma", "-b", "0.0.0.0", "-p", "3000", "-e", "production"]
```

```dockerfile:./front/Dockerfile.pro
FROM node:12.5.0-alpine

ENV HOME="/app" \
    LANG=C.UTF-8 \
    TZ=Asia/Tokyo

ENV HOST 0.0.0.0

WORKDIR ${HOME}
COPY package.json .
COPY . .

RUN apk update && \
    apk upgrade && \
    npm install -g n && \
    yarn install &&\
    rm -rf /var/cache/apk/*

RUN yarn run build
EXPOSE 3000
CMD ["yarn", "start"]
```

```yml:./docker-compose.yml
version:                "3"

services:
  db:
    image:              mysql:5.7
    env_file:
      - ./back/environments/db.env
    restart:            always
    volumes:
      - db-data:/var/lib/mysql:cached

  back:
    build:              back/
    # rm -f tmp/pids/server.pidしとくとrailsのサーバ消し損ねたときに便利
    command:            /bin/sh -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
    env_file:
      - ./back/environments/db.env
    environment: # 追加
      RAILS_MASTER_KEY: ${RAILS_MASTER_KEY}
    volumes:
      - ./back:/app:cached
    depends_on:
      - db
    # ホストコンピュータのポート：Docker内のポート
    ports:
      - 3000:3000

  front:
    build:              front/
    command:            yarn run dev
    volumes:
      - ./front:/app:cached
    ports:
      # ホストコンピュータのポート：Docker内のポート
      - 8080:3000
    depends_on:
      - back
volumes:
  public-data:
  tmp-data:
  log-data:
  db-data:

```
##デプロイ
お疲れ様でした！
以下のように、全ての工程がSuccessしたら無事にデプロイが完了しています。
![circleci success deploy.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235522/570705b4-8298-0ac8-70b1-09713a60fc05.png)

#参考
VPC、AWSのネットワークなどを学びたい方
[動画で学ぶAWS講座 VPC編 【section1】 リージョン, アベイラビリティゾーン, サブネット](https://www.youtube.com/watch?v=aQpMBqn5mRY&list=PLtpYHR4V8Mg-hNPfIpCToq3ZLhvXMHFel)

Dockerのネットワーク、ECSについて深く知りたい方
[今から追いつくDocker講座！AWS ECSとFargateで目指せコンテナマスター！〜シリーズ1回目〜](https://www.youtube.com/watch?v=DS5HBTMG1RI&list=PLtpYHR4V8Mg-jbuk4yoXhXwJtreodnvzg)

動画で学べるなんて、本当に便利な時代ですね・・・。30回くらい繰り返してみました。

#最後に
お疲れ様でした。
これでNuxtとRailsを、ECS・ECR・CircleCIを使って本番環境にデプロイする事ができました。
これは一例であり、他にも良い構成があると思います。
ですが、一度構築・デバッグしてしまえば、あとはやり方が自分なりにわかってくると思います。（私はまだまだですが
もしかしたら、この記事でタイポや間違った部分があるかもしれません。
そういった際は、優しくマサカリを投げていただけると有り難いです。

次回はterraform化まで書けたらいいな。