はじめに
以前、AWSブログでzstd圧縮イメージを使用すると起動時間が短縮されると発表されたので実際に試した内容を投稿しました。
GoQSystemのバックエンド環境で実際に使用しているDockerfileの内容で実施しましたが、Dockerイメージサイズが小さく(約150MB)イマイチ効果がわかりにくかったので、イメージサイズが大きめの環境を使って再度確認してみることにしました。
本来であれば機械学習(Python)などで使用する数GBイメージサイズのDockerfileを用意できればよかったのですが、PythonやMLには疎いので、今回はNode.jsでNext.jsを実行する環境を用意して検証してみました。
今回は使用したDockerfile、GitHub ActionsでAWS ECSへデプロイ、インフラ構成のTerraformを一部掲載しています。
ディレクトリ構成
GitHub Actions設定ファイルディレクトリの/.github
、Next.jsディレクトリの/example
、ルートディレクトリにDockerfile
を置いています。
example/
┣━ .github/
┃ └ workflows/
┃ └ deploy.yaml
┣━ example/
┃ └ Next.jsのファイル
┣━ Dockerfile
※ Next.jsは単純に以下のコマンドでアプリケーションを作成しただけです。
npx create-next-app@latest
# or
yarn create next-app
# or
pnpm create next-app
ファイル内容
※ 全て検証用に作成したファイルですので、そのまま使用する場合はご注意ください。
※ GitHub ActionsからAWSへの認証はOIDCを使用しています。
通常のDocker build用GitHub Actions
name: Aws_Ecs_Deploy
on:
push:
permissions:
id-token: write
contents: read
jobs:
nextjs:
runs-on: ubuntu-latest
env:
AWS_REGION: AWSリージョン
AWS_IAM_ROLE_ARN: OIDC用IAMロール
ECR_REPOSITORY: ECRリポジトリ名
ECS_CLUSTER: ECSクラスター名
ECS_SERVICE: ECSサービス名
ECS_TASK_DEFINITION: ECSタスクARN
steps:
-
name: Checkout
uses: actions/checkout@v3
-
name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-region: ${{ env.AWS_REGION }}
role-to-assume: ${{ env.AWS_IAM_ROLE_ARN }}
-
name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v1
-
name: Build, tag, and push docker image to Amazon ECR
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
ECR_REPOSITORY: ${{ env.ECR_REPOSITORY }}
IMAGE_TAG: ${{ github.sha }}
run: |
docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
-
name: Download task definition
run: |
aws ecs describe-task-definition --task-definition ${{ env.ECS_TASK_DEFINITION }} --query taskDefinition > task-definition.json
-
name: Render Amazon ECS task definition for app container
id: render-app-container
uses: aws-actions/amazon-ecs-render-task-definition@v1
with:
task-definition: task-definition.json
container-name: app
image: ${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_REPOSITORY }}:${{ github.sha }}
-
name: Deploy to Amazon ECS service
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
with:
task-definition: ${{ steps.render-app-container.outputs.task-definition }}
service: ${{ env.ECS_SERVICE }}
cluster: ${{ env.ECS_CLUSTER }}
通常のDocker buildx build用GitHub Actions
name: Aws_Ecs_Deploy
on:
push:
permissions:
id-token: write
contents: read
jobs:
nextjs:
runs-on: ubuntu-latest
env:
AWS_REGION: AWSリージョン
AWS_IAM_ROLE_ARN: OIDC用IAMロール
ECR_REPOSITORY: ECRリポジトリ名
ECS_CLUSTER: ECSクラスター名
ECS_SERVICE: ECSサービス名
ECS_TASK_DEFINITION: ECSタスクARN
steps:
-
name: Checkout
uses: actions/checkout@v3
-
name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-region: ${{ env.AWS_REGION }}
role-to-assume: ${{ env.AWS_IAM_ROLE_ARN }}
-
name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v1
-
name: Set up QEMU
uses: docker/setup-qemu-action@v2
-
name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v2
-
name: Build, tag, and push docker image to Amazon ECR
uses: docker/build-push-action@v3
with:
context: .
push: true
builder: ${{ steps.buildx.outputs.name }}
tags: ${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_REPOSITORY }}:${{ github.sha }}
outputs: |
type=image,oci-mediatypes=true,compression=zstd,compression-level=3,force-compression=true
-
name: Download task definition
run: |
aws ecs describe-task-definition --task-definition ${{ env.ECS_TASK_DEFINITION }} --query taskDefinition > task-definition.json
-
name: Render Amazon ECS task definition for app container
id: render-app-container
uses: aws-actions/amazon-ecs-render-task-definition@v1
with:
task-definition: task-definition.json
container-name: app
image: ${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_REPOSITORY }}:${{ github.sha }}
-
name: Deploy to Amazon ECS service
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
with:
task-definition: ${{ steps.render-app-container.outputs.task-definition }}
service: ${{ env.ECS_SERVICE }}
cluster: ${{ env.ECS_CLUSTER }}
Dockerfile
FROM node:19
RUN apt-get update
COPY ./example /var/www/app
WORKDIR /var/www/app
RUN yarn --ignore-scripts && yarn build
EXPOSE 3000
CMD ["yarn","start"]
インフラ構成
ざっくりですが以下のような一般的なWEBアプリケーション構成を構築しています。
ECSタスクは以下の内容です。
上述しているアプリケーション用のDockerイメージ1つで構成しています。
ECSタスク(Terraform)
resource "aws_ecs_task_definition" "ecs_task" {
family = var.app_name
requires_compatibilities = ["FARGATE"]
network_mode = "awsvpc"
cpu = 512
memory = 1024
task_role_arn = aws_iam_role.task_role.arn
execution_role_arn = aws_iam_role.task_exec_role.arn
container_definitions = jsonencode([
{
"name" : "app",
"image" : "${aws_ecr_repository.ecr_repo.repository_url}:${var.docker_image_tag}",
"memoryReservation" : 1024,
"essential" : true,
"logConfiguration" : {
"logDriver" : "awslogs",
"options" : {
"awslogs-group" : "${aws_cloudwatch_log_group.cloudwatch_log.name}",
"awslogs-region" : "${var.region}",
"awslogs-stream-prefix" : "ecs"
}
},
"portMappings" : [
{
"containerPort" : 3000
}
]
}
])
runtime_platform {
operating_system_family = "LINUX"
cpu_architecture = "X86_64"
}
}
ALBターゲットグループは下記設定にしています。(Terraform)
ALBターゲットグループ(Terraform)
resource "aws_lb_target_group" "target_group" {
name = "${var.app_name}-TG"
port = 80
protocol = "HTTP"
target_type = "ip"
vpc_id = var.vpc_id
deregistration_delay = 5
health_check {
path = "/"
healthy_threshold = 2
unhealthy_threshold = 2
interval = 10
}
}
計測方法
ECS TaskのStarted at
とCreated at
の「差分 = 起動時間」とします。
通常のDocker buildのイメージとbuildxでzstd圧縮したイメージそれぞれを使用したECS Taskを50個立ち上げて確認しています。(10タスク×5回)
ECS Taskなどの構成内容はどちらも同じものです。
aws cli:ECS TaskのStarted at
とCreated at
を確認
aws ecs describe-tasks --tasks $TASK_ARN --cluster $CLUSTER_NAME
※ コンソール画面でも確認できます
Docker build
でイメージを使用したECS Task
■ イメージサイズ
- Next.js: 833.05MB
■ ECS Taskサイズ
- CPU: 0.5
- Memory: 1GB
■ ECS Task起動時間
- 平均: 41.96(秒)
- 最大: 56.00(秒)
- 最小: 37.00(秒)
- 中央値: 41.00(秒)
Docker buildx build
でzstd圧縮したイメージを使用したECS Task
■ イメージサイズ
- Next.js: 741.50MB
Docker build
で生成したDockerイメージサイズより約92MB小さくなっています。
■ ECS Taskサイズ
- CPU: 0.5
- Memory: 1GB
■ ECS Task起動時間
- 平均: 37.05(秒)
- 最大: 44.00(秒)
- 最小: 34.00(秒)
- 中央値: 36.00(秒)
Docker build
で生成したDockerイメージを使用したECSタスクより平均して約4~5秒早く起動するようになりました。
まとめ
前回はDockerイメージサイズが比較的小さいECSタスクで検証したので効果がイマイチわかりにくかったですが、上記のようにzstd圧縮
を使用するとECSタスクの起動時間が早くなりました。
AWSブログにあるようにイメージサイズが大きいほど効果的なようです。
AWS ECS on Fargateのタスク起動時間がzstd圧縮されたDockerイメージを利用すると早くなるということで初めてzstd圧縮にチャレンジし、同時にGitHub ActionsのECSデプロイを勉強できたので有益な検証になりました。
今後も新しい技術や方法を試してより良い環境を構築できるように改善していきたいと思います。
最後に
GoQSystemでは一緒に働いてくれる仲間を募集中です!
求人は出していませんが、SREやQAエンジニアも今後募集していこうと考えています!
ご興味がある方は以下リンクよりご確認ください。