デプロイは「動けばいい」ではなく、安全に・繰り返し可能に・鍵を漏らさずに回す仕組みが本番品質を決めます。長期シークレットをGitHubに置く、latestタグで上書きする、失敗時に戻せない——どれも事故の温床です。
この記事は、Microsoft LearnのGitHub Actionsドキュメントに忠実に、Azure Container Apps(ACA)の本番CI/CDを組み立てます。私はAWSでOIDCによる鍵レスCI/CDとFargateのBlue/Greenデプロイを本番運用してきました。「短命トークンを信頼する」「失敗したら切り替えない」というデプロイの原則は、Azureでも同型です。ACA全体は Azure Container Apps 本番運用ガイド を参照してください。
全体像:コミット → 新リビジョン
Azure Container Apps allows you to use GitHub Actions to publish revisions to your container app. As commits are pushed to your GitHub repository, a workflow is triggered which updates the container image in the container registry. Azure Container Apps creates a new revision based on the updated container image.(— Publish revisions with GitHub Actions)
流れはシンプル:コミット → ワークフロー起動 → イメージをビルドしてレジストリへ → ACAが新リビジョンを作成。ACAのリビジョンは不変スナップショットなので、デプロイ=新リビジョンへの切り替えです。
公式アクション:azure/container-apps-deploy-action
To build and deploy your container app, you add the
azure/container-apps-deploy-actionaction to your GitHub Actions workflow.
このアクションは3つのシナリオに対応します。
- Dockerfileからビルドしてデプロイ
- Dockerfile無しでソースからビルドしてデプロイ(.NET・Java・Node.js・PHP・Python)
- 既存のコンテナイメージをデプロイ
既存イメージをデプロイ(最も制御しやすい)
ビルドを別ステップで行い、**一意タグ(コミットSHA)**のイメージをデプロイする形が、本番では最も追跡しやすい:
- name: Build and deploy Container App
uses: azure/container-apps-deploy-action@v1
with:
acrName: myregistry
containerAppName: my-container-app
resourceGroup: my-rg
imageToDeploy: myregistry.azurecr.io/app:${{ github.sha }}
公式も警告しています。
If you're building a container image in a separate step, make sure you use a unique tag such as the commit SHA instead of a stable tag like
latest.
latestは使わず、${{ github.sha }}等で一意に。これはキャッシュ問題と「どのリビジョンが何のコードか分からない」事故を防ぎます。
認証:サービスプリンシパル vs OIDC(鍵レス)
最短だが負債になりうる:サービスプリンシパル
公式のスターターは、サービスプリンシパルのJSON資格情報をAZURE_CREDENTIALSシークレットに置く方式です。
az ad sp create-for-rbac --name my-app-credentials \
--role contributor \
--scopes /subscriptions/<SUBSCRIPTION_ID>/resourceGroups/my-container-app-rg \
--json-auth
動きますが、長期の資格情報(パスワード)をGitHubに保存します。漏洩リスクとローテーション負担が残る。学習やPoCならよいですが、本番では次のOIDCにします。
本番の正解:OIDC(フェデレーション資格情報)
GitHub ActionsのOIDCトークンをMicrosoft Entraのフェデレーション資格情報で信頼し、長期シークレットを1つも置かない。azure/loginはclient-id/tenant-id/subscription-idだけで認証します。
name: deploy-aca
on:
push: { branches: [main] }
permissions:
id-token: write # OIDCトークン発行に必須
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Log in to Azure (OIDC, keyless)
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- name: Build and deploy Container App
uses: azure/container-apps-deploy-action@v1
with:
acrName: myregistry
containerAppName: my-container-app
resourceGroup: my-rg
imageToDeploy: myregistry.azurecr.io/app:${{ github.sha }}
フェデレーション資格情報は、対象のEntraアプリ/マネージドIDに「このリポジトリのmainブランチからのトークンを信頼する」という条件(subject)を登録するだけ。鍵の保存・ローテーションが消えます。考え方はAWSのIAM OIDCプロバイダと完全に同型です(GitHub Actions OIDCで鍵を捨てる)。
レジストリ認証:push と pull を分ける
CI(push)とアプリ(pull)は、それぞれ別の最小権限IDで認証します。
To pull images, Azure Container Apps uses either managed identity (recommended) or admin credentials to authenticate with the Azure Container Registry.(— github-actions)
アプリのイメージpullはマネージドID(推奨)。アプリのマネージドIDにAcrPullロールを付与し、レジストリ設定で--identityを指定します。
# アプリにシステム割当IDを付与
az containerapp identity assign \
--name my-container-app --resource-group my-rg --system-assigned
# そのIDにACRのAcrPullロールを付与(pull専用の最小権限)
az role assignment create \
--assignee <MANAGED_IDENTITY_PRINCIPAL_ID> \
--role AcrPull --scope <ACR_RESOURCE_ID>
# レジストリをマネージドID認証に設定
az containerapp registry set \
--name my-container-app --resource-group my-rg \
--server myregistry.azurecr.io --identity system
これでレジストリのパスワードをどこにも持たない。CIはpush権限、アプリはpull権限だけ——責務と権限が分離されます(最小権限の原則)。
GHCR等の非ACRレジストリも使えます。その場合は
az containerapp registry set --server ghcr.ioで認証情報(PATのread:packages)を設定します。公開イメージでも認証設定は必要です。
デプロイ戦略:Blue/Green・カナリア・ロールバック
単一リビジョンモード:ゼロダウンタイム(既定)
何も設定しなければ、ACAは新リビジョンが準備完了(全プローブ通過+旧台数までスケール)してから自動で全切替します。失敗すれば旧リビジョンに留まる。多くのサービスはこれで十分です。AWS Fargateの「ローリング+デプロイサーキットブレーカー」と同じ安全側の挙動です。
複数リビジョンモード:トラフィック分割
慎重に出したいなら複数リビジョンモードにし、トラフィックを%で割り当てます。
# 複数リビジョンモードに(一度だけ)
az containerapp revision set-mode --name my-api --resource-group my-rg --mode multiple
# カナリア:新版に10%だけ
az containerapp ingress traffic set --name my-api --resource-group my-rg \
--revision-weight my-api--green=10 my-api--blue=90
# 問題なければ昇格(Blue/Green切替)
az containerapp ingress traffic set --name my-api --resource-group my-rg \
--revision-weight my-api--green=100
# 異常を検知したら即ロールバック(旧版へ100%戻す)
az containerapp ingress traffic set --name my-api --resource-group my-rg \
--revision-weight my-api--blue=100
ロールバックが「旧リビジョンに100%戻す」だけで済むのが、不変リビジョンモデルの強みです。旧版はアクティブに残っているので、再ビルドも再デプロイも要らず、秒で戻せる。
パイプラインに組み込むなら、デプロイ後にスモークテスト→メトリクス監視→
traffic setで昇格、という段階を自動化します。SLO違反を検知したら自動でロールバックする、というガード(症状ベースのアラート設計)を足すと、本番品質が一段上がります。
IaC:Bicep / Terraform で宣言的に
クリック操作やazの手打ちは再現性がなく、技術的負債になります。本番は宣言的なコードで。
Bicep(Azureネイティブ)
param image string // CIから一意タグを渡す(例: myregistry.azurecr.io/app:<sha>)
resource app 'Microsoft.App/containerApps@2025-02-02-preview' = {
name: 'my-api'
location: location
identity: { type: 'SystemAssigned' }
properties: {
managedEnvironmentId: environmentId
configuration: {
activeRevisionsMode: 'single'
ingress: { external: true, targetPort: 8080, transport: 'auto' }
registries: [
{ server: 'myregistry.azurecr.io', identity: 'system' } // マネージドIDでpull
]
}
template: {
containers: [
{ name: 'api', image: image, resources: { cpu: json('0.5'), memory: '1.0Gi' } }
]
scale: { minReplicas: 1, maxReplicas: 20 }
}
}
}
CIからはaz deployment group create --parameters image=...:${{ github.sha }}でこのBicepを流す。インフラもアプリも同じパイプラインで宣言的にデプロイできます。Terraform(azurerm_container_app)派なら同じ構成をHCLで書けます(本番運用ガイドのTerraform例)。
スターターワークフローの自動生成
ゼロから書くのが面倒なら、az containerapp github-action add系のCLIでスターターワークフローを生成できます。まず動かして、そこから本番要件(OIDC・IaC・トラフィック分割)に育てる——KISS/YAGNIの順序です。
az containerapp up --source . --ingress externalは、リソース作成・イメージビルド・レジストリ保存・デプロイを一気にやる学習用の最短コマンド。PoCはup、本番はIaCと使い分けます。
CI/CDチェックリスト
- **認証はOIDC(フェデレーション資格情報)**で鍵レス。
AZURE_CREDENTIALSの長期シークレットを本番で使わない。 - イメージタグはコミットSHAで一意。
latest禁止。 - ACR pullはマネージドID(AcrPull)。CIのpushとアプリのpullを別IDで最小権限に。
- デプロイは単一モードでゼロダウンタイム、慎重に出すなら複数モードでカナリア→昇格。
- ロールバックは
traffic setで旧リビジョンへ(秒で戻る)。 - インフラはBicep/Terraformで宣言的に。手打ち・クリックを排除。
- デプロイ後にスモークテスト+SLO監視、違反で自動ロールバック。
まとめ
ACAのCI/CDは、azure/container-apps-deploy-actionでビルド&デプロイし、コミットごとに不変リビジョンを作るシンプルなモデルです。本番品質の鍵は4つ——OIDC鍵レス認証、コミットSHAの一意タグ、マネージドIDによる最小権限pull、トラフィック分割によるBlue/Green+秒で戻せるロールバック。これらはAWSで鍵レスCI/CDとBlue/Greenを運用してきた原則と、語彙が違うだけで同型です。
鍵レスCI/CD・Blue/Greenデプロイ・IaC化のご相談はお問い合わせへ。本番運用全体は Azure Container Apps 本番運用ガイド をどうぞ。