1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

GitHub ActionsでECSに自動デプロイする [ 備忘録 ]

Last updated at Posted at 2025-05-25

はじめに

クラウドネイティブな環境において、継続的インテグレーション・継続的デリバリー(CI/CD)パイプラインの構築は、開発効率の向上とリリースサイクルの高速化に不可欠です。この記事では、GitHub Actionsを使用してAWS ECS(Elastic Container Service)にアプリケーションを自動デプロイする方法を解説します。
特に注目すべき点は、GitHub ActionsとAWSの連携にOpenID Connect(OIDC)を使用することで、長期的なアクセスキーを保存せずに安全な認証を実現していることです。これにより、セキュリティリスクを大幅に軽減しながら、効率的なデプロイパイプラインを構築できます。

前提条件

こちらを作成している。

今回使用したリポジトリ

GitHub Actionsワークフローの定義

cicd.yml

name: API Deploy Pipeline
on:
  push:
    paths:
      # apiフォルダ内とこのyamlファイルに変更があった場合のみcicdを走らせるという条件
      # YAML ファイルのパス指定はリポジトリのルートディレクトリ基準で解釈される
      - '.github/workflows/**'
      - 'api/**'

# 以下のjobsで使える環境変数を定義
env:
  AWS_REGION: ap-northeast-1
  ECS_CLUSTER: my-app-cluster
  ECS_SERVICE: my-app-api-service
  ECS_REPOSITORY: my-app-api
  ECS_TASK_DEFINITION_API: .aws/task-def-api.json

# Open ID Connectという手法を使って認証を行う際に必要な記述
permissions:
  id-token: write
  contents: read

jobs:
# Test/Build
  test-and-build:
    runs-on: ubuntu-latest
    defaults:
      run:
        # apiディレクトリをワーキングディレクトリに指定
        working-directory: api
    steps:
      # Checkout code コードをダウンロードする、コードをワーキングディレクトリに持ってくるみたいなこと
      # usesはgithub actionsのテンプレートを参照する時に使用する。第三者が作った便利なものを使う
      # https://github.com/marketplace/actions/checkout
      - uses: actions/checkout@v4

      - name: Run Tests and Build an Image
        run: docker image build -t temp_api_image:latest .


      - name: Configure AWS credentials
        # https://github.com/marketplace/actions/configure-aws-credentials-action-for-github-actions
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-region: ${{ env.AWS_REGION }}
          # 秘匿性の高い情報を扱いたいときはsecretsという別の領域を使う。githubのsettingで定義する。
          role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }}

      - name: Login to Amazon ECR
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v2

      - name: Push the image to Amazon ECR
        # このステップだけで有効な環境変数
        env:
          ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
        # 仮に命名したTagの名前を書き換える
        # `github.sha`はコミットのハッシュを取得できる 例: 52c94cds
        run: |
          docker image tag temp_api_image:latest $ECR_REGISTRY/$ECR_REPOSITORY:${{ github.sha }}
          docker image push $ECR_REGISTRY/$ECR_REPOSITORY:${{ github.sha }}
# Deploy

Open ID Connectに関するAWS側の設定

IAM IDプロバイダ

image.png

参考資料
https://docs.github.com/en/actions/security-for-github-actions/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services#adding-the-identity-provider-to-aws

https://token.actions.githubusercontent.com
sts.amazonaws.com

image.png

image.png

image.png

image.png

image.png

GitHubActionsEcsLearning
Role for Github Actions ecs-github-actions-learning repository.
image.png

image.png

image.png

image.png

スクショ内のコードは以下。

{
	"Version": "2012-10-17",
	"Statement": [
		{
            "Sid": "Statement1",
            "Effect": "Allow",
            "Action": [
                "ecr:GetDownloadUrlForLayer",
                "ecr:BatchGetImage",
                "ecr:BatchCheckLayerAvailability",
                "ecr:PutImage",
                "ecr:InitiateLayerUpload",
                "ecr:UploadLayerPart",
                "ecr:CompleteLayerUpload",
                "ecr:GetAuthorizationToken"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "ecs:UpdateService",
                "ecs:RegisterTaskDefinition",
                "ecs:ListTaskDefinitions",
                "ecs:DescribeServices"
            ],
            "Resource": "*"
        }
	]
}

image.png

image.png

image.png

secrets.AWS_ROLE_TO_ASSUME の設定

image.png

AWS_ROLE_TO_ASSUME
作成したIAMロールであるGitHubActionsEcsLearningのARNをコピーして、AWS_ROLE_TO_ASSUMEのSecretに貼り付ける。

image.png

image.png

cicd.ymlに追記

name: API Deploy Pipeline
on:
  push:
    paths:
      # apiフォルダ内とこのyamlファイルに変更があった場合のみcicdを走らせるという条件
      # YAML ファイルのパス指定はリポジトリのルートディレクトリ基準で解釈される
      - '.github/workflows/**'
      - 'api/**'

# 以下のjobsで使える環境変数を定義
env:
  AWS_REGION: ap-northeast-1
  ECS_CLUSTER: my-app-cluster
  ECS_SERVICE: my-app-api-service
  ECR_REPOSITORY: my-app-api
  ECS_TASK_DEFINITION_API: .aws/task-def-api.json

# Open ID Connectという手法を使って認証を行う際に必要な記述
permissions:
  id-token: write
  contents: read

jobs:
# Test/Build
  test-and-build:
    runs-on: ubuntu-latest
    defaults:
      run:
        # apiディレクトリをワーキングディレクトリに指定
        working-directory: api
    steps:
      # Checkout code コードをダウンロードする、コードをワーキングディレクトリに持ってくるみたいなこと
      # usesはgithub actionsのテンプレートを参照する時に使用する。第三者が作った便利なものを使う
      # https://github.com/marketplace/actions/checkout
      - uses: actions/checkout@v4

      - name: Run Tests and Build an Image
        run: docker image build -t temp_api_image:latest .


      - name: Configure AWS credentials
        # https://github.com/marketplace/actions/configure-aws-credentials-action-for-github-actions
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-region: ${{ env.AWS_REGION }}
          # 秘匿性の高い情報を扱いたいときはsecretsという別の領域を使う。githubのsettingで定義する。
          role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }}

      - name: Login to Amazon ECR
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v2

      - name: Push the image to Amazon ECR
        # このステップだけで有効な環境変数
        env:
          ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
        # 仮に命名したTagの名前を書き換える
        # `github.sha`はコミットのハッシュを取得できる 例: 52c94cds
        run: |
          docker image tag temp_api_image:latest $ECR_REGISTRY/$ECR_REPOSITORY:${{ github.sha }}
          docker image push $ECR_REGISTRY/$ECR_REPOSITORY:${{ github.sha }}
# Deploy

image.png

ECRにプッシュできている
image.png

定義したstepが通っているのを確認
image.png

ECSへのデプロイ

ECSのアプリをアップデートするとは、Task定義(JSON)を更新し、デプロイするということ。リビジョン番号を更新しデプロイし直す。具体的にはimageを変えるだけ。

cicd.ymlの更新

name: API Deploy Pipeline
on:
  push:
    paths:
      # apiフォルダ内とこのyamlファイルに変更があった場合のみcicdを走らせるという条件
      # YAML ファイルのパス指定はリポジトリのルートディレクトリ基準で解釈される。
      - '.github/workflows/**'
      - 'api/**'

# 以下のjobsで使える環境変数を定義
env:
  AWS_REGION: ap-northeast-1
  ECS_CLUSTER: my-app-cluster
  ECS_SERVICE: my-app-api-service
  ECR_REPOSITORY: my-app-api
  ECS_TASK_DEFINITION_API: .aws/task-def-api.json

# Open ID Connectという手法を使って認証を行う際に必要な記述
permissions:
  id-token: write
  contents: read

jobs:
  # Test/Build
  test-and-build:
    runs-on: ubuntu-latest
    defaults:
      run:
        # apiディレクトリをワーキングディレクトリに指定
        working-directory: api
    steps:
      # `actions/checkout@v4`の役割: リポジトリのコードをワークフローの実行環境にダウンロード(チェックアウト)する
      # usesはgithub actionsのテンプレートを参照する時に使用する。第三者が作った便利なものを使う
      # https://github.com/marketplace/actions/checkout
      - uses: actions/checkout@v4

      - name: Run Tests and Build an Image
        run: docker image build -t temp_api_image:latest .


      - name: Configure AWS credentials
        # https://github.com/marketplace/actions/configure-aws-credentials-action-for-github-actions
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-region: ${{ env.AWS_REGION }}
          # 秘匿性の高い情報を扱いたいときはsecretsという別の領域を使う。githubのsettingで定義する。
          role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }}

      - name: Login to Amazon ECR
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v2

      - name: Push the image to Amazon ECR
        # このステップだけで有効な環境変数
        env:
          ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
        # 仮に命名したTagの名前を書き換える
        # `github.sha`はコミットのハッシュを取得できる 例: 52c94cds
        run: |
          docker image tag temp_api_image:latest $ECR_REGISTRY/$ECR_REPOSITORY:${{ github.sha }}
          docker image push $ECR_REGISTRY/$ECR_REPOSITORY:${{ github.sha }}
          echo $ECR_REGISTRY/$ECR_REPOSITORY:${{ github.sha }} > api-image-uri.txt

      # artifactは記憶領域。ジョブ間でのデータ共有手段。今回はimage名を次のJobで使用するために保存する
      - name: Upload the image uri file as an artifact
        uses: actions/upload-artifact@v4
        with:
          name: api-image-uri
          # usesの中ではワーキングディレクトリは効かないので、ルートパスから入れる必要がある。
          path: api/api-image-uri.txt

  # Deploy
  deploy:
    runs-on: ubuntu-latest
    # needsは `test-and-build`のjobが完了していることが必須という意味。deployの前に走る必要がある。needsは複数入れることも可能。
    needs: [test-and-build]

    steps:
      - name: Checkout
        # `actions/checkout@v4`の役割: リポジトリのコードをワークフローの実行環境にダウンロード(チェックアウト)する
        uses: actions/checkout@v4

      - name: Configure AWS credentials
        # https://github.com/marketplace/actions/configure-aws-credentials-action-for-github-actions
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-region: ${{ env.AWS_REGION }}
          # 秘匿性の高い情報を扱いたいときはsecretsという別の領域を使う。githubのsettingで定義する。
          role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }}

      - name: Download the artifact
        uses: actions/download-artifact@v4
        with:
          name: api-image-uri
          # ここでのpathはダウンロード先のpathになる。api-image-uriの中のデータがartifacts野中にダウンロードされる。
          path: artifacts

      - name: Define the image URI
        # `$GITHUB_ENV`はGitHub Actionsの特殊なファイルで、このファイルに書き込むことで環境変数を設定できます。
        # API_IMAGE_URIが以降で使えるようになる。
        run: |
          echo "API_IMAGE_URI=$(cat artifacts/api-image-url.txt)" >> $GITHUB_ENV

      - name: Fill in the new image URI in the amazon ECS task definition
        # idが設定されたステップの出力は、steps.[id].outputs
        id: render-task-def
        # https://github.com/marketplace/actions/amazon-ecs-render-task-definition-action-for-github-actions
        uses: aws-actions/amazon-ecs-render-task-definition@v1
        with:
          task-definition: ${{ env.ECS_TASK_DEFINITION_API }}
          container-name: api
          image: ${{ env.API_IMAGE_URI }}

      - name: Deploy ECS task
        uses: aws-actions/amazon-ecs-deploy-task-definition@v1
        with:
          task-definition: ${{ steps.render-task-def.outputs.task-definition }}
          service: ${{ env.ECS_SERVICE }}
          cluster: ${{ env.ECS_CLUSTER }}
          # サービスが正常に更新されたことを確認してから次のステップに進みます
          wait-for-service-stability: true

task-def-api.jsonファイルの作成

my-app-api:1に移動し、Jsonをコピー。
image.png

{
    "taskDefinitionArn": "arn:aws:ecs:ap-northeast-1:032559375316:task-definition/my-app-api:1",
    "containerDefinitions": [
        {
            "name": "dummy",
            "image": "public.ecr.aws/nginx/nginx:stable-perl",
            "cpu": 0,
            "portMappings": [
                {
                    "name": "dummy-80-tcp",
                    "containerPort": 80,
                    "hostPort": 80,
                    "protocol": "tcp",
                    "appProtocol": "http"
                }
            ],
            "essential": true,
            "environment": [],
            "environmentFiles": [],
            "mountPoints": [],
            "volumesFrom": [],
            "ulimits": [],
            "logConfiguration": {
                "logDriver": "awslogs",
                "options": {
                    "awslogs-group": "/ecs/my-app-api",
                    "mode": "non-blocking",
                    "awslogs-create-group": "true",
                    "max-buffer-size": "25m",
                    "awslogs-region": "ap-northeast-1",
                    "awslogs-stream-prefix": "ecs"
                },
                "secretOptions": []
            },
            "systemControls": []
        }
    ],
    "family": "my-app-api",
    "executionRoleArn": "arn:aws:iam::032559375316:role/ecsTaskExecutionRole",
    "networkMode": "awsvpc",
    "revision": 1,
    "volumes": [],
    "status": "ACTIVE",
    "requiresAttributes": [
        {
            "name": "com.amazonaws.ecs.capability.logging-driver.awslogs"
        },
        {
            "name": "ecs.capability.execution-role-awslogs"
        },
        {
            "name": "com.amazonaws.ecs.capability.docker-remote-api.1.19"
        },
        {
            "name": "com.amazonaws.ecs.capability.docker-remote-api.1.28"
        },
        {
            "name": "com.amazonaws.ecs.capability.docker-remote-api.1.18"
        },
        {
            "name": "ecs.capability.task-eni"
        },
        {
            "name": "com.amazonaws.ecs.capability.docker-remote-api.1.29"
        }
    ],
    "placementConstraints": [],
    "compatibilities": [
        "EC2",
        "FARGATE"
    ],
    "requiresCompatibilities": [
        "FARGATE"
    ],
    "cpu": "512",
    "memory": "1024",
    "runtimePlatform": {
        "cpuArchitecture": "X86_64",
        "operatingSystemFamily": "LINUX"
    },
    "registeredAt": "2025-05-25T04:47:34.560Z",
    "registeredBy": "arn:aws:iam::032559375316:user/main",
    "enableFaultInjection": false,
    "tags": []
}

そのままだとgithub actionsで失敗してしまうので、以下のように加工する。(不要なものを削除)

{
  "containerDefinitions": [
      {
          "name": "api",
          "image": "<image uri>",
          "cpu": 0,
          "portMappings": [
              {
                  "name": "api-8080-tcp",
                  "containerPort": 8080,
                  "hostPort": 8080,
                  "protocol": "tcp",
                  "appProtocol": "http"
              }
          ],
          "essential": true,
          "environment": [],
          "environmentFiles": [],
          "mountPoints": [],
          "volumesFrom": [],
          "ulimits": [],
          "logConfiguration": {
              "logDriver": "awslogs",
              "options": {
                  "awslogs-group": "/ecs/my-app-api",
                  "mode": "non-blocking",
                  "awslogs-create-group": "true",
                  "max-buffer-size": "25m",
                  "awslogs-region": "ap-northeast-1",
                  "awslogs-stream-prefix": "ecs"
              },
              "secretOptions": []
          },
          "systemControls": []
      }
  ],
  "family": "my-app-api",
  "executionRoleArn": "ecsTaskExecutionRole",
  "networkMode": "awsvpc",
  "volumes": [],
  "placementConstraints": [],
  "requiresCompatibilities": [
      "FARGATE"
  ],
  "cpu": "512",
  "memory": "1024",
  "runtimePlatform": {
      "cpuArchitecture": "X86_64",
      "operatingSystemFamily": "LINUX"
  }
}

コミットしてpushするとdeployのjobでcicdが落ちる。理由はIAMの権限が足りないから。

image.png

Open ID Connectのために作ったロールであるGitHubActionsEcsLearningにiam:PassRoleというアクションを実行する権限が足りないという意味のエラー。
"executionRoleArn": "ecsTaskExecutionRole"の権限を与えるという権限も非常に重要な力を持った権限。

「ロールを渡す(PassRole)」 = あるAWSサービスが、別のサービスやリソースに対して「このロールを使って動作してください」と指定すること。

例1: ECSでの「ロール渡し」

GitHubActions → ECS → コンテナ
     ↓          ↓        ↓
(PassRole)    (使用)    (実行)
  1. GitHubActionsが「ECSタスクを作成してください」とリクエスト
  2. その際に「このタスクはecsTaskExecutionRoleを使って実行してください」とロールを渡す
  3. ECSがそのロールを使ってタスクを実行
1. GitHubActions(GitHubActionsEcsLearning role)
   ↓ "ecsTaskExecutionRoleを使ってタスクを実行して"
2. ECS Service
   ↓ ecsTaskExecutionRoleを使用
3. コンテナ起動・実行

IAMロールの編集 GitHubActionsEcsLearning

image.png

編集をクリック。

image.png

IAMを選択。

image.png

image.png
次へ。

image.png

ここまでやったら、Github Actionsを再度実行する。
Re-run this jobをクリック。

image.png

成功しました。が、アクセスができない問題が残っている。
image.png

image.png

以下でアクセスすると、グルグルが終わらない。セキュリティーグループの設定が今足りていないため、ファイヤーウォールでブロックされている。
http://52.194.185.72:8080/api/hello

image.png

image.png

もう一度叩く
http://52.194.185.72:8080/api/hello

成功しました。
image.png

1
2
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
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?