GHCR / ECR へのプッシュ実践
GHCR と ECR それぞれへの安全な認証(OIDC)と、本番運用に耐えるタグ戦略(sha / semver / latest の使い分け)。
認証は長期キーを避ける
コンテナレジストリへの push で長年の定番だった「長期アクセスキーをシークレットに保存」は、今は推奨されません。
- 漏洩したときの影響範囲が広い
- 失効管理(ローテーション)が属人化しやすい
- 誰がいつ使ったかの追跡がしづらい
短命な認証情報を都度発行する方式(OIDC による一時クレデンシャル)に切り替えるのが現代の標準です。
GHCR への push(GITHUB_TOKEN)
GHCR は GitHub Actions に標準で付いてくる GITHUB_TOKEN で認証できます。
ジョブごとに発行・失効する短命トークンなので、漏洩リスクが小さい上、設定の手間がありません。
packages: write 権限を付けるのを忘れずに。
プライベートリポジトリの場合は対象パッケージ側で「このリポジトリからの push を許可」を設定する必要があります。
permissions:
contents: read
packages: write
jobs:
push:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}ECR への push(OIDC で IAM ロール引き受け)
AWS 側では GitHub Actions を OIDC プロバイダとして登録し、リポジトリや環境を条件にした IAM ロールを作成しておきます。
ワークフロー側では aws-actions/configure-aws-credentials でロールを引き受けると、AK/SK を保存せずに ECR にアクセスできます。
AWS コンソール / CDK / Terraform で設定する要点:
- IAM OIDC プロバイダ:
https://token.actions.githubusercontent.com - ロールの信頼ポリシー
Conditionにtoken.actions.githubusercontent.com:sub=repo:OWNER/REPO:ref:refs/heads/mainなどを指定し、対象リポジトリとブランチを限定 する - ロールの権限ポリシーは
ecr:GetAuthorizationToken,ecr:BatchCheckLayerAvailability,ecr:InitiateLayerUpload,ecr:UploadLayerPart,ecr:CompleteLayerUpload,ecr:PutImageを対象リポジトリだけに付与
permissions:
id-token: write # OIDC に必須
contents: read
jobs:
push:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/github-actions-ecr-push
aws-region: ap-northeast-1
- uses: aws-actions/amazon-ecr-login@v2
id: ecr
- uses: docker/build-push-action@v6
with:
context: .
push: true
tags: ${{ steps.ecr.outputs.registry }}/myapp:${{ github.sha }}タグ戦略
どのタグを付けるかは運用の肝です。
用途ごとに分けましょう。
| タグ | 用途 | 可変性 |
|---|---|---|
sha-<commit>(例: sha-a1b2c3d) |
本番デプロイが参照する不変 ID。どのコミットから作ったか一意に分かる | 不変(上書き禁止) |
v1.2.3(完全 semver) |
リリース時のマーカー。通常 git タグと一致させる | 不変 |
v1.2, v1 |
メジャー / マイナーで最新版を指すポインタ | 可変(後方互換があれば) |
latest |
デフォルトブランチの HEAD を指すポインタ | 可変 |
pr-<number>, branch-<name> |
レビュー用の一時タグ | 可変。古いものは自動削除設定が望ましい |
本番環境は必ず不変タグ(sha-* または完全 semver)を参照 してください。
latest は便利ですが「いつ何が入るか分からない」ため本番参照は事故のもとです。
metadata-action でのタグ生成例
上記の方針を docker/metadata-action で表現すると次のようになります。
これを docker/build-push-action の tags: に繋げるだけで一気に必要なタグが付きます。
- id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository }}
tags: |
# 不変 ID: 本番はこれを指す
type=sha,prefix=sha-,format=short
# git タグからの完全 semver + ポインタ
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
# 開発用ポインタ
type=ref,event=branch
type=ref,event=pr,prefix=pr-
# デフォルトブランチのみ latest
type=raw,value=latest,enable={{is_default_branch}}その他の実務ポイント
- 不変タグの上書き防止: GHCR はリポジトリ設定で immutable tag を有効化できる。ECR は「タグ変更不可」設定 + ライフサイクルポリシーで古い sha タグをクリーンアップ
- 脆弱性スキャン結果と合わせる: push したイメージに対して
aquasecurity/trivy-actionを走らせ、問題があればタグごと削除する運用も可能 - サイズと転送量:
registryキャッシュ + 本番イメージを同じレジストリに置くと転送コストが下がる。ECR の場合はアカウントとリージョンも揃える - マルチアーキ配信:
docker buildx imagetools createでマニフェストリストを作る(cicd-multi-arch-build参照)。マニフェストリスト自体には脆弱性は含まれないので、スキャンは各アーキのイメージに対して行う - プライベート+プル時の認証: 本番クラスタから pull するときも GitHub Actions OIDC(EKS / ECS の IRSA や Task Role)で ECR 認証を回す。長期キーをノードに配らない
