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

- 1 はじめに
- 2 AWSアカウントの作成
- 2.1 個人アカウントの作成
- 2.2 請求設定の確認
- 2.3 セキュリティ設定
- 2.4 リージョンの選択
- 2.5 料金について
- 3 ECRとは
- 3.1 主な特徴
- 3.2 プライベートリポジトリとパブリックリポジトリ
- 4 AWS認証の準備
- 4.1 OpenID Connect(OIDC)による認証
- 4.2 ステップ1: OIDC IDプロバイダーをAWSに登録する
- 4.3 ステップ2: GitHub Actions専用のIAMロールを作成する
- 5 ECRリポジトリの作成
- 6 GitHub Actionsワークフローの作成
- 6.1 基本的なワークフロー
- 6.2 ワークフローの解説
- 6.3 Secretsの設定
- 7 実行結果の確認
- 7.1 GitHub Actionsでの確認
- 7.2 ECRでの確認
- 8 セキュリティ強化:脆弱性スキャン
- 9 おわりに
はじめに
第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 アクセス許可】
- IAMコンソールから「GitHubActionsRole」を開く
- 「許可を追加」から「インラインポリシーを作成」
- ポリシーを入力
{ "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リポジトリを作成します。
プライベートリポジトリの作成方法
- ECRコンソールを開く
- 左メニューの「Private repository」から「Repositories」をクリック
- 「リポジトリを作成」をクリック
- リポジトリ名:
my-app
を入力し「作成」をクリック
作成されたリポジトリのURIは以下の形式になります。
ACCOUNT_ID.dkr.ecr.REGION.amazonaws.com/my-app
コストに注意
ECRはデフォルトの状態だとプッシュしたイメージを全て保存します。使用したデータ量に対して課金が発生するため、不要なイメージは削除するようにしましょう。ライフサイクルポリシーの設定を推奨します。
【ライフサイクルポリシーを使用したイメージのクリーンアップの自動化】
GitHub Actionsワークフローの作成
それでは、実際にECRにプッシュするワークフローを作成しましょう。コンテナのソースコードは第12回のコードを使用します。
基本的なワークフロー
.github/workflows/ecr-push.ymlname: 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に以下の値を設定します。
- リポジトリの Settings → Secrets and variables → Actions を開く
- 「New repository secret」をクリック
AWS_ACCOUNT_ID
を追加し、AWSアカウントIDを設定
実行結果の確認
GitHub Actionsでの確認
ワークフローを実行すると、以下のような流れで処理が進みます。
- 認証: AWS IAMロールの引き受け
- ECRログイン: ECRへの認証
- ビルド: Dockerイメージの作成
- スキャン: 脆弱性チェック(オプション)
- プッシュ: ECRへのイメージプッシュ
ECRでの確認
AWS ECRコンソールで、プッシュされたイメージを確認できます。
- ECRコンソールを開く
- 該当リポジトリを選択
- イメージ一覧でタグと詳細を確認
イメージは以下のような形式で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はこちらを強く推奨
デフォルトではプッシュ時の自動スキャンが有効になっていません。自動スキャンを有効化するには、スキャンの種類に応じて下記の手順を行います。
- ECRコンソールの左側メニュー「Features & Settings」>「Scanning」を選択
- 「基本スキャン」または「拡張スキャン」を選択
- 基本スキャンを選択した場合: 「プッシュ時にスキャンするリポジトリ」フィルターを設定し、自動スキャンしたいリポジトリを指定して保存
- 拡張スキャンを選択した場合: スキャン頻度(プッシュ時、継続的など)を設定して保存
設定後、再度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)にデプロイし、本格的なコンテナアプリケーションの運用環境を構築していきます。
連載バックナンバー
Think ITメルマガ会員登録受付中
全文検索エンジンによるおすすめ記事
- CI/CDサービス「GitHub Actions」で「コンテナビルド」を自動化してみよう
- Oracle Cloud Hangout Cafe Season4 #3「CI/CD 最新事情」(2021年6月9日開催)
- 「GitHub」にブランチ保護、Dependabot、Secret Scanningを設定してみよう
- CI/CDを実現するツール「GitHub Actions」を使ってみよう
- Pulumi Kubernetes Operatorを活用してPulumiのCI/CDを実現しよう
- Pulumiの最新機能「Pulumi ESC」を使ってみよう
- TFXを使った機械学習パイプラインの構築(デプロイ編)
- テストコードを書いて「GitHub Actions」でCIを実行してみよう
- コンテナは場所を選ばない!「オンプレ or クラウド×コンテナ」(後編)
- CNDT2020シリーズ:CAのインフラエンジニアが解説するKubernetesネイティブなCI/CD