Erstellen einer Funktion unter Linux mit einem benutzerdefinierten Container

In diesem Tutorial erstellen Sie Code und stellen ihn anschließend in Azure Functions als benutzerdefinierten Docker-Container mit einem Linux-Basisimage bereit. Normalerweise verwenden Sie ein benutzerdefiniertes Image, wenn Sie für Ihre Funktionen eine bestimmte Sprachversion benötigen oder über eine bestimmte Abhängigkeit oder Konfiguration verfügen, die über das integrierte Image nicht bereitgestellt wird.

Von Azure Functions wird jede Sprache oder Runtime mit benutzerdefinierten Handlern unterstützt. Für einige Sprachen (beispielsweise die in diesem Tutorial verwendete Programmiersprache R) müssen Sie die Runtime oder zusätzliche Bibliotheken als Abhängigkeiten installieren, die die Verwendung eines benutzerdefinierten Containers erfordern.

Die Bereitstellung Ihres Funktionscodes in einem benutzerdefinierten Linux-Container erfordert Hosting im Premium-Tarif oder in einem Dedizierten Tarif (App Service-Tarif). Das Abschließen dieses Tutorials verursacht Kosten von einigen USD auf Ihrem Azure-Konto, die Sie durch Bereinigen der Ressourcen minimieren können, wenn Sie fertig sind.

Sie können auch einen Azure App Service-Standardcontainer verwenden. Dies ist unter Schnellstart: Erstellen Ihrer ersten unter Linux gehosteten Funktion unter Verwendung von Befehlszeilentools beschrieben. Unterstützte Basisimages für Azure Functions finden Sie im Azure Functions-Repository für Basisimages.

In diesem Tutorial lernen Sie Folgendes:

  • Erstellen einer Funktions-App und Dockerfile mit den Azure Functions Core Tools.
  • Erstellen eines benutzerdefinierten Images mit Docker.
  • Veröffentlichen eines benutzerdefinierten Images in einer Containerregistrierung.
  • Erstellen von unterstützenden Ressourcen in Azure für die Funktions-App.
  • Bereitstellen einer Funktions-App über Docker Hub.
  • Hinzufügen von Anwendungseinstellungen für die Funktions-App.
  • Aktivieren Sie Continuous Deployment.
  • Aktivieren Sie SSH-Verbindungen mit dem Container.
  • Fügen Sie eine Queue Storage-Ausgabebindung hinzu.
  • Erstellen einer Funktions-App und Dockerfile mit den Azure Functions Core Tools.
  • Erstellen eines benutzerdefinierten Images mit Docker.
  • Veröffentlichen eines benutzerdefinierten Images in einer Containerregistrierung.
  • Erstellen von unterstützenden Ressourcen in Azure für die Funktions-App.
  • Bereitstellen einer Funktions-App über Docker Hub.
  • Hinzufügen von Anwendungseinstellungen für die Funktions-App.
  • Aktivieren Sie Continuous Deployment.
  • Aktivieren Sie SSH-Verbindungen mit dem Container.

Sie können dieses Tutorial auf allen Computern durcharbeiten, auf denen Windows, macOS oder Linux ausgeführt wird.

Konfigurieren Ihrer lokalen Umgebung

Bevor Sie mit diesem Lernprogramm beginnen können, benötigen Sie Folgendes:

  • Node.js, Active LTS- und Maintenance LTS-Versionen (8.11.1 und 10.14.1 empfohlen)
  • Entwicklungstools für die von Ihnen verwendete Sprache. In diesem Tutorial wird als Beispiel die Programmiersprache R verwendet.

Sie benötigen außerdem ein Azure-Konto mit einem aktiven Abonnement. Sie können kostenlos ein Konto erstellen.

Erstellen und Aktivieren einer virtuellen Umgebung

Führen Sie die folgenden Befehle in einem geeigneten Ordner aus, um eine virtuelle Umgebung mit dem Namen .venv zu erstellen und zu aktivieren. Achten Sie darauf, dass Sie Python 3.8, 3.7 oder 3.6 verwenden. Diese Versionen werden von Azure Functions unterstützt.

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

Führen Sie den folgenden Befehl aus, wenn über Python das venv-Paket auf Ihrer Linux-Distribution nicht installiert wurde:

sudo apt-get install python3-venv

Sie führen alle nachfolgenden Befehle in dieser aktivierten virtuellen Umgebung aus.

Erstellen und Testen des lokalen Funktionsprojekts

Führen Sie in einem Terminal oder an einer Eingabeaufforderung den folgenden Befehl für die gewählte Sprache aus, um ein Funktions-App-Projekt im aktuellen Ordner zu erstellen.

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

Führen Sie in einem leeren Ordner den folgenden Befehl aus, um das Functions-Projekt über einen Maven-Archetyp zu generieren.

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

Der -DjavaVersion-Parameter teilt der Functions-Runtime mit, welche Version von Java verwendet werden soll. Verwenden Sie -DjavaVersion=11, wenn Sie Ihre Funktionen in Java 11 ausführen möchten. Wenn Sie -DjavaVersion nicht angeben, wird für Maven standardmäßig Java 8 verwendet. Weitere Informationen finden Sie unter Java-Versionen.

Wichtig

Damit Sie diesen Artikel durcharbeiten können, muss die Umgebungsvariable JAVA_HOME auf den Installationsspeicherort der richtigen Version des JDK festgelegt sein.

Maven fordert Sie zur Eingabe von Werten auf, die erforderlich sind, um die Generierung des Projekts bei der Bereitstellung abzuschließen.
Geben Sie die folgenden Werte ein, wenn Sie dazu aufgefordert werden:

Prompt Wert BESCHREIBUNG
groupId com.fabrikam Ein Wert, der Ihr Projekt projektübergreifend eindeutig identifiziert. Für den Wert müssen die Paketbenennungsregeln für Java eingehalten werden.
artifactId fabrikam-functions Der Name des Behälters (ohne Versionsnummer).
version 1.0-SNAPSHOT Wählen Sie den Standardwert aus.
package com.fabrikam.functions Das Java-Paket für den generierten Funktionscode. Verwenden Sie den Standardwert.

Geben Sie zur Bestätigung Y ein, oder drücken Sie die EINGABETASTE.

Maven erstellt die Projektdateien in einem neuen Ordner und benennt ihn mit dem Wert von artifactId (in diesem Beispiel: fabrikam-functions).

func init --worker-runtime custom --docker

Mit der Option --docker wird eine Dockerfile für das Projekt generiert. Hiermit wird ein geeigneter benutzerdefinierter Container zur Verwendung mit Azure Functions und der ausgewählten Runtime definiert.

Navigieren Sie zum Projektordner:

cd fabrikam-functions

An der Dockerfile-Datei sind keine Änderungen erforderlich.

Fügen Sie dem Projekt eine Funktion hinzu, indem Sie den unten angegebenen Befehl verwenden. Hierbei ist das --name-Argument der eindeutige Name Ihrer Funktion, und mit dem --template-Argument wird der Trigger der Funktion angegeben. func new erstellt eine C#-Codedatei in Ihrem Projekt.

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

Fügen Sie dem Projekt eine Funktion hinzu, indem Sie den unten angegebenen Befehl verwenden. Hierbei ist das --name-Argument der eindeutige Name Ihrer Funktion, und mit dem --template-Argument wird der Trigger der Funktion angegeben. Mit func new wird ein Unterordner mit dem Funktionsnamen erstellt, der eine Konfigurationsdatei mit dem Namen function.json enthält.

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

Erstellen Sie in einem Text-Editor eine Datei im Projektordner mit dem Namen handler.R. Fügen Sie den folgenden Inhalt hinzu:

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)

Ändern Sie in host.json den Abschnitt customHandler, um den Startbefehl des benutzerdefinierten Handlers zu konfigurieren.

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

Starten Sie zum lokalen Testen der Funktion den lokalen Azure Functions-Runtimehost im Stammverzeichnis des Projektordners:

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

Navigieren Sie zu http://localhost:7071/api/HttpExample?name=Functions, nachdem in der Ausgabe der HttpExample-Endpunkt angezeigt wird. Im Browser sollte eine Begrüßungsnachricht mit Functions (dem für den Abfrageparameter name angegebenen Wert) angezeigt werden.

Verwenden Sie STRG-C, um den Host zu beenden.

Erstellen und lokales Testen des Containerimages

(Optional) Untersuchen Sie die Dockerfile im Stammverzeichnis des Projektordners. In der Dockerfile wird die Umgebung beschrieben, die zum Ausführen der Funktions-App unter Linux benötigt wird. Die vollständige Liste mit den unterstützten Basisimages für Azure Functions finden Sie auf der Seite mit Azure Functions-Basisimages.

Untersuchen Sie das Dockerfile im Stammverzeichnis des Projektordners. In der Dockerfile wird die Umgebung beschrieben, die zum Ausführen der Funktions-App unter Linux benötigt wird. Von Anwendungen mit benutzerdefiniertem Handler wird das Image mcr.microsoft.com/azure-functions/dotnet:3.0-appservice als Grundlage verwendet.

Ändern Sie das Dockerfile, um R zu installieren. Ersetzen Sie den Inhalt des Dockerfile durch Folgendes:

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

Führen Sie im Stammprojektordner den Befehl docker build aus, und geben Sie einen Namen (azurefunctionsimage) und ein Tag (v1.0.0) an. Ersetzen Sie <DOCKER_ID> durch Ihre Docker Hub-Konto-ID. Dieser Befehl erstellt das Docker-Image für den Container.

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

Nachdem der Befehl abgeschlossen wurde, können Sie den neuen Container lokal ausführen.

Führen Sie das Image zum Testen des Builds in einem lokalen Container aus, indem Sie den Befehl docker run verwenden und erneut <DOCKER_ID durch Ihre Docker-ID ersetzen und das Portargument -p 8080:80 hinzufügen:

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

Nachdem das Image im lokalen Container gestartet wird, navigieren Sie zu http://localhost:8080/api/HttpExample?name=Functions, das die gleiche „Hallo“-Meldung wie zuvor anzeigen sollte. Da die von Ihnen erstellte von HTTP ausgelöste Funktion anonyme Autorisierung verwendet, können Sie die im Container ausgeführte Funktion aufrufen, ohne einen Zugriffsschlüssel abrufen zu müssen. Weitere Informationen finden Sie unter Autorisierungsschlüssel.

Nachdem das Image im lokalen Container gestartet wird, navigieren Sie zu http://localhost:8080/api/HttpExample?name=Functions, das die gleiche „Hallo“-Meldung wie zuvor anzeigen sollte. Da die von Ihnen erstellte von HTTP ausgelöste Funktion anonyme Autorisierung verwendet, können Sie die im Container ausgeführte Funktion aufrufen, ohne einen Zugriffsschlüssel abrufen zu müssen. Weitere Informationen finden Sie unter Autorisierungsschlüssel.

Nachdem Sie die Funktions-App im Container überprüft haben, beenden Sie Docker mit STRG+C.

Übertragen des Images per Pushvorgang auf Docker Hub

Docker Hub ist eine Containerregistrierung, mit der Images gehostet und Image- und Containerdienste bereitgestellt werden. Um Ihr Image freizugeben (umfasst auch die Bereitstellung in Azure), müssen Sie es per Pushvorgang in eine Registrierung übertragen.

  1. Wenn Sie sich noch nicht bei Docker angemeldet haben, können Sie dies durchführen, indem Sie den Befehl docker login verwenden und <docker_id> durch Ihre Docker-ID ersetzen. Mit diesem Befehl werden Sie zum Eingeben Ihres Benutzernamens und Kennworts aufgefordert. Mit einer Meldung der Art „Anmeldung erfolgreich“ wird bestätigt, dass die Anmeldung erfolgreich war.

    docker login
    
  2. Übertragen Sie das Image nach dem Anmelden per Pushvorgang an Docker Hub, indem Sie den Befehl docker push verwenden und <docker_id> wieder durch Ihre Docker-ID ersetzen.

    docker push <docker_id>/azurefunctionsimage:v1.0.0
    
  3. Je nach Netzwerkgeschwindigkeit kann der erste Pushvorgang für das Image einige Minuten dauern (das Pushen der nachfolgenden Änderungen geht deutlich schneller). Während Sie warten, können Sie mit dem nächsten Abschnitt fortfahren und Azure-Ressourcen in einem anderen Terminal erstellen.

Erstellen von unterstützenden Azure-Ressourcen für Ihre Funktion

Zum Bereitstellen Ihres Funktionscodes in Azure müssen Sie drei Ressourcen erstellen:

  • Eine Ressourcengruppe, bei der es sich um einen logischen Container für verwandte Ressourcen handelt.
  • Ein Speicherkonto, das verwendet wird, um den Status und andere Informationen zu Ihren Funktionen zu verwalten.
  • Eine Funktions-App, die als Umgebung zum Ausführen Ihres Funktionscodes dient. Eine Funktions-App ist Ihrem lokalen Funktionsprojekt zugeordnet und ermöglicht Ihnen das Gruppieren von Funktionen als logische Einheit, um die Verwaltung, Bereitstellung und Freigabe von Ressourcen zu vereinfachen.

Verwenden Sie die folgenden Befehle, um diese Elemente zu erstellen. Sowohl die Azure CLI als auch PowerShell werden unterstützt.

  1. Melden Sie sich bei Azure an, falls dies noch nicht geschehen ist:

    az login
    

    Mit dem Befehl az login werden Sie bei Ihrem Azure-Konto angemeldet.

  2. Erstellen Sie eine Ressourcengruppe mit dem Namen AzureFunctionsContainers-rg in der ausgewählten Region:

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

    Mit dem Befehl az group create wird eine Ressourcengruppe erstellt. Ersetzen Sie im Befehl oben <REGION> durch eine Region in Ihrer Nähe, indem Sie einen verfügbaren Regionscode verwenden, der mit dem Befehl az account list-locations zurückgegeben wird.

  3. Erstellen Sie in Ihrer Ressourcengruppe und Region ein universelles Speicherkonto:

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

    Der Befehl az storage account create erstellt ein Speicherkonto.

    Ersetzen Sie im vorherigen Beispiel <STORAGE_NAME> durch einen Namen, der für Sie geeignet und eindeutig in Azure Storage ist. Namen dürfen nur 3 bis 24 Zeichen und ausschließlich Kleinbuchstaben enthalten. Mit Standard_LRS wird ein universelles Konto angegeben, das von Functions unterstützt wird.

  4. Verwenden Sie den Befehl, um einen Premium-Plan für Azure Functions mit dem Namen myPremiumPlan in der Preisstufe Elastic Premium 1 (--sku EP1) in Ihrer <REGION> und in einem Linux-Container (--is-linux) zu erstellen.

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

    Wir verwenden hier den Premium-Plan, der je nach Bedarf skaliert werden kann. Weitere Informationen zum Hosting finden Sie unter Vergleich von Hostingplänen für Azure Functions. Informationen zum Berechnen der Kosten finden Sie auf der Seite Azure Functions – Preise.

    Darüber hinaus wird mit dem Befehl auch eine zugeordnete Azure Application Insights-Instanz in derselben Ressourcengruppe bereitgestellt, mit der Sie Ihre Funktions-App überwachen und Protokolle anzeigen können. Weitere Informationen finden Sie unter Überwachen von Azure Functions. Für die Instanz fallen erst Kosten an, wenn Sie sie aktivieren.

Erstellen und Konfigurieren einer Funktions-App in Azure mit dem Image

Mit einer Funktions-App in Azure wird die Ausführung der Funktionen Ihres Hostingplans verwaltet. In diesem Abschnitt verwenden Sie die Azure-Ressourcen aus dem vorherigen Abschnitt, um eine Funktions-App aus einem Image unter Docker Hub zu erstellen und mit einer Verbindungszeichenfolge für Azure Storage zu konfigurieren.

  1. Erstellen Sie mit dem folgenden Befehl eine Funktions-App:

    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
    

    Im Befehl az functionapp create gibt der Parameter deployment-container-image-name das für die Funktions-App zu verwendende Image an. Sie können den Befehl az functionapp config container show verwenden, um Informationen zum für die Bereitstellung verwendeten Image anzuzeigen. Darüber hinaus können Sie den Befehl az functionapp config container set nutzen, um ein anderes Image für die Bereitstellung zu verwenden.

    Ersetzen Sie in diesem Beispiel <STORAGE_NAME> durch den Namen, den Sie im vorherigen Abschnitt für das Speicherkonto verwendet haben. Ersetzen Sie außerdem <APP_NAME> durch einen für Sie geeigneten global eindeutigen Namen und <DOCKER_ID> durch Ihre DockerHub-ID.

    Tipp

    Sie können die DisableColor-Einstellung in der host.json-Datei verwenden, um zu verhindern, dass ANSI-Steuerzeichen in die Containerprotokolle geschrieben werden.

  2. Verwenden Sie den folgenden Befehl, um die Verbindungszeichenfolge für das von Ihnen erstellte Speicherkonto abzurufen:

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

    Die Verbindungszeichenfolge für das Speicherkonto wird mit dem Befehl az storage account show-connection-string zurückgegeben.

    Ersetzen Sie <STORAGE_NAME> durch den Namen des zuvor erstellten Speicherkontos.

  3. Fügen Sie diese Einstellung mit dem folgenden Befehl der Funktions-App hinzu:

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

    Der Befehl az functionapp config appsettings set erstellt die Einstellung.

    Ersetzen Sie in diesem Befehl <APP_NAME> durch den Namen Ihrer Funktions-App und <CONNECTION_STRING> durch die Verbindungszeichenfolge aus dem vorherigen Schritt. Die Verbindung sollte eine lang codierte Zeichenfolge sein, die mit DefaultEndpointProtocol= beginnt.

  4. Die Funktion kann diese Verbindungszeichenfolge jetzt verwenden, um auf das Speicherkonto zuzugreifen.

Hinweis

Wenn Sie Ihr benutzerdefiniertes Image unter einem privaten Containerkonto veröffentlichen, sollten Sie stattdessen Umgebungsvariablen in der Dockerfile für die Verbindungszeichenfolge verwenden. Weitere Informationen finden Sie unter der ENV-Anweisung. Sie sollten auch die Variablen DOCKER_REGISTRY_SERVER_USERNAME und DOCKER_REGISTRY_SERVER_PASSWORD festlegen. Zum Verwenden der Werte müssen Sie das Image dann neu erstellen und per Pushvorgang in die Registrierung übertragen und anschließend die Funktions-App in Azure neu starten.

Überprüfen Ihrer Funktionen in Azure

Nachdem das Image in Ihrer Funktions-App in Azure bereitgestellt wurde, können Sie die Funktion nun wie zuvor über HTTP-Anforderungen aufrufen. Navigieren Sie in Ihrem Browser zu einer URL wie der folgenden:

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

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

Ersetzen Sie <APP_NAME> durch den Namen der Funktions-App. Wenn Sie zu dieser URL navigieren, sollte der Browser eine ähnliche Ausgabe anzeigen wie bei der lokalen Ausführung der Funktion.

Aktivieren von Continuous Deployment in Azure

Sie können Azure Functions aktivieren, um Ihre Bereitstellung eines Images jeweils automatisch zu aktualisieren, wenn Sie das Bild in der Registrierung aktualisieren.

  1. Aktivieren Sie Continuous Deployment, und rufen Sie die Webhook-URL mit den folgenden Befehlen ab:

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

    Der Befehl az functionapp deployment container config aktiviert Continuous Deployment und gibt die Webhook-URL der Bereitstellung zurück. Sie können diese URL später jederzeit abrufen, indem Sie den Befehl az functionapp deployment container show-cd-url verwenden.

    Ersetzen Sie wie zuvor <APP_NAME> durch den Namen Ihrer Funktions-App.

  2. Kopieren Sie die Webhook-URL der Bereitstellung in die Zwischenablage.

  3. Öffnen Sie Docker Hub, melden Sie sich an, und wählen Sie in der Navigationsleiste die Option Repositories (Repositorys) aus. Suchen Sie nach dem Image, und wählen Sie es aus. Wählen Sie die Registerkarte Webhooks aus, geben Sie einen Webhooknamen ein, fügen Sie unter Webhook-URL Ihre URL ein, und wählen Sie Erstellen aus:

    Hinzufügen des Webhooks in Ihrem DockerHub-Repository

  4. Nachdem der Webhook festgelegt wurde, wird Ihr Image von Azure Functions jeweils erneut bereitgestellt, wenn Sie es in Docker Hub aktualisieren.

Aktivieren von SSH-Verbindungen

SSH ermöglicht die sichere Kommunikation zwischen einem Container und einem Client. Wenn SSH aktiviert ist, können Sie mithilfe der erweiterten App Service-Tools (Kudu) eine Verbindung mit Ihrem Container herstellen. Azure Functions stellt ein Basisimage bereit, für das SSH bereits aktiviert ist, um das Herstellen einer Verbindung mit Ihrem Container per SSH zu vereinfachen. Sie müssen nur Ihre Dockerfile bearbeiten und dann das Image neu erstellen und bereitstellen. Anschließend können Sie mit den Advanced Tools (Kudu) eine Verbindung mit dem Container herstellen.

  1. Fügen Sie in Ihrer Dockerfile die Zeichenfolge -appservice an das Basisimage in Ihrer FROM-Anweisung an:

    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. Erstellen Sie das Image neu, indem Sie den Befehl docker build verwenden und <docker_id> durch Ihre Docker-ID ersetzen:

    docker build --tag <docker_id>/azurefunctionsimage:v1.0.0 .
    
  3. Übertragen Sie das aktualisierte Image per Pushvorgang an Docker Hub. Dies sollte deutlich weniger Zeit als der erste Pushvorgang in Anspruch nehmen, da nur die aktualisierten Segmente des Images hochgeladen werden müssen.

    docker push <docker_id>/azurefunctionsimage:v1.0.0
    
  4. Azure Functions stellt das Image automatisch für Ihre Funktions-App bereit. Der Prozess dauert weniger als eine Minute.

  5. Öffnen Sie in einem Browser https://<app_name>.scm.azurewebsites.net/, indem Sie <app_name> durch Ihren eindeutigen Namen ersetzen. Diese URL ist der Advanced Tools-Endpunkt (Kudu) für Ihren Funktions-App-Container.

  6. Melden Sie sich an Ihrem Azure-Konto an, und wählen Sie SSH aus, um eine Verbindung mit dem Container herzustellen. Die Verbindungsherstellung kann etwas dauern, falls das Containerimage von Azure noch aktualisiert wird.

  7. Nachdem eine Verbindung mit Ihrem Container hergestellt wurde, können Sie den Befehl top ausführen, um die derzeit ausgeführten Prozesse anzuzeigen.

    Linux-Befehl „top“, der in einer SSH-Sitzung ausgeführt wird

Schreiben in Azure Queue Storage

Mit Azure Functions können Sie Ihre Funktionen mit anderen Azure-Diensten und -Ressourcen verbinden, ohne dass Sie eigenen Integrationscode schreiben müssen. Diese Bindungen, die sowohl Eingabe als auch Ausgabe darstellen, werden innerhalb der Funktionsdefinition deklariert. Daten von Bindungen werden der Funktion als Parameter bereitgestellt. Ein Trigger ist ein spezieller Typ von Eingabebindung. Eine Funktion hat zwar nur einen Trigger, kann aber mehrere Ein- und Ausgabebindungen haben. Weitere Informationen finden Sie unter Konzepte der Trigger und Bindungen in Azure Functions.

In diesem Abschnitt wird gezeigt, wie Sie Ihre Funktion in eine Azure Queue Storage-Instanz integrieren. Die Ausgabebindung, die Sie dieser Funktion hinzufügen, schreibt Daten aus einer HTTP-Anforderung in eine Nachricht in der Warteschlange.

Abrufen der Azure Storage-Verbindungszeichenfolge

Zuvor haben Sie ein Azure Storage-Konto erstellt, das von der Funktions-App verwendet werden kann. Die Verbindungszeichenfolge für dieses Konto wird sicher in App-Einstellungen in Azure gespeichert. Indem Sie die Einstellung in die Datei local.settings.json herunterladen, können Sie diese Verbindung zum Schreiben in eine Storage-Warteschlange unter demselben Konto verwenden, wenn Sie die Funktion lokal ausführen.

  1. Führen Sie im Stammverzeichnis des Projekts den folgenden Befehl aus, indem Sie <APP_NAME> durch den Namen Ihrer Funktions-App aus der vorherigen Schnellstartanleitung ersetzen. Mit diesem Befehl werden alle vorhandenen Werte in der Datei überschrieben.

    func azure functionapp fetch-app-settings <APP_NAME>
    
  2. Öffnen Sie local.settings.json, und suchen Sie nach dem Wert mit dem Namen AzureWebJobsStorage. Dies ist die Verbindungszeichenfolge des Storage-Kontos. Sie verwenden den Namen AzureWebJobsStorage und die Verbindungszeichenfolge noch in anderen Abschnitten dieses Artikels.

Wichtig

Da local.settings.json aus Azure heruntergeladene Geheimnisse enthält, sollten Sie diese Datei immer aus der Quellcodeverwaltung ausschließen. In der GITIGNORE-Datei, die für ein lokales Funktionsprojekt erstellt wird, ist die Datei standardmäßig ausgeschlossen.

Registrieren von Bindungserweiterungen

Mit Ausnahme von HTTP- und Timertriggern werden Bindungen als Erweiterungspakete implementiert. Führen Sie den folgenden dotnet add package-Befehl im Terminalfenster aus, um Ihrem Projekt das Storage-Erweiterungspaket hinzuzufügen.

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

Dann können Sie dem Projekt die Storage-Ausgabebindung hinzufügen.

Hinzufügen einer Ausgabebindungsdefinition zur Funktion

Eine Funktion kann zwar nur über einen Trigger verfügen, aber über mehrere Eingabe- und Ausgabebindungen. Hiermit können Sie eine Verbindung mit anderen Azure-Diensten und -Ressourcen herstellen, ohne benutzerdefinierten Integrationscode zu schreiben.

Sie deklarieren diese Bindungen in der Datei function.json in Ihrem Funktionsordner. In der vorherigen Schnellstartanleitung hat die Datei function.json im Ordner HttpExample zwei Bindungen in der Sammlung bindings enthalten:

"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"
  }
]

Jede Bindung verfügt mindestens über einen Typ, eine Richtung und einen Namen. Im obigen Beispiel hat die erste Bindung den Typ httpTrigger und die Richtung in. Für die Richtung in wird mit name der Name eines Eingabeparameters angegeben, der an die Funktion gesendet wird, wenn sie vom Trigger aufgerufen wird.

Die zweite Bindung in der Sammlung hat den Namen res. Diese http-Bindung ist eine Ausgabebindung (out), die zum Schreiben der HTTP-Antwort verwendet wird.

Fügen Sie zum Schreiben in eine Azure Storage-Warteschlange aus dieser Funktion eine out-Bindung vom Typ queue mit dem Namen msg hinzu. Dies ist im Code unten veranschaulicht:

    {
      "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"
    }
  ]
}

Die zweite Bindung der Sammlung hat den Typ http mit der Richtung out. In diesem Fall wird mit dem speziellen name-Element von $return angegeben, dass für diese Bindung der Rückgabewert der Funktion verwendet wird, anstatt einen Eingabeparameter bereitzustellen.

Fügen Sie zum Schreiben in eine Azure Storage-Warteschlange aus dieser Funktion eine out-Bindung vom Typ queue mit dem Namen msg hinzu. Dies ist im Code unten veranschaulicht:

"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"
  }
]

Die zweite Bindung in der Sammlung hat den Namen res. Diese http-Bindung ist eine Ausgabebindung (out), die zum Schreiben der HTTP-Antwort verwendet wird.

Fügen Sie zum Schreiben in eine Azure Storage-Warteschlange aus dieser Funktion eine out-Bindung vom Typ queue mit dem Namen msg hinzu. Dies ist im Code unten veranschaulicht:

    {
      "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"
    }
  ]
}

In diesem Fall wird msg als Ausgabeargument für die Funktion bereitgestellt. Für einen queue-Typ müssen Sie auch den Namen der Warteschlange in queueName und den Namen (name) der Azure Storage-Verbindung (aus local.settings.json) in connection angeben.

In einem C#-Projekt werden die Bindungen als Bindungsattribute der Funktionsmethode definiert. Bestimmte Definitionen hängen davon ab, ob Ihre App In-Process (C#-Klassenbibliothek) oder in einem isolierten Prozess ausgeführt wird.

Öffnen Sie die Projektdatei HttpExample.cs, und fügen Sie der Definition der Methode Run den folgenden Parameter hinzu:

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

Der Parameter msg ist ein ICollector<T>-Typ und stellt eine Sammlung von Nachrichten dar, die in eine Ausgabebindung geschrieben werden, wenn die Funktion abgeschlossen wird. In diesem Fall ist die Ausgabe eine Speicherwarteschlange mit dem Namen outqueue. Die Verbindungszeichenfolge für das Storage-Konto wird durch StorageAccountAttribute festgelegt. Dieses Attribut gibt die Einstellung an, die die Verbindungszeichenfolge des Storage-Kontos enthält, und kann auf Klassen-, Methoden- oder Parameterebene angewandt werden. In diesem Fall können Sie StorageAccountAttribute weglassen, da Sie bereits das Standardspeicherkonto verwenden.

Die Run-Methodendefinition sollte nun wie folgt aussehen:

[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)

In einem Java-Projekt werden die Bindungen als Bindungsanmerkungen für die Funktionsmethode definiert. Die Datei function.json wird dann automatisch auf der Grundlage dieser Anmerkungen generiert.

Navigieren Sie zum Speicherort Ihres Funktionscodes (unter src/main/java), öffnen Sie die Projektdatei Function.java, und fügen Sie der Definition der Methode run die folgenden Parameter hinzu:

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

Der Parameter msg ist ein OutputBinding<T>-Typ. Dieser stellt eine Sammlung von Zeichenfolgen dar, die als Nachrichten in eine Ausgabebindung geschrieben werden, wenn die Funktion abgeschlossen wird. In diesem Fall ist die Ausgabe eine Speicherwarteschlange mit dem Namen outqueue. Die Verbindungszeichenfolge für das Storage-Konto wird durch die Methode connection festgelegt. Übergeben Sie anstelle der eigentlichen Verbindungszeichenfolge die Anwendungseinstellung, die die Verbindungszeichenfolge für das Storage-Konto enthält.

Die Definition der Methode run sollte nun wie im folgenden Beispiel aussehen:

@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) {
    ...
}

Hinzufügen von Code für die Verwendung der Ausgabebindung

Nachdem die Warteschlangenbindung definiert wurde, können Sie nun Ihre Funktion aktualisieren, um Nachrichten mithilfe des Bindungsparameters in die Warteschlange zu schreiben.

Aktualisieren Sie HttpExample\__init__.py gemäß dem folgenden Code, und fügen Sie den Parameter msg der Funktionsdefinition und msg.set(name) unter der if name:-Anweisung hinzu.

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
        )

Der Parameter msg ist eine Instanz der azure.functions.Out class. Die zugehörige set-Methode schreibt eine Zeichenfolgennachricht in die Warteschlange. In diesem Fall ist dies der Name, der in der URL-Abfragezeichenfolge an die Funktion übergeben wird.

Fügen Sie Code hinzu, der das Ausgabebindungsobjekt msg für context.bindings verwendet, um eine Warteschlangennachricht zu erstellen. Fügen Sie diesen Code vor der context.res-Anweisung hinzu.

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

Die Funktion sollte nun wie folgt aussehen:

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"
        };
    }
};

Fügen Sie Code hinzu, der das Ausgabebindungsobjekt msg für context.bindings verwendet, um eine Warteschlangennachricht zu erstellen. Fügen Sie diesen Code vor der context.res-Anweisung hinzu.

context.bindings.msg = name;

Die Funktion sollte nun wie folgt aussehen:

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;

Fügen Sie Code hinzu, der das Cmdlet Push-OutputBinding verwendet, um Text unter Verwendung der Ausgabebindung msg in die Warteschlange zu schreiben. Fügen Sie diesen Code hinzu, bevor Sie den OK-Status in der if-Anweisung festlegen.

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

Die Funktion sollte nun wie folgt aussehen:

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
})

Fügen Sie Code hinzu, der das Ausgabebindungsobjekt msg verwendet, um eine Warteschlangennachricht zu erstellen. Fügen Sie diesen Code vor der Rückgabe der Methode hinzu.

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

Die Funktion sollte nun wie folgt aussehen:

[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");
}

Nun können Sie den neuen Parameter msg verwenden, um von Ihrem Funktionscode aus in die Ausgabebindung zu schreiben. Fügen Sie vor der Erfolgsantwort die folgende Codezeile hinzu, um der Ausgabebindung msg den Wert name hinzuzufügen:

msg.setValue(name);

Bei Verwendung einer Ausgabebindung müssen Sie weder den Azure Storage SDK-Code für die Authentifizierung verwenden noch einen Warteschlangenverweis abrufen oder Daten schreiben. Die Functions-Runtime und die Warteschlangenausgabebindung übernehmen diese Aufgaben für Sie.

Die Methode run sollte bei Ihnen nun wie im folgenden Beispiel aussehen:

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();
    }
}

Aktualisieren der Tests

Da der Archetyp auch eine Reihe von Tests erstellt, müssen Sie diese Tests aktualisieren, um den neuen Parameter msg in der Signatur der Methode run zu behandeln.

Navigieren Sie zum Speicherort Ihres Testcodes (unter src/test/java), öffnen Sie die Projektdatei Function.java, und ersetzen Sie die Codezeile unter //Invoke durch den folgenden Code:

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

Aktualisieren des Images in der Registrierung

  1. Führen Sie im Stammordner erneut docker build aus, und aktualisieren Sie die Version im Tag dieses Mal auf v1.0.1. Ersetzen Sie <docker_id> wie zuvor durch Ihre Docker Hub-Konto-ID:

    docker build --tag <docker_id>/azurefunctionsimage:v1.0.1 .
    
  2. Übertragen Sie das aktualisierte Image per docker push zurück in das Repository:

    docker push <docker_id>/azurefunctionsimage:v1.0.1
    
  3. Da Sie Continuous Delivery konfiguriert haben, wird Ihre Funktions-App beim erneuten Aktualisieren des Images in der Registrierung automatisch in Azure aktualisiert.

Anzeigen der Nachricht in der Azure Storage-Warteschlange

Verwenden Sie in einem Browser dieselbe URL wie zuvor, um Ihre Funktion aufzurufen. Im Browser sollte die gleiche Antwort wie zuvor angezeigt werden, da Sie diesen Teil des Funktionscodes nicht geändert haben. Aufgrund des hinzugefügten Codes wurde aber eine Nachricht in die Speicherwarteschlange outqueue geschrieben, indem der URL-Parameter name verwendet wurde.

Sie können die Warteschlange im Azure-Portal oder im Microsoft Azure Storage-Explorer anzeigen. Sie haben auch die Möglichkeit, die Warteschlange per Azure CLI anzuzeigen. Dies ist in den folgenden Schritten beschrieben:

  1. Öffnen Sie die Datei local.setting.json des Funktionsprojekts, und kopieren Sie den Wert der Verbindungszeichenfolge. Führen Sie in einem Terminal oder Befehlsfenster den folgenden Befehl aus, um eine Umgebungsvariable mit dem Namen AZURE_STORAGE_CONNECTION_STRING zu erstellen, und fügen Sie anstelle von <MY_CONNECTION_STRING> Ihre spezifische Verbindungszeichenfolge ein. (Die Verwendung dieser Umgebungsvariablen bewirkt, dass Sie die Verbindungszeichenfolge nicht für jeden weiteren Befehl per --connection-string-Argument angeben müssen.)

    export AZURE_STORAGE_CONNECTION_STRING="<MY_CONNECTION_STRING>"
    
  2. (Optional) Verwenden Sie den Befehl az storage queue list, um die Storage-Warteschlangen in Ihrem Konto anzuzeigen. Die Ausgabe dieses Befehls sollte eine Warteschlange mit dem Namen outqueue enthalten. Sie wurde erstellt, als die Funktion ihre erste Nachricht in diese Warteschlange geschrieben hat.

    az storage queue list --output tsv
    
  3. Verwenden Sie den Befehl az storage message get, um die Nachricht aus dieser Warteschlange zu lesen. Dies sollte der erste Name sein, den Sie beim zuvor durchgeführten Testen der Funktion genutzt haben. Mit dem Befehl wird die erste Nachricht der Warteschlange gelesen und daraus entfernt.

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

    Da der Nachrichtentext im Base64-codierten Format gespeichert ist, muss die Nachricht vor dem Anzeigen decodiert werden. Nachdem Sie az storage message get ausgeführt haben, wird die Nachricht aus der Warteschlange entfernt. Falls in outqueue nur eine Nachricht enthalten war, rufen Sie keine Nachricht ab, wenn Sie diesen Befehl zum zweiten Mal ausführen, sondern erhalten einen Fehler.

Bereinigen von Ressourcen

Wenn Sie mit Azure Functions weiterarbeiten und die in diesem Tutorial erstellten Ressourcen weiterhin nutzen möchten, können Sie sie alle beibehalten. Da Sie einen Premium-Plan für Azure Functions erstellt haben, fallen für Sie pro Tag ein oder zwei US-Dollar an laufenden Kosten an.

Löschen Sie zur Vermeidung laufender Kosten die Ressourcengruppe AzureFunctionsContainer-rg, um alle Ressourcen dieser Gruppe zu bereinigen:

az group delete --name AzureFunctionsContainer-rg

Nächste Schritte