はじめに
GitHub Actions で Docker イメージをビルドして ECR にプッシュして ECS にデプロイするまでの手順をまとめます。
ディレクト構成
今回は以下のようなディレクトリ構成で進めます。
aspnetapp 以下には ASP.NET Core のプロジェクトがあります。
.
├── .github
│ └── workflows
│ └── deploy.yml
├── deploy
│ └── ecs-task-def.json
├── aspnetapp
│ └── aspnetapp.csproj など
└── Dockerfile
.github/workflows 以下に yml ファイルを配置します。
これにより、GitHub Actions を定義することができます。
deploy ディレクトリには、ECS タスク定義の JSON ファイルを配置します。
ASP.NET Core プロジェクトや Dockerfile の作成は省略します。
参考までに公式ドキュメントを貼っておきます。
成果物
先に完成図だけ示します。
name: ecr push image
on:
push:
branches:
- main
env:
AWS_ACCOUNT_ID: ${{ secrets.AWS_ACCOUNT_ID }}
jobs:
push:
runs-on: ubuntu-latest
# `permissions` を設定しないと OIDC が使えないので注意
permissions:
id-token: write
contents: read
steps:
- uses: actions/checkout@v3
# AWS 認証
- uses: aws-actions/configure-aws-credentials@v4
with:
aws-region: ap-northeast-1
role-to-assume: ${{ secrets.AWS_IAM_ROLE_ARN }} # GitHub Actions 用の IAM ロールの ARN
# ECR ログイン
- uses: aws-actions/amazon-ecr-login@v2
id: login-ecr # outputs で参照するために id を設定
# Docker イメージを build・push する
- name: build and push docker image to ecr
id: build-image # outputs で参照するために id を設定
env:
# ECR レジストリを `aws-actions/amazon-ecr-login` アクションの `outputs.registry` から取得
REGISTRY: ${{ steps.login-ecr.outputs.registry }}
# イメージを push する ECR リポジトリ名
REPOSITORY: ecs-sample
# 任意のイメージタグ
IMAGE_TAG: latest
run: |
docker build . --tag ${{ env.REGISTRY }}/${{ env.REPOSITORY }}:${{ env.IMAGE_TAG }}
docker push ${{ env.REGISTRY }}/${{ env.REPOSITORY }}:${{ env.IMAGE_TAG }}
# outputs で参照するために `image_id` という名前で出力
echo "image_id=${{ env.REGISTRY }}/${{ env.REPOSITORY }}:${{ env.IMAGE_TAG }}" >> $GITHUB_OUTPUT
- name: Update ECS task definition and ecs-task-def.json with AWS Account ID
run: |
sed -i 's|\[AWS_ACCOUNT_ID\]|${{ env.AWS_ACCOUNT_ID }}|g' ./deploy/ecs-task-def.json
sed -i 's|\[IMAGE_ID\]|${{ steps.build-image.outputs.image_id }}|g' ./deploy/ecs-task-def.json
- name: Fill in the new image ID in the Amazon ECS task definition
id: task-def
uses: aws-actions/amazon-ecs-render-task-definition@v1
with:
task-definition: deploy/ecs-task-def.json
container-name: ecs-sample-container
image: ${{ steps.build-image.outputs.image_id }}
- name: Deploy Amazon ECS task definition
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
with:
task-definition: ${{ steps.task-def.outputs.task-definition }}
service: ecs-sample-service
cluster: ecs-sample-service
wait-for-service-stability: true
force-new-deployment: true
GitHub Actions の設定
おおまかな流れは以下のようになります。
- OIDC を使用して AWS の認証を行う
- Docker イメージをビルド & ECR にプッシュする
- ECS にデプロイする
1. OIDC を使用して AWS の認証を行う
GitHub Actions で AWS の認証を行うには、OIDC を使用する必要があります。
まずは AWS コンソール上で、ロールを作成します。
作成したロールには、後続の処理を実行するための権限を付与しておきます。
[AWS_ACCOUNT_ID], [ECR_REPOSITORY_NAME] は適宜置き換えてください。
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "ecr:GetAuthorizationToken",
"Effect": "Allow",
"Resource": "*"
},
{
"Action": [
"ecr:UploadLayerPart",
"ecr:PutImage",
"ecr:InitiateLayerUpload",
"ecr:CompleteLayerUpload",
"ecr:BatchCheckLayerAvailability"
],
"Effect": "Allow",
"Resource": "arn:aws:ecr:ap-northeast-1:[AWS_ACCOUNT_ID]:repository/[ECR_REPOSITORY_NAME]"
}
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"ecs:DeregisterTaskDefinition",
"ecs:RegisterTaskDefinition",
"ecs:ListTaskDefinitions",
"ecs:DescribeTaskDefinition",
"ecs:DescribeServices",
"ecs:UpdateService"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": "iam:PassRole",
"Resource": "arn:aws:iam::[AWS_ACCOUNT_ID]:role/ecsTaskExecutionRole"
}
]
}
iam:PassRole
権限は、ECS タスク実行ロールである ecsTaskExecutionRole
に処理を引き継ぐために必要です。(ecs-task-def.json
で使用します。同じロール実行させる場合は不要です。)
OIDC については以下の記事が参考になりました。
yml ファイルには以下のように記述します。
push:
runs-on: ubuntu-latest
# `permissions` を設定しないと OIDC が使えないので注意
permissions:
id-token: write
contents: read
steps:
- uses: actions/checkout@v3
# AWS 認証
- uses: aws-actions/configure-aws-credentials@v4
with:
aws-region: ap-northeast-1
role-to-assume: ${{ secrets.AWS_IAM_ROLE_ARN }} # GitHub Actions 用の IAM ロールの ARN
はじめは記事を参考に aws-actions/configure-aws-credentials@v1
を使用していましたが、v4
に変更しました。
GitHub Actions の仕様変更により、すべてのアクションで Node16
がデフォルトで使用されるにようになったためです。
2. Docker イメージをビルド & ECR にプッシュする
Docker イメージをビルドするための処理を記述します。
また、同時に ECR にプッシュする処理も記述します。
# Docker イメージを build・push する
- name: build and push docker image to ecr
id: build-image # outputs で参照するために id を設定
env:
# ECR レジストリを `aws-actions/amazon-ecr-login` アクションの `outputs.registry` から取得
REGISTRY: ${{ steps.login-ecr.outputs.registry }}
# イメージを push する ECR リポジトリ名
REPOSITORY: ecs-sample
# 任意のイメージタグ
IMAGE_TAG: latest
run: |
docker build . --tag ${{ env.REGISTRY }}/${{ env.REPOSITORY }}:${{ env.IMAGE_TAG }}
docker push ${{ env.REGISTRY }}/${{ env.REPOSITORY }}:${{ env.IMAGE_TAG }}
# outputs で参照するために `image_id` という名前で出力
echo "image_id=${{ env.REGISTRY }}/${{ env.REPOSITORY }}:${{ env.IMAGE_TAG }}" >> $GITHUB_OUTPUT
このとき、${{ steps.login-ecr.outputs.registry }}
という記述で、aws-actions/amazon-ecr-login
アクションの 出力を参照しています。
他にも、$GITHUB_OUTPUT
という変数を利用することでも、出力結果を受け渡すことが可能です。
かつては、save-state
と set-output
というコマンドを使用していましたが、現在は非推奨となっています。
そのため、$GITHUB_OUTPUT
を使用するようにしましょう。
3. ECS にデプロイする
ECS にデプロイするためには タスクの定義が必要です。
今回はリポジトリ内で管理している ecs-task-def.json
を使用します。
{
"family": "ecs-sample-task",
"executionRoleArn": "arn:aws:iam::[AWS_ACCOUNT_ID]:role/ecsTaskExecutionRole",
"containerDefinitions": [
{
"name": "ecs-sample-container",
"image": "[IMAGE_ID]",
"essential": true,
"portMappings": [
{
"containerPort": 8080,
"hostPort": 8080
}
],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-create-group": "true",
"awslogs-group": "/ecs/ecs-sample-task",
"awslogs-region": "ap-northeast-1",
"awslogs-stream-prefix": "ecs"
}
}
}
],
"requiresCompatibilities": ["FARGATE"],
"networkMode": "awsvpc",
"cpu": "256",
"memory": "512"
}
yml ファイルには以下のように記述します。
- name: Update ECS task definition and ecs-task-def.json with AWS Account ID
run: |
sed -i 's|\[AWS_ACCOUNT_ID\]|${{ env.AWS_ACCOUNT_ID }}|g' ./deploy/ecs-task-def.json
sed -i 's|\[IMAGE_ID\]|${{ steps.build-image.outputs.image_id }}|g' ./deploy/ecs-task-def.json
- name: Fill in the new image ID in the Amazon ECS task definition
id: task-def
uses: aws-actions/amazon-ecs-render-task-definition@v1
with:
task-definition: deploy/ecs-task-def.json
container-name: ecs-sample-container
image: ${{ steps.build-image.outputs.image_id }}
- name: Deploy Amazon ECS task definition
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
with:
task-definition: ${{ steps.task-def.outputs.task-definition }}
service: ecs-sample-service
cluster: ecs-sample-service
wait-for-service-stability: true
force-new-deployment: true
では順番に解説していきます。
まずは、ecs-task-def.json
の [AWS_ACCOUNT_ID]
と [IMAGE_ID]
についてです。
ecs-task-def.json
を Git で管理する場合、次の点に注意する必要があります。
- AWS アカウントなどの機密情報の管理
- 動的に変化する値の管理
そのため、以下のようにして、ecs-task-def.json
に値を埋め込むようにしています。
sed -i 's|\[AWS_ACCOUNT_ID\]|${{ env.AWS_ACCOUNT_ID }}|g' ./deploy/ecs-task-def.json
sed -i 's|\[IMAGE_ID\]|${{ steps.build-image.outputs.image_id }}|g' ./deploy/ecs-task-def.json
そして aws-actions/amazon-ecs-render-task-definition@v1
, aws-actions/amazon-ecs-deploy-task-definition@v1
アクションを使用して、ECS にデプロイします。
注意として、事前に ECS クラスターとサービスを作成しておく必要があります。
また、force-new-deployment
を true
にすることで、新しいタスク定義でデプロイするようにしています。
ポート番号はコンテナ内で使用しているポート番号を指定する必要があります。
今回は ASP.NET Core で作成したアプリケーションを使用しているため、8080
を指定しています。
まとめ
GitHub Actions で Docker イメージをビルドして ECR にプッシュして ECS にデプロイするまでの手順をまとめました。
誤りや改善点などありましたら、ご指摘いただけると幸いです。