【AWS】ECS on FargateでWebサイトを構築してみた

# はじめに
今まではEC2にApacheなどをインストールしてWebサイトを構築し運用してきましたが、この度ECS on FargateでWebサイトを運用していきたいよねという話がちらほら出てきたので、実際にFargateでWebサイトを構築した際の内容や躓いた点をまとめます。

まず、ECSについて私自身知っていることとして、
+ 仮想環境
+ EC2よりお手軽で簡単

くらいで本当にコンテナ初心者です。
そのため、この記事ではFargateを使ってWebサイトを構築することを目標としています。

# 参考にしたECSやコンテナについて書かれている記事や入門書

+ ECSやコンテナについて書かれている記事

https://qiita.com/K5K/items/0d8dbdb39fbb0375e2bd

https://qiita.com/tatsuya11bbs/items/a7898275535a766718bf

+ ECSの設定について書かれている記事

https://blog.serverworks.co.jp/2023/10/24/104221

+ ECSの入門書

https://www.sbcr.jp/product/4815607654/


# 構築イメージ図
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2784723/5aaaf8b7-9705-1e3f-0eca-43c470f0be13.png)

クラスメソッド様の[記事](https://dev.classmethod.jp/articles/security-for-ecs-on-fargate-secjawsdays/)を参考に設計構築する際に考慮した方がよい箇所をまとめました。

<details><summary>ECS設計構築時の考慮点をざっとまとまています。</summary>

**IAM部分**
+ アカウント分離/認証/権限管理

**ネットワーク部分**
+ EC2などのインフラ構築と同様な部分とコンテナ特定の部分を意識して設計
+ AWSのセキュリティ関連サービスで設定変更検知を実施しミスを防止

**レジストリ部分**
+ ECRへアクセスできるユーザの権限を制限
+ レジストリ内の古いイメージの削除
+ イミュータブルなタグ名のみ許可

**イメージ部分**
+ イメージスキャン（Inspector V2）を利用する
+ CI/CDにイメージスキャンを組み込み、CD経由のみデプロイ
+ Dockerfileの脆弱性をdockleやTrivyなどでチェック
+ 機密情報はSecretManager/SSM ParameterStoreに保存

**Container部分**
+ ルートファイルシステムを読み取り専用に変更

:::note
ECSのベストプラクティスは今後内容が更新される可能性がありますので、[最新のベストプラクティス](https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/bestpracticesguide/bestpracticesguide.pdf)を参照して下さい。
:::

</div></details>


# 構築するAWSサービス
 + VPC
 + サブネット（パブリック×2、プライベート×2）
 + インターネットゲートウェイ
 + NATゲートウェイ
 + ルートテーブル（パブリック×1、プライベート×1）
 + セキュリティグループ（ALB用、ECS用）
 + ALB
 + IAM（ポリシー、ユーザ）
 + ECR
 + ECS


今回、ネットワーク周りやセキュリティグループ、IAMの設定は下記の設定値で作成しています。
ご自身の環境に応じて、設定変更いただければと思います。

**ネットワーク周り**
| AWSサービス | 設定値 |
|---|---|
| VPC |192.168.0.0/20  |
| パブリックサブネット1 | 192.168.1.0/24 |
| パブリックサブネット2 | 192.168.2.0/24 |
| プライベートサブネット1 | 192.168.3.0/24 |
| プライベートサブネット2 | 192.168.4.0/24 |

**パブリックルートテーブル**

| 送信先 | ターゲット | 備考 |
|---|---|---|
| 0.0.0.0/0 | igw-********** | インターネットゲートウェイを指定します |

**プライベートルートテーブル**

| 送信先 | ターゲット | 備考 |
|---|---|---|
| 0.0.0.0/0 | nat-********** | NATゲートウェイを指定します |

**ALB用セキュリティグループ**
| ルール | タイプ | プロトコル | ポート範囲 | ソース |
|---|---|---|---|---|
| インバウンドルール① | HTTP | TCP | 80 | 0.0.0.0/0 |

**ECS用セキュリティグループ**
| ルール | タイプ | プロトコル | ポート範囲 | ソース |
|---|---|---|---|---|
| インバウンドルール① | HTTP | TCP | 80 | ALB用セキュリティグループ |


**IAM**
ECRにnginxイメージをプッシュできるようにするために様々な方法がありますが、今回はIAMユーザのアクセスキーを使用してイメージのプッシュを行います。
下記内容でのIAMポリシーの作成します。
その後、任意のIAMユーザを作成して、IAMポリシーをアタッチしアクセスキーを発行します。
```:IAMポリシー内容
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": "ecr:GetAuthorizationToken",
            "Resource": "*"
        },
        {
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": [
                "ecr:CompleteLayerUpload",
                "ecr:UploadLayerPart",
                "ecr:InitiateLayerUpload",
                "ecr:BatchCheckLayerAvailability",
                "ecr:PutImage"
            ],
            "Resource": "arn:aws:ecr:ap-northeast-1:<AWSアカウントID>:repository/hello-world-from-ecs"
        }
    ]
}
```


# 手順
今回のFargateを利用してのWebサイトの構築は、構成イメージが似ている[記事](https://kleinblog.net/ecs-ecr-hello-world.html)を参考にして構築させていただきました。
構築するのにあたり、ネットワーク周りやセキュリティグループ作成、IAM作成の説明は省きます。


## ECRにnginxイメージをプッシュ
手元のPCがWindows10なので、[こちらの記事](https://qiita.com/urushibata/items/6bb2865b663c820fdb72)を参考に、nginxイメージをプッシュしていきます。

### AWS CLIのインストール
AWS CLIがインストールされていない場合は、公開されているインストール[手順](https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/getting-started-install.html)を参考に、AWS CLIのインストールを行います。
※特に変更などせずデフォルトのままインストールを行っています。
※AWS CLIに任意の認証情報を設定してください。


### Dockerのインストール
Dockerがインストールされていない場合は、公開されているインストール[手順](https://docs.docker.jp/docker-for-windows/install.html)を参考に、Dockerのインストールを行います。
※特に変更などせずデフォルトのままインストールを行っています。


### nginxイメージの作成
nginxイメージを作成します。

+ Dockerfileの作成

1\.任意の場所に「ecs_test」という名前のフォルダを新規作成します。

2\.「ecs_test」フォルダ配下に、「Dockerfile」という名前のファイルを新規作成します。

```:Dockerfile
FROM nginx:latest

COPY ./src /usr/share/nginx/html
```

3\.「ecs_test」フォルダ配下に、「src」という名前のフォルダを新規作成します。

4\.「src」フォルダ配下に、「index.html」という名前のファイルを新規作成します。

```:index.html
Hello world from ECS nginx!!!
```

<details><summary>今回作成したフォルダ構成はこちらになります</summary>

```
C:
├ Users
│ └ user
│   └ ecs_test
│     └ Dockerfile
│     └ src
│       └ index.html
︙
```

</div></details>


### ECRレポジトリの作成
公開されている[手順](https://docs.aws.amazon.com/ja_jp/AmazonECR/latest/userguide/repository-create.html)を参考に、レポジトリの作成を行います。

| 手順内容 | スクリーンショット |
|---|---|
| Amazon ECR > 「Repositories」 > 「レポジトリを作成」 | ![20231117_AWS_ECS_000001.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2784723/6ab1a080-506b-2d8e-56c6-6001a15bd8c8.jpeg) |
| 次の箇所を入力し「レポジトリを作成」をクリックします。<br> <br> ・レジストリ名：hello-world-from-ecs <br> ・タグのイミュータビリティ：有効 <br> ・KMS 暗号化：有効 | ![20231117_AWS_ECS_000002.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2784723/ae88e1ac-74e5-935c-48f5-96c30e5553e9.jpeg) |
| 「プッシュコマンドを表示」をクリックします。 | ![20231117_AWS_ECS_000003.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2784723/ee524f52-9c6c-ffe8-3b15-6d5a98c130e6.jpeg) |
| 表示された画面の手順に従いコマンドを実行します。 <br> ※画面内の手順2に関してはDockerfileがある場所でコマンドを実行します| ![20231117_AWS_ECS_000004.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2784723/efc4cbf4-60e6-a0c8-613e-e9634c8e2298.jpeg) |
| nginxイメージがプッシュされました。| ![20231117_AWS_ECS_000005.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2784723/35d24beb-6e49-d08e-5991-2f32ea6ac452.jpeg) |


## ALBの作成
[こちらの記事](https://qiita.com/K5K/items/2bab270b8fad24505303)を参考に、ALBの作成を行います。

### ターゲットグループの作成
| 手順内容 | スクリーンショット |
|---|---|
| EC2 > ターゲットグループ > 「ターゲットグループの作成」 | ![20231117_AWS_ECS_000006.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2784723/8a75ea45-0b6c-82b4-6d9c-29289539fb77.jpeg) |
| 次の箇所を入力し「ターゲットグループを作成」をクリックします。<br> <br> ・ターゲットタイプの選択：IPアドレス <br> ・ターゲットグループ名：ecs-targetgroup01 <br> ・プロトコル : ポート：HTTP <br> ・VPC：任意のVPC | ![20231117_AWS_ECS_000007.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2784723/f11a1729-a82f-d29e-708a-467e0809d63f.jpeg) <br> ![20231117_AWS_ECS_000008.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2784723/abb28631-4fcf-7c15-11ff-9a28259cfef7.jpeg) |
| 「ターゲットグループの作成」をクリックします。 | ![20231117_AWS_ECS_000009.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2784723/26738c83-684b-ee7d-4c63-acdd42d5575f.jpeg) |
| ターゲットグループが作成されました。 | ![20231117_AWS_ECS_000010.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2784723/7945d0b3-9c0c-b794-b93d-eea79ec42384.jpeg) |

### ALBの作成
| 手順内容 | スクリーンショット |
|---|---|
| EC2 > ロードバランサー > 「ロードバランサーの作成」 | ![20231117_AWS_ECS_000011.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2784723/cae67eb3-f979-457e-7b5a-43850b650957.jpeg) |
| Application Load Balancer > 「作成」 | ![20231117_AWS_ECS_000012.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2784723/2cad68f5-9304-47f2-39a1-c201ee5368fb.jpeg) |
| 次の箇所を入力し「ロードバランサーを作成」をクリックします。 <br> <br> ・ロードバランサー名：ecs-elb-01 <br> ・スキーム：インターネット向け <br> ・IPアドレスタイプ：IPv4 <br> ・VPC：任意のVPC <br> ・マッピング：任意のパブリックサブネット <br> ・セキュリティグループ：ALB用セキュリティグループ| ![20231117_AWS_ECS_000013.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2784723/429bf69c-ae90-8c56-302c-47b8fcd96f4d.jpeg) <br> ![20231117_AWS_ECS_000014.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2784723/e53d8a04-5252-919f-3adf-ca6a48faecdb.jpeg) <br> ![20231117_AWS_ECS_000015.jpg.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2784723/82a13f14-ae9f-f527-9fea-160d19b8fcfa.jpeg) |
| ロードバランサーが作成されました。 | ![20231117_AWS_ECS_000016.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2784723/9d4b4f52-8ead-24f5-d3bc-946fd6fa001c.jpeg) |


## ECSの作成
### クラスターの作成
| 手順内容 | スクリーンショット |
|---|---|
| Amazon Elastic Container Service > クラスター > 「クラスターの作成」 | ![20231117_AWS_ECS_000017.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2784723/6c40462e-8f59-1035-c946-3b99d5036113.jpeg) |
|次の箇所を入力し「ターゲットグループを作成」をクリックします。 <br> <br> ・クラスター名：ecs-hello-world-test-cluster | ![20231117_AWS_ECS_000018.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2784723/23a5a7af-67eb-8e17-2862-1b2b6da65655.jpeg) |
| クラスターが作成されました。 | ![20231117_AWS_ECS_000019.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2784723/be4b62b0-ce73-7867-ab97-5c9fc8b57448.jpeg) |

### タスク定義の作成
| 手順内容 | スクリーンショット |
|---|---|
| Amazon Elastic Container Service > タスク定義 > 「新しいタスク定義の作成」 | ![20231117_AWS_ECS_000020.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2784723/7b62c923-f0a7-340c-082f-02469157e5ef.jpeg) |
| 次の箇所を入力し「作成」をクリックします。 <br> <br> ・タスク定義ファミリー：hello-world-from-ecs-task-definition <br> ・起動タイプ：AWS Fargate <br> ・CPU：.25vCPU <br> ・メモリ：.5 GB <br> ・名前： hello-world-from-ecs-nginx <br> ・イメージ URI：ECRにアップロードしたURL <br> ・コンテナポート：80 <br> ・プロトコル：TCP | ![20231117_AWS_ECS_000021.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2784723/a1cf6909-bdb3-53a4-d5a2-33aea24c3422.jpeg) <br> ![20231117_AWS_ECS_000022.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2784723/21f1f89c-63ee-bdbc-1e4d-81f60dac454c.jpeg) <br> ![20231117_AWS_ECS_000023.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2784723/47f521f8-64fd-21a0-be03-898f5a0be216.jpeg) |
| タスク定義が作成されました。 | ![20231117_AWS_ECS_000024.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2784723/94389762-5476-3060-315b-ccaabe16ee3f.jpeg) |

### サービスの作成
| 手順内容 | スクリーンショット |
|---|---|
| Amazon Elastic Container Service > クラスター > 作成したクラスター名 > サービス > 作成 | ![20231117_AWS_ECS_000025.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2784723/327918ce-c01e-14e1-70aa-47c4ef52f812.jpeg)　|
| 次の箇所を入力し「サービスの作成」をクリックします。 <br> <br> ・コンピューティングオプション：起動タイプ <br> ・起動タイプ：FARGATE <br> ・プラットフォームのバージョン：LATEST <br> ・アプリケーションタイプ：サービス <br> ・ファミリー：hello-world-from-ecs-task-definition <br> ・サービス名：ecs-hello-world-test <br> ・サービスタイプ：レプリカ <br> ・必要なタスク：2 <br> ・VPC：作成したVPC <br> ・サブネット：プライベートサブネット <br> ・セキュリティグループ：ECS用セキュリティグループ <br> ・パブリックIP：オフ <br> ・ロードバランサーの種類：Application Load Balancer <br> ・ロードバランサー：作成したALB <br> ・ロードバランス用のコンテナの選択：hello-world-from-ecs-nginx 80:80 <br> ・リスナー：作成したリスナー <br> ・ターゲットグループ：作成したターゲットグループ | ![20231117_AWS_ECS_000026.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2784723/aade4eb4-34b2-9c0d-f8ff-61fe3ca4e1eb.jpeg) <br> ![20231117_AWS_ECS_000027.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2784723/a9a96717-bba5-87de-b649-042623ce85cc.jpeg) <br> ![20231117_AWS_ECS_000028.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2784723/ace1cbaf-5dde-30db-5422-1a7e65ff2efe.jpeg) <br> ![20231117_AWS_ECS_000029.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2784723/f605c0eb-259e-7fe8-4017-7e278202295d.jpeg) |
| サービスが作成されました。 | ![20231117_AWS_ECS_000030.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2784723/81b1ffc4-54bf-f397-cf95-c9644eb0c268.jpeg) |

:::note
ECS作成時の注意事項
・ECSクラスターやサービスはCloudFormationにて作成されるので、誤ってClooudFormationのスタックを削除しないように注意します
・タスク定義で設定するCloudWatch設定について、該当のCloudWatchロググループを削除すると起動できないのでロググループがあることを確認します（サービス作成時にCloudWatchロググループが新規作成らしい）
:::


## 動作確認

| 手順内容 | スクリーンショット |
|---|---|
| EC2 > ロードバランサー > 作成したロードバランサー > DNS 名 | ![20231117_AWS_ECS_000031.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2784723/1d541363-67c4-8a7c-dd55-036b20f30216.jpeg) |
| DNS名にアクセスできることを確認します。 | ![20231117_AWS_ECS_000032.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2784723/1839f4bf-40ad-0c9c-4afc-94404566709f.jpeg) |


# さいごに
無事にFargateを利用してWebサイトを構築することができました。
試験的にWebサイトを構築したことによって、コンテナへの理解に繋がりました。