Create your first function on Azure Arc using a custom container (preview)

In this quickstart, you create an Azure Functions project running in a custom container and deploy it to an Azure Arc-enabled Kubernetes cluster from your Docker Hub account. To learn more, see App Service, Functions, and Logic Apps on Azure Arc. This scenario only supports function apps running on Linux.

Note

Support for running functions on an Azure Arc-enabled Kubernetes cluster is currently in preview.

Prerequisites

On your local computer:

Create an App Service Kubernetes environment

Before you begin, you must create an App Service Kubernetes environment for an Azure Arc-enabled Kubernetes cluster.

Note

When you create the environment, make sure to make note of both the custom location name and the name of the resource group that contains the custom location. You can use these to find the custom location ID, which you'll need when creating your function app in the environment.

If you didn't create the environment, check with your cluster administrator.

Add Azure CLI extensions

Launch the Bash environment in Azure Cloud Shell.

Launch Cloud Shell in a new window

Because these CLI commands are not yet part of the core CLI set, add them with the following commands:

az extension add --upgrade --yes --name customlocation
az extension remove --name appservice-kube
az extension add --yes --source "https://aka.ms/appsvc/appservice_kube-latest-py2.py3-none-any.whl"

Create the local function project

In Azure Functions, a function project is the context for one or more individual functions that each responds to a specific trigger. All functions in a project share the same local and hosting configurations. In this section, you create a function project that contains a single function.

  1. Run the func init command, as follows, to create a functions project in a folder named LocalFunctionProj with the specified runtime:

    func init LocalFunctionProj --dotnet --docker
    

    The --docker option generates a Dockerfile for the project, which defines a suitable custom container for use with Azure Functions and the selected runtime.

  2. Navigate into the project folder:

    cd LocalFunctionProj
    

    This folder contains the Dockerfile other files for the project, including configurations files named local.settings.json and host.json. By default, the local.settings.json file is excluded from source control in the .gitignore file. This exclusion is because the file can contain secrets that are downloaded from Azure.

  3. Open the generated Dockerfile and locate the 3.0 tag for the base image. If there's a 3.0 tag, replace it with a 3.0.15885 tag. For example, in a JavaScript application, the Docker file should be modified to have FROM mcr.microsoft.com/azure-functions/node:3.0.15885. This version of the base image supports deployment to an Azure Arc-enabled Kubernetes cluster.

  4. Add a function to your project by using the following command, where the --name argument is the unique name of your function (HttpExample) and the --template argument specifies the function's trigger (HTTP).

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

Build the container image and test locally

The Dockerfile in the project root describes the minimum required environment to run the function app in a container. The complete list of supported base images for Azure Functions can be found in the Azure Functions base image page.

  1. In the root project folder, run the docker build command, and provide a name, azurefunctionsimage, and tag, v1.0.0.

    The following command builds the Docker image for the container.

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

    In this example, replace <DOCKER_ID> with your Docker Hub account ID. When the command completes, you can run the new container locally.

  2. To test the build, run the image in a local container using the docker run command, with the adding the ports argument, -p 8080:80.

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

    Again, replace <DOCKER_ID with your Docker ID and adding the ports argument, -p 8080:80

  3. After the image is running in a local container, browse to http://localhost:8080/api/HttpExample?name=Functions, which should display the same "hello" message as before. Because the HTTP triggered function uses anonymous authorization, you can still call the function even though it's running in the container. Function access key settings are enforced when running locally in a container. If you have problems calling the function, make sure that access to the function is set to anonymous.

After you've verified the function app in the container, stop docker with Ctrl+C.

Push the image to Docker Hub

Docker Hub is a container registry that hosts images and provides image and container services. To share your image, which includes deploying to Azure, you must push it to a registry.

  1. If you haven't already signed in to Docker, do so with the docker login command, replacing <docker_id> with your Docker ID. This command prompts you for your username and password. A "Login Succeeded" message confirms that you're signed in.

    docker login
    
  2. After you've signed in, push the image to Docker Hub by using the docker push command, again replacing <docker_id> with your Docker ID.

    docker push <docker_id>/azurefunctionsimage:v1.0.0
    
  3. Depending on your network speed, pushing the image the first time might take a few minutes (pushing subsequent changes is much faster). While you're waiting, you can proceed to the next section and create Azure resources in another terminal.

Get the custom location

To be able to create a function app in a custom location, you'll need to get information about the environment.

Get the following information about the custom location from your cluster administrator (see Create a custom location).

customLocationGroup="<resource-group-containing-custom-location>"
customLocationName="<name-of-custom-location>"

Get the custom location ID for the next step.

customLocationId=$(az customlocation show \
    --resource-group $customLocationGroup \
    --name $customLocationName \
    --query id \
    --output tsv)

Create Azure resources

Before you can deploy your container to your new App Service Kubernetes environment, you need to create two more resources:

  • A Storage account, which is currently required by tooling and isn't part of the environment.
  • A function app, which provides the context for running your container. The function app runs in the App Service Kubernetes environment and maps to your local function project. A function app lets you group functions as a logical unit for easier management, deployment, and sharing of resources.

Note

Function apps run in an App Service Kubernetes environment on a Dedicated (App Service) plan. When you create your function app without an existing plan, a plan is created for you.

Create Storage account

Use the az storage account create command to create a general-purpose storage account in your resource group and region:

az storage account create --name <STORAGE_NAME> --location westeurope --resource-group myResourceGroup --sku Standard_LRS

Note

A storage account is currently required by Azure Functions tooling.

In the previous example, replace <STORAGE_NAME> with a name that is appropriate to you and unique in Azure Storage. Names must contain three to 24 characters numbers and lowercase letters only. Standard_LRS specifies a general-purpose account, which is supported by Functions. The --location value is a standard Azure region.

Create the function app

Run the az functionapp create command to create a new function app in the environment.

az functionapp create --resource-group MyResourceGroup --name <APP_NAME> --custom-location <CUSTOM_LOCATION_ID> --storage-account <STORAGE_NAME> --functions-version 3 --runtime dotnet --deployment-container-image-name <DOCKER_ID>/azurefunctionsimage:v1.0.0

In this example, replace <CUSTOM_LOCATION_ID> with the ID of the custom location you determined for the App Service Kubernetes environment. Also, replace <STORAGE_NAME> with the name of the account you used in the previous step, <APP_NAME> with a globally unique name appropriate to you, and <DOCKER_ID> with your Docker Hub ID.

The deployment-container-image-name parameter specifies the image to use for the function app. You can use the az functionapp config container show command to view information about the image used for deployment. You can also use the az functionapp config container set command to deploy from a different image.

When you first create the function app, it pulls the initial image from your Docker Hub. You can also Enable continuous deployment to Azure from Docker Hub.

To learn how to enable SSH in the image, see Enable SSH connections.

Set required app settings

Run the following commands to create an app setting for the storage account connection string:

storageConnectionString=$(az storage account show-connection-string --resource-group AzureFunctionsContainers-rg --name <STORAGE_NAME> --query connectionString --output tsv)
az functionapp config appsettings set --name <app_name> --resource-group AzureFunctionsContainers-rg --settings AzureWebJobsStorage=$storageConnectionString

This code must be run either in Cloud Shell or in Bash on your local computer. Replace <STORAGE_NAME> with the name of the storage account and <APP_NAME> with the function app name.

Invoke the function on Azure

Because your function uses an HTTP trigger, you invoke it by making an HTTP request to its URL in the browser or with a tool like curl.

Copy the complete Invoke URL shown in the output of the publish command into a browser address bar, appending the query parameter &name=Functions. The browser should display similar output as when you ran the function locally.

The output of the function run on Azure in a browser

Next steps

Now that you have your function app running in a container an Azure Arc-enabled App Service Kubernetes environment, you can connect it to Azure Storage by adding a Queue Storage output binding.