はじめに
クラウドネイティブな環境において、継続的インテグレーション・継続的デリバリー(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プロバイダ
https://token.actions.githubusercontent.com
sts.amazonaws.com
GitHubActionsEcsLearning
Role for Github Actions ecs-github-actions-learning repository.

スクショ内のコードは以下。
{
"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": "*"
}
]
}
secrets.AWS_ROLE_TO_ASSUME の設定
AWS_ROLE_TO_ASSUME
作成したIAMロールであるGitHubActionsEcsLearningのARNをコピーして、AWS_ROLE_TO_ASSUMEのSecretに貼り付ける。
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
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ファイルの作成
{
"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の権限が足りないから。
Open ID Connectのために作ったロールであるGitHubActionsEcsLearningにiam:PassRoleというアクションを実行する権限が足りないという意味のエラー。
"executionRoleArn": "ecsTaskExecutionRole"の権限を与えるという権限も非常に重要な力を持った権限。
「ロールを渡す(PassRole)」 = あるAWSサービスが、別のサービスやリソースに対して「このロールを使って動作してください」と指定すること。
例1: ECSでの「ロール渡し」
GitHubActions → ECS → コンテナ
↓ ↓ ↓
(PassRole) (使用) (実行)
- GitHubActionsが「ECSタスクを作成してください」とリクエスト
- その際に「このタスクはecsTaskExecutionRoleを使って実行してください」とロールを渡す
- ECSがそのロールを使ってタスクを実行
1. GitHubActions(GitHubActionsEcsLearning role)
↓ "ecsTaskExecutionRoleを使ってタスクを実行して"
2. ECS Service
↓ ecsTaskExecutionRoleを使用
3. コンテナ起動・実行
IAMロールの編集 GitHubActionsEcsLearning
編集をクリック。
IAMを選択。
ここまでやったら、Github Actionsを再度実行する。
Re-run this jobをクリック。
以下でアクセスすると、グルグルが終わらない。セキュリティーグループの設定が今足りていないため、ファイヤーウォールでブロックされている。
http://52.194.185.72:8080/api/hello






























