はじめに
Docker化したWebアプリを、できるだけ固定費を抑えてAzureにデプロイしたときの手順メモです。
今回は次の構成にしました。
ローカル
-> docker build
-> GitHub Container Registry(GHCR)へpush
-> Azure Container Appsでpullして公開
Azure Container Appsを選んだ理由は、コンテナイメージをそのまま動かせることと、Consumption構成で min replicas = 0 にできることです。アクセスがないときは0台まで落とせるので、MVPや検証用途ではかなり扱いやすいです。
この記事ではアプリケーションの中身には触れず、デプロイ工程だけを書きます。
前提
ローカル環境:
- Windows + PowerShell
- Docker Desktop
- Azure CLI
- Git
利用するもの:
- Azure Container Apps
- GitHub Container Registry,
ghcr.io - Dockerイメージ
Azure CLIのログインと拡張機能の準備:
az login
az extension add --name containerapp --upgrade
az provider register --namespace Microsoft.App
az provider register --namespace Microsoft.OperationalInsights
変数を用意する
作業しやすいようにPowerShell変数を置いておきます。
$RG="rg-your-app"
$LOCATION="japaneast"
$ENV_NAME="your-app-env"
$APP_NAME="your-app"
$GHCR_OWNER="your-github-user-or-org"
$IMAGE="ghcr.io/$GHCR_OWNER/your-app:latest"
$LOCATION は好きなリージョンでOKです。日本向けなら japaneast か japanwest あたりが選択肢になります。
GHCRにログインする
GitHub Container Registryにpushするため、GitHubのPersonal Access Tokenを用意します。
必要な権限の目安:
- pushする場合:
write:packages - private imageをAzureからpullする場合:
read:packages
トークンをファイルに保存している場合は、次のようにログインできます。
Get-Content .\ghcr.txt | docker login ghcr.io -u YOUR_GITHUB_USERNAME --password-stdin
成功すると次のように出ます。
Login Succeeded
Dockerイメージをbuildしてpushする
リポジトリ直下に Dockerfile がある前提です。
docker build -t $IMAGE .
docker push $IMAGE
初回は依存関係のインストールも走るので時間がかかります。
Container Apps環境を作る
まずリソースグループを作ります。
az group create `
--name $RG `
--location $LOCATION
次にContainer Apps Environmentを作ります。
低コスト優先なので、ここではログ保存先を none にしています。
az containerapp env create `
--name $ENV_NAME `
--resource-group $RG `
--location $LOCATION `
--logs-destination none
ログをAzure上でちゃんと見たい場合は、Log Analyticsを使う構成にした方がよいです。ただし検証用途では、ログ周りも課金ポイントになり得るので今回は切っています。
Container Appを作る
GHCRのイメージがpublicの場合
GHCR packageをpublicにしている場合、registry認証なしでpullできます。
az containerapp create `
--name $APP_NAME `
--resource-group $RG `
--environment $ENV_NAME `
--image $IMAGE `
--ingress external `
--target-port 10000 `
--min-replicas 0 `
--max-replicas 1 `
--cpu 1.0 `
--memory 2Gi `
--env-vars `
PORT=10000
--target-port はコンテナが待ち受けているポートに合わせます。
たとえばアプリが 10000 で起動するなら --target-port 10000 です。
GHCRのイメージがprivateの場合
private packageのまま使う場合は、Azure Container AppsにGHCRの認証情報を渡します。
$REGISTRY_SERVER="ghcr.io"
$REGISTRY_USERNAME="YOUR_GITHUB_USERNAME"
$REGISTRY_PASSWORD=Get-Content ".\ghcr.txt" -Raw
$REGISTRY_PASSWORD=$REGISTRY_PASSWORD.Trim()
Trim() は地味に大事です。トークンファイル末尾の改行が混ざると認証に失敗することがあります。
private imageの場合の作成コマンド:
az containerapp create `
--name $APP_NAME `
--resource-group $RG `
--environment $ENV_NAME `
--image $IMAGE `
--registry-server $REGISTRY_SERVER `
--registry-username $REGISTRY_USERNAME `
--registry-password $REGISTRY_PASSWORD `
--ingress external `
--target-port 10000 `
--min-replicas 0 `
--max-replicas 1 `
--cpu 1.0 `
--memory 2Gi `
--env-vars `
PORT=10000
環境変数とシークレットを設定する
APIキーなどを直接環境変数に入れたくない場合は、Container Appsのsecretを使います。
例:
$MY_SECRET="your-secret-value"
az containerapp secret set `
--name $APP_NAME `
--resource-group $RG `
--secrets my-secret="$MY_SECRET"
secretを環境変数として参照します。
az containerapp update `
--name $APP_NAME `
--resource-group $RG `
--set-env-vars MY_SECRET=secretref:my-secret
通常の環境変数ならそのまま指定できます。
az containerapp update `
--name $APP_NAME `
--resource-group $RG `
--set-env-vars APP_ENV=production
URLを確認する
作成できたら、公開URLを取得します。
$APP_URL = az containerapp show `
--name $APP_NAME `
--resource-group $RG `
--query properties.configuration.ingress.fqdn `
--output tsv
"https://$APP_URL"
ヘルスチェック用のエンドポイントがある場合は確認します。
Invoke-RestMethod "https://$APP_URL/health"
{"status":"ok"} のようなレスポンスが返れば、アプリは起動しています。
更新デプロイ
コードを変更したら、新しいタグでイメージをpushして、Container Appのimageを差し替えます。
未コミット変更も含めてわかりやすいタグにしたい場合:
$TAG="deploy-$(Get-Date -Format yyyyMMddHHmm)"
$IMAGE="ghcr.io/$GHCR_OWNER/your-app:$TAG"
docker build -t $IMAGE .
docker push $IMAGE
az containerapp update `
--name $APP_NAME `
--resource-group $RG `
--image $IMAGE
コミット単位で管理したい場合:
$TAG=(git rev-parse --short HEAD)
$IMAGE="ghcr.io/$GHCR_OWNER/your-app:$TAG"
latest だけで運用すると、どのバージョンが動いているかわかりにくくなるので、検証でもタグを切る方が安心です。
scale-to-zero設定を確認する
低コスト運用で大事なのは min replicas = 0 です。
az containerapp show `
--name $APP_NAME `
--resource-group $RG `
--query "{min:properties.template.scale.minReplicas,max:properties.template.scale.maxReplicas,cpu:properties.template.containers[0].resources.cpu,memory:properties.template.containers[0].resources.memory}"
期待値:
{
"cpu": 1.0,
"max": 1,
"memory": "2Gi",
"min": 0
}
min = 0 だとアクセスがないときにコンテナが停止します。
その代わり、久しぶりのアクセスではcold startで初回表示が遅くなることがあります。
検証イベント中だけ常時起動したい場合:
az containerapp update `
--name $APP_NAME `
--resource-group $RG `
--min-replicas 1 `
--max-replicas 1
終わったら戻します。
az containerapp update `
--name $APP_NAME `
--resource-group $RG `
--min-replicas 0 `
--max-replicas 1
失敗したところ
private GHCR imageでUNAUTHORIZEDになる
private packageを認証なしでpullしようとすると、次のようなエラーになります。
UNAUTHORIZED: authentication required
この場合は、次のどちらかで対応します。
- GHCR packageをpublicにする
-
--registry-server,--registry-username,--registry-passwordを付ける
privateのまま使うなら、PATに read:packages が必要です。
createに失敗したContainer Appが残る
az containerapp create が途中で失敗すると、ProvisioningStateが Failed のリソースが残ることがあります。
その状態では registry set などが通らない場合があるので、一度削除して作り直すのが早いです。
az containerapp delete `
--name $APP_NAME `
--resource-group $RG `
--yes
Budget Alertを設定する
低コスト構成でも、公開URLにアクセスが集まると費用が増える可能性があります。
Azure PortalからBudget Alertを作っておくと安心です。今回はCLIではなくPortal UIで設定しました。
手順:
- Azure Portalで
コストの管理と請求を開く -
Cost Managementを開く -
予算を開く -
追加または作成を押す - 対象スコープを選ぶ
- 予算名、金額、期間、通知先を設定する
設定例:
Scope: リソースグループ
Budget amount: 10 USD / month
Reset: Monthly
Alert:
50%
80%
100%
Recipients:
自分のメールアドレス
Budget Alertは課金を自動停止する機能ではなく、通知するためのものです。
止めたい場合は、手動で max replicas を下げるか、Container Appを停止・削除します。
緊急でアクセスを止めたい場合の例:
az containerapp update `
--name $APP_NAME `
--resource-group $RG `
--min-replicas 0 `
--max-replicas 0
もし max-replicas 0 が使えない場合は、ingressを無効化するか、リソースを削除する運用に切り替えます。
後片付け
検証が終わって完全に消す場合は、リソースグループごと削除します。
az group delete --name $RG
GHCRのpackageや外部DBなど、Azure外のリソースは別途削除が必要です。
まとめ
Azure Container Appsを使うと、Docker化したWebアプリを比較的少ない手順で公開できます。
低コストに寄せるなら、特に次を意識するとよさそうです。
- Consumption構成を使う
min replicas = 0max replicas = 1- Log Analyticsは必要になるまで使わない
- Budget Alertを入れる
- Docker imageはタグを切って更新する
小さく公開して反応を見るMVP用途なら、この構成はかなり扱いやすいと感じました。