LoginSignup
7
4

GitHub Actions で ECR に Docker イメージをプッシュして ECS にデプロイまで

Posted at

はじめに

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 の設定

おおまかな流れは以下のようになります。

  1. OIDC を使用して AWS の認証を行う
  2. Docker イメージをビルド & ECR にプッシュする
  3. 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-stateset-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-deploymenttrue にすることで、新しいタスク定義でデプロイするようにしています。

ポート番号はコンテナ内で使用しているポート番号を指定する必要があります。
今回は ASP.NET Core で作成したアプリケーションを使用しているため、8080 を指定しています。

まとめ

GitHub Actions で Docker イメージをビルドして ECR にプッシュして ECS にデプロイするまでの手順をまとめました。
誤りや改善点などありましたら、ご指摘いただけると幸いです。

7
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
4