ECSのバックエンドをEC2からFargateに変更

## 概要

ECS+EC2で動作しているアプリケーションをFargateに変更にします。
ECS+EC2での環境構築は、[LaravelアプリケーションをAWS上のDockerで動かす](https://qiita.com/kobayashi-m42/private/95ba3f611b3def27241e#dockerignore%E3%81%AE%E8%BF%BD%E5%8A%A0)を参考にしてください。

全てマネジメントコンソール上で行います。

## 前提条件

- [LaravelアプリケーションをAWS上のDockerで動かす](https://qiita.com/kobayashi-m42/private/95ba3f611b3def27241e#dockerignore%E3%81%AE%E8%BF%BD%E5%8A%A0)と同様のECS+EC2の環境が構築されていること

## Farageteについて

### メリット
- コンテナのためのEC2の管理が不要
  - コンテナのスケールに合わせてインスタンスタイプの変更、あるいはスケールアウトさせる必要がない
  - インスタンスタイプの選択が不要
- AutoScaleのタイミング設定が不要

### デメリット
- コンテナホストにssh接続できない
 - Dockerコマンドが実行できないため、コンテナの状況を確認するといったことができない
- ログドライバがawslogsのみ
 
### 料金
EC2単位ではなく、コンテナ単位で料金が発生します。
下記の記事に最新の料金が記載されていましたので、参考にしてみてください。
[2019年1月にAWS Fargateが大幅値下げしたのでEC2との価格比を確認してみた ｜ DevelopersIO](https://dev.classmethod.jp/cloud/aws/compare-fees-for-fargat-and-ec2-2019-1/)

## nginxの設定ファイルを修正
Fargateでは、コンテナ間通信の宛先は`127.0.0.1`になります。
nginxの設定ファイルの一部を下記の通り変更します。

```:default.conf
# 変更前
fastcgi_pass php-fpm:9000;
# 変更後
fastcgi_pass 127.0.0.1:9000;
```

## IAMロールを作成
ECSタスク実行ロールを作成します。
手順は、下記の`ecsTaskExecutionRole IAM ロールを作成するには`に従って作成してください。
[Amazon ECS タスク実行 IAM ロール - Amazon Elastic Container Service](https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/task_execution_IAM_role.html)

なお、タスク定義のリビジョンの作成前にECSタスク実行ロールが作成されている必要があります。新しいリビジョンの作成の途中でロールを作成した場合、ECSタスク実行ロールを選択することができず、エラーとなります。

## ALBのターゲットグループを作成
ALBのターゲットグループのターゲットの種類は`ip`である必要があります。
既存のEC2のALBのターゲットグループがターゲットの種類が`instance`となっている場合、新規で作成する必要があります。

## タスクの定義のリビジョンを作成する

`Amazon ECS`  > `タスク定義` から作成済みのタスクを選択し、`新しいリビジョンの作成`を選択します。

以下の項目を変更します。

| 項目 | 値 |
| --- | --- |
| ネットワークモード | awsvpc |
| 互換性が必要 | FARGATE |

Fargateの場合、ネットワークモードは`awsvpc`を設定する必要があります。
`nginxの設定ファイルを修正`で、コンテナ間通信の宛先を`127.0.0.1`に変更した理由もここにあります。

**タスクサイズ**

| 項目 | 値 |
| --- | --- |
| タスクメモリ (GB) | 0.5GB |
| タスクメモリ (GB) | 0.25vCPU |

EC2の場合、設定はオプション項目となっていますが、FARGATEを選択した場合は必須となります。

**コンテナの定義**
コンテナのネットワーク設定でリンクを設定している場合、awsvpcではサポートされていないため変更する必要があります。今回は、EC2で動作している際に、nginxコンテナのネットワーク設定のリンクの設定を削除します。

また、ここではログの設定も行います。
コンテナでの運用の場合、コンテナを落とした際にログが消えてしまうため、ログを外部に出力する必要があります。
今回は、`awslogs`ログドライバーを利用して、標準出力に出力されたログをCloudWatchに送信します。

`ストレージとログ`のログ設定でログの出力先を設定します。
`Auto-configure CloudWatch Logs`にチェックを入れると、自動でロググループが作成されます。以上でログの設定は完了です。CloudWatchからログを確認することが可能となりました。

<img width="642" alt="スクリーンショット 2019-03-02 15.15.24.png" src="https://qiita-image-store.s3.amazonaws.com/0/206741/93e24978-d563-df44-0587-86e3472b80a8.png">


そのほかの設定の変更がなければ`作成`を押下して、新しいリビジョンの作成は完了です。

## サービスを作成する

`Amazon ECS`  > `クラスター` から作成済みのクラスタを選択し、サービスタブの`作成`を押下します。

| 項目 | 値 |
| --- | --- |
| 起動タイプ | FARGATE |
| タスク定義 | 上記で作成した新しいリビジョンを選択|
| プラットフォームのバージョン | LATEST|
| クラスタ | sample-api-cluster |
| サービス名 | sample-fargate-api |
| サービスタイプ | REPLICA |
| タスクの数 | 1 |

**Elastic Load Balancing（オプション）**

| 項目 | 値 |
| --- | --- |
| ELB タイプ | Application Load Balancer |
| サービス用の IAM ロールの選択 | 自動で生成されるAWSServiceRoleForECSサービスリンクロールが使用される | 
| ELB 名 | 作成したALBを選択 |

**負荷分散用のコンテナ**

`nginx:80:80`を指定し、`ELBへの追加`を選択。
ターゲットグループ名に、`作成したALBに紐づくターゲットグループを選択`を選択。

以上で、ECSの環境構築は完了です。

## 動作確認
作成したALBの`DNS名`にアクセスし、動作確認を行います。

## 余談
Laravelのログを標準出力する際に、以下の余分なメッセージが出力される問題にぶつかりました。

```
WARNING: [pool www] child 10 said into stdout
```

原因と対応方法は、https://github.com/docker-library/php/issues/207 に記載されています。
また、PHP7.3ではこの問題が解消しており、 Dockerイメージ`FROM php:7.3.2-fpm-alpine`では余分なメッセージが出力されていないことを確認できています。

## 参考記事
参考にさせていただきました。ありがとうございます。
[AWS FargateでElixirのコンテンツ配信システムを動かしてみた (実装編) - エムスリーテックブログ](https://www.m3tech.blog/entry/elixir-fargate-impl-1)
[AWSにおける本番環境を想定したCI/CD実践 - y-ohgi's blog](https://y-ohgi.hatenablog.com/entry/2018/12/26/024738)
[AWS Fargate のすヽめ | 開発ブログ | Elastic Infra](https://elastic-infra.com/blog/aws-fargate-intro/)