Tworzenie funkcji w systemie Linux przy użyciu kontenera niestandardowego

W tym samouczku utworzysz i wdrożysz kod, aby Azure Functions jako niestandardowy kontener platformy Docker przy użyciu obrazu podstawowego systemu Linux. Zwykle używasz obrazu niestandardowego, gdy funkcje wymagają określonej wersji językowej lub mają określoną zależność lub konfigurację, która nie jest dostarczana przez wbudowany obraz.

Azure Functions obsługuje dowolny język lub środowisko uruchomieniowe przy użyciu niestandardowych procedur obsługi. W przypadku niektórych języków, takich jak język programowania języka R używany w tym samouczku, należy zainstalować środowisko uruchomieniowe lub więcej bibliotek jako zależności, które wymagają użycia kontenera niestandardowego.

Wdrożenie kodu funkcji w niestandardowym kontenerze systemu Linux wymaga Premium planu lub hostingu dedykowanego (App Service). Ukończenie tego samouczka wiąże się z kosztami kilku dolarów amerykańskich na koncie platformy Azure, które można zminimalizować przez czyszczenie zasobów po zakończeniu pracy.

Możesz również użyć domyślnego kontenera Azure App Service zgodnie z opisem w temacie Create your first function hosted on Linux (Tworzenie pierwszej funkcji hostowanej w systemie Linux). Obsługiwane obrazy podstawowe dla Azure Functions znajdują się w repozytorium obrazów podstawowych Azure Functions.

Ten samouczek zawiera informacje na temat wykonywania następujących czynności:

  • Utwórz aplikację funkcji i plik Dockerfile przy użyciu narzędzi Azure Functions Core Tools.
  • Kompilowanie obrazu niestandardowego na platformie Docker.
  • Publikowanie obrazu niestandardowego w rejestrze kontenerów.
  • Tworzenie zasobów pomocniczych na platformie Azure dla aplikacji funkcji.
  • Wdrażanie aplikacji funkcji z usługi Docker Hub.
  • Dodawanie ustawień aplikacji do aplikacji funkcji.
  • Włącz ciągłe wdrażanie.
  • Włącz połączenia SSH z kontenerem.
  • Dodaj powiązanie wyjściowe usługi Queue Storage.
  • Utwórz aplikację funkcji i plik Dockerfile przy użyciu narzędzi Azure Functions Core Tools.
  • Kompilowanie obrazu niestandardowego na platformie Docker.
  • Publikowanie obrazu niestandardowego w rejestrze kontenerów.
  • Tworzenie zasobów pomocniczych na platformie Azure dla aplikacji funkcji.
  • Wdrażanie aplikacji funkcji z usługi Docker Hub.
  • Dodawanie ustawień aplikacji do aplikacji funkcji.
  • Włącz ciągłe wdrażanie.
  • Włącz połączenia SSH z kontenerem.

Ten samouczek można wykonać na dowolnym komputerze z systemem Windows, macOS lub Linux.

Konfigurowanie środowiska lokalnego

Przed rozpoczęciem należy spełnić następujące wymagania:

  • Node.js, zalecane wersje Active LTS i Maintenance LTS (zalecane wersje 8.11.1 i 10.14.1).
  • Narzędzia programistyczne dla używanego języka. W tym samouczku użyto języka programowania R jako przykładu.

Jeśli nie masz subskrypcji platformy Azure, przed rozpoczęciem utwórz bezpłatne konto platformy Azure .

Należy również uzyskać identyfikator platformy Docker i platformy Docker:

Tworzenie i aktywowanie środowiska wirtualnego

W odpowiednim folderze uruchom następujące polecenia, aby utworzyć i aktywować środowisko wirtualne o nazwie .venv. Upewnij się, że używasz języka Python 3.8, 3.7 lub 3.6, które są obsługiwane przez Azure Functions.

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

Jeśli język Python nie zainstalował pakietu venv w dystrybucji systemu Linux, uruchom następujące polecenie:

sudo apt-get install python3-venv

Wszystkie kolejne polecenia są uruchamiane w tym aktywowanym środowisku wirtualnym.

Tworzenie i testowanie projektu funkcji lokalnych

W terminalu lub wierszu polecenia uruchom następujące polecenie dla wybranego języka, aby utworzyć projekt aplikacji funkcji w bieżącym folderze:

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

W pustym folderze uruchom następujące polecenie, aby wygenerować projekt usługi Functions na podstawie archetypu narzędzia Maven:

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

Parametr -DjavaVersion informuje środowisko uruchomieniowe usługi Functions, która wersja języka Java ma być używana. Użyj polecenia -DjavaVersion=11, jeśli chcesz, aby funkcje działały w języku Java 11. Jeśli nie określisz -DjavaVersionparametru , program Maven domyślnie maven to Java 8. Aby uzyskać więcej informacji, zobacz Wersje języka Java.

Ważne

Aby JAVA_HOME ukończyć ten artykuł, należy ustawić zmienną środowiskową na lokalizację instalacji poprawnej wersji zestawu JDK.

Narzędzie Maven prosi o wartości potrzebne do zakończenia generowania projektu we wdrożeniu. Postępuj zgodnie z monitami i podaj następujące informacje:

Monit Wartość Opis
groupId com.fabrikam Wartość, która jednoznacznie identyfikuje projekt we wszystkich projektach, zgodnie z regułami nazewnictwa pakietów dla języka Java.
artifactId fabrikam-functions Wartość, która jest nazwą pliku jar bez numeru wersji.
Wersja 1.0-SNAPSHOT Wybierz wartość domyślną.
Pakiet com.fabrikam.functions Wartość, która jest pakietem Java dla wygenerowanego kodu funkcji. Użyj wartości domyślnej.

Wpisz Y lub naciśnij klawisz Enter, aby potwierdzić.

Narzędzie Maven tworzy pliki projektu w nowym folderze o nazwie artifactId, który w tym przykładzie to fabrikam-functions.

func init --worker-runtime custom --docker

Opcja --docker generuje plik Dockerfile dla projektu, który definiuje odpowiedni kontener niestandardowy do użycia z Azure Functions i wybranym środowiskiem uruchomieniowym.

Przejdź do folderu projektu:

cd fabrikam-functions

Do pliku Dockerfile nie są potrzebne żadne zmiany.

Użyj następującego polecenia, aby dodać funkcję do projektu, gdzie --name argument jest unikatową nazwą funkcji, a --template argument określa wyzwalacz funkcji. func new tworzy plik kodu w języku C# w projekcie.

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

Użyj następującego polecenia, aby dodać funkcję do projektu, gdzie --name argument jest unikatową nazwą funkcji, a --template argument określa wyzwalacz funkcji. func new Tworzy podfolder pasujący do nazwy funkcji zawierającej plik konfiguracji o nazwie function.json.

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

W edytorze tekstów utwórz plik w folderze projektu o nazwie handler. R. Dodaj następujący kod jako jego zawartość:

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)

W pliku host.json zmodyfikuj sekcję customHandler , aby skonfigurować polecenie uruchamiania niestandardowego programu obsługi.

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

Aby przetestować funkcję lokalnie, uruchom lokalny host środowiska uruchomieniowego Azure Functions w katalogu głównym folderu projektu.

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

Po wyświetleniu punktu końcowego HttpExample w danych wyjściowych przejdź do http://localhost:7071/api/HttpExample?name=Functionsadresu . Przeglądarka musi wyświetlić komunikat "hello", który odsunie z powrotem Functionswartość dostarczoną do parametru name zapytania.

Naciśnij klawisze Ctrl+C , aby zatrzymać hosta.

Kompilowanie obrazu kontenera i testowanie lokalnie

(Opcjonalnie) Sprawdź plik Dockerfile w katalogu głównym folderu projektu. Plik Dockerfile opisuje wymagane środowisko do uruchamiania aplikacji funkcji w systemie Linux. Pełna lista obsługiwanych obrazów bazowych dla Azure Functions można znaleźć na stronie obrazu podstawowego Azure Functions.

Sprawdź plik Dockerfile w katalogu głównym folderu projektu. Plik Dockerfile opisuje wymagane środowisko do uruchamiania aplikacji funkcji w systemie Linux. Niestandardowe aplikacje obsługi używają mcr.microsoft.com/azure-functions/dotnet:3.0-appservice obrazu jako podstawy.

Zmodyfikuj plik Dockerfile , aby zainstalować język R. Zastąp zawartość pliku Dockerfile następującym kodem:

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

W folderze głównym projektu uruchom polecenie docker build , podaj nazwę jako azurefunctionsimage, i tag jako v1.0.0. Zastąp ciąg <DOCKER_ID> identyfikatorem konta usługi Docker Hub. To polecenie powoduje skompilowanie obrazu platformy Docker dla kontenera.

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

Po zakończeniu wykonywania polecenia można uruchomić nowy kontener lokalnie.

Aby przetestować kompilację, uruchom obraz w kontenerze lokalnym przy użyciu polecenia docker run, zastąp <docker_id> ponownie identyfikatorem konta Docker Hub i dodaj argument portów jako -p 8080:80:

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

Po uruchomieniu obrazu w kontenerze lokalnym przejdź do http://localhost:8080/api/HttpExample?name=Functionsstrony , która musi wyświetlić ten sam komunikat "hello", jak poprzednio. Ponieważ utworzona funkcja HTTP wyzwalana używa autoryzacji anonimowej, można wywołać funkcję uruchomioną w kontenerze bez konieczności uzyskiwania klucza dostępu. Aby uzyskać więcej informacji, zobacz klucze autoryzacji.

Po uruchomieniu obrazu w kontenerze lokalnym przejdź do http://localhost:8080/api/HttpExample?name=Functionsstrony , która musi wyświetlić ten sam komunikat "hello", jak poprzednio. Ponieważ utworzona funkcja HTTP wyzwalana używa autoryzacji anonimowej, można wywołać funkcję uruchomioną w kontenerze bez konieczności uzyskiwania klucza dostępu. Aby uzyskać więcej informacji, zobacz klucze autoryzacji.

Po zweryfikowaniu aplikacji funkcji w kontenerze naciśnij klawisze Ctrl+C , aby zatrzymać platformę Docker.

Wypychanie obrazu do Docker Hub

Docker Hub to rejestr kontenerów, który hostuje obrazy i udostępnia usługi obrazów i kontenerów. Aby udostępnić obraz, który obejmuje wdrażanie na platformie Azure, należy wypchnąć go do rejestru.

  1. Jeśli jeszcze nie zalogowaliśmy się do platformy Docker, wykonaj to za pomocą polecenia logowania platformy Docker, zastępując ciąg <docker_id> identyfikatorem konta Docker Hub. To polecenie wyświetla monit o nazwę użytkownika i hasło. Komunikat "Logowanie powiodło się" potwierdza, że zalogowałeś się.

    docker login
    
  2. Po zalogowaniu wypchnij obraz do Docker Hub przy użyciu polecenia wypychania platformy Docker, ponownie zastąp <docker_id> element identyfikatorem konta Docker Hub.

    docker push <docker_id>/azurefunctionsimage:v1.0.0
    
  3. W zależności od szybkości sieci wypychanie obrazu po raz pierwszy może potrwać kilka minut (wypychanie kolejnych zmian jest znacznie szybsze). Czekając, możesz przejść do następnej sekcji i utworzyć zasoby platformy Azure w innym terminalu.

Tworzenie pomocniczych zasobów platformy Azure dla funkcji

Przed wdrożeniem kodu funkcji na platformie Azure należy utworzyć trzy zasoby:

  • Grupa zasobów, która jest logicznym kontenerem powiązanych zasobów.
  • Konto Storage używane do obsługi stanu i innych informacji o funkcjach.
  • Aplikacja funkcji, która udostępnia środowisko do wykonywania kodu funkcji. Aplikacja funkcji mapuje projekt funkcji lokalnej i umożliwia grupowanie funkcji jako jednostki logicznej w celu łatwiejszego zarządzania, wdrażania i udostępniania zasobów.

Użyj następujących poleceń, aby utworzyć te elementy. Obsługiwane są zarówno interfejs wiersza polecenia platformy Azure, jak i program PowerShell.

  1. Jeśli jeszcze tego nie zrobiono, zaloguj się na platformie Azure.

    az login
    

    Polecenie az login powoduje zalogowanie się do konta platformy Azure.

  2. Utwórz grupę zasobów o nazwie AzureFunctionsContainers-rg w wybranym regionie.

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

    Polecenie az group create tworzy grupę zasobów. W powyższym poleceniu zastąp <REGION> element regionem w pobliżu, używając dostępnego kodu regionu zwróconego z polecenia az account list-locations .

  3. Utwórz konto magazynu ogólnego przeznaczenia w grupie zasobów i regionie.

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

    Polecenie az storage account create tworzy konto magazynu.

    W poprzednim przykładzie zastąp <STORAGE_NAME> ciąg nazwą odpowiednią dla Ciebie i unikatową w usłudze Azure Storage. Storage nazwy muszą zawierać tylko od 3 do 24 znaków i małe litery. Standard_LRS określa konto ogólnego przeznaczenia obsługiwane przez funkcje.

  4. Użyj polecenia , aby utworzyć plan Premium dla Azure Functions nazwany myPremiumPlan w warstwie cenowej Elastic Premium 1 (), w <REGION>kontenerze systemu Linux (--sku EP1--is-linux).

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

    W tym miejscu użyjemy planu Premium, który może być skalowany zgodnie z potrzebami. Aby uzyskać więcej informacji na temat hostingu, zobacz porównanie planów hostingu Azure Functions. Aby uzyskać więcej informacji na temat sposobu obliczania kosztów, zobacz stronę cennika usługi Functions.

    Polecenie aprowizuje również skojarzone wystąpienie aplikacja systemu Azure Szczegółowe informacje w tej samej grupie zasobów, za pomocą której można monitorować aplikację funkcji i wyświetlać dzienniki. Aby uzyskać więcej informacji, zobacz Monitorowanie Azure Functions. Wystąpienie nie wiąże się z żadnymi kosztami do momentu jego aktywowania.

Tworzenie i konfigurowanie aplikacji funkcji na platformie Azure przy użyciu obrazu

Aplikacja funkcji na platformie Azure zarządza wykonywaniem funkcji w planie hostingu. W tej sekcji użyjesz zasobów platformy Azure z poprzedniej sekcji, aby utworzyć aplikację funkcji na podstawie obrazu na Docker Hub i skonfigurować ją za pomocą parametrów połączenia z usługą Azure Storage.

  1. Utwórz aplikację funkcji przy użyciu następującego polecenia:

    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
    

    W poleceniu az functionapp create parametr deployment-container-image-name określa obraz do użycia dla aplikacji funkcji. Możesz użyć polecenia az functionapp config container show , aby wyświetlić informacje o obrazie używanym do wdrożenia. Możesz również użyć polecenia az functionapp config container set , aby wdrożyć z innego obrazu.

    Uwaga

    Jeśli używasz niestandardowego rejestru kontenerów, parametr deployment-container-image-name odwołuje się do adresu URL rejestru.

    W tym przykładzie zastąp <STORAGE_NAME> ciąg nazwą użytą w poprzedniej sekcji konta magazynu. Zastąp <APP_NAME> również globalnie unikatową nazwą odpowiednią dla Ciebie i <DOCKER_ID> identyfikatorem konta Docker Hub. Podczas wdrażania z niestandardowego rejestru kontenerów użyj parametru deployment-container-image-name , aby wskazać adres URL rejestru.

    Porada

    Możesz użyć DisableColor ustawienia w pliku host.json , aby zapobiec zapisywaniu znaków kontrolnych ANSI w dziennikach kontenera.

  2. Użyj następującego polecenia, aby pobrać parametry połączenia dla utworzonego konta magazynu:

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

    Parametry połączenia dla konta magazynu są zwracane za pomocą polecenia az storage account show-connection-string .

    Zastąp <STORAGE_NAME> ciąg nazwą utworzonego wcześniej konta magazynu.

  3. Użyj następującego polecenia, aby dodać ustawienie do aplikacji funkcji:

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

    Polecenie az functionapp config appsettings set tworzy ustawienie.

    W tym poleceniu zastąp <APP_NAME> ciąg nazwą aplikacji funkcji i <CONNECTION_STRING> parametrami połączenia z poprzedniego kroku. Połączenie powinno być długimi zakodowanymi parametrami rozpoczynającymi DefaultEndpointProtocol=się od .

  4. Funkcja może teraz używać tych parametrów połączenia do uzyskiwania dostępu do konta magazynu.

Uwaga

Jeśli publikujesz obraz niestandardowy w prywatnym rejestrze kontenerów, musisz zamiast tego użyć zmiennych środowiskowych w pliku Dockerfile dla parametrów połączenia. Aby uzyskać więcej informacji, zobacz instrukcję ENV. Należy również ustawić DOCKER_REGISTRY_SERVER_USERNAME zmienne i DOCKER_REGISTRY_SERVER_PASSWORD . Aby użyć wartości, należy ponownie skompilować obraz, wypchnąć obraz do rejestru, a następnie ponownie uruchomić aplikację funkcji na platformie Azure.

Weryfikowanie funkcji na platformie Azure

Po wdrożeniu obrazu w aplikacji funkcji na platformie Azure można teraz wywołać funkcję tak jak wcześniej za pośrednictwem żądań HTTP. W przeglądarce przejdź do następującego adresu URL:

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

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

Zastąp <APP_NAME> ciąg nazwą aplikacji funkcji. Po przejściu do tego adresu URL przeglądarka musi wyświetlić podobne dane wyjściowe, jak podczas lokalnego uruchamiania funkcji.

Włączanie ciągłego wdrażania na platformie Azure

Możesz włączyć Azure Functions, aby automatycznie aktualizować wdrożenie obrazu przy każdej aktualizacji obrazu w rejestrze.

  1. Użyj następującego polecenia, aby włączyć ciągłe wdrażanie i uzyskać adres URL elementu webhook:

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

    Polecenie az functionapp deployment container config umożliwia ciągłe wdrażanie i zwraca adres URL elementu webhook wdrożenia. Ten adres URL można pobrać w dowolnym późniejszym czasie za pomocą polecenia az functionapp deployment container show-cd-url .

    Tak jak poprzednio zastąp <APP_NAME> ciąg nazwą aplikacji funkcji.

  2. Skopiuj adres URL elementu webhook wdrożenia do schowka.

  3. Otwórz Docker Hub, zaloguj się i wybierz pozycję Repozytoria na pasku nawigacyjnym. Znajdź i wybierz obraz, wybierz kartę Elementy webhook , określ nazwę elementu webhook, wklej adres URL w adresie URL elementu webhook, a następnie wybierz pozycję Utwórz.

    Screenshot showing how to add the webhook in your Docker Hub window.

  4. Po ustawieniu elementu webhook Azure Functions ponownie wdrożyć obraz przy każdej aktualizacji w Docker Hub.

Włączanie połączeń SSH

Protokół SSH umożliwia bezpieczną komunikację między kontenerem i klientem. Po włączeniu protokołu SSH możesz nawiązać połączenie z kontenerem przy użyciu narzędzi App Service Advanced Tools (Kudu). Aby łatwo nawiązać połączenie z kontenerem przy użyciu protokołu SSH, Azure Functions udostępnia obraz podstawowy, który ma już włączony protokół SSH. Wystarczy edytować plik Dockerfile, a następnie ponownie skompilować i wdrożyć obraz. Następnie możesz nawiązać połączenie z kontenerem za pomocą narzędzi Zaawansowanych (Kudu).

  1. W pliku Dockerfile dołącz ciąg -appservice do obrazu podstawowego w FROM instrukcji.

    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. Ponownie skompiluj obraz, używając docker build polecenia , zastąp element <docker_id> identyfikatorem konta Docker Hub.

    docker build --tag <docker_id>/azurefunctionsimage:v1.0.0 .
    
  3. Wypchnij zaktualizowany obraz do Docker Hub, co powinno zająć znacznie mniej czasu niż pierwsze wypchnięcie. Teraz należy przekazać tylko zaktualizowane segmenty obrazu.

    docker push <docker_id>/azurefunctionsimage:v1.0.0
    
  4. Azure Functions automatycznie ponownie wdraża obraz w aplikacji funkcji; proces odbywa się w mniej niż minutę.

  5. W przeglądarce otwórz https://<app_name>.scm.azurewebsites.net/ plik i zastąp <app_name> unikatową nazwą. Ten adres URL to punkt końcowy narzędzi zaawansowanych (Kudu) dla kontenera aplikacji funkcji.

  6. Zaloguj się do konta platformy Azure, a następnie wybierz protokół SSH , aby nawiązać połączenie z kontenerem. Nawiązywanie połączenia może potrwać kilka chwil, jeśli platforma Azure nadal aktualizuje obraz kontenera.

  7. Po nawiązaniu połączenia z kontenerem uruchom top polecenie , aby wyświetlić aktualnie uruchomione procesy.

    Screenshot that shows Linux top command running in an SSH session.

Zapisywanie w usłudze Azure Queue Storage

Azure Functions umożliwia łączenie funkcji z innymi usługami i zasobami platformy Azure bez konieczności pisania własnego kodu integracji. Te powiązania, które reprezentują zarówno dane wejściowe, jak i wyjściowe, są deklarowane w definicji funkcji. Dane z powiązań są podawane do funkcji jako parametry. Wyzwalacz jest specjalnym typem powiązania wejściowego. Chociaż funkcja ma tylko jeden wyzwalacz, może mieć wiele powiązań wejściowych i wyjściowych. Aby uzyskać więcej informacji, zobacz pojęcia dotyczące wyzwalaczy i powiązań Azure Functions.

W tej sekcji pokazano, jak zintegrować funkcję z usługą Azure Queue Storage. Powiązanie wyjściowe dodane do tej funkcji zapisuje dane z żądania HTTP do komunikatu w kolejce.

Pobieranie parametrów połączenia usługi Azure Storage

Wcześniej utworzono konto usługi Azure Storage na potrzeby użycia aplikacji funkcji. Parametry połączenia dla tego konta są bezpiecznie przechowywane w ustawieniach aplikacji na platformie Azure. Pobierając ustawienie do pliku local.settings.json, możesz użyć połączenia do zapisu w kolejce Storage na tym samym koncie podczas lokalnego uruchamiania funkcji.

  1. W katalogu głównym projektu uruchom następujące polecenie, zastąp <APP_NAME> ciąg nazwą aplikacji funkcji z poprzedniego kroku. To polecenie zastępuje wszystkie istniejące wartości w pliku.

    func azure functionapp fetch-app-settings <APP_NAME>
    
  2. Otwórz plik local.settings.json i znajdź wartość o nazwie AzureWebJobsStorage, która jest parametrami połączenia konta Storage. Nazwa i parametry połączenia są używane AzureWebJobsStorage w innych sekcjach tego artykułu.

Ważne

Ponieważ plik local.settings.json zawiera wpisy tajne pobrane z platformy Azure, zawsze wykluczaj ten plik z kontroli źródła. Plik .gitignore utworzony za pomocą projektu funkcji lokalnych domyślnie wyklucza plik.

Rejestrowanie rozszerzeń do wiązania

Z wyjątkiem wyzwalaczy HTTP i czasomierza powiązania są implementowane jako pakiety rozszerzeń. Uruchom następujące polecenie dotnet add package w oknie terminalu, aby dodać pakiet rozszerzenia Storage do projektu.

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

Teraz możesz dodać powiązanie danych wyjściowych magazynu do projektu.

Dodawanie definicji powiązania wyjściowego do funkcji

Chociaż funkcja może mieć tylko jeden wyzwalacz, może mieć wiele powiązań wejściowych i wyjściowych, co umożliwia łączenie się z innymi usługami i zasobami platformy Azure bez konieczności pisania niestandardowego kodu integracji.

Te powiązania są deklarowane w pliku function.json w folderze funkcji. W poprzednim przewodniku Szybki start plik function.json w folderze HttpExample zawiera dwa powiązania w kolekcji 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"
  }
]

Każde powiązanie ma co najmniej typ, kierunek i nazwę. W powyższym przykładzie pierwsze powiązanie jest typu httpTrigger z kierunkiem in. in Dla kierunku name określa nazwę parametru wejściowego, który jest wysyłany do funkcji po wywołaniu przez wyzwalacz.

Drugie powiązanie w kolekcji nosi nazwę res. To http powiązanie jest powiązaniem wyjściowym (out), które jest używane do zapisywania odpowiedzi HTTP.

Aby zapisać w kolejce usługi Azure Storage z tej funkcji, dodaj out powiązanie typu queue o nazwie msg, jak pokazano w poniższym kodzie:

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

Drugie powiązanie w kolekcji jest typu http z kierunkiem out, w którym przypadku specjalna name$return wartość wskazuje, że to powiązanie używa wartości zwracanej funkcji zamiast podawania parametru wejściowego.

Aby zapisać w kolejce usługi Azure Storage z tej funkcji, dodaj out powiązanie typu queue o nazwie msg, jak pokazano w poniższym kodzie:

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

Drugie powiązanie w kolekcji nosi nazwę res. To http powiązanie jest powiązaniem wyjściowym (out), które jest używane do zapisywania odpowiedzi HTTP.

Aby zapisać w kolejce usługi Azure Storage z tej funkcji, dodaj out powiązanie typu queue o nazwie msg, jak pokazano w poniższym kodzie:

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

W tym przypadku msg funkcja jest podawana jako argument wyjściowy. queue W przypadku typu należy również określić nazwę kolejki w queueName pliku i podać nazwę połączenia usługi Azure Storage (z pliku local.settings.json) w pliku connection.

W projekcie języka C# powiązania są definiowane jako atrybuty powiązania w metodzie funkcji. Określone definicje zależą od tego, czy aplikacja działa w procesie (biblioteka klas języka C#), czy w izolowanym procesie.

Otwórz plik projektu HttpExample.cs i dodaj następujący parametr do Run definicji metody:

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

Parametr msg jest typem ICollector<T> reprezentującym kolekcję komunikatów zapisywanych w powiązaniu wyjściowym po zakończeniu działania funkcji. W takim przypadku dane wyjściowe są kolejką magazynu o nazwie outqueue. Ustawia StorageAccountAttribute parametry połączenia dla konta magazynu. Ten atrybut wskazuje ustawienie, które zawiera parametry połączenia konta magazynu i można je zastosować na poziomie klasy, metody lub parametru. W takim przypadku można pominąć StorageAccountAttribute , ponieważ używasz już domyślnego konta magazynu.

Definicja metody Run musi teraz wyglądać podobnie do następującego kodu:

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

W projekcie Java powiązania są definiowane jako adnotacje powiązań w metodzie funkcji. Plik function.json jest następnie automatycznie generowany na podstawie tych adnotacji.

Przejdź do lokalizacji kodu funkcji w obszarze src/main/java, otwórz plik projektu Function.java i dodaj następujący parametr do run definicji metody:

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

Parametr msg jest typem OutputBinding<T> , który reprezentuje kolekcję ciągów. Te ciągi są zapisywane jako komunikaty do powiązania wyjściowego po zakończeniu działania funkcji. W takim przypadku dane wyjściowe są kolejką magazynu o nazwie outqueue. Parametry połączenia dla konta Storage są ustawiane przez metodę connection . Przekazujesz ustawienie aplikacji zawierające parametry połączenia konta Storage, zamiast przekazywać same parametry połączenia.

Definicja run metody musi teraz wyglądać podobnie do poniższego przykładu:

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

Dodawanie kodu w celu użycia powiązania wyjściowego

Po zdefiniowaniu powiązania kolejki można teraz zaktualizować funkcję w celu zapisywania komunikatów w kolejce przy użyciu parametru powiązania.

Zaktualizuj plik HttpExample\__init__.py , aby był zgodny z następującym kodem, dodaj msg parametr do definicji funkcji i msg.set(name) w instrukcji 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
        )

Parametr msg jest wystąpieniem klasy azure.functions.Out class. Metoda set zapisuje komunikat ciągu w kolejce. W tym przypadku jest to nazwa przekazana do funkcji w ciągu zapytania adresu URL.

Dodaj kod, który używa obiektu powiązania wyjściowego msg do context.bindings utworzenia komunikatu w kolejce. Dodaj ten kod przed instrukcją 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);

W tym momencie funkcja musi wyglądać następująco:

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

Dodaj kod, który używa obiektu powiązania wyjściowego msg do context.bindings utworzenia komunikatu w kolejce. Dodaj ten kod przed instrukcją context.res .

context.bindings.msg = name;

W tym momencie funkcja musi wyglądać następująco:

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;

Dodaj kod, który używa Push-OutputBinding polecenia cmdlet do pisania tekstu w kolejce przy użyciu powiązania wyjściowego msg . Dodaj ten kod przed ustawieniem stanu OK w instrukcji if .

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

W tym momencie funkcja musi wyglądać następująco:

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

Dodaj kod, który używa obiektu powiązania wyjściowego msg do utworzenia komunikatu w kolejce. Dodaj ten kod przed zwróceniem metody .

if (!string.IsNullOrEmpty(name))
{
    // Add a message to the output collection.
    msg.Add(name);
}

W tym momencie funkcja musi wyglądać następująco:

[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(name);
    }
    return name != null
        ? (ActionResult)new OkObjectResult($"Hello, {name}")
        : new BadRequestObjectResult("Please pass a name on the query string or in the request body");
}

Teraz możesz użyć nowego msg parametru do zapisania do powiązania wyjściowego z kodu funkcji. Dodaj następujący wiersz kodu przed odpowiedzią powodzenia, aby dodać wartość name do powiązania wyjściowego msg .

msg.setValue(name);

Jeśli używasz powiązania wyjściowego, nie musisz używać kodu zestawu SDK usługi Azure Storage do uwierzytelniania, pobierania odwołania do kolejki ani zapisywania danych. Powiązanie danych wyjściowych środowiska uruchomieniowego i kolejki usługi Functions wykonuje te zadania.

Metoda run musi teraz wyglądać podobnie do następującego przykładu:

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

Aktualizowanie testów

Ponieważ archetyp tworzy również zestaw testów, należy zaktualizować te testy, aby obsługiwać nowy msg parametr w podpisie run metody.

Przejdź do lokalizacji kodu testowego w obszarze src/test/java, otwórz plik projektu Function.java i zastąp wiersz kodu poniżej //Invoke następującym kodem:

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

Aktualizowanie obrazu w rejestrze

  1. W folderze głównym uruchom ponownie polecenie docker build , a tym razem zaktualizuj wersję w tagu na v1.0.1. Tak jak poprzednio zastąp <docker_id> wartość identyfikatorem konta Docker Hub.

    docker build --tag <docker_id>/azurefunctionsimage:v1.0.1 .
    
  2. Wypchnij zaktualizowany obraz z powrotem do repozytorium za pomocą polecenia docker push.

    docker push <docker_id>/azurefunctionsimage:v1.0.1
    
  3. Ponieważ skonfigurowano ciągłe dostarczanie, aktualizacja obrazu w rejestrze ponownie automatycznie aktualizuje aplikację funkcji na platformie Azure.

Wyświetlanie komunikatu w kolejce usługi Azure Storage

W przeglądarce użyj tego samego adresu URL co wcześniej, aby wywołać funkcję. Przeglądarka musi wyświetlać tę samą odpowiedź co poprzednio, ponieważ nie zmodyfikowano tej części kodu funkcji. Dodany kod napisał jednak komunikat przy użyciu parametru name ADRESU URL do outqueue kolejki magazynu.

Kolejkę można wyświetlić w Azure Portal lub w Eksplorator usługi Microsoft Azure Storage. Kolejkę można również wyświetlić w interfejsie wiersza polecenia platformy Azure, zgodnie z opisem w następujących krokach:

  1. Otwórz plik local.setting.json projektu funkcji i skopiuj wartość parametrów połączenia. W terminalu lub oknie polecenia uruchom następujące polecenie, aby utworzyć zmienną środowiskową o nazwie AZURE_STORAGE_CONNECTION_STRINGi wklej określone parametry połączenia zamiast <MY_CONNECTION_STRING>. (Ta zmienna środowiskowa oznacza, że nie trzeba podawać parametrów połączenia do każdego kolejnego polecenia przy użyciu argumentu --connection-string ).

    export AZURE_STORAGE_CONNECTION_STRING="<MY_CONNECTION_STRING>"
    
  2. (Opcjonalnie) az storage queue list Użyj polecenia , aby wyświetlić kolejki Storage na koncie. Dane wyjściowe tego polecenia muszą zawierać kolejkę o nazwie outqueue, która została utworzona, gdy funkcja napisała swój pierwszy komunikat do tej kolejki.

    az storage queue list --output tsv
    
  3. az storage message get Użyj polecenia , aby odczytać komunikat z tej kolejki, który powinien być wartością podaną podczas wcześniejszego testowania funkcji. Polecenie odczytuje i usuwa pierwszy komunikat z kolejki.

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

    Ponieważ treść komunikatu jest przechowywana w formacie Base64, komunikat musi zostać zdekodowany przed jego wyświetleniem. Po wykonaniu az storage message getpolecenia komunikat zostanie usunięty z kolejki. Jeśli w programie był tylko jeden komunikat outqueue, nie można pobrać komunikatu po uruchomieniu tego polecenia po raz drugi i zamiast tego zostanie wyświetlony błąd.

Czyszczenie zasobów

Jeśli chcesz kontynuować pracę z funkcją platformy Azure przy użyciu zasobów utworzonych w tym samouczku, możesz pozostawić wszystkie te zasoby na swoim miejscu. Ponieważ utworzono plan Premium dla Azure Functions, poniesiesz jeden lub dwa USD dziennie w kosztach bieżących.

Aby uniknąć bieżących kosztów, usuń grupę AzureFunctionsContainers-rg zasobów, aby wyczyścić wszystkie zasoby w tej grupie:

az group delete --name AzureFunctionsContainers-rg

Następne kroki