透過 Azure Container Registry 佈署 Azure Functions

Posted by blueskyson on December 13, 2022

開發環境

透過命令列開發:

  • .NET 6.0 SDK
  • Azure Functions Core Tools 4.x 版
  • Azurite 儲存體模擬器
  • Azure CLI 2.4.0 版或更新版本。
  • Docker

建立本機專案

用以下指令在當前目錄中建立一個 Timer Trigger 的 Function,並產生預設的 Dockerfile。

func init --name ContainerApp --worker-runtime dotnet --docker
func new --name TimerTriggerExample --template "timer trigger"

先執行 Azurite 然後測試執行 Function App:

azurite -l azurite_workspace
func start

測試完成後,打包成鏡像檔並執行,注意用 docker 執行時,需要令 AzureWebJobsStorageUseDevelopmentStorage=true 並且透過 Azurite 來模擬儲存體。將你的 Azure Container Registry 的名稱填入下方指令區塊中的 ACR_NAME 環境變數:

export ACR_NAME=<your azure container registry name>

docker build -t ${ACR_NAME}.azurecr.io/container-app:latest .
docker run --name container-app -p 8000:80 --network host \
    -e AzureWebJobsStorage="UseDevelopmentStorage=true" ${ACR_NAME}.azurecr.io/container-app:latest

特別注意目前 Azure Functions 的容器無法直接在 Mac M1 上執行。

順帶一提,Azurite 也有提供 Docker 鏡像,可以透過以下指令直接執行:

docker run --name azurite -p 10000:10000 -p 10001:10001 -p 10002:10002 -d mcr.microsoft.com/azure-storage/azurite

測試完本地的鏡像檔後,將鏡像推送到 Azure Container Registry 中:

az login
az acr login -n ${ACR_NAME}
docker push ${ACR_NAME}.azurecr.io/container-app:latest

接下來可以透過兩種方法來透過 Azure Container Registry 佈署 Azure Functions:

Method 1: Admin Credentials

給 Functon App 一個 Container Registry 的帳號密碼,讓 Function App 透過這組帳號密碼來登入 Container Registry 並拉取鏡像檔。

首先在 Azure Portal 中創建一個資源群組,將此資源群組的名稱填入下方指令區塊中的 RG_NAME 環境變數,將 Azure Function 所需的資源創建、佈署在這個資源群組中:

export ACR_NAME=<azure container registry name>
export RG_NAME=<function app's resource group>
export APP_NAME=<function app name>
export STORAGE_NAME=<storage account name>

# 建立儲存體帳號
az storage account create --name ${STORAGE_NAME} --resource-group ${RG_NAME} --sku Standard_LRS --kind StorageV2

# 建立 App Service Plan
az appservice plan create --name PlanB1 --resource-group ${RG_NAME} --is-linux --sku B1

# 獲取 ACR 的 username
az acr credential show -n ${ACR_NAME} --query username --output tsv

# 獲取 ACR 的 password
az acr credential show -n ${ACR_NAME} --query passwords[0].value --output tsv

# 建立/重新佈署 Function App,一次將所有必要資訊設定好
az functionapp create --name ${APP_NAME} --storage-account ${STORAGE_NAME} --resource-group ${RG_NAME} \
    --plan PlanB1 --functions-version 4 \
    --deployment-container-image-name ${ACR_NAME}.azurecr.io/container-app:latest \
    --docker-registry-server-password <acr password> \
    --docker-registry-server-user <acr username>

Method 2: Managed Identity

為 Function App 註冊一個 Identity,讓 Container Registry 給予它 “AcrPull” 的權限。

export ACR_NAME=<azure container registry name>
export RG_NAME=<function app's resource group>
export APP_NAME=<function app name>
export STORAGE_NAME=<storage account name>

# 建立儲存體帳號
az storage account create --name ${STORAGE_NAME} --resource-group ${RG_NAME} --sku Standard_LRS --kind StorageV2

# 建立 App Service Plan
az appservice plan create --name PlanB1 --resource-group ${RG_NAME} --is-linux --sku B1

# 建立一個 Function App 並指定 image 的位址
az functionapp create --name ${APP_NAME} --storage-account ${STORAGE_NAME} --resource-group ${RG_NAME} \
    --plan PlanB1 --functions-version 4 \
    --deployment-container-image-name ${ACR_NAME}.azurecr.io/container-app:latest

# 為 Function App 註冊一個 Identity 並取得 principalId
az functionapp identity assign --resource-group ${RG_NAME} --name ${APP_NAME} --query principalId --output tsv

# 取得 subscriptionId
az account show --query id --output tsv

# 給予 Function App "AcrPull" 權限
az role assignment create --assignee <principal-id> \
    --scope /subscriptions/<subscription-id>/resourceGroups/<container registry resource group>/providers/Microsoft.ContainerRegistry/registries/${ACR_NAME} \
    --role "AcrPull"

# 設定必須透過 Identity 驗證 Container Registry 拉取權限
az resource update --ids /subscriptions/<subscription-id>/resourceGroups/${RG_NAME}/providers/Microsoft.Web/sites/${APP_NAME}/config/web --set properties.acrUseManagedIdentityCreds=True

# 佈屬 Function App
az functionapp config container set --name ${APP_NAME} --resource-group ${RG_NAME} \
    --docker-custom-image-name ${ACR_NAME}.azurecr.io/container-app:latest \
    --docker-registry-server-url https://${ACR_NAME}.azurecr.io

此時終端機會顯示 “No credential was provided to access Azure Container Registry. Trying to look up…“,azure 將自動套用當前的 identity 去登入 acr。