GitHub Actions + Amazon ECS CI/CD ハンズオン

OIDC 認証で AWS に接続し ECS Fargate へ自動デプロイするワークフローを構築する

GitHub Actions OIDC 連携 Amazon ECR ECS Fargate 中級 所要時間 75〜90 分 v1.0

📋 概要

このハンズオンでは GitHub Actions から AWS に OIDC(OpenID Connect) で認証し、アクセスキーを使わずに安全に ECR へ Docker イメージをプッシュして ECS Fargate サービスを自動デプロイするワークフローを構築します。

項目内容
対象サービスGitHub Actions、IAM(OIDC)、ECR、ECS Fargate
主な学習内容OIDC 連携・IAM ロール設定・GitHub Actions ワークフロー・ECS デプロイ
所要時間75〜90 分
難易度★★★☆☆(中級者向け)
前提知識Docker・ECS Fargate の基礎・GitHub の基本操作
費用目安約 0〜2 USD(GitHub Actions 無料枠 2000 分/月 あり)
ℹ️ なぜ OIDC?アクセスキー方式との違い
方式安全性運用負荷
IAM ユーザー + アクセスキー(非推奨)キー漏洩リスク・ローテーション必要高い
OIDC(推奨)一時認証トークン・キー不要低い

OIDC では GitHub Actions の実行ごとに一時的な認証情報が発行され、AWS に長期キーを保存する必要がありません。

🏗️ アーキテクチャ

🐙 GitHub リポジトリ
main ブランチへの push がトリガー
↓ GitHub Actions ワークフロー起動
🔑 OIDC トークン発行
GitHub → AWS STS AssumeRoleWithWebIdentity → 一時認証情報
↓ IAM ロール引き受け
⚙️ GitHub Actions Runner(ubuntu-latest)
docker build → ECR push → ECS デプロイ
↓ イメージプッシュ
📦 Amazon ECR
イメージ保管(タグ: commit SHA)
↓ タスク定義更新 → サービス更新
⚡ ECS Fargate サービス
新タスク起動 → ヘルスチェック → 旧タスク停止

作成するリソース

リソース名前 / 値備考
IAM OIDC プロバイダーtoken.actions.githubusercontent.comGitHub Actions 用
IAM ロールgithub-actions-ecs-roleECR・ECS 操作権限
ECR リポジトリhandson-appDocker イメージ保管
GitHub Actions ワークフロー.github/workflows/deploy.yml自動デプロイ定義

✅ 前提条件

⚠️ ECS リソース名の確認

以下の名前を事前にメモしておいてください。ワークフローファイルで使用します。

  • ECS クラスター名(例: handson-ecs-cluster
  • ECS サービス名(例: handson-ecs-service
  • タスク定義名(ECS ハンズオンでは handson-task-def
  • タスク定義内のコンテナ名(ECS ハンズオンでは handson-app

🔑 ステップ 1 ― IAM OIDC プロバイダーの設定

GitHub Actions が AWS に OIDC で認証できるよう、IAM に GitHub の OIDC プロバイダーを登録します。

1-1. IAM コンソールを開く

AWS マネジメントコンソールで 「IAM」 を検索して開きます。左メニューの 「ID プロバイダー」→「プロバイダーを追加」 をクリックします。

1-2. OIDC プロバイダーを設定
プロバイダーのタイプOpenID Connect
プロバイダーの URLhttps://token.actions.githubusercontent.com

「サムプリントを取得」をクリックします(自動入力されます)。

対象者(Audience)sts.amazonaws.com

「プロバイダーを追加」をクリックします。

✅ 確認ポイント

「ID プロバイダー」一覧に token.actions.githubusercontent.com が表示されれば成功です。

🛡️ ステップ 2 ― IAM ロールの作成

GitHub Actions が引き受ける IAM ロールを作成し、ECR・ECS の操作権限を付与します。

2-1. IAM ロールを作成

IAM コンソール → 「ロール」→「ロールを作成」 をクリックします。

信頼されたエンティティタイプウェブアイデンティティ
ID プロバイダーtoken.actions.githubusercontent.com
Audiencests.amazonaws.com
GitHub 組織(自分の GitHub ユーザー名)
GitHub リポジトリhandson-app
GitHub ブランチmain

「次へ」をクリックします。

2-2. 許可ポリシーをアタッチ

以下のポリシーを検索してチェックします。

  • AmazonEC2ContainerRegistryPowerUser — ECR への push 権限
  • AmazonECS_FullAccess — ECS タスク定義・サービス更新権限
⚠️ 本番環境では最小権限を推奨

このハンズオンでは学習目的で AmazonECS_FullAccess を使いますが、本番環境では必要なアクション(ecs:UpdateService など)のみを許可するカスタムポリシーを作成してください。

2-3. ロール名を設定
ロール名github-actions-ecs-role

「ロールを作成」をクリックします。作成後、ロールの ARN(arn:aws:iam::<account-id>:role/github-actions-ecs-role)をメモします。

2-4. 信頼ポリシーを確認・調整

作成したロール → 「信頼関係」タブ → 「信頼ポリシーを編集」で以下の形式になっていることを確認します。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::<account-id>:oidc-provider/token.actions.githubusercontent.com"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
        },
        "StringLike": {
          "token.actions.githubusercontent.com:sub": "repo:<GitHubユーザー名>/handson-app:ref:refs/heads/main"
        }
      }
    }
  ]
}
ℹ️ StringLike の sub 条件について

sub 条件でリポジトリとブランチを絞り込むことで、指定リポジトリ・ブランチからの実行のみが IAM ロールを引き受けられます。* ワイルドカードで全ブランチを許可することも可能です。

✅ 確認ポイント

IAM ロール github-actions-ecs-role が作成され、信頼ポリシーに token.actions.githubusercontent.com が含まれていれば成功です。

📦 ステップ 3 ― Amazon ECR リポジトリの作成

3-1. ECR リポジトリを作成

AWS マネジメントコンソールで 「Elastic Container Registry」 を開き 「リポジトリを作成」 をクリックします。

可視性設定プライベート
リポジトリ名handson-app

「リポジトリを作成」をクリックします。

作成後、URI(<account-id>.dkr.ecr.ap-northeast-1.amazonaws.com/handson-app)をメモします。

⚙️ ステップ 4 ― GitHub リポジトリの設定

GitHub Actions ワークフローが使用する変数と、サンプルアプリのファイルを設定します。

4-1. GitHub Variables を設定

GitHub リポジトリ → 「Settings」→「Secrets and variables」→「Actions」→「Variables」タブ で以下の変数を追加します。

Variable 名
AWS_ACCOUNT_ID(12 桁の AWS アカウント ID)
AWS_REGIONap-northeast-1
ECR_REPOSITORYhandson-app
ECS_CLUSTERhandson-ecs-cluster
ECS_SERVICEhandson-ecs-service
ECS_TASK_DEFINITIONhandson-task-def
CONTAINER_NAMEhandson-app
IAM_ROLE_ARNarn:aws:iam::(アカウントID):role/github-actions-ecs-role
ℹ️ Secrets vs Variables の使い分け

パスワードや認証情報は Secrets(マスキングされる)、アカウント ID やリージョンなど非機密情報は Variables(ログに表示可能)を使います。IAM ロール ARN は Variables で問題ありません。

4-2. サンプルアプリファイルを作成

リポジトリに以下の 2 ファイルを追加します(CodePipeline ハンズオンと同じ内容を使用できます)。

app.js

const http = require('http');
const PORT = process.env.PORT || 3000;
const VERSION = process.env.APP_VERSION || 'v1.0';

const server = http.createServer((req, res) => {
  res.writeHead(200, { 'Content-Type': 'text/plain' });
  res.end(`Hello from GitHub Actions CI/CD! Version: ${VERSION}\n`);
});

server.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});

Dockerfile

FROM node:20-alpine
WORKDIR /app
COPY app.js .
EXPOSE 3000
CMD ["node", "app.js"]

📄 ステップ 5 ― GitHub Actions ワークフローの作成

リポジトリに .github/workflows/deploy.yml を作成します。これが CI/CD の本体です。

5-1. ワークフローファイルを作成

リポジトリに .github/workflows/deploy.yml を以下の内容で作成します。

name: Deploy to Amazon ECS

on:
  push:
    branches:
      - main

env:
  AWS_REGION: ${{ vars.AWS_REGION }}
  ECR_REGISTRY: ${{ vars.AWS_ACCOUNT_ID }}.dkr.ecr.${{ vars.AWS_REGION }}.amazonaws.com
  ECR_REPOSITORY: ${{ vars.ECR_REPOSITORY }}
  ECS_CLUSTER: ${{ vars.ECS_CLUSTER }}
  ECS_SERVICE: ${{ vars.ECS_SERVICE }}
  ECS_TASK_DEFINITION: ${{ vars.ECS_TASK_DEFINITION }}
  CONTAINER_NAME: ${{ vars.CONTAINER_NAME }}

jobs:
  deploy:
    name: Build and Deploy
    runs-on: ubuntu-latest

    # OIDC トークン発行に必要な permissions
    permissions:
      id-token: write   # OIDC トークン取得に必要
      contents: read    # リポジトリ読み取りに必要

    steps:
      # 1. ソースコードを取得
      - name: Checkout
        uses: actions/checkout@v4

      # 2. AWS 認証(OIDC)
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ vars.IAM_ROLE_ARN }}
          aws-region: ${{ env.AWS_REGION }}

      # 3. ECR にログイン
      - name: Login to Amazon ECR
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v2

      # 4. Docker ビルド・タグ付け・ECR プッシュ
      - name: Build, tag, and push image to ECR
        id: build-image
        run: |
          IMAGE_URI=$ECR_REGISTRY/$ECR_REPOSITORY:${{ github.sha }}
          docker build -t $IMAGE_URI .
          docker push $IMAGE_URI
          echo "image=$IMAGE_URI" >> $GITHUB_OUTPUT

      # 5. 現在のタスク定義を JSON ファイルとして取得
      - name: Download current task definition
        run: |
          aws ecs describe-task-definition \
            --task-definition $ECS_TASK_DEFINITION \
            --query taskDefinition > task-definition.json

      # 6. タスク定義の image を新しいものに差し替え
      - name: Render new task definition
        id: task-def
        uses: aws-actions/amazon-ecs-render-task-definition@v1
        with:
          task-definition: task-definition.json
          container-name: ${{ env.CONTAINER_NAME }}
          image: ${{ steps.build-image.outputs.image }}

      # 7. ECS サービスを更新してデプロイ
      - name: Deploy to ECS
        uses: aws-actions/amazon-ecs-deploy-task-definition@v2
        with:
          task-definition: ${{ steps.task-def.outputs.task-definition }}
          service: ${{ env.ECS_SERVICE }}
          cluster: ${{ env.ECS_CLUSTER }}
          wait-for-service-stability: true
5-2. ワークフローの各ステップ解説
ステップ使用 Action処理内容
Checkoutactions/checkout@v4ソースコードを Runner に取得
AWS 認証aws-actions/configure-aws-credentials@v4OIDC トークンで IAM ロールを引き受け一時認証情報を取得
ECR ログインaws-actions/amazon-ecr-login@v2Docker が ECR に push できるよう認証
イメージビルド(run: docker build/push)commit SHA をタグにしてビルド・プッシュ
タスク定義取得(run: aws ecs describe-task-definition)現在のタスク定義を task-definition.json として保存
タスク定義更新aws-actions/amazon-ecs-render-task-definition@v1取得した JSON ファイルのイメージ URI を新しいものに置換
ECS デプロイaws-actions/amazon-ecs-deploy-task-definition@v2新タスク定義を登録しサービスを更新、安定するまで待機
ℹ️ なぜ「タスク定義取得」ステップが必要か

amazon-ecs-render-task-definition Action の task-definition 入力は、ローカルの JSON ファイルパスを期待します(タスク定義名ではありません)。そのため直前に aws ecs describe-task-definition --query taskDefinition で現在のタスク定義を task-definition.json として書き出しておき、そのファイルパスを渡します。Action はこの JSON のうち指定コンテナのイメージ URI だけを差し替え、続く deploy Action が新リビジョンを登録します(登録時に読み取り専用フィールドは自動的に除去されます)。

✅ ステップ 6 ― 動作確認

6-1. main ブランチへ push して起動を確認

ワークフローファイルを含めてリポジトリに push すると Actions が自動起動します。

git add . git commit -m "ci: add GitHub Actions deployment workflow" git push origin main
6-2. GitHub Actions の実行ログを確認

GitHub リポジトリ → 「Actions」タブ → 最新の実行をクリックします。各ステップのログが確認できます。

ステップ正常時の出力例
Configure AWS credentialsAssumed role arn:aws:iam::...:role/github-actions-ecs-role
Login to Amazon ECRLogin Succeeded
Build, tag, and push imagedigest: sha256:...
Deploy to ECSService updated successfully
6-3. コード変更 → 自動デプロイを確認

app.js のバージョンを変更してプッシュし、ECS サービスに新しいバージョンが自動デプロイされることを確認します。

// v1.0 → v2.0 に変更
const VERSION = process.env.APP_VERSION || 'v2.0';
git add app.js git commit -m "feat: update to v2.0" git push origin main

Actions が完了後、ALB の DNS 名にアクセスして Version: v2.0 が表示されれば成功です。

🎉 GitHub Actions CI/CD 完成!

main ブランチへの push だけで、アクセスキー不要の安全な OIDC 認証により自動でビルド・デプロイが実行されます。

🧹 クリーンアップ

⚠️ 課金防止のためクリーンアップを必ず実施してください

ECS Fargate(タスク稼働時間)・ECR(イメージ保管量)で課金が発生します。GitHub Actions 自体は無料枠(2000 分/月)内で完結します。

削除手順(逆順で実施)

  1. ECS サービスを停止・削除
  2. ECR リポジトリを削除
    ECR → handson-app →「削除」
  3. IAM ロールを削除
    IAM → ロール → github-actions-ecs-role →「削除」
  4. IAM OIDC プロバイダーを削除
    IAM → ID プロバイダー → token.actions.githubusercontent.com →「削除」
  5. GitHub Variables を削除(任意)
    GitHub → Settings → Actions → Variables で各変数を削除

学習のまとめ

習得したスキル実践内容
OIDC 認証アクセスキー不要の安全な GitHub → AWS 認証
IAM ロール設計信頼ポリシーの sub 条件でリポジトリ・ブランチを限定
GitHub Actions ワークフローpush トリガー・permissions・env・steps の組み合わせ
ECS 自動デプロイタスク定義の image 差し替え + サービス更新の自動化
ECR 連携commit SHA タグによるイメージ管理