経緯
業務で分散ロードテストフレームワークを作るために ECS on Fargate を使用する。
最終的にはECSクラスターをTerraformでデプロイする予定。その前段階としてAWS console上でぽちぽちいじりながら作ってみる。
やりたいこと
最終的に以下をできるようにする。
- AWS ECS task をローカルの aws cli で実行する。この際に、コンテナ内実行時に参照可能な環境変数を指定する。
- AWS ECS task では、ECR上のプライベートレポジトリ内のDocker imageを使用してコンテナを立ち上げる。
- AWS ECS task 内で、S3へファイルをアップロードする。
ステップ
1. ECS cluster の初期化
2. Task definition の作成
以下のように作成された。
3. Task を実行してみる
以下のように実行された。
4. カスタマイズ(1)
自前の Docker image をECR内に用意し、ECSがそれをpullしてきて実行させたい。
ECRのレポジトリの作成
ローカルでDockerfileを作成する。
FROM ubuntu:latest
CMD echo "Hello from Daiki in custom docker file"
これをビルドしてプッシュする。
aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 111111111111.dkr.ecr.us-east-1.amazonaws.com
docker build -t daiki-test .
docker tag daiki-test:latest 111111111111.dkr.ecr.us-east-1.amazonaws.com/daiki-test:latest
docker push 111111111111.dkr.ecr.us-east-1.amazonaws.com/daiki-test:latest
ECR上にイメージが存在するのを確認できた。
これを使って、ECS task definition の新しいrevisionを作成する。
daiki-loadtesting-test-task:2
が完成した。
自分のMacは、arm64
なので、以下のようにECSクラスターの定義を更新した。
このECSタスクを実行する。
実行完了。以下がログ。
AWS CLIから実行する
以下をローカルで実行する。subnetは、default subnetを指定した。
(AWS console上でタスクを実行した場合はこれが使用されていた。)
aws ecs run-task \
--cluster daiki-load-testing-test \
--task-definition daiki-loadtesting-test-task:3 \
--launch-type FARGATE \
--network-configuration "awsvpcConfiguration={subnets=[subnet-6331723a],assignPublicIp=ENABLED}"
(
assignPublicIp=ENABLED
は ECRからベースイメージをfetchしてくる場合に必要そう?)
5. カスタマイズ(2)
タスク実行時できるパラメータを、環境変数としてrun task時に外部から注入したい。
まずは、Dockerfileを以下のように更新し、push。
FROM ubuntu:latest
CMD echo "Hello from Daiki in custom docker file. I got injected_var=${injected_var}"
docker build -t daiki-test .
docker tag daiki-test:latest 1111111111111.dkr.ecr.us-east-1.amazonaws.com/daiki-test:latest
docker push 111111111111.dkr.ecr.us-east-1.amazonaws.com/daiki-test:latest
Task definition内から、containerDefinitions.name
を調べる。
これと、環境変数を指定して、以下をローカル上で実行する。
aws ecs run-task \
--cluster daiki-load-testing-test \
--task-definition daiki-loadtesting-test-task:3 \
--launch-type FARGATE \
--network-configuration "awsvpcConfiguration={subnets=[subnet-6331723a],assignPublicIp=ENABLED}" \
--overrides '{
"containerOverrides": [
{
"name": "daiki-test-image-in-ecr",
"environment": [
{
"name": "injected_var",
"value": "injected_from_aws_cli_ecs_run_task"
}
]
}
]
}'
ECSの実行ログから、環境変数が注入されているのを確認できた。
6. カスタマイズ(3)
タスク内でログをファイルに書き出し、それをS3へアップロードしたい。
S3に bucket daiki-test-ecs
を作成する。
ローカルのDockerfileを以下のように更新する。
(TODO: aws-cli を含むベースイメージがないか調べる)
FROM ubuntu:latest
RUN apt-get update && apt-get install -y less vim curl unzip sudo
RUN curl "https://awscli.amazonaws.com/awscli-exe-linux-aarch64.zip" -o "awscliv2.zip"
RUN unzip awscliv2.zip
RUN sudo ./aws/install
CMD echo "Hello from Daiki in custom docker file. I got injected_var=${injected_var}" >> "/log.txt" && \
aws s3 cp /log.txt s3://daiki-test-ecs/
ビルドしてECRにpushする。
docker build -t daiki-test . && \
docker tag daiki-test:latest 111111111111.dkr.ecr.us-east-1.amazonaws.com/daiki-test:latest && \
docker push 111111111111.dkr.ecr.us-east-1.amazonaws.com/daiki-test:latest
ecsTaskExecutionRole
に、AmazonEC2ContainerRegistryReadOnly
ポリシーを付与してやる。
TFで自前で作成する場合
CW logにログをダンプする権限も忘れずに。
resource "aws_iam_role" "ecs_task_execution_role" {
name = local.ecs_task_execution_role_name
assume_role_policy = jsonencode({
"Version" : "2012-10-17",
"Statement" : [
{
"Effect" : "Allow",
"Principal" : {
"Service" : "ecs-tasks.amazonaws.com"
},
"Action" : "sts:AssumeRole"
},
]
})
inline_policy {
name = "DumpLogsToCloudWatchLogs"
policy = jsonencode({
"Version" : "2012-10-17",
"Statement" : [
{
Effect = "Allow"
Action = ["logs:CreateLogGroup"]
Resource = "arn:aws:logs:${local.ecs_task_log_region}:${var.account_id}:*"
},
{
Action = [
"logs:CreateLogStream",
"logs:PutLogEvents"
]
Effect = "Allow"
Resource = "arn:aws:logs:${local.ecs_task_log_region}:${var.region}:log-group:/ecs/benchmarking-personalization-api:*"
},
]
})
}
tags = var.tags
}
resource "aws_iam_role_policy_attachment" "aws_managed_ecs_execution" {
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"
role = aws_iam_role.ecs_task_execution_role.name
}
resource "aws_iam_role_policy_attachment" "aws_managed_ecr_read_only" {
policy_arn = "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly"
role = aws_iam_role.ecs_task_execution_role.name
}
ECSのタスクを実行する。
aws ecs run-task \
--cluster daiki-load-testing-test \
--task-definition daiki-loadtesting-test-task:3 \
--launch-type FARGATE \
--network-configuration "awsvpcConfiguration={subnets=[subnet-6331723a],assignPublicIp=ENABLED}" \
--overrides '{
"containerOverrides": [
{
"name": "daiki-test-image-in-ecr",
"environment": [
{
"name": "injected_var",
"value": "injected_from_aws_cli_ecs_run_task"
}
]
}
]
}'
結果はUnable to locate credentials
とのこと。
必要な権限をECS task definitionに付与する。
IAM policyの作成
IAM roleの作成
作成したIAM roleをECS taskに紐付ける。
新しく、daiki-loadtesting-test-task:4
を発行した。
aws ecs run-task \
--cluster daiki-load-testing-test \
--task-definition daiki-loadtesting-test-task:4 \
--launch-type FARGATE \
--network-configuration "awsvpcConfiguration={subnets=[subnet-6331723a],assignPublicIp=ENABLED}" \
--overrides '{
"containerOverrides": [
{
"name": "daiki-test-image-in-ecr",
"environment": [
{
"name": "injected_var",
"value": "injected_from_aws_cli_ecs_run_task"
}
]
}
]
}'
タスク内でS3へのアップロードが完了していた。
S3内にファイルが存在することも確認した。
(雑な追記) ECSのRunTaskに必要となる権限
例えばLambdaから実行する場合は以下の権限が必要。
iam:PassRole
を忘れずに。
resource "aws_iam_policy" "ecs-task-runner-lambda" {
name = "ecs-task-runner-lambda"
path = "/"
policy = jsonencode({
Version = "2012-10-17",
Statement = [
{
Effect = "Allow",
Action = [
"ecs:RunTask",
],
Resource = "arn:aws:ecs:us-east-1:111111111111:task-definition/my-ecs-task:*",
},
{
Effect = "Allow",
Action = [
"iam:PassRole",
],
Resource = [
"arn:aws:iam::111111111111:role/my-ecs-task-role",
"arn:aws:iam::111111111111:role/my-ecs-task-execution-role",
]
},
]
})
}
Ref.