在 Linux 上使用自訂容器建立函式

在本教學課程中,您會使用 Linux 基礎映像,建立您的程式碼並將其部署至 Azure Functions 作為自訂 Docker 容器。 當您的函式需要特定的語言版本,或有內建映像所未提供的特定相依性或組態時,您通常會想使用自訂映像。

Azure Functions 支援使用自訂處理常式的任何語言或執行階段。 針對某些語言 (例如本教學課程中使用的 R 程式設計語言),您需要將執行階段或其他程式庫安裝為需要使用自訂容器的相依性。

在自訂 Linux 容器中部署您的函式程式碼需要進階方案專用 (App Service) 方案裝載。 完成本教學課程會在您的 Azure 帳戶中產生費用 (以美元計價),您可以在完成時清除資源,將這些成本降到最低。

您也可以如建立您在 Linux 上託管的第一個函式所述,使用預設 Azure App Service 容器。 在 Azure Functions 基底映像存放庫中可找到針對 Azure Functions 支援的基底映像。

在本教學課程中,您會了解如何:

  • 使用 Azure Functions Core Tools 建立函式應用程式和 Dockerfile。
  • 使用 Docker 建置自訂映像。
  • 將自訂映像發佈到容器登錄中。
  • 在 Azure 中建立函式應用程式的支援資源
  • 從 Docker Hub 部署函式應用程式。
  • 將應用程式設定加入函式應用程式。
  • 啟用持續部署。
  • 啟用容器的 SSH 連線。
  • 新增佇列儲存體輸出繫結。
  • 使用 Azure Functions Core Tools 建立函式應用程式和 Dockerfile。
  • 使用 Docker 建置自訂映像。
  • 將自訂映像發佈到容器登錄中。
  • 在 Azure 中建立函式應用程式的支援資源
  • 從 Docker Hub 部署函式應用程式。
  • 將應用程式設定加入函式應用程式。
  • 啟用持續部署。
  • 啟用容器的 SSH 連線。

您可以在任何執行 Windows、macOS 或 Linux 的電腦上遵循此教學課程。

設定您的本機環境

開始之前,您必須具備下列條件:

  • Node.js、作用中 LTS 和維修 LTS 版本 (建議使用 8.11.1 和 10.14.1)。

您也需要具有作用中訂用帳戶的 Azure 帳戶。 免費建立帳戶

建立並啟用虛擬環境

在適用的資料夾中執行下列命令,以建立並啟用名為 .venv 的虛擬環境。 請務必使用受 Azure Functions 支援的 Python 3.8、3.7 或 3.6。

python -m venv .venv
source .venv/bin/activate

如果 Python 未在您的 Linux 發行版本上安裝 venv 套件,請執行下列命令:

sudo apt-get install python3-venv

您將在這個已啟用的虛擬環境中執行所有後續命令。

建立和測試本機 Functions 專案

在終端機或命令提示字元中,針對您選擇的語言執行下列命令,以在目前資料夾中建立函式應用程式專案。

func init --worker-runtime dotnet --docker
func init --worker-runtime node --language javascript --docker
func init --worker-runtime powershell --docker
func init --worker-runtime python --docker
func init --worker-runtime node --language typescript --docker

在空的資料夾中,執行下列命令以從 Maven 原型 \(英文\) 產生 Functions 專案。

mvn archetype:generate -DarchetypeGroupId=com.microsoft.azure -DarchetypeArtifactId=azure-functions-archetype -DjavaVersion=8 -Ddocker

-DjavaVersion 參數會告知函式執行階段要使用哪個 JAVA 版本。 如果您希望函式在 Java 11 上執行,請使用 -DjavaVersion=11。 若未指定 -DjavaVersion,Maven 預設為 JAVA 8。 如需詳細資訊,請參閱 JAVA 版本

重要

JAVA_HOME 環境變數必須設定為正確 JDK 版本的安裝位置,才能完成本文。

Maven 會要求您提供在部署時完成產生專案所需的值。
當系統提示時,提供下列值:

Prompt 描述
groupId com.fabrikam 此值可在所有專案中唯一識別您的專案,並遵循適用於 Java 的套件命名規則
artifactId fabrikam-functions 此值是 jar 的名稱 (不含版本號碼)。
version 1.0-SNAPSHOT 選擇預設值。
套件 com.fabrikam.functions 此值是所產生函式程式碼的 Java 套件。 使用預設值。

輸入 Y 或按 Enter 進行確認。

Maven 會以 artifactId 名稱在新資料夾中建立專案檔案,在此例中為 fabrikam-functions

func init --worker-runtime custom --docker

--docker 選項會產生專案的 Dockerfile,這會定義適合用於 Azure Functions 和所選執行階段的自訂容器。

瀏覽至專案資料夾:

cd fabrikam-functions

Dockerfile 不需要變更。

使用下列命令,將函式新增至您的專案,其中 --name 引數是函式的唯一名稱,而 --template 引數可指定函式的觸發程序。 func new 在您的專案中建立 C# 程式碼檔案。

func new --name HttpExample --template "HTTP trigger" --authlevel anonymous

使用下列命令,將函式新增至您的專案,其中 --name 引數是函式的唯一名稱,而 --template 引數可指定函式的觸發程序。 func new 會建立符合函式名稱的子資料夾,其中包含名為 function.json 的組態檔。

func new --name HttpExample --template "HTTP trigger" --authlevel anonymous

在文字編輯器中,於名為 handler.R 的專案資料夾中建立檔案。 新增下列內容做為其內容。

library(httpuv)

PORTEnv <- Sys.getenv("FUNCTIONS_CUSTOMHANDLER_PORT")
PORT <- strtoi(PORTEnv , base = 0L)

http_not_found <- list(
  status=404,
  body='404 Not Found'
)

http_method_not_allowed <- list(
  status=405,
  body='405 Method Not Allowed'
)

hello_handler <- list(
  GET = function (request) {
    list(body=paste(
      "Hello,",
      if(substr(request$QUERY_STRING,1,6)=="?name=") 
        substr(request$QUERY_STRING,7,40) else "World",
      sep=" "))
  }
)

routes <- list(
  '/api/HttpExample' = hello_handler
)

router <- function (routes, request) {
  if (!request$PATH_INFO %in% names(routes)) {
    return(http_not_found)
  }
  path_handler <- routes[[request$PATH_INFO]]

  if (!request$REQUEST_METHOD %in% names(path_handler)) {
    return(http_method_not_allowed)
  }
  method_handler <- path_handler[[request$REQUEST_METHOD]]

  return(method_handler(request))
}

app <- list(
  call = function (request) {
    response <- router(routes, request)
    if (!'status' %in% names(response)) {
      response$status <- 200
    }
    if (!'headers' %in% names(response)) {
      response$headers <- list()
    }
    if (!'Content-Type' %in% names(response$headers)) {
      response$headers[['Content-Type']] <- 'text/plain'
    }

    return(response)
  }
)

cat(paste0("Server listening on :", PORT, "...\n"))
runServer("0.0.0.0", PORT, app)

host.json 中,修改 customHandler 區段以設定自訂處理常式的啟動命令。

"customHandler": {
  "description": {
      "defaultExecutablePath": "Rscript",
      "arguments": [
      "handler.R"
    ]
  },
  "enableForwardingHttpRequest": true
}

若要在本機測試函式,請啟動專案根資料夾中的本機 Azure Functions 執行階段主機:

func start  
func start  
npm install
npm start
mvn clean package  
mvn azure-functions:run
R -e "install.packages('httpuv', repos='http://cran.rstudio.com/')"
func start

一旦看到 HttpExample 端點出現在輸出中,請瀏覽至 http://localhost:7071/api/HttpExample?name=Functions。 瀏覽器應該會顯示 "hello" 訊息,回應 Functions,這是提供給 name 查詢參數的值。

使用 Ctrl-C 將主機停止。

建立容器映像並在本機進行測試

(選用) 檢查專案根資料夾中的 Dockerfile。 Dockerfile 描述在 Linux 上執行函式應用程式所需的環境。 在 Azure Functions 基底映像頁面中可找到針對 Azure Functions 支援的完整基底映像清單。

檢查專案根資料夾中的 Dockerfile。 Dockerfile 描述在 Linux 上執行函式應用程式所需的環境。 自訂處理常式應用程式會使用 mcr.microsoft.com/azure-functions/dotnet:3.0-appservice 映像做為其基底。

修改 Dockerfile 以安裝 R。將 Dockerfile 的內容取代為下列內容。

FROM mcr.microsoft.com/azure-functions/dotnet:3.0-appservice 
ENV AzureWebJobsScriptRoot=/home/site/wwwroot \
    AzureFunctionsJobHost__Logging__Console__IsEnabled=true

RUN apt update && \
    apt install -y r-base && \
    R -e "install.packages('httpuv', repos='http://cran.rstudio.com/')"

COPY . /home/site/wwwroot

在根專案資料夾中,執行 docker build 命令,然後提供名稱、azurefunctionsimage 和標記 (v1.0.0)。 將 <DOCKER_ID> 取代為 Docker Hub 帳戶識別碼。 此命令會建置容器的 Docker 映像。

docker build --tag <DOCKER_ID>/azurefunctionsimage:v1.0.0 .

當命令完成時,您就能在本機執行新容器。

若要測試組建,請使用 docker run 命令在本機容器中執行映像,再以您的 Docker ID 取代 <DOCKER_ID 並新增連接埠引數 -p 8080:80

docker run -p 8080:80 -it <docker_id>/azurefunctionsimage:v1.0.0

映射在本機容器中啟動之後,流覽至 http://localhost:8080/api/HttpExample?name=Functions ,應該會顯示與之前相同的 「hello」 訊息。 因為您建立的 HTTP 觸發函式使用匿名授權,所以您可以呼叫在容器中執行的函式,而不需要取得存取金鑰。 若要深入瞭解,請參閱 授權金鑰

映射在本機容器中啟動之後,流覽至 http://localhost:8080/api/HttpExample?name=Functions ,應該會顯示與之前相同的 「hello」 訊息。 因為您建立的 HTTP 觸發函式使用匿名授權,所以您可以呼叫在容器中執行的函式,而不需要取得存取金鑰。 若要深入瞭解,請參閱 授權金鑰

在容器中驗證過函式應用程式容器之後,請利用 Ctrl+C停止 Docker。

將映像推送至 Docker 中樞

Docker Hub 是一個容器登錄,其裝載映像並提供映像和容器服務。 若要共用您的映像 (包括部署至 Azure),您必須將其推送到登錄。

  1. 如果您尚未登入 Docker,請使用 docker login 命令,將 <docker_id> 取代為您的 Docker 識別碼。 此命令會提示您輸入使用者名稱和密碼。 「登入成功」訊息會確認您已登入。

    docker login
    
  2. 登入之後,使用 docker push 命令將映像推送到 Docker Hub,再次將 <docker_id> 取代為您的 Docker 識別碼。

    docker push <docker_id>/azurefunctionsimage:v1.0.0
    
  3. 根據您的網路速度而定,第一次推送映像可能需要幾分鐘的時間 (推送後續變更的速度會更快)。 在等候時,您可以繼續進行下一節,並在另一個終端機中建立 Azure 資源。

為您的函式建立支援的 Azure 資源

若要將函式程式碼部署至 Azure,您必須先建立三個資源:

  • 資源群組,這是相關資源的邏輯容器。
  • 儲存體帳戶,用來維護函式的狀態和其他資訊。
  • 函式應用程式,可提供用來執行函式程式碼的環境。 函式應用程式可對應至您的本機函式專案,並可讓您將函式分組為邏輯單位,以便管理、部署和共用資源。

請使用下列命令來建立這些項目。 Azure CLI 和 PowerShell 均受支援。

  1. 如果您尚未登入 Azure,請於此時登入:

    az login
    

    az login 命令會將您登入您的 Azure 帳戶。

  2. 在您選擇的區域中建立名為 AzureFunctionsContainers-rg 的資源群組:

    az group create --name AzureFunctionsContainers-rg --location <REGION>
    

    az group create 命令會建立資源群組。 在上述命令中,使用az account list-locations命令傳回的可用區功能變數代碼,將 取代 <REGION> 為您附近的區域。

  3. 在您的資源群組和區域中建立一般用途的儲存體帳戶:

    az storage account create --name <STORAGE_NAME> --location <REGION> --resource-group AzureFunctionsContainers-rg --sku Standard_LRS
    

    az storage account create 命令會建立儲存體帳戶。

    在上述範例中,請將 <STORAGE_NAME> 取代為適合您且在 Azure 儲存體中是唯一的名稱。 名稱只能包含 3 到 24 個字元的數字和小寫字母。 Standard_LRS 會指定受 Functions 支援的一般用途帳戶。

  4. 使用 命令,在Elastic 進階版 1定價層 (--sku EP1) 、您的 <REGION> 和 Linux (--is-linux 容器中建立名為 Azure Functions) 的進階版方案 myPremiumPlan

    az functionapp plan create --resource-group AzureFunctionsContainers-rg --name myPremiumPlan --location <REGION> --number-of-workers 1 --sku EP1 --is-linux
    

    我們在此使用進階方案,其可視需要進行調整。 若要深入了解裝載,請參閱 Azure Functions 裝載方案比較。 若要計算成本,請參閱 Functions 定價頁面

    此命令也會在相同的資源群組中佈建相關聯的 Azure Application Insights 執行個體,您可將其用於監視函式應用程式和檢視記錄。 如需詳細資訊,請參閱監視 Azure Functions。 在您啟用此執行個體之前,並不會產生任何成本。

使用映像在 Azure 上建立及設定函式應用程式

函式應用程式可管理您主控方案中函式的執行。 在這一節中,您會使用上一節的 Azure 資源,從 Docker Hub 上的映像建立函式應用程式,並使用 Azure 儲存體的連接字串加以設定。

  1. 使用下列命令建立函式應用程式:

    az functionapp create --name <APP_NAME> --storage-account <STORAGE_NAME> --resource-group AzureFunctionsContainers-rg --plan myPremiumPlan --deployment-container-image-name <DOCKER_ID>/azurefunctionsimage:v1.0.0
    

    az functionapp create 命令中, deployment-container-image-name 參數會指定要用於函式應用程式的映射。 您可使用 az functionapp config container show 命令來檢視部署所用映像的相關資訊。 您也可使用 az functionapp config container set 命令,從不同映像進行部署。 注意:如果您使用自訂容器登錄, deployment-container-image-name 參數會參考登錄 URL。

    在此範例中,將 取代 <STORAGE_NAME> 為您在上一節中為儲存體帳戶使用的名稱。 另請以適合您的全域唯一名稱取代 ,並以 <DOCKER_ID> 您的 DockerHub 識別碼取代 <APP_NAME> 。 從自訂容器登錄進行部署時,請使用 deployment-container-image-name 參數來指出登錄的 URL。

    提示

    您可以使用DisableColor host.json 檔案中的 設定,以防止 ANSI 控制字元寫入容器記錄。

  2. 使用下列命令來取得您所建立儲存體帳戶的連接字串:

    az storage account show-connection-string --resource-group AzureFunctionsContainers-rg --name <STORAGE_NAME> --query connectionString --output tsv
    

    儲存體帳戶的連接字串會使用 az storage account show-connection-string 命令傳回。

    將 取代 <STORAGE_NAME> 為您先前建立的儲存體帳戶名稱。

  3. 使用下列命令將此設定新增至函式應用程式:

    az functionapp config appsettings set --name <APP_NAME> --resource-group AzureFunctionsContainers-rg --settings AzureWebJobsStorage=<CONNECTION_STRING>
    

    az functionapp config appsettings set命令會建立設定。

    在此命令中,將 取代為函式應用程式的名稱,並以 <CONNECTION_STRING> 上一個步驟中的連接字串取代 <APP_NAME> 。 連接應該是開頭為 的長編碼字串 DefaultEndpointProtocol=

  4. 函式現在可以使用此連接字串來存取儲存體帳戶。

注意

如果您將自訂映射發佈至私人容器登錄,您應該改用 Dockerfile 中的環境變數作為連接字串。 如需詳細資訊,請參閱 ENV 指示。 您也應該設定變數 DOCKER_REGISTRY_SERVER_USERNAMEDOCKER_REGISTRY_SERVER_PASSWORD。 若要使用這些值,您必須重建映像、將映像推送至登錄,然後在 Azure 上重新啟動函式應用程式。

在 Azure 上驗證您的函式

將映射部署至 Azure 中的函式應用程式之後,您現在可以透過 HTTP 要求叫用函式。 在您的瀏覽器中,流覽至如下所示的 URL:

https://<APP_NAME>.azurewebsites.net/api/HttpExample?name=Functions

https://<APP_NAME>.azurewebsites.net/api/HttpExample?name=Functions

以函式應用程式的名稱取代 <APP_NAME>。 當您流覽至此 URL 時,瀏覽器應該會顯示類似您在本機執行函式時的輸出。

啟用持續部署至 Azure

您可以在每次更新登錄中的映像時,讓 Azure Functions 自動更新您的映像部署。

  1. 使用下列命令啟用持續部署並取得 Webhook URL:

    az functionapp deployment container config --enable-cd --query CI_CD_URL --output tsv --name <APP_NAME> --resource-group AzureFunctionsContainers-rg
    

    az functionapp deployment container config命令會啟用持續部署,並傳回部署 Webhook URL。 您稍後可以使用 az functionapp deployment container show-cd-url 命令來擷取此 URL。

    如同先前,請將 取代 <APP_NAME> 為您的函式應用程式名稱。

  2. 將部署 Webhook URL 複製到剪貼簿。

  3. 開啟 Docker Hub、登入,然後選取導覽列上的 [存放庫]。 找出並選取映像,選取 [Webhook] 索引標籤、指定 [Webhook 名稱]、在 [Webhook URL] 中貼入您的 URL,然後選取 [建立]:

    Add the webhook in your DockerHub repo

  4. 設定 Webhook 後,每當您在 Docker Hub 中更新映像時,Azure Functions 就會重新部署您的映像。

啟用 SSH 連線

SSH 可讓容器和用戶端之間進行安全通訊。 啟用 SSH 之後,您就能使用 App Service 進階工具 (Kudu) 連線到您的容器。 為了讓您可以輕鬆地使用 SSH 連線到容器,Azure Functions 會提供已經啟用 SSH 的基底映像。 您只需要編輯 Dockerfile,然後重建並重新部署映像。 接著,您可以透過進階工具 (Kudu) 連線到容器

  1. 在您的 Dockerfile 中,將字串 -appservice 附加至 FROM 指令中的基底映像:

    FROM mcr.microsoft.com/azure-functions/dotnet:3.0-appservice
    
    FROM mcr.microsoft.com/azure-functions/node:2.0-appservice
    
    FROM mcr.microsoft.com/azure-functions/powershell:2.0-appservice
    
    FROM mcr.microsoft.com/azure-functions/python:2.0-python3.7-appservice
    
    FROM mcr.microsoft.com/azure-functions/node:2.0-appservice
    
  2. 再次使用 docker build 命令重建映像,並以您的 Docker ID 取代 <docker_id>

    docker build --tag <docker_id>/azurefunctionsimage:v1.0.0 .
    
  3. 將已更新的映像推送至 Docker Hub,這應該比第一次只推送所需上傳的映像更新後區段少很多時間。

    docker push <docker_id>/azurefunctionsimage:v1.0.0
    
  4. Azure Functions 會自動將映像重新部署至您的函式應用程式;此程序所需的時間不到一分鐘。

  5. 在瀏覽器中,開啟 https://<app_name>.scm.azurewebsites.net/,以您的唯一名稱取代 <app_name>。 此 URL 是您函式應用程式容器的進階工具(Kudu) 端點。

  6. 登入您的 Azure 帳戶,然後選取 [SSH],以建立與容器的連線。 如果 Azure 仍在更新容器映像,連線可能需要幾分鐘的時間。

  7. 建立與容器的連線之後,執行 top 命令來檢視目前執行中的程序。

    Linux top command running in an SSH session

寫入 Azure 佇列儲存體

Azure Functions 可讓您無須撰寫自己的整合程式碼,就能將函式連線到其他 Azure 服務與資源。 這些繫結同時代表輸入和輸出,會宣告於函式定義內。 繫結中的資料會提供給函式作為參數。 「觸發程序」是一種特殊的輸入繫結。 雖然函式只有一個觸發程序,但可以有多個輸入和輸出繫結。 若要深入了解,請參閱 Azure Functions 觸發程序和繫結概念

本節說明如何將函式與 Azure 佇列儲存體整合。 您新增至此函式的輸出繫結,會將資料從 HTTP 要求寫入至佇列中的訊息。

擷取 Azure 儲存體連接字串

您稍早已建立可供函式應用程式使用的 Azure 儲存體帳戶。 此帳戶的連接字串會安全地儲存在 Azure 的應用程式設定中。 藉由將設定下載到 local.settings.json 檔案中,您可以在本機執行函式時,使用該連線寫入至相同帳戶中的儲存體佇列。

  1. 從專案的根目錄執行下列命令 (請將 <APP_NAME> 取代為先前快速入門中的函式應用程式名稱)。 此命令將會覆寫檔案中任何現有的值。

    func azure functionapp fetch-app-settings <APP_NAME>
    
  2. 開啟 local.settings.json 並找出名為 AzureWebJobsStorage 的值,也就是儲存體帳戶連接字串。 您會在本文的其他章節中使用名稱 AzureWebJobsStorage 和連接字串。

重要

由於 local.settings.json 中包含從 Azure 下載的祕密,因此請一律將此檔案排除在原始檔控制以外。 使用本機函式專案建立的 .gitignore 檔案依預設會排除該檔案。

註冊繫結延伸模組

除了 HTTP 和計時器觸發程序之外,繫結皆會以擴充套件的形式實作。 在終端機視窗中執行下列 dotnet add package 命令來將儲存體套件新增至您的專案。

dotnet add package Microsoft.Azure.WebJobs.Extensions.Storage 

現在,您可以將儲存體輸出繫結新增至您的專案。

將輸出繫結定義新增至函式

一個函式只能有一個觸發程序,但可以有多個輸入和輸出繫結,如此,您無須撰寫自訂整合程式碼,即可連線至其他 Azure 服務和資源。

您會在函式資料夾中的 function.json 檔案中宣告這些繫結。 在先前的快速入門中,HttpExample 資料夾中的 function.json 檔案包含 bindings 集合中的兩個繫結:

"bindings": [
    {
        "authLevel": "function",
        "type": "httpTrigger",
        "direction": "in",
        "name": "req",
        "methods": [
            "get",
            "post"
        ]
    },
    {
        "type": "http",
        "direction": "out",
        "name": "res"
    }
]
"scriptFile": "__init__.py",
"bindings": [
    {
        "authLevel": "function",
        "type": "httpTrigger",
        "direction": "in",
        "name": "req",
        "methods": [
            "get",
            "post"
        ]
    },
    {
        "type": "http",
        "direction": "out",
        "name": "$return"
    }
"bindings": [
  {
    "authLevel": "function",
    "type": "httpTrigger",
    "direction": "in",
    "name": "Request",
    "methods": [
      "get",
      "post"
    ]
  },
  {
    "type": "http",
    "direction": "out",
    "name": "Response"
  }
]

每個繫結至少有一個類型、一個方向和一個名稱。 在上述範例中,第一個繫結的類型為 httpTrigger、方向為 in。 針對 in 方向,name 會指定觸發程序叫用函式時所傳入的輸入參數名稱。

集合中的第二個繫結名為 res。 此 http 繫結是用來寫入 HTTP 回應的輸出繫結 (out)。

若要從這個函式寫入至 Azure 儲存體佇列,請新增類型為 queue、名稱為 msgout 繫結,如下列程式碼所示:

    {
      "authLevel": "function",
      "type": "httpTrigger",
      "direction": "in",
      "name": "req",
      "methods": [
        "get",
        "post"
      ]
    },
    {
      "type": "http",
      "direction": "out",
      "name": "res"
    },
    {
      "type": "queue",
      "direction": "out",
      "name": "msg",
      "queueName": "outqueue",
      "connection": "AzureWebJobsStorage"
    }
  ]
}

集合中第二個繫結的類型為 http、方向為 out,在此情況下,$return 的特殊 name 表示此繫結會使用函式的傳回值,而不是提供輸入參數。

若要從這個函式寫入至 Azure 儲存體佇列,請新增類型為 queue、名稱為 msgout 繫結,如下列程式碼所示:

"bindings": [
  {
    "authLevel": "anonymous",
    "type": "httpTrigger",
    "direction": "in",
    "name": "req",
    "methods": [
      "get",
      "post"
    ]
  },
  {
    "type": "http",
    "direction": "out",
    "name": "$return"
  },
  {
    "type": "queue",
    "direction": "out",
    "name": "msg",
    "queueName": "outqueue",
    "connection": "AzureWebJobsStorage"
  }
]

集合中的第二個繫結名為 res。 此 http 繫結是用來寫入 HTTP 回應的輸出繫結 (out)。

若要從這個函式寫入至 Azure 儲存體佇列,請新增類型為 queue、名稱為 msgout 繫結,如下列程式碼所示:

    {
      "authLevel": "function",
      "type": "httpTrigger",
      "direction": "in",
      "name": "Request",
      "methods": [
        "get",
        "post"
      ]
    },
    {
      "type": "http",
      "direction": "out",
      "name": "Response"
    },
    {
      "type": "queue",
      "direction": "out",
      "name": "msg",
      "queueName": "outqueue",
      "connection": "AzureWebJobsStorage"
    }
  ]
}

在此案例下,會將 msg 提供給函式作為輸出引數。 針對 queue 類型,您也必須在 queueName 中指定佇列的名稱,並在 connection 中提供 Azure 儲存體連線 (來自 local.settings.json) 的名稱

在 C# 專案中,系結會定義為函式方法上的系結屬性。 特定定義取決於您的應用程式是在 C# 類別庫 () 或在隔離進程中執行。

開啟 HttpExample.cs 專案檔,並將下列參數新增至 Run 方法定義:

[Queue("outqueue"),StorageAccount("AzureWebJobsStorage")] ICollector<string> msg,

msg 參數是 ICollector<T> 類型,其代表會在函式完成時寫入輸出繫結的訊息集合。 在此情況下,輸出是名為 outqueue 的儲存體佇列。 儲存體帳戶的連接字串是由 StorageAccountAttribute 設定。 此屬性表示包含儲存體帳戶連接字串的設定,並可以被套用至類別、方法或參數層級。 在此情況下,您可以省略 StorageAccountAttribute,因為您已經正在使用預設的儲存體帳戶。

Run 方法定義現在應該如下所示︰

[FunctionName("HttpExample")]
public static async Task<IActionResult> Run(
    [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req, 
    [Queue("outqueue"),StorageAccount("AzureWebJobsStorage")] ICollector<string> msg, 
    ILogger log)

在 Java 專案中,繫結會被定義為函式方法上的繫結註釋。 系統接著會根據這些註釋自動產生 function.json 檔案。

瀏覽至 src/main/java 底下您的函式程式碼位置,開啟 Function java 專案檔案,然後將下列參數新增至 run 方法定義:

@QueueOutput(name = "msg", queueName = "outqueue", connection = "AzureWebJobsStorage") OutputBinding<String> msg

msg 參數是 OutputBinding<T> 類型,其代表會在函式完成時寫入輸出繫結的字串集合。 在此情況下,輸出是名為 outqueue 的儲存體佇列。 儲存體帳戶的連接字串是由 connection 方法設定。 您會傳遞包含儲存體帳戶連接字串的應用程式設定,而不是連接字串本身。

run 方法定義現在應該如下列範例所示︰

@FunctionName("HttpTrigger-Java")
public HttpResponseMessage run(
        @HttpTrigger(name = "req", methods = {HttpMethod.GET, HttpMethod.POST}, authLevel = AuthorizationLevel.FUNCTION)  
        HttpRequestMessage<Optional<String>> request, 
        @QueueOutput(name = "msg", queueName = "outqueue", connection = "AzureWebJobsStorage") 
        OutputBinding<String> msg, final ExecutionContext context) {
    ...
}

新增程式碼以使用輸出繫結

定義佇列系結之後,您現在可以更新函式,以使用系結參數將訊息寫入佇列。

更新 HttpExample\__init__.py 以符合下列程式碼,並將 參數新增 msg 至函式定義和 msg.set(name) 語句底下 if name:

import logging

import azure.functions as func


def main(req: func.HttpRequest, msg: func.Out[func.QueueMessage]) -> str:

    name = req.params.get('name')
    if not name:
        try:
            req_body = req.get_json()
        except ValueError:
            pass
        else:
            name = req_body.get('name')

    if name:
        msg.set(name)
        return func.HttpResponse(f"Hello {name}!")
    else:
        return func.HttpResponse(
            "Please pass a name on the query string or in the request body",
            status_code=400
        )

msg 參數是 azure.functions.Out class 的執行個體。 其 set 方法會將字串訊息寫入至佇列,在此案例中為傳至 URL 查詢字串中所含函式的名稱。

加入會使用 context.bindings 上的 msg 輸出繫結物件的程式碼來建立佇列訊息。 在 context.res 陳述式前面新增此程式碼。

// Add a message to the Storage queue,
// which is the name passed to the function.
context.bindings.msg = (req.query.name || req.body.name);

此時,您的函式看起來應如下所示:

module.exports = async function (context, req) {
    context.log('JavaScript HTTP trigger function processed a request.');

    if (req.query.name || (req.body && req.body.name)) {
        // Add a message to the Storage queue,
        // which is the name passed to the function.
        context.bindings.msg = (req.query.name || req.body.name);
        context.res = {
            // status: 200, /* Defaults to 200 */
            body: "Hello " + (req.query.name || req.body.name)
        };
    }
    else {
        context.res = {
            status: 400,
            body: "Please pass a name on the query string or in the request body"
        };
    }
};

加入會使用 context.bindings 上的 msg 輸出繫結物件的程式碼來建立佇列訊息。 在 context.res 陳述式前面新增此程式碼。

context.bindings.msg = name;

此時,您的函式看起來應如下所示:

import { AzureFunction, Context, HttpRequest } from "@azure/functions"

const httpTrigger: AzureFunction = async function (context: Context, req: HttpRequest): Promise<void> {
    context.log('HTTP trigger function processed a request.');
    const name = (req.query.name || (req.body && req.body.name));

    if (name) {
        // Add a message to the storage queue, 
        // which is the name passed to the function.
        context.bindings.msg = name; 
        // Send a "hello" response.
        context.res = {
            // status: 200, /* Defaults to 200 */
            body: "Hello " + (req.query.name || req.body.name)
        };
    }
    else {
        context.res = {
            status: 400,
            body: "Please pass a name on the query string or in the request body"
        };
    }
};

export default httpTrigger;

新增使用 Push-OutputBinding Cmdlet 的程式碼,以使用 msg 輸出繫結將文字寫入至佇列。 在 if 陳述式中設定「確定」狀態之前,請先新增此程式碼。

$outputMsg = $name
Push-OutputBinding -name msg -Value $outputMsg

此時,您的函式看起來應如下所示:

using namespace System.Net

# Input bindings are passed in via param block.
param($Request, $TriggerMetadata)

# Write to the Azure Functions log stream.
Write-Host "PowerShell HTTP trigger function processed a request."

# Interact with query parameters or the body of the request.
$name = $Request.Query.Name
if (-not $name) {
    $name = $Request.Body.Name
}

if ($name) {
    # Write the $name value to the queue, 
    # which is the name passed to the function.
    $outputMsg = $name
    Push-OutputBinding -name msg -Value $outputMsg

    $status = [HttpStatusCode]::OK
    $body = "Hello $name"
}
else {
    $status = [HttpStatusCode]::BadRequest
    $body = "Please pass a name on the query string or in the request body."
}

# Associate values to output bindings by calling 'Push-OutputBinding'.
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
    StatusCode = $status
    Body = $body
})

加入會使用 msg 輸出繫結物件的程式碼來建立佇列訊息。 在方法傳回之前加入此程式碼。

if (!string.IsNullOrEmpty(name))
{
    // Add a message to the output collection.
    msg.Add(string.Format("Name passed to the function: {0}", name));
}

此時,您的函式看起來應如下所示:

[FunctionName("HttpExample")]
public static async Task<IActionResult> Run(
    [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req, 
    [Queue("outqueue"),StorageAccount("AzureWebJobsStorage")] ICollector<string> msg, 
    ILogger log)
{
    log.LogInformation("C# HTTP trigger function processed a request.");

    string name = req.Query["name"];

    string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
    dynamic data = JsonConvert.DeserializeObject(requestBody);
    name = name ?? data?.name;

    if (!string.IsNullOrEmpty(name))
    {
        // Add a message to the output collection.
        msg.Add(string.Format("Name passed to the function: {0}", name));
    }
    return name != null
        ? (ActionResult)new OkObjectResult($"Hello, {name}")
        : new BadRequestObjectResult("Please pass a name on the query string or in the request body");
}

現在,您可以使用新的 msg 參數,從您的函式程式碼寫入輸出繫結。 在成功回應之前新增下列一行程式碼,以將 name 的值新增至 msg 輸出繫結。

msg.setValue(name);

當您使用輸出繫結時,無須使用 Azure 儲存體 SDK 程式碼來進行驗證、取得佇列參考或寫入資料。 Functions 執行階段和佇列輸出繫結會為您進行這些工作。

您的 run 方法現在看起來應該如下列範例所示:

public HttpResponseMessage run(
        @HttpTrigger(name = "req", methods = {HttpMethod.GET, HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) 
        HttpRequestMessage<Optional<String>> request, 
        @QueueOutput(name = "msg", queueName = "outqueue", 
        connection = "AzureWebJobsStorage") OutputBinding<String> msg, 
        final ExecutionContext context) {
    context.getLogger().info("Java HTTP trigger processed a request.");

    // Parse query parameter
    String query = request.getQueryParameters().get("name");
    String name = request.getBody().orElse(query);

    if (name == null) {
        return request.createResponseBuilder(HttpStatus.BAD_REQUEST)
        .body("Please pass a name on the query string or in the request body").build();
    } else {
        // Write the name to the message queue. 
        msg.setValue(name);

        return request.createResponseBuilder(HttpStatus.OK).body("Hello, " + name).build();
    }
}

更新測試

因為原型也會建立一組測試,所以您需要更新這些測試,以處理 run 方法簽章中的新 msg 參數。

瀏覽至 src/test/java 底下的測試程式碼位置,開啟 Function.java 專案檔案,並以下列程式碼取代 //Invoke 下的程式碼行。

@SuppressWarnings("unchecked")
final OutputBinding<String> msg = (OutputBinding<String>)mock(OutputBinding.class);
final HttpResponseMessage ret = new Function().run(req, msg, context);

更新登錄中的映像

  1. 在根資料夾中,再次執行 docker build,這次會將標記中的版本更新為 v1.0.1。 和之前一樣,以您的 Docker Hub 帳戶識別碼取代 <docker_id>

    docker build --tag <docker_id>/azurefunctionsimage:v1.0.1 .
    
  2. 使用 docker push 將已更新的映像推送回存放庫:

    docker push <docker_id>/azurefunctionsimage:v1.0.1
    
  3. 因為您設定了持續傳遞,所以再次更新登錄中的映像,會自動在 Azure 中更新您的函式應用程式。

檢視 Azure 儲存體佇列中的訊息

在瀏覽器中,使用與之前相同的 URL 來叫用您的函式。 瀏覽器應會顯示與之前相同的回應,因為您未修改該部分的函式程式碼。 不過,新增的程式碼會使用 name URL 參數,將訊息寫入 outqueue 儲存體佇列。

您可以在Azure 入口網站Microsoft Azure 儲存體總管中檢視佇列。 您也可以在 Azure CLI 中檢視佇列,如下列步驟所說明:

  1. 開啟函式專案的 local.setting.json 檔案,並複製連接字串值。 在終端機或命令視窗中,執行下列命令來建立名為 的 AZURE_STORAGE_CONNECTION_STRING 環境變數,並貼上您的特定連接字串來取代 <MY_CONNECTION_STRING> 。 (此環境變數意味著您無須使用 --connection-string 引數將連接字串提供給每個後續命令。)

    export AZURE_STORAGE_CONNECTION_STRING="<MY_CONNECTION_STRING>"
    
  2. (選擇性) 使用 az storage queue list 命令檢視您帳戶中的儲存體佇列。 此命令的輸出應該會包含名為 outqueue 的佇列,這是函式將其第一個訊息寫入至該佇列時所建立的。

    az storage queue list --output tsv
    
  3. 使用 az storage message get 命令來讀取此佇列中的訊息,這應該是您先前測試函式時所使用的名字。 此命令會讀取並移除佇列中的第一個訊息。

    echo `echo $(az storage message get --queue-name outqueue -o tsv --query '[].{Message:content}') | base64 --decode`
    

    由於訊息本文會以 base64 編碼儲存,因此必須先將訊息解碼才會顯示。 執行 az storage message get 之後,就會從佇列中移除此訊息。 如果 outqueue 中只有一個訊息,則當您第二次執行此命令時,將不會擷取訊息,而是會收到錯誤。

清除資源

如果您想使用您在本教學課程中建立的資源來繼續使用 Azure 函式,您可以保留所有這些資源。 由於您已建立 Azure Functions 的進階方案,因此您會每天持續產生一或兩美元的成本。

若要避免持續產生成本,請刪除 AzureFunctionsContainer-rg 資源群組,以清除該群組中的所有資源:

az group delete --name AzureFunctionsContainer-rg

後續步驟