定期自動化執行ACR Repository維護Azure CLI指令碼的N種方法

定期自動化執行ACR Repository維護Azure CLI指令碼的N種方法

前篇使用Azure CLI-自動刪除Azure Container Registry過期映像檔,我寫好了維護 ACR Repository 維護指令碼。我們希望能定期自動化執行這份維護指令碼(或說排程執行也行),這份指令碼有幾個麻煩的地方,一、註解第一行「First we need to login to Azure」,你必須先登入 Azure 帳號,登入之後才能執行 Azure CLI。二、因為需要登入 Azure 帳號,也就被 Azure 帳號這件事給限制住了,它就不太可能離開你的主機去別的地方做自動化。因此,在自動化之前,我們必須先解決 ACR 訪問權限的問題。

題外話:Azure CLI 執行很慢...

朋友 Ian Chen 提問,執行 Azure CLI 會不會很慢。

答案是:會。

如指令碼所示,因為 Azure CLI 基本上都是一筆一筆的執行,做完這一筆才做下一筆(它要等 Azure 回應),因此它的反應速度不會太快。

目前我有想到一個辦法,透過 PoewrShell 裡的 Start-Job 把刪除的 az acr repository delete 指令給包起來。我沒確定可行性(我沒試),因為同時執行多條 az acr 指令不確定會不會被 Azure Cloud 當成攻擊,而造成什麼麻煩。另外,快速且大量產生背景執行程式,會造成耗費系統大量資源,也是不太好。

以我的需求來說,只要減短維護週期(每天、每週),就能減低 Azure CLI "慢"造成的影響。

ACR訪問權限

這種類似 Docker Registry 服務,你一定用過 Docker Registry 帳密登入後,透過 Docker CLI 來與 ACR 溝通,這也可以達到 ACR 的維護目的。但我們這裡的目標是透過 Azure CLI 來維護,因此 Docker Registry 帳密登入的方式在 Azure CLI 並不可行,不可行的另一原因是,通常 Registry 帳密權限太大。

其實,ACR 有提供另一種身分管理機制 Repository permissions。它可以把我們對 Repository 的 C/R/U/D 操作都切開來設定,並且它可以細到針對某一個 Repository 來設定,這樣就能針對不同的情境非常有彈性來進行權限設定。完成之後你會取得如同 Docker Registry 帳密一樣的 Token 帳密,利用 Token 帳密就能如同 Docker Registry 帳密一樣,讓 Azure CLI 在不登入 Azure 帳號的情況下,也能取得操作 ACR 的權限。

目前還是在 Azure CLI 登入 Azure Portal 狀態下。

az acr token create --name AzureCliToken --registry YourRegistryName `
--repository * `
metadata/read content/delete `
--output json
  • --repository *:剛剛提到,權限可以細到針對某一個 Repository 來設定,如果指定為 *,意思是 All Repository。
  • metadata/read content/delete:分析 az acr 指令碼之後,維護 ACR Repository 只需要 metadata/readcontent/delete 這兩組權限。

ACR 文件寫的很清楚,--repositorymetadata/read content/delete 參數的細節,我就不過多解釋了。

沒問題的話,可以取得兩組密碼。

{
  "credentials": {
    "passwords": [
      {
        "name": "password1",
        "value": "密碼1"
      },
      {
        "name": "password2",
        "value": "密碼2"
      }
    ],
    "username": "AzureCliToken"
  }, //...

Azure CLI - azure acr 使用 Token 帳密

我們先登出 Azure 帳號,在無登入 Azure 帳號情況下手動測試執行 azure acr 並使用 Token 帳密來進行 ACR 資源的存取。

PS C:\> az logout
PS C:\> az acr repository list -n $registryName -o tsv --username AzureCliToken --password "密碼1"
azure-vote-front

以 Token 帳密改寫 azure acr 維護指令碼

讓我們以 Token 帳密改寫 azure acr 維護指令碼:

# ###############################################
# az acr command reference: # https://learn.microsoft.com/zh-tw/cli/azure/acr?view=azure-cli-latest
# ###############################################

function Get-repositoryInfo {
    param (
        [string]$registryName,
        [string]$repository,
        [string]$tokenUserName,
        [string]$tokenPwd
    )

    $digests = az acr manifest list-metadata --name $repository --registry $registryName --orderby time_desc -o tsv --query "[*].digest" --username $tokenUserName --password $tokenPwd

    return @{
        digests   = $digests
        infoCount = $digests.Count
    }
}

function Remove-OldRepositoryImages {
    param (
        [string]$registryName,
        [string]$repository,
        [object]$reposInfo,
        [string]$tokenUserName,
        [string]$tokenPwd
    )

    Write-Output "Deleting repository $repository by digests"

    ### Keep the latest 2 digests ###
    $tetainDigests = $reposInfo.digests | Select-Object -First 2
    $digestList = $reposInfo.digests | Select-String -Pattern $tetainDigests -NotMatch
       
    Write-Output "deleting digest count: $($digestList.Count)"
    foreach ($digest in $digestList) {
        ### 'name@digest' is the format of the --image parameter ###
        Write-Output "az acr repository delete --name $registryName --image $($repository + '@' + $digest) --yes"
        az acr repository delete --name $registryName --image $($repository + '@' + $digest) --yes --username $tokenUserName --password $tokenPwd
    }
    Write-Output "az acr repository $registryName maintain compeleted."
}

function Invoke-ACRDeleteProcess {
    param (
        [string]$registryName,
        [string]$tokenUserName,
        [string]$tokenPwd
    )
        
    ### In my situation, "dev" and "doc" are limited to the ":latest" tag, no deletion operation is required. ###
    $repositories = az acr repository list -n $registryName -o tsv --username $tokenUserName --password $tokenPwd `
                    | Where-Object { $_ -like "*qas*" -or $_ -like "*release*" } `
                    | Where-Object { $_ -notlike "*doc*" }

    $repositories | ForEach-Object {
        $repository = $_
        Write-Output "Processing repository $repository"
        
        $reposInfo = Get-repositoryInfo -registryName $registryName -repository $repository -tokenUserName $tokenUserName -tokenPwd $tokenPwd
        Write-Output "Repository information count: $($reposInfo.infoCount)"
        # confirm the count of images in the repository is greater than 2
        if ($reposInfo.infoCount -gt 2) {
            Remove-OldRepositoryImages -registryName $registryName -repository $repository -reposInfo $reposInfo -tokenUserName $tokenUserName -tokenPwd $tokenPwd
        }
        else {
            Write-Output "No images to delete for $repository"
        }   
    }
}

# Main
### 這裡是愉懶,應該在呼叫 .ps1 時來傳入,不應該把帳號寫在指令碼內 ###
$registryName = "YourRegistryName"
$tokenUserName = "YourTokenUserName"
$tokenPwd = "YourTokenPwd"
Invoke-ACRDeleteProcess -registryName $registryName -tokenUserName $tokenUserName -tokenPwd $tokenPwd

重構部分,我把前篇用不到 Tag 程式碼給移除了。為 function 加上 $tokenUserName$tokenPwd 兩個參數,而 az acr 則是以 --username--password 來接收 Token 帳密。

有了不被 Azure 帳號限制的 azure acr 指令碼,那麼我們能在任何地方執行這份 ACR 維護指令碼。

以 pwsh 執行(手動)

pwsh 執行是指開啟 Windows Terminal 之類的終端機的 PowerShell Core(pwsh 是跨平台哦),手動執行這支 PowerShell 指令碼。

PS C:\> Z:\Invoke-ACRRepositoryDeleteImages.ps1
Processing repository azure-vote-front
WARNING: Command group 'acr manifest' is in preview and under development. Reference and support levels: https://aka.ms/CLI_refstatus
Repository information count: 1
No images to delete for azure-vote-front

以工作排程器執行(自動化)

因為我們現在的 Azure CLI 指令碼已經與 Azure 登入帳號無關,因此最簡單就是找一台伺服器,設定一下 Windows 的工作排程程式,定時跑一下這段 azure acr 的 pwsh 指令碼即可。

以 Power Automate 執行(非自動化)

談到自動化的話,那麼 RPA 也是一個不錯的選擇。

如果你是 Windows 11 的電腦,那麼應該已經預先安裝了 Power Automate 桌面版工具在你的電腦中。你可以把 Power Automate 桌面版想成免費版(因為還有個付費版),不要因為免費版就小看它,它是一個強大的 RPA 個人工具。

免費版有個小小的限制,就是無法排程自動執行你設定好的流程(所以我說"個人"工具)。以我目前的情境來說,工作排程器是完勝 Power Automate 桌面版的,但是…

ACR with Power Automate

圖我故意抓大張一點。如果你點開把「動作」裡可以幫助你自動化的項目看一遍,並多做一些研究或測試,相信我,你會喜歡上它的。

網路上我有看到那種 UI 小幫手,幫忙開啟 Power Automate 桌面版,然後幫你把要執行的流程全部「點一下」執行按鈕。因此 Power Automate 桌面版要做成自動化也是可行的。但離題太遠,請有興趣的朋友請自行研究。

以 Azure DevOps 執行(自動)

這是我這篇的最終極目標,雲的事(Azure Cloud)就讓雲去處理(Azure DevOps)。

AzureDevOps PowerShell pipeline

不被 Azure 帳號限制後 Azure CLI 用 Azure DevOps 來跑也只是剛好而已。另外在 Azure DevOps 裡 Agent pool 不論你是選 Windows 或 Ubuntu(Linux 執行效率會好些),不只內建 pwsh 執行環境並且 Azure CLI 也都是內建的。之前無法直接用 PowerShell Task 來做,就是一直被「Azure 帳號」這關卡住。雖然還是有另一個 Azure CLI Task 能解決Azure 帳號的問題,但我喜歡東西越簡單越好。

小結

你可能還有其他創意,例如,把指令碼包成容器,利用 Kubernetes 的 CronJobs 機制定時跑也行。反正就是找個地方,定時跑一下這支 ACR Repository 維護指令碼,讓你的 ACR Repository 常保乾淨。

沒有留言:

張貼留言

感謝您的留言,如果我的文章你喜歡或對你有幫助,按個「讚」或「分享」它,我會很高興的。