Dalam banyak skenario, Anda mungkin ingin mengotomatiskan penghentian (auto-stop) Container Apps untuk mengurangi biaya atau mengoptimalkan penggunaan resource, terutama jika aplikasi hanya diperlukan pada waktu-waktu tertentu.
Terdapat dua pendekatan utama untuk melakukan Auto-Stop Container Apps:
-
Function App Menjalankan kode PowerShell untuk menghentikan Container App berdasarkan jadwal tertentu menggunakan Timer Trigger.
-
Time-Based Scaling dengan KEDA Mengonfigurasi KEDA agar melakukan scale down ke 0 replika di luar jam operasional.
Function App #
1. Buat Resource Group #
az group create \
--name <resourceGroupName> \
--location <REGION>
2. Buat Storage Account #
az storage account create \
--name <STORAGE_NAME> \
--resource-group <resourceGroupName> \
--location <REGION> \
--sku Standard_RAGRS \
--kind StorageV2 \
--min-tls-version TLS1_2 \
--allow-shared-key-access true
3. Buat User Assigned Managed Identity dan Tambahkan Role #
Tambahkan role berikut ke identity:
- Storage Blob Data Owner
- Contributor
output=$(az identity create \
--name "demo-app" \
--resource-group <resourceGroupName> \
--location <REGION> \
--query "{userId:id, principalId: principalId, clientId: clientId}" -o json)
userId=$(echo $output | jq -r '.userId')
principalId=$(echo $output | jq -r '.principalId')
clientId=$(echo $output | jq -r '.clientId')
storageId=$(az storage account show \
--resource-group <resourceGroupName> \
--name <STORAGE_NAME> \
--query 'id' -o tsv)
TARGET_RESOURCE_GROUP=$(az group show \
--name <resourceGroupName> \
--query 'id' -o tsv)
az role assignment create \
--assignee-object-id $principalId \
--assignee-principal-type ServicePrincipal \
--role "Storage Blob Data Owner" \
--scope $storageId
az role assignment create \
--assignee-object-id $principalId \
--assignee-principal-type ServicePrincipal \
--role "Contributor" \
--scope $TARGET_RESOURCE_GROUP
4. Buat Function App (Consumption Plan) #
IDENTITY_ID=$(az identity show \
--name demo-app \
--resource-group <resourceGroupName> \
--query 'id' -o tsv)
az functionapp create \
--resource-group <resourceGroupName> \
--consumption-plan-location <REGION> \
--runtime powershell \
--functions-version 4 \
--os-type Windows \
--name <APP_NAME> \
--storage-account <STORAGE_NAME> \
--assign-identity $IDENTITY_ID
5. Konfigurasi Environment Variables pada Function App #
az functionapp config appsettings set \
--name <APP_NAME> \
--resource-group <resourceGroupName> \
--settings \
CONTAINER_APP_NAME=<TARGET_CONTAINER_APP> \
RESOURCE_GROUP=<TARGET_RESOURCE_GROUP> \
AZURE_CLIENT_ID=$(az identity show --name demo-app --resource-group <resourceGroupName> --query clientId -o tsv) \
SUBSCRIPTION_ID=$(az account show --query id -o tsv) \
AzureFunctionsJobHost__logging__console__isEnabled=true
6. Inisialisasi Proyek Function #
Buat folder proyek lalu jalankan func init:
mkdir stop-container-app-function && cd stop-container-app-function
func init --worker-runtime powershell
Buat function dengan template TimerTrigger:
func new --name StopContainerApp --template "TimerTrigger"
7. Update File ./StopContainerApp/run.ps1
#
param($Timer)
$ErrorActionPreference = "Stop"
# đ Load environment variables secara aman
$clientId = [System.Environment]::GetEnvironmentVariable('AZURE_CLIENT_ID')
$containerAppName = [System.Environment]::GetEnvironmentVariable('CONTAINER_APP_NAME')
$resourceGroup = [System.Environment]::GetEnvironmentVariable('RESOURCE_GROUP')
$subscriptionId = [System.Environment]::GetEnvironmentVariable('SUBSCRIPTION_ID')
# â
Validasi environment variables
$requiredVars = @{
AZURE_CLIENT_ID = $clientId
CONTAINER_APP_NAME = $containerAppName
RESOURCE_GROUP = $resourceGroup
SUBSCRIPTION_ID = $subscriptionId
}
foreach ($key in $requiredVars.Keys) {
if (-not $requiredVars[$key]) {
throw "Environment variable '$key' tidak di-set"
}
}
try {
Write-Information "đ Autentikasi menggunakan Managed Identity..."
Connect-AzAccount -Identity -AccountId $clientId
Write-Information "đ Mengecek status Container App: $containerAppName"
$currentState = Get-AzContainerAppRevision -ContainerAppName $containerAppName `
-ResourceGroupName $resourceGroup `
-SubscriptionId $subscriptionId
if ($null -eq $currentState) {
throw "Container App '$containerAppName' tidak ditemukan di resource group '$resourceGroup'"
}
$runningState = $($currentState.RunningState)
Write-Information "đĻ Status saat ini: $runningState"
# Debugging output
# Write-Information "đ Dumping Get-AzContainerAppRevision output:"
# $currentState | ConvertTo-Json -Depth 10
if ($runningState -eq 'Running') {
Write-Information "đ Menghentikan Container App: $containerAppName"
Stop-AzContainerApp -Name $containerAppName `
-ResourceGroupName $resourceGroup `
-SubscriptionId $subscriptionId
Write-Information "â
Container App berhasil dihentikan"
} else {
Write-Information "âšī¸ Container App sudah dalam keadaan '$runningState'"
}
Write-Information "â° Function dijalankan pada: $($Timer.ScheduleStatus.Last)"
} catch {
Write-Error "â Terjadi error: $($_.Exception.Message)"
throw
}
8. Update requirements.psd1
#
@{
'Az.App' = '2.0.0'
'Az.Accounts' = '5.0.0'
}
9. Update Jadwal Timer pada ./StopContainerApp/function.json
#
{
"bindings": [
{
"name": "Timer",
"type": "timerTrigger",
"direction": "in",
"schedule": "0 0 17 * * *"
}
]
}
10. Deploy Function App #
func azure functionapp publish <APP_NAME>
Time-Based Scaling #
Konfigurasi KEDA Cron Scaler agar Container App melakukan scale down ke 0 replika di luar jam kerja.
# Scale down setelah jam kerja (17:00â23:59)
az containerapp update \
--name demo-app \
--resource-group <NAMA_RESOURCE_GROUP> \
--scale-rule-name "afterhours" \
--scale-rule-type "cron" \
--scale-rule-metadata \
"timezone=Asia/Jakarta" \
"start=0 0 17 * * *" \
"end=59 59 23 * * *" \
"desiredReplicas=0"
# Scale down pada dini hari (00:00â08:59)
az containerapp update \
--name demo-app \
--resource-group <NAMA_RESOURCE_GROUP> \
--scale-rule-name "earlyhours" \
--scale-rule-type "cron" \
--scale-rule-metadata \
"timezone=Asia/Jakarta" \
"start=0 0 0 * * *" \
"end=59 59 8 * * *" \
"desiredReplicas=0"
Referensi: