インフラおじさんのはじめてののAmazon ECS（1）

# はじめに

この記事は JSL (日本システム技研) Advent Calendar 2022 - Qiita 20日目の記事です。

毎年恒例、一人アニバーサリー記念です。:tada:
今後、birthday駆動開発（BDD）と呼びます😅

最近はインフラおじさんになっているので、何を書くべきか悩みましたがIaaSでEC2/Lightsailを選択することが多くなってきたものの、[Amazon ECS](https://aws.amazon.com/jp/ecs/)も今後の候補に入れたいなと思いこれを期に触ってみることにしました。


# Amazon ECS とは
Amazon ECS は、コンテナ化されたアプリケーションを簡単にデプロイ、管理ができるサービスです。

https://aws.amazon.com/jp/ecs/

    
# 今回のゴール
+ Nginx + gunicorn環境のDjangoをデプロイします。
+ 今回は[こちら](https://anikitech.com/amazon-ecsdjangonginx-part1/)の記事を参考にしながら進めてます。

# 作業環境
* M1 Mac（macOS 12.6）

# IAMロールの設定
## IAMロールの作成
ECSタスク実行用のIAMロール作成します。

- ユースケース：`Elastic Container Service Task`(他の AWS のサービスのユースケースから「`Elastic Container Service`」選択)

- 許可ポリシー：`AmazonECSTaskExecutionRolePolicy`

- タスク名：`TaskExecution-ECSTaskRole`
```
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ecr:GetAuthorizationToken",
                "ecr:BatchCheckLayerAvailability",
                "ecr:GetDownloadUrlForLayer",
                "ecr:BatchGetImage",
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": "*"
        }
    ]
}

・ECSのコンテナイメージはECR(Elastic Container Registry)に格納されるため、ECRへのアクセス権限を付与
・このポリシーによりCloudWatch Logsにログ出力が可能となります（コンテナではローカルでログ保存できないたため）。

```

### ECSタスクについて

そもそもECSタスクってなんぞやと思い調べました。

https://blog.serverworks.co.jp/amazon-elastic-container-service

* タスク ＝ コンテナ
* タスク定義 = 何のタスクを動かすか。例:Nginx、MySql etc 
* docker-compose的な理解で良さそう

## ECSタスク用のIAMロール

コンテナ自体に必要なロールを設定する。
コンテナからS3等のAWSサービスにアクセスする必要がある場合はこれに追加する

- ユースケース：`Elastic Container Service`(他の AWS のサービスのユースケースから「`Elastic Container Service`」選択)

- 許可ポリシー：`AmazonEC2ContainerServiceRole`

- タスク名：`Test-ECSTaskRole`

```
{
    "Version": "2008-10-17",
    "Statement": [
        {
            "Sid": "",
            "Effect": "Allow",
            "Principal": {
                "Service": "ecs.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}
```

# ネットワークの作成

## 想定するネットワーク

![VPC.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20512/56975c73-b9e7-9dbc-146f-1a05dda6025a.png)


## VPCの作成

以下のように作成します

![Screen Shot 2022-12-16 at 11.22.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20512/e2da189f-6a79-2e96-0b70-1954d6455645.png)

- VPCを作成した後に、作成したVPCを選択し、「アクション」から「VPCの設定を編集」を選択
- [DNS ホスト名を有効化」にチェックして保存

## サブネットの作成

以下のように作成します

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20512/279bb54a-8e24-757d-984b-93ed0b764294.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20512/da599db6-408c-3626-0bec-8585996607e6.png)

![Screen Shot 2022-12-16 at 17.34.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20512/2f5e52b1-ed9f-2b8e-763e-fee9b6b51397.png)
![Screen Shot 2022-12-16 at 17.36.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20512/6f1592a9-bbc2-4e24-dac7-a3900ae86b6d.png)

## インターネットゲートウェイ（IGW）の作成

以下のように作成します

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20512/6d7f165d-0625-5ed7-2748-bf402cd60b0b.png)

次にVPCにIGWをアタッチします。
「アクション」->「VPCにアタッチ」で作成したVPCとアタッチします。

# ルートテーブルの作成

外部公開用サブネットからインターネットに接続できるように設定します。
以下のように作成したVPCを選択後に作成します。

![Screen Shot 2022-12-16 at 20.01.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20512/7fe5bb10-675c-c1d2-44cb-1197690d8042.png)

作成したルートテーブルの「ルートの編集」から「ルートの追加」をクリックし
以下のように設定します。ターゲットは先程作成したIGWを選択します。

![68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f32303531322f63333133323534362d306339362d303634622d323766642d3864633161663236316537362e706e67.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20512/321d0945-f5ac-d064-c351-dad23e0cdad4.png)

次の設定したルートテーブルを外部公開用サブネットに紐付けします。
を外部公開用サブネット(名前がPUBLIC)のサブネットを選択して
「ルートテーブル」タブから「ルートテーブルの関連付けを編集」をクリックして
「ルートテーブルID」を先程で作成したルートテーブルを選択して関連付けをします。

![Screen Shot 2023-01-19 at 15.05.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20512/ee2e0c18-5710-0bd1-230b-53147c33a342.png)


# VPCエンドポイントの設定

VPCエンドポイントの設定をします。
内部公開用サブネットからECSタスクを起動する際に必要な設定のようですが
VPCエンドポイントが分からなかったため調べました。

インターネットに接続せずにAWSサービスを使用するための仕様です。
https://qiita.com/miyuki_samitani/items/9d9f7a0c417cb75a6c85

エンドポイントには、Interface型とGateway型（S3とDynamoDB）があります。
https://qiita.com/leomaro7/items/cbe8f6e3ff41805c1b49

Interface型はそれぞれ内部公開用サブネット用のIPが設定されますが
Gateway型は設定されないので、ルートテーブルの設定が必要です。

今回、S3はGateway型、その他はInterface型で設定します。


今回設定するエンドポイントは以下です。

- ecr.dkr
- ecr.api
- s3
- logs

「VPCダッシュボード」から「エンドポイント」を選択し設定します。

|サービスカテゴリ	|AWSサービス|
|---|---|
|サービス名|	com.amazonaws.ap-northeast-1.ecr.dkr|
|VPC	|Test-vpc（先ほど作成したVPC）|
|vap-northeast-1a(apne1-az4)|Test-subnet-az1a-private（先ほど作成したプライベート用のサブネット1つ目）|
|ap-northeast-1c(apne1-az1)	|Test-subnet-az1c-private（先ほど作成したプライベート用のサブネット2つ目）|
|プライベートDNS名を有効にする|	このエンドポイントで有効にする|
|セキュリティグループ|	（デフォルトのセキュリティグループのまま）|
|ポリシー|	フルアクセス|

|サービスカテゴリ	|AWSサービス|
|---|---|
|サービス名|	com.amazonaws.ap-northeast-1.ecr.api|
|VPC|	Test-vpc（先ほど作成したVPC）|
|ap-northeast-1a(apne1-az4)	|Test-subnet-az1a-private（先ほど作成したプライベート用|のサブネット1つ目）
|ap-northeast-1c(apne1-az1)	|Test-subnet-az1c-private（先ほど作成したプライベート用のサブネット2つ目）
|プライベートDNS名を有効にする|	このエンドポイントで有効にする
セキュリティグループ|	（デフォルトのセキュリティグループのまま）
|ポリシー|	フルアクセス


|サービスカテゴリ	|AWSサービス|
|---|---|
|サービス名|	com.amazonaws.ap-northeast-1.s3（タイプ：Gateway）|
|VPC|	Test-vpc（先ほど作成したVPC）|
|ルートテーブルの設定|	（プライベート用のサブネットのルート）|
|セキュリティグループ|	（デフォルトのセキュリティグループのまま）|
|ポリシー|	フルアクセス|

|サービスカテゴリ	|AWSサービス|
|---|---|
|サービス名|	com.amazonaws.ap-northeast-1.logs
|VPC|	Test-vpc（先ほど作成したVPC）
|ap-northeast-1a(apne1-az4)|	Test-subnet-az1a-private（先ほど作成したプライベート用のサブネット1つ目）
|ap-northeast-1c(apne1-az1)|	Test-subnet-az1c-private（先ほど作成したプライベート用のサブネット2つ目）
|プライベートDNS名を有効にする|	このエンドポイントで有効にする
|セキュリティグループ|	（デフォルトのセキュリティグループのまま）
|ポリシー|	フルアクセス


最後にS3以外のエンドポイントがプライベート用のサブネットに割り当てられていることを確認します

# 内部公開用サブネットのルートテーブルの設定

内部公開用サブネットのルートテーブルを作成します。

![Screen Shot 2022-12-18 at 16.06.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20512/ba7fade1-b193-2823-be38-b391709347a6.png)

作成したルートテーブルをS3用エンドポイント（ECR-TEST-EP03）に設定します。「ルートテーブル」タブから「ルートテーブルを管理」をクリックして
作成した内部公開用サブネットのルートテーブルを選択して関連付けをします。

![Screen Shot 2022-12-18 at 16.14.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20512/2a803627-963b-5045-515e-825cfacc4546.png)

# ECR用セキュリティグループの更新

セキュリティグループからVPC（ECR-TEST-VPC）用のセキュリティグループを選択します。

![Screen Shot 2022-12-19 at 22.26.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20512/bfebd39a-a3c9-0059-527c-cf4e89345fed.png)

「インバウンドのルールを編集」でカスタムから既存のデフォルトのセキュリティグループを☓マークをクリックして削除し、代わりに`0.0.0.0/0`を設定します。
タイプは、「すべてのトラフィック」を選択します。
理由は調べきれていないのですが、ECSからECRのイメージのpullが失敗するからのようです（これは別途解決策を検討したいです）。

![Screen Shot 2022-12-19 at 22.30.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20512/075e3dc1-5300-1bae-36b1-1e9d59200062.png)

`「既存の 参照先のグループ ID ルールに 1 つの IPv4 CIDR を指定することはできません」`というエラーになってしまいました。

https://qiita.com/himorishuhei/items/7426cab6cd83c3d8e4e3

一旦ルールを削除して、再度作成すれば良いようです。

![Screen Shot 2023-01-03 at 12.15.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20512/206b4b09-f6fa-2581-4df2-fe11e66c5d6a.png)


# （1）の感想
* (1)はECS環境を構築するためのネットワーク設定が主となりました。VPCエンドポントを使用したAWSサービス連携はNAT Gatewayを使用せず接続できることが分かりました。
* ECSからECRのイメージのpullが失敗するためにセキュリティグループの全ての通信を許可してしまいましたが、他に良い方法がありそうなので今後探したいです。
* [(2)](https://qiita.com/katekichi/items/36aac89f16ecd6d66414)に続きます。