Skip to content

Instantly share code, notes, and snippets.

@onionhammer
Created October 4, 2022 01:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save onionhammer/df3f4af9d0e615b11ce062020508b15b to your computer and use it in GitHub Desktop.
Save onionhammer/df3f4af9d0e615b11ce062020508b15b to your computer and use it in GitHub Desktop.
Deploy ACA with zero downtime
<#
.DESCRIPTION
deploy-app-revision Creates a new revision of the app, waits for it to be healthy and swaps traffic
#>
Param (
[String] $Environment = "some-rg",
[String] $AppName = "some-app",
[String] $HealthcheckPath = "api/healthcheck",
[String] $Subscription = "",
[int] $TimeoutSeconds = 100,
[int] $PollingIntervalSeconds = 5
)
Write-Host "Reading information from $appName..."
# Get revisions
$revisions = az containerapp revision list -n $appName -g $Environment --subscription $Subscription `
| ConvertFrom-Json
# Get hostname info
$hostnames = az containerapp show -g $Environment -n $appName --subscription $Subscription `
--query "properties.[latestRevisionFqdn,latestRevisionName]" `
| ConvertFrom-Json
# i.e. somethingsomething-ff1921d9.centralus.azurecontainerapps.io
$hostSuffix = ($hostnames[0] -replace $hostnames[1], "").Substring(1)
# Define a health checking function
function Wait-Ready($Revision) {
# Wait for revision to be ready
$fqdn = "$Revision.$hostSuffix"
Write-Host "Invoking https://$fqdn/$HealthcheckPath with $TimeoutSeconds seconds remaining..."
# Start a stopwatch
$stopwatch = [system.diagnostics.stopwatch]::StartNew()
$remaining = $TimeoutSeconds
$healthy = $false
$tries = 0
while (-not $healthy -and $remaining -gt 0) {
$start = $stopwatch.Elapsed.TotalSeconds
try {
$timeoutFor = $remaining -gt $PollingIntervalSeconds ? $PollingIntervalSeconds : $remaining
$response = Invoke-WebRequest -Uri "https://$fqdn/$HealthcheckPath" -UseBasicParsing -Timeout $timeoutFor
$healthy = $response.StatusCode -eq 200
}
catch {
$tries++
$end = $stopwatch.Elapsed.TotalSeconds
$remaining = $TimeoutSeconds - $end
Write-Host "Revision not ready... (try $tries, $remaining seconds remaining)"
if ($remaining -gt 0 -and $end - $start -lt $PollingIntervalSeconds) {
$sleep = $PollingIntervalSeconds - ($end - $start)
Start-Sleep -Seconds $sleep
}
$remaining = $TimeoutSeconds - $stopwatch.Elapsed.TotalSeconds
}
}
return $healthy
}
# Step 1: Set traffic to currently active traffic revision
$active = $revisions | Where-Object { $_.properties.trafficWeight -gt 0 } | Select-Object -First 1
$activeRevName = $active.name
Write-Host "Setting $activeRevName to 100% traffic..."
az containerapp ingress traffic set -n $appName -g $Environment --subscription $Subscription --revision-weight "$activeRevName=100"
# Step 2: Clone latest revision with new suffix
$newId = [guid]::NewGuid().ToString("N").Substring(0, 8)
$revName = "$appName--$newId"
Write-Host "Creating a new revision $revName..."
az containerapp revision copy -n $appName -g $Environment --subscription $Subscription --revision-suffix $newId
# Step 3: Wait for newRevision to be ready
if (Wait-Ready -Revision $revName) {
# Step 4: Update traffic weights to new revision
Write-Host "Updating traffic weights..."
az containerapp ingress traffic set -n $appName -g $Environment --subscription $Subscription --revision-weight "latest=100"
# Step 5: Deactivate revisions with 0 traffic
foreach ($rev in $revisions) {
Write-Host "Deactivating " $rev.name
az containerapp revision deactivate -n $appName -g $Environment --subscription $Subscription --revision $rev.name
}
}
else {
Write-Host "Revision failed to become ready" -ForegroundColor Red
Exit 1
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment