「GitHub Actions」と「AWS ECR」を連携してセキュアなCI/CDパイプラインを構築してみよう

2025年9月24日(水)
田中 智明
第13回の今回は、「GitHub Actions」と「AWS ECR」を連携して、セキュアで実用的なCI/CDパイプラインを構築する方法について解説します。

はじめに

第12回では「GitHub Actions」を使ってコンテナイメージを自動ビルドし、「GitHub Container Registry」(ghcr)へプッシュする方法を学びました。GitHub内で完結する開発フローとしては非常に便利ですが、企業として本格的なプロダクション環境を運用する場合には、以下のようないくつかの課題が出てくることでしょう。

1. コスト
ghcrのプライベートリポジトリはGitHubのプランに応じて使用できるストレージ量と転送量が設定されています。大規模なKubernetesクラスターを使用すると多数のPodが頻繁にイメージをpullするため、利用可能な使用量を超える可能性が考えられます。

2. GitHubへの依存
ghcrはGitHub Actionsとの連携が非常にスムーズである反面、GitHubエコシステムに強く依存します。もしソースコード管理をGitHub以外に移す場合、もしくは、そもそもGitHubを使用できない環境ではghcrを単体で利用するメリットは薄れます。

3. 企業のコンプライアンス要件
規制の厳しい業界では、データの保管場所や暗号化、監査ログの要件があります。しかし、ghcrでは「誰がコンテナイメージを管理したか」は分かりますが、「誰がコンテナイメージを利用したか」を把握できません。監査ログのイベント(packges)にログから確認できる項目が列挙されています。

ghcrが持つ課題を考慮すると、より大規模で本格的な運用ではパブリッククラウドの利用が視野に入ります。近年、DevOpsとの親和性やスケーラビリティを理由にAWSのようなパブリッククラウドの採用が進んでおり、そのネイティブなコンテナレジストリを利用することはインフラ全体の一貫性を高め、前述の課題を解決する自然なアプローチと言えます。

本記事では、AWSを題材に、その中心的なサービスである「AWS ECR(Elastic Container Registry)」(以下、ECR)とGitHub Actionを連携して、セキュアで実用的なCI/CDパイプラインを構築する方法について解説していきます。

AWSアカウントの作成

ECRを使用するには、まずAWSアカウントが必要です。すでにAWSアカウントをお持ちの方は、この項をスキップして進んでください。

個人アカウントの作成

AWSアカウントの作成は無料ですが、クレジットカード情報の登録が必要です。多くのAWSサービスを試すことができる12ヶ月間の無料利用枠が提供されています。

アカウント作成の詳細な手順については、下記のAWS公式ドキュメントを参照ください。
AWSアカウントの作成方法

請求設定の確認

AWSアカウントの作成後には、予期しない課金を避けるため、以下の設定を確認することを強く推奨します。

1. 請求アラートの設定
使用料金が一定額を超えた場合に通知を受け取られるように設定します。
請求アラートの作成

2. AWS Budgetの設定
月額予算を設定してコストを管理します。
AWS Budget の使用開始

3. 無料利用枠の確認
ECRの無料利用枠についても確認しておきましょう。
AWS無料利用枠

セキュリティ設定

請求設定の確認が終わったら、続けてセキュリティを強化するために以下の設定を行いましょう。

1. MFA(多要素認証)の有効化
ルートユーザーには必ずMFAを設定します。
AWSルートユーザーのMFAを有効にする

2. IAMユーザーの作成
日常的な操作にはIAMユーザーを使用し、ルートユーザーの使用は最小限に抑えます。
初回IAM管理者の作成

リージョンの選択

AWSは世界中にリージョン(データセンターの地理的な場所)が存在します。利用可能なリージョンの一覧は公式ドキュメントで確認できます。日本から利用する場合は、一般的に以下のリージョンを選択します。

  • ap-northeast-1(東京): 日本国内からの最適なアクセス
  • ap-northeast-3(大阪): 災害対策やマルチリージョン構成

本記事では、東京リージョン(ap-northeast-1)を使用します。

料金について

AWS ECRの料金は、主に以下の要素で構成されます。

  • ストレージ料金(保存されているイメージのサイズに基づく)
  • データ転送料金(イメージのpush/pullに基づく)

12ヶ月間の無料利用枠では、月間500MBのストレージが利用できます。詳細な料金については「Amazon ECRの料金」をご確認ください。

ECRとは

ECRは、AWSが提供するフルマネージドなDockerコンテナレジストリサービスです。コンテナイメージの保存、管理、配布を安全かつ効率的に行えます。

主な特徴

1. AWSサービスとの統合
「ECS(Elastic Container Service)」「EKS(Elastic Kubernetes Service)」「Lambda」など、AWSのコンテナサービスとシームレスに連携できます。

2. セキュリティ機能
ECRへのアクセス制御および実行可能な操作はIAMによりきめ細やかに設定できます。ECRはAmazon ECRが管理するS3にイメージを保存します。保存されたデータは暗号化されているため、もし万が一S3を覗かれたとしても安全です。ECRには保管中のイメージに脆弱性スキャンを実行できます。

3. 高可用性と性能
ECRは自動スケーリングや複数のAZにわたるレプリケーションの機能を備え、99.9%のサービス・レベル・アグリーメント(SLA)が適用されます。

4. ライフサイクル管理
ECRにはイメージの自動削除ポリシーを設定できます。「古いイメージから削除」「最新から何世代残す」など、要件に応じて設定可能です。

プライベートリポジトリとパブリックリポジトリ

ECRには2つのタイプがあります。

リポジトリタイプ 説明
ECR Private プライベートなコンテナイメージを保存するサービス。アクセスにはIAM認証が必要で、組織内でのイメージ共有に適する
ECR Public パブリックなコンテナイメージを保存・配布するサービス。Docker Hubのように誰でもアクセス可能なイメージを公開できる

本記事では、一般的な用途である「ECR Private」を使用します。

AWS認証の準備

GitHub ActionsからECRにアクセスするには、適切な認証設定が必要です。ここでは、セキュリティのベストプラクティスである「OpenID Connect (OIDC)」を使った方法について、その仕組みや設定手順を図解を交えながら詳しく解説します。

OpenID Connect(OIDC)による認証

OIDC認証は、IAMユーザーのキーを使わずに、GitHub Actionsのワークフローへ一時的なAWSへのアクセス権を付与する仕組みです。

OIDCのコンセプト:身分証明書と一時入館証

OIDCによる認証を、現実世界の「身分証明書」と「入館証」に例えてみましょう。

1. GitHubが「市役所」
GitHub Actionsがワークフローを実行すると、GitHubは「このワークフローは正真正銘、あなたのリポジトリから実行されました」ということを証明する「身分証明書(OIDCトークン)」を動的に発行します。

2. AWSが「オフィスビル」
AWS側では、あらかじめ「〇〇リポジトリのmainブランチからの依頼であれば信頼する」というルール(信頼ポリシー)を設定した「受付(IAMロール)」を用意しておきます。

3. 認証プロセス

  • GitHub Actionsは、GitHub市役所が発行した「身分証明書」を持ってAWSビルの「受付」に行く
  • 受付は、その身分証明書が信頼できる発行元(GitHub)のもので、かつルールに合致しているか(リポジトリやブランチは正しいか)を厳格にチェックする
  • 確認が取れると、受付はビルの中(AWSリソース)に入るための「一時的な入館証(一時的なAWS認証情報)」をGitHub Actionsに渡す

この仕組みにより、パスワードのような固定の認証情報(恒久的なキー)をGitHubに保存する必要がなくなり、安全性が飛躍的に向上します。実際の流れを図にすると、以下のようになります。

それでは、この仕組みをAWS上で設定していきましょう。

ステップ1: OIDC IDプロバイダーをAWSに登録する

まず、AWSに「GitHubを信頼できる身分証明書の発行元(IDプロバイダー)ですよ」と教えてあげる必要があります。この設定は一度だけ行います。

ステップ2: GitHub Actions専用のIAMロールを作成する

次に、GitHub ActionsがAWS内で担当する「役割(ロール)」を作成します。このロールには2種類のポリシー(権限設定)を設定します。

  • 信頼ポリシー: 誰がこのロールになることを許可するかを定義
  • 権限ポリシー: このロールになった後、何をすることを許可するかを定義

ここでは、まず最小権限の原則に従って権限が空のロールを作成し、その後で本当に必要な権限だけを持つインラインポリシーを追加する、という安全な手順を踏みます。

1. ロールの作成
下記のドキュメントを参考にカスタム信頼ポリシーのロールを作成します。
カスタム信頼ポリシーを使用してロールを作成する

  • ロール名: GitHubActionsRole
  • 信頼ポリシー:
       {
         "Version": "2012-10-17",
         "Statement": [
           {
             "Effect": "Allow",
             "Principal": {
               "Federated": "arn:aws:iam::ACCOUNT_ID:oidc-provider/token.actions.githubusercontent.com"   # (1)
             },
             "Action": "sts:AssumeRoleWithWebIdentity",
             "Condition": {
               "StringEquals": {
                 "token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
                 "token.actions.githubusercontent.com:sub": "repo:GITHUB-OWNER/GITHUB-REPOSITORY:ref:refs/heads/main"   # (2)
               }
             }
           }
         ]
       }
  • ※(1): ACCOUNT_IDにはAWSアカウントのID(12桁の整数)で置き換えてください。
  • ※(2): GITHUB-OWNER/GITHUB-REPOSITORYはECRにGitHub Actionsを設定するリポジトリ名とオーナーで置き換えてください(例:https://github.com/GITHUB-OWNER/GITHUB-REPOSITORY)。

信頼ポリシーの解説

キー 説明
Principal このロールを引き受けられる「誰か」を定義する。ここでは、ステップ1で登録したGitHubのOIDCプロバイダーを指定
Action プリンシパル(GitHub Actions)に「sts:AssumeRoleWithWebIdentity(Web IDを使ってロールを引き受ける)」操作を許可
Condition 最も重要な「条件」。この条件を満たさない限り、たとえ同じOIDCプロバイダーからの要求であっても弾かれる
...:aud OIDCトークンの発行先(audience)がsts.amazonaws.com(AWSの認証サービス)であることを要求
...:sub OIDCトークンの発行元(subject)をrepo:オーナー名/リポジトリ名:ref:refs/heads/mainのように、特定のリポジトリのmainブランチに限定する。これにより、他のリポジトリやブランチからのワークフローがこのロールを不正に利用することを防ぐ

2. 権限ポリシーの設定
ロールに特定のリソースに対する操作を許可または拒否を設定します。

ECRへのイメージプッシュに必要な最小限の権限を付与します。セキュリティを強化するため、Resourceには対象のリポジトリARNを具体的に指定することが重要です。プライベートリポジトリにイメージのプッシュを許可する設定は公式ドキュメントに記載されています。
Amazon ECR プライベートリポジトリにイメージをプッシュするための IAM アクセス許可

  1. IAMコンソールから「GitHubActionsRole」を開く
  2. 「許可を追加」から「インラインポリシーを作成」
  3. ポリシーを入力
       {
         "Version": "2012-10-17",
         "Statement": [
           {
             "Effect": "Allow",
             "Action": "ecr:GetAuthorizationToken",   # (1)
             "Resource": "*"
           },
           {
             "Effect": "Allow",
             "Action": [
               "ecr:CompleteLayerUpload",
               "ecr:UploadLayerPart",
               "ecr:InitiateLayerUpload",
               "ecr:BatchCheckLayerAvailability",
               "ecr:PutImage",
               "ecr:BatchGetImage"   # (2)
             ],
             "Resource": "arn:aws:ecr:ap-northeast-1:ACCOUNT_ID:repository/my-app"   # (3)
           }
         ]
       }
  • ※(1): 公式ドキュメントに記載されているポリシーではecr:GetAuthorizationTokenの定義が他の操作と一緒に定義されていますが、この操作は特定のリポジトリに許可するものではなく、プライベートリポジトリ全体で認証トークンを要求する操作のため"Resource": "*"を指定しています。
  • ※(2): 公式ドキュメントに記載されているポリシーにはecr:BatchGetImageの記載はありません。通常のdocker pushであればこの権限は不要ですが、GitHub Actionsで使用するdocker/build-and-pushアクションではリポジトリからメタ情報を取得するため、この権限が必要になります。
  • ※(3): ACCOUNT_IDはAWSアカウントのID(12桁の整数)で置き換えてください。

3. ポリシー名を付けて保存

ECRリポジトリの作成

コンテナイメージを保存するECRリポジトリを作成します。
プライベートリポジトリの作成方法

  1. ECRコンソールを開く
  2. 左メニューの「Private repository」から「Repositories」をクリック
  3. 「リポジトリを作成」をクリック
  4. リポジトリ名: my-appを入力し「作成」をクリック

作成されたリポジトリのURIは以下の形式になります。

ACCOUNT_ID.dkr.ecr.REGION.amazonaws.com/my-app

コストに注意
ECRはデフォルトの状態だとプッシュしたイメージを全て保存します。使用したデータ量に対して課金が発生するため、不要なイメージは削除するようにしましょう。ライフサイクルポリシーの設定を推奨します。
ライフサイクルポリシーを使用したイメージのクリーンアップの自動化

GitHub Actionsワークフローの作成

それでは、実際にECRにプッシュするワークフローを作成しましょう。コンテナのソースコードは第12回のコードを使用します。

基本的なワークフロー

.github/workflows/ecr-push.yml
name: Build and Push to ECR

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

env:
  AWS_REGION: ap-northeast-1
  ECR_REPOSITORY: my-app

jobs:
  build-and-push:
    runs-on: ubuntu-latest
    permissions:
      id-token: write   # (1) OIDCトークン取得のため
      contents: read    # リポジトリのコードを読み取るため

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4   # (2)
        with:
          role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/GitHubActionsRole
          aws-region: ${{ env.AWS_REGION }}

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

      - name: Extract metadata
        id: meta
        uses: docker/metadata-action@v5   # (4)
        with:
          images: ${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_REPOSITORY }}
          tags: |
            type=ref,event=branch
            type=ref,event=pr
            type=sha,prefix={{branch}}-

      - name: Build and push Docker image
        uses: docker/build-push-action@v5
        with:
          context: .
          push: ${{ github.event_name != 'pull_request' }}   # (5)
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}

ワークフローの解説

1. 権限設定
id-token: writeにより、OIDCトークンの取得が可能になります。

2. AWS認証
aws-actions/configure-aws-credentialsアクションを使用して、IAMロールを引き受けます。

3. ECRログイン
aws-actions/amazon-ecr-loginアクションでECRへの認証を行います。

4. メタデータ抽出
ブランチ名から適切なタグを自動生成します。

5. 条件付きプッシュ
プルリクエストではビルドのみ行い、メインブランチへのプッシュ時のみECRにプッシュします。

Secretsの設定

GitHub Secretsに以下の値を設定します。

  1. リポジトリの Settings → Secrets and variables → Actions を開く
  2. 「New repository secret」をクリック
  3. AWS_ACCOUNT_IDを追加し、AWSアカウントIDを設定

実行結果の確認

GitHub Actionsでの確認

ワークフローを実行すると、以下のような流れで処理が進みます。

  1. 認証: AWS IAMロールの引き受け
  2. ECRログイン: ECRへの認証
  3. ビルド: Dockerイメージの作成
  4. スキャン: 脆弱性チェック(オプション)
  5. プッシュ: ECRへのイメージプッシュ

ECRでの確認

AWS ECRコンソールで、プッシュされたイメージを確認できます。

  1. ECRコンソールを開く
  2. 該当リポジトリを選択
  3. イメージ一覧でタグと詳細を確認

イメージは以下のような形式でpullできます。

aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin ACCOUNT_ID.dkr.ecr.ap-northeast-1.amazonaws.com
docker pull ACCOUNT_ID.dkr.ecr.ap-northeast-1.amazonaws.com/my-app:main

セキュリティ強化:脆弱性スキャン

ECRには、コンテナイメージの脆弱性をスキャンする機能が組み込まれており、「基本スキャン」と「拡張スキャン」の2種類から選択できます。

基本スキャン
  • 料金: 無料
  • 設定: リポジトリごとに「プッシュ時にスキャン」または「手動スキャン」として設定する
  • 機能: イメージがプッシュされたタイミング、または手動でスキャンを実行した際に、OSパッケージの脆弱性を検出する
拡張スキャン
  • 料金: 有料(Amazon Inspectorの料金(https://aws.amazon.com/jp/inspector/pricing/)に準ずる)
  • 設定: プライベートレジストリ全体に対して有効にする。Amazon Inspectorのサービスと統合されている
  • 機能: 基本スキャンの機能に加え、プログラミング言語のパッケージも検出する。また、イメージをプッシュした後も新たな脆弱性が発見されるたびに継続的に再スキャンを実行できる。AWSはこちらを強く推奨

デフォルトではプッシュ時の自動スキャンが有効になっていません。自動スキャンを有効化するには、スキャンの種類に応じて下記の手順を行います。

  1. ECRコンソールの左側メニュー「Features & Settings」>「Scanning」を選択
  2. 「基本スキャン」または「拡張スキャン」を選択
  3. 基本スキャンを選択した場合: 「プッシュ時にスキャンするリポジトリ」フィルターを設定し、自動スキャンしたいリポジトリを指定して保存
  4. 拡張スキャンを選択した場合: スキャン頻度(プッシュ時、継続的など)を設定して保存

設定後、再度GitHub Actionsを実行し、コンテナイメージをプッシュするとコンテナイメージにスキャンが実行されます。実行結果を確認するには、リポジトリ(my-app)> 最新のイメージと進み、スキャンと脆弱性を確認してみてください。スキャンのステータス、スキャンの完了日時を見ると自動スキャンが実行されたことが分かります。

拡張スキャンは有料オプションなので、有効化する場合には注意してください。もし無料で同じようなスキャンを行いたい場合には、第12回で解説したセキュリティスキャンの追加を参考に、GitHub ActionsでTrivyを動かしスキャンするという手もあります。

おわりに

GitHub ActionsとECRを連携させることで、セキュアで実用的なCI/CDパイプラインを構築できました。OIDC認証により、長期間有効なクレデンシャルを使用することなく、安全にAWSリソースにアクセスできます。

今回紹介した構成は、エンタープライズ環境での実用に耐える設計となっています。IAMロールによる最小権限の原則、脆弱性スキャンの自動化、イメージライフサイクルの管理など、プロダクション運用に必要な要素を含んでいます。

さらに高度な運用を目指す場合は、以下のような拡張も検討できます。

  • 複数環境(dev/staging/prod)への段階的デプロイ
  • ECSやEKSとの自動連携
  • Slack/Teams通知の統合
  • カスタムセキュリティポリシーの適用

次回は、実際にECRにプッシュしたコンテナイメージをEKS(Elastic Kubernetes Service)にデプロイし、本格的なコンテナアプリケーションの運用環境を構築していきます。

日本仮想化技術株式会社
ソーシャルゲーム業界で10年間インフラエンジニアとして活動し、現在は日本仮想化技術でOpsエンジニアを担当。DevOps支援サービス「かんたんDevOps」では仕組み作りや導入支援、技術調査などを行っている。

連載バックナンバー

システム開発技術解説
第13回

「GitHub Actions」と「AWS ECR」を連携してセキュアなCI/CDパイプラインを構築してみよう

2025/9/24
第13回の今回は、「GitHub Actions」と「AWS ECR」を連携して、セキュアで実用的なCI/CDパイプラインを構築する方法について解説します。
システム開発技術解説
第12回

CI/CDサービス「GitHub Actions」で「コンテナビルド」を自動化してみよう

2025/9/2
第12回の今回は、「GitHub Actions」によるコンテナビルドと「ghcr」への自動プッシュ、「Trivy」によるセキュリティスキャンについて解説します。
システム開発技術解説
第11回

Ops視点で見直す「コンテナビルド」の落とし穴とベストプラクティス

2025/8/5
第11回の今回は、Ops視点から見たコンテナビルドの落とし穴とベストプラクティスを解説します。

Think ITメルマガ会員登録受付中

Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。

Think ITメルマガ会員のサービス内容を見る

他にもこの記事が読まれています