AWS初心者の私がECS on Fargateの管理者環境を構築する際につまずいたポイント3選

# はじめに
今回、部内の新人研修の一環としてチームでの開発演習に取り組みました。

開発はフロントエンド、バックエンド、基盤に役割分担し複数人で実施し、私はその中の基盤を担当しました。基盤構築を行うにあたってはAWSを活用しましたが、AWSを触るのは初めてで、ESC on Fargateの環境構築には大変苦戦しました。この記事ではその経験を踏まえて、ESC on Fargate環境におけるアプリ管理者の環境構築で躓いたポイントを紹介したいと思います。

# 本記事の対象読者
* これから初めてECSでAWSサービスを構築する方
* 踏み台EC2からECS Execでコンテナへ接続する際に同じ個所でつまずいている方

# ECS とは
https://aws.amazon.com/jp/ecs/

Amazon ECS (Elastic Container Service) は、Amazon Web Services (AWS) が提供するフルマネージド型のコンテナオーケストレーションサービスです。アプリケーションのトラフィックに応じてコンテナの数を自動的に増減するスケーラブルなアプリ構築やコンテナごとにきめ細かくセキュリティやスケジューリングなどの設定を行うことができます。コンピューティングリソースにはFargateとEC2を選択でき、Fargateの場合はサーバーレスなコンテナ実行環境を実現することができます。

ECSに関する詳細な説明は本記事の主旨から逸脱するため省略しますが、以下の記事などが分かりやすいと思います。

https://o2mamiblog.com/aws-ecs-beginner/

# AWS初心者の私がつまずいたポイント
作成したアプリにおいて、アプリの管理者は、パブリックサブネットに設置された踏み台EC2から、コンテナのデプロイや、起動しているコンテナへのアクセスなど様々な管理を行います。

以下にアプリ管理者の環境構築をする際に私がつまずいた点をまとめたいと思います。

##### 1. EC2からECRにコンテナイメージをプッシュするために必要な設定
* EC2からECRへイメージをプッシュするためのIAMロールの設定が分からなかった
* ECRのプッシュコマンドを実行した際に生じた、「no basic auth credetials」の対処に苦戦した

##### 2. ECS Execを用いて踏み台EC2からFargateで動いているコンテナへ接続する方法
* ECSとEC2に必要なIAMロールの設定が分からなかった
* ECS Execを有効化したのにも関わらずコンテナに接続できなかった

##### 3. プライベートサブネットでECS on Fargate環境を構築する方法
* 必要なVPCエンドポイントが分からなかった

上記の三つのつまずいたポイントに対して、その解決策と学びをまとめたいと思います。

# ① EC2からECRにイメージをプッシュするために必要なIAMポリシーの設定
### サービスの関係図
今回考えるAWSのサービスの関係図は以下です。

![ECR_push.drawio (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3934148/4dbf0aa0-d17c-7c9d-74a3-6386fa6e0ee6.png)

### Amazon Elastic Container Registry（ECR）とは
[公式ページ](https://docs.aws.amazon.com/AmazonECR/latest/userguide/what-is-ecr.html?pg=ln&sec=hs)から引用すると以下のように説明されています。
> Amazon Elastic Container Registry (Amazon ECR) is an AWS managed container image registry service that is secure, scalable, and reliable. Amazon ECR supports private repositories with resource-based permissions using AWS IAM. This is so that specified users or Amazon EC2 instances can access your container repositories and images. You can use your preferred CLI to push, pull, and manage Docker images, Open Container Initiative (OCI) images, and OCI compatible artifacts.

簡単に言えば、AWSが提供しているDockerイメージのリモートリポジトリです。

ECRを利用することで作成したコンテナイメージをAWSのフルマネージドな環境で保存、管理することができます。例えば、ECSを利用する場合、ECRにプッシュしたコンテナイメージのURIをタスク定義に指定すると、指定したコンテナイメージを容易にデプロイすることができます。また、ライフサイクルポリシーを使用してリポジトリ内のライフサイクルを管理したり、プッシュ時にイメージスキャンを実行して、脆弱性がないか調べたりすることもできます。

### 踏み台EC2に設定すべきIAMロール
図に示す通り、管理者は踏み台EC2にコンテナイメージを用意し、ECRにアクセスしてイメージをプッシュします。この時、EC2に適切にIAMロールを設定しなければならず、私はこのIAMロールの設定で初めに躓きました。

解決策としては、[EC2でECRイメージを作成しpushするロールを作る体験](https://note.com/mocotech/n/nd8049aef8b4d)が大変分かりやすく、参考にしてIAMロールの設定を行いました。

EC2からECRへイメージをプッシュするには以下のIAMロールの設定が必要となります。

```json:EC2のIAMロール設定
{
	"Version": "2012-10-17",
	"Statement": [
		{
			"Sid": "AllowGetLoginPasswordOnly",
			"Effect": "Allow",
			"Action": "ecr:GetAuthorizationToken",
			"Resource": "*"
		},
		{
			"Sid": "AllowECRPushOnly",
			"Effect": "Allow",
			"Action": [
				"ecr:InitiateLayerUpload",
				"ecr:UploadLayerPart",
				"ecr:CompleteLayerUpload",
				"ecr:PutImage",
				"ecr:BatchCheckLayerAvailability"
			],
			"Resource": "*"
		}
	]
}
```

簡単に説明すると、一つ目の「AllowGetLoginPasswordOnly」は、DockerクライアントがECRのリポジトリにログインするための設定です。二つ目の「AllowECRPushOnly」はEC2からECRへイメージをプッシュできるようにするための設定です。


### プッシュコマンドの注意点
上記の設定でEC2からECRにイメージをプッシュすることができるようになりました。
ECRでリポジトリを作成したあと画面右上の「プッシュコマンドを表示」から、上から順にコマンドを実行していきます。

```shell:ECRへのイメージのプッシュ
$ aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin <AWSのアカウントID>.dkr.ecr.ap-northeast-1.amazonaws.com
$ sudo docker build -t sample-repository .
$ sudo docker tag sample-repository:latest <AWSのアカウントID>.dkr.ecr.ap-northeast-1.amazonaws.com/sample-repository:latest
$ sudo docker push <AWSのアカウントID>.dkr.ecr.ap-northeast-1.amazonaws.com/sample-repository:latest
```
問題なく進みそうなのですが、最後のコマンドで「no basic auth credetials」というエラーが出力され、認証まわりのエラーが出ていることが分かります。

![スクリーンショット 2024-12-04 114809.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3934148/91106681-ddd9-491c-b519-0084aa63e3fb.png)

しかし、一つ目のDockerクライアントの認証コマンドの出力を見てみると「Login Succeeded」となっているため、特に問題は起きていないように思われます。



いろいろと悩んだのですが、結論としては一つ目のコマンドのパイプライン処理の二つ目のdoker loginコマンドの前にsudoが必要だったみたいです。出力ではあたかもログインに成功しているような表示だったため気が付くのに時間がかかりました。

以下の正しいプッシュコマンドを実行したうえで試みると
```shell:正しいプッシュコマンド
$ aws ecr get-login-password --region ap-northeast-1 | sudo docker login --username AWS --password-stdin <AWSのアカウントID>.dkr.ecr.ap-northeast-1.amazonaws.com
```

プッシュに成功しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3934148/1990d733-554a-b4a1-70c9-7395b2f9d041.png)


# ② ECS Execを用いた接続に必要な各種設定
### サービスの関係図
今回考えるAWSのサービスの関係図は以下です。

![ECSonFargate.drawio (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3934148/8c80a330-1316-bbe1-858c-4e6a6964a47c.png)


### ECS Execとは
[公式ページ](https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/ecs-exec.html)から引用すると以下のように説明されています。
> With Amazon ECS Exec, you can directly interact with containers without needing to first interact with the host container operating system, open inbound ports, or manage SSH keys. You can use ECS Exec to run commands in or get a shell to a container running on an Amazon EC2 instance or on AWS Fargate. This makes it easier to collect diagnostic information and quickly troubleshoot errors. For example, in a development context, you can use ECS Exec to easily interact with various process in your containers and troubleshoot your applications. And in production scenarios, you can use it to gain break-glass access to your containers to debug issues.

簡単に言えば、ECS Execの機能を使うと、稼働中のFargate上のコンテナにアクセスすることができます。

また、[公式ページ](https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/ecs-exec.html)によると、以下のようにあります。
> ECS Exec makes use of AWS Systems Manager (SSM) Session Manager to establish a connection with the running container and uses AWS Identity and Access Management (IAM) policies to control access to running commands in a running container. This is made possible by bind-mounting the necessary SSM agent binaries into the container. The Amazon ECS or AWS Fargate agent is responsible for starting the SSM core agent inside the container alongside your application code. For more information, see Systems Manager Session Manager.

ECS Execの機能は、SSM Agentのバイナリをコンテナにバインドマウントすることで、Session Managerの仕組みを使用してコンテナと接続を確立しています。そのため、コンテナにSSHポートを開ける必要なく簡単かつセキュアにコンテナへアクセスすることができます。また、Session ManagerをベースとしているためFargateでSSM Agentが動作していなければなりませんが、特定のバージョン以降のプラットフォームバージョンを利用することでSSM Agentがプリインストールされた環境を使うことができます。

### 適切なロールの設定
ECS Execを用いたコンテナ管理で最初に躓いたところは適切なロール設定です。ECRの設定でロールの設定方法について理解しているつもりでしたが、ECSの場合、どこでロールを設定すればいいのかわかりませんでした。

ECS Execの設定方法に関しては[[アップデート]実行中のコンテナに乗り込んでコマンドを実行できる「ECS Exec」が公開されました](https://dev.classmethod.jp/articles/ecs-exec/)が非常にわかりやすく、参考にしながら設定を行いました。

結論から言うと、ECSのロールの設定はタスク定義の「タスクロール」に以下のポリシーを設定する必要があります。タスクロールはAmazon ECSタスクにIAMロールを関連付けることができ、コンテナ上のアプリケーションが他のAWSサービスを使用できるようになります。
```json:タスクロールの設定
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "ssmmessages:CreateControlChannel",
        "ssmmessages:CreateDataChannel",
        "ssmmessages:OpenControlChannel",
        "ssmmessages:OpenDataChannel"
      ],
      "Resource": "*"
    }
  ]
}
```

この後はECS Execを使用できるように以下のコマンドでSession Manager pluginをインストールした後に、対象のECSサービスでECS Execを有効化しようとするのですが、「AccessDeniedException」が出力され、有効化に失敗します。
```shell:Session Manager pluginのインストール
$ sudo yum install -y https://s3.amazonaws.com/session-manager-downloads/plugin/latest/linux_64bit/session-manager-plugin.rpm
```
```shell:ECS Execの有効化
$ aws ecs update-service --region ap-northeast-1 --cluster クラスター名 --service サービス名 --enable-execute-command

> An error occurred (AccessDeniedException) when calling the UpdateService operation: User: arn:aws:sts::<AWSのアカウントのD>:assumed-role/ec2-role/i-055e1e744dd8ab549 is not authorized to perform: ecs:UpdateService on resource: arn:aws:ecs:ap-northeast-1:<AWSのアカウントID>:<ECSサービス名> because no identity-based policy allows the ecs:UpdateService action
```

これはECS Execを有効化しようとしたEC2インスタンスにECSのUpdateServiceアクションを実行するための権限が欠如していることを示しています。

したがって、EC2に設定したIAMロールに対して「AmazonECS_FullAccess」ポリシーをアタッチします。

![スクリーンショット 2024-12-04 134842.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3934148/b4322f1f-deb5-6210-5f9b-bb3e70261459.png)

EC2を再起動したうえで再度ECS Execの有効化コマンドを実行すると、有効化に成功しJSON形式でECSサービスの仕様が表示されます。「ebableExecuteCommand」が```true```となっていることからも成功が確認できます。

![スクリーンショット 2024-12-04 132802.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3934148/c694a3b7-7386-4436-e015-f3af92698a1b.png)

### ECS Exec有効化の際の注意点
続いて以下のコマンドで、ECS Execを用いてコンテナに接続していくのですが、「InvalidParameterException」が出力され接続に失敗します。
```shell:コンテナへの接続コマンド
$ aws ecs execute-command --region ap-northeast-1 --cluster クラスター名 --task タスクID --container コンテナ名 --interactive --command "/bin/sh"

> An error occurred (InvalidParameterException) when calling the ExecuteCommand operation: The execute command failed because execute command was not enabled when the task was run or the execute command agent isn’t running. Wait and try again or run a new task with execute command enabled and try again.
```

これはECSの仕様で、実行中のタスクに関してはECS Execが有効化されないためです。したがって、ECSのサービスを再起動する必要があります。

まず、タスクをいったん停止させます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3934148/17615720-2aaf-c501-a1d3-742eb4180644.png)

その後、「サービス」タブで対象のサービスを選択し、サービスを更新からサービスを再起動させます。
![スクリーンショット 2024-12-02 175658.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3934148/62934226-cb8c-5154-e0ee-3c6989bac193.png)

再度「タスク」タブに移動して、タスクが起動していることを確認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3934148/5be73c6f-2b02-b32a-3e08-60bf913cb364.png)

最後にもう一度コンテナへの接続コマンドを実行すると接続に成功します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3934148/d3d7e086-bcc3-0a8f-7d35-403ab37e55c1.png)


# ③ プライベートサブネット上のFargate環境を構築する際のVPCエンドポイントの設定
<!--
* デプロイに必要なVPCエンドポイントの設定
    * ECR
    * S3
    * Logを取り除いた
* ECS Execでアクセスする際のVPCエンドポイントの設定
-->

### サービスの関係図
今回考えるAWSのサービスの関係図は以下です。

![ECS_pvt.drawio (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3934148/cc4d358e-52d2-a877-f531-094d23bd1478.png)


### ECRのコンテナイメージをデプロイするために必要なVPCエンドポイント
今までのFargate環境はパブリックサブネット上に構築する場合を考えてきました。しかし、プライベートサブネットでコンテナを動かす場合、上記の設定だけでは不十分です。

試しにパブリックサブネットの時と同様の設定で、プライベートサブネットでサービスを起動してみると以下のようなエラーメッセージが表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3934148/fffdc918-ee28-338a-cd4d-226bfa3ed44b.png)

エラー内容としては「タスク定義で指定したECRのリポジトリにECSから接続できない」といったものです。この問題を解決するためには以下のような方法が考えられます。

* パブリックサブネットに設置したNAT Gateway経由で通信する
* VPCエンドポイントを作成し、外部と通信する

NAT Gatewayは比較的使用料金が高く、また、従量課金制であるり、イメージのデプロイ回数が増えると料金が意外とかかるため、演習ではVPCエンドポイントを作成することにしました。

[Amazon ECR interface VPC endpoints (AWS PrivateLink)](https://docs.aws.amazon.com/ja_jp/AmazonECR/latest/userguide/vpc-endpoints.html)によると、ECRからのイメージのデプロイに考慮すべきVPCエンドポイントは三つあります。
* Amazon ECR エンドポイント
    * com.amazonaws.region.ecr.dkr (pushやpullなどのDockerクライアントコマンドで利用される)
    * com.amazonaws.region.ecr.api (DescribeImagesやCreateRepositoryなどのAPIアクションで利用される)
* Amazon S3 エンドポイント
    * com.amazonaws.region.s3

Amazon S3 エンドポイントが必要なのは意外かもしれませんが、Amazon ECR は Amazon S3 を使用してイメージレイヤーを保存するためこのゲートウェイエンドポイントが必要になります。

また、

> Amazon ECS tasks hosted on Fargate using platform version 1.4.0 or later require both Amazon ECR VPC endpoints and the Amazon S3 gateway endpoints.

> Amazon ECS tasks hosted on Fargate that use platform version 1.3.0 or earlier only require the com.amazonaws.region.ecr.dkr Amazon ECR VPC endpoint and the Amazon S3 gateway endpoints.

とあるように、プラットフォームバージョンによって必要なVPCエンドポイントが異なります。以下はプラットフォームバージョンごとに必要なエンドポイントです。

| プラットフォームバージョン | 必要なエンドポイント |
|:-:|:-|
| 1.3.0 以下  | com.amazonaws.[region].ecr.api <br> com.amazonaws.[region].s3 |
| 1.4.0 以上  | com.amazonaws.[region].ecr.dkr <br> com.amazonaws.[region].ecr.ap <br> com.amazonaws.[region].s3 |

ここで今回動作させるECS Fargateのプラットフォームバージョンを確認すると 1.4.0となっているため、必要なエンドポイントは3つであることが分かります。

![スクリーンショット 2024-12-04 144351.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3934148/7fa6f536-cb15-762a-a5e2-1734bccf6824.png)

したがって、Fargateが起動するのと同じサブネットにVPCエンドポイントを作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3934148/1bb764d6-4250-a9b3-a1ce-29a15c2e1e44.png)

以上でECRからFargateにイメージをデプロイする準備は整ったはずなので、サービスを作成してみます。
すると以下のようなエラーが生じてデプロイに失敗します。

![image-2024-10-31_16-28-59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3934148/7b8fb8af-c99d-f7d3-95a8-f91c584c5d21.png)

エラー内容は「タスク定義で定義したAmazon CloudWatch Logsのグループが見つからない」というものです。

このエラーについてと調べてみると、[Amazon ECS interface VPC endpoints (AWS PrivateLink)](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/vpc-endpoints.html)にそれらしいものがありました。
> If your VPC doesn't have an internet gateway and your tasks use the awslogs log driver to send log information to CloudWatch Logs, you must create an interface VPC endpoint for CloudWatch Logs. For more information, see Using CloudWatch Logs with Interface VPC Endpoints in the Amazon CloudWatch Logs User Guide.

[Using CloudWatch Logs with interface VPC endpoints](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/cloudwatch-logs-and-interface-VPC.html)を参照するようにと指示があるため、読んでみると、
> To start using CloudWatch Logs with your VPC, create an interface VPC endpoint for CloudWatch Logs. The service to choose is com.amazonaws.Region.logs. You do not need to change any settings for CloudWatch Logs. For more information, see Creating an Interface Endpoint in the Amazon VPC User Guide.

とあります。すなわち、Fargateが起動しているサブネットに com.amazonaws.<リージョン名>.logsのVPCエンドポイントが設定されていないため、タスク定義したCloudWatch Logsのグループが見つけられないというエラーが生じたのだと思われます。CloudWatch Logs用のVPCエンドポイントを作成して、再度サービスを起動すると、成功しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3934148/b74e9bfb-8bf3-8d7a-4143-ef3fc4c5720a.png)


### 踏み台EC2からECS Execで接続するために必要なVPCエンドポイント
タスクの起動に成功したので、次にプライベートサブネットで動作しているコンテナに対して踏み台EC2からECS Execで接続しようとしました。しかし、以下のようなエラーが出ました。

```shell:コンテナへの接続コマンド
$ aws ecs execute-command --region ap-northeast-1 --cluster クラスター名 --task タスクID --container コンテナ名 --interactive --command "/bin/sh"

> An error occurred (InvalidParameterException) when calling the ExecuteCommand operation: The specified task was not found. Specify a valid task ID and try again.
```

先程と同様の「InvalidParameterException」エラーで、「指定されたタスクIDが見つからない」というエラー内容です。おそらくVPCエンドポイント周りの問題だろうと思ったので、調べてみると、[Monitor Amazon ECS containers with ECS Exec](https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/ecs-exec.html)にそれらしきものがありました。
> If you are using interface Amazon VPC endpoints with Amazon ECS, you must create the interface Amazon VPC endpoints for the Systems Manager Session Manager (ssmmessages). For more information about Systems Manager VPC endpoints, see Use AWS PrivateLink to set up a VPC endpoint for Session Manager in the AWS Systems Manager User Guide.

[Step 6: (Optional) Use AWS PrivateLink to set up a VPC endpoint for Session Manager](https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-getting-started-privatelink.html)を参照するようにと指示があるため、読んでみると、System Managerはssmmessages.Region.amazonaws.comを使用して、SSM Agentからクラウド上のSession Managerサービスを呼び出すようです。やはり、VPCエンドポイントの設定が必要だったようなので、VPCエンドポイント「ssmmessages.ap-northeast-1.amazonaws.com」を作成して、再度ECS Execで踏み台EC2から接続すると、見事接続に成功しました。

# 総評
本記事では、AWS初心者の私がECS on Fargate環境を構築する際に直面した3つの課題について振り返り、解決策を紹介しました。ECSは強力で柔軟なコンテナオーケストレーションサービスですが、初めて触れる際には多くの細かい設定や理解が必要であることが分かりました。特に、IAMポリシーの設定やVPCエンドポイントの設定など、思わぬところでつまずきが生じましたが、それらの解決を通してAWSのセキュリティやネットワーク設定について理解することができました。
これからECS on Fargateを使い始める方にとって、この記事が少しでも参考になれば嬉しいです。

# 参考サイト
https://docs.aws.amazon.com/AmazonECS/latest/developerguide/vpc-endpoints.html

https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs-exec.html

https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/cloudwatch-logs-and-interface-VPC.html

https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-getting-started-privatelink.html

https://dev.classmethod.jp/articles/ecs-exec/

https://qiita.com/hirai-11/items/0725a476f587e1d64253

https://qiita.com/okubot55/items/b1fb07b2de08c354275b

https://zenn.dev/yoshinori_satoh/articles/ecs-fargate-vpc-endpoint