Create a function on Linux using a custom image

Azure Functions lets you host your functions on Linux in your own custom container. You can also host on a default Azure App Service container. This functionality requires the Functions 2.x runtime.

In this tutorial, you learn how to deploy your functions to Azure as a custom Docker image. This pattern is useful when you need to customize the built-in App Service container image. You may want to use a custom image when your functions need a specific language version or require a specific dependency or configuration that isn't provided within the built-in image. Supported base images for Azure Functions are found in the Azure Functions base images repo. Python support is in preview at this time.

This tutorial walks you through how to use Azure Functions Core Tools to create a function in a custom Linux image. You publish this image to a function app in Azure, which was created using the Azure CLI.

In this tutorial, you learn how to:

  • Create a function app and Dockerfile using Core Tools.
  • Build a custom image using Docker.
  • Publish a custom image to a container registry.
  • Create an Azure Storage account.
  • Create a Linux App Service plan.
  • Deploy a function app from Docker Hub.
  • Add application settings to the function app.
  • Enable continuous deployment

The following steps are supported on a Mac, Windows, or Linux computer.

Prerequisites

Before running this sample, you must have the following:

If you don't have an Azure subscription, create a free account before you begin.

Create the local function app project

Run the following command from the command line to create a function app project in the MyFunctionProj folder of the current local directory.

func init MyFunctionProj --docker

When you include the --docker option, a dockerfile is generated for the project. This file is used to create a custom container in which to run the project. The base image used depends on the worker runtime language chosen.

When prompted, choose a worker runtime from the following languages:

  • dotnet: creates a .NET class library project (.csproj).
  • node: creates a JavaScript project.
  • python: creates a Python project.

Note

Python for Azure Functions is currently in preview. To receive important updates, subscribe to the Azure App Service announcements repository on GitHub.

When the command executes, you see something like the following output:

Writing .gitignore
Writing host.json
Writing local.settings.json
Writing Dockerfile

Use the following command to navigate to the new MyFunctionProj project folder.

cd MyFunctionProj

Create a function

The following command creates an HTTP-triggered function named MyHttpTrigger.

func new --name MyHttpTrigger --template "HttpTrigger"

When the command executes, you see something like the following output:

The function "MyHttpTrigger" was created successfully from the "HttpTrigger" template.

Update the function

By default, the template creates a function that requires a function key when making requests. To make it easier to test the function in Azure, you need to update the function to allow anonymous access. The way that you make this change depends on your functions project language.

C#

Open the MyHttpTrigger.cs code file that is your new function and update the AuthorizationLevel attribute in the function definition to a value of Anonymous and save your changes.

[FunctionName("MyHttpTrigger")]
public static async Task<IActionResult> Run(
    [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
    ILogger log)

JavaScript

Open the function.json file for your new function in a text editor, update the authLevel property in bindings to anonymous, and save your changes.

  "bindings": [
    {
      "authLevel": "anonymous",
      "type": "httpTrigger",
      "direction": "in",
      "name": "req",
      "methods": [
        "get",
        "post"
      ]
    },
    {
      "type": "http",
      "direction": "out",
      "name": "$return"
    }
  ]

Now you can call the function in Azure without having to supply the function key. The function key is never required when running locally.

Run the function locally

The following command starts the function app. The app runs using the same Azure Functions runtime that is in Azure.

func host start --build

The --build option is required to compile C# projects. You don't need this option for a JavaScript project.

When the Functions host starts, it write something like the following output, which has been truncated for readability:


                  %%%%%%
                 %%%%%%
            @   %%%%%%    @
          @@   %%%%%%      @@
       @@@    %%%%%%%%%%%    @@@
     @@      %%%%%%%%%%        @@
       @@         %%%%       @@
         @@      %%%       @@
           @@    %%      @@
                %%
                %

...

Content root path: C:\functions\MyFunctionProj
Now listening on: http://0.0.0.0:7071
Application started. Press Ctrl+C to shut down.

...

Http Functions:

        HttpTrigger: http://localhost:7071/api/MyHttpTrigger

[8/27/2018 10:38:27 PM] Host started (29486ms)
[8/27/2018 10:38:27 PM] Job host started

Copy the URL of your HttpTrigger function from the runtime output and paste it into your browser's address bar. Append the query string ?name=<yourname> to this URL and execute the request. The following shows the response in the browser to the GET request returned by the local function:

Test locally in the browser

Now that you have run your function locally, you can create the function app and other required resources in Azure.

Build the image from the Docker file

Take a look at the Dockerfile in the root folder of the project. This file describes the environment that is required to run the function app on Linux. The following example is a Dockerfile that creates a container that runs a function app on the JavaScript (Node.js) worker runtime:

FROM mcr.microsoft.com/azure-functions/node:2.0

ENV AzureWebJobsScriptRoot=/home/site/wwwroot
COPY . /home/site/wwwroot

Note

The complete list of supported base images for Azure Functions can be found in the Azure Functions base image page.

Run the build command

In the root folder, run the docker build command, and provide a name, mydockerimage, and tag, v1.0.0. Replace <docker-id> with your Docker Hub account ID. This command builds the Docker image for the container.

docker build --tag <docker-id>/mydockerimage:v1.0.0 .

When the command executes, you see something like the following output, which in this case is for a JavaScript worker runtime:

Sending build context to Docker daemon  17.41kB
Step 1/3 : FROM mcr.microsoft.com/azure-functions/node:2.0
2.0: Pulling from azure-functions/node
802b00ed6f79: Pull complete
44580ea7a636: Pull complete
73eebe8d57f9: Pull complete
3d82a67477c2: Pull complete
8bd51cd50290: Pull complete
7bd755353966: Pull complete
Digest: sha256:480e969821e9befe7c61dda353f63298f2c4b109e13032df5518e92540ea1d08
Status: Downloaded newer image for mcr.microsoft.com/azure-functions/node:2.0
 ---> 7c71671b838f
Step 2/3 : ENV AzureWebJobsScriptRoot=/home/site/wwwroot
 ---> Running in ed1e5809f0b7
Removing intermediate container ed1e5809f0b7
 ---> 39d9c341368a
Step 3/3 : COPY . /home/site/wwwroot
 ---> 5e196215935a
Successfully built 5e196215935a
Successfully tagged <docker-id>/mydockerimage:v1.0.0

Test the image locally

Verify that the image you built works by running the Docker image in a local container. Issue the docker run command and pass the name and tag of the image to it. Be sure to specify the port using the -p argument.

docker run -p 8080:80 -it <docker-ID>/mydockerimage:v1.0.0

With the custom image running in a local Docker container, verify the function app and container are functioning correctly by browsing to http://localhost:8080.

Test the function app locally.

You can optionally test your function again, this time in the local container using the following URL:

http://localhost:8080/api/myhttptrigger?name=<yourname>

After you have verified the function app in the container, stop the execution. Now, you can push the custom image to your Docker Hub account.

Push the custom image to Docker Hub

A registry is an application that hosts images and provides services image and container services. To share your image, you must push it to a registry. Docker Hub is a registry for Docker images that allows you to host your own repositories, either public or private.

Before you can push an image, you must sign in to Docker Hub using the docker login command. Replace <docker-id> with your account name and type in your password into the console at the prompt. For other Docker Hub password options, see the docker login command documentation.

docker login --username <docker-id>

A "login succeeded" message confirms that you are logged in. After you have signed in, you push the image to Docker Hub by using the docker push command.

docker push <docker-id>/mydockerimage:v1.0.0

Verify that the push succeeded by examining the command's output.

The push refers to a repository [docker.io/<docker-id>/mydockerimage:v1.0.0]
24d81eb139bf: Pushed
fd9e998161c9: Mounted from <docker-id>/mydockerimage
e7796c35add2: Mounted from <docker-id>/mydockerimage
ae9a05b85848: Mounted from <docker-id>/mydockerimage
45c86e20670d: Mounted from <docker-id>/mydockerimage
v1.0.0: digest: sha256:be080d80770df71234eb893fbe4d... size: 1796

Now, you can use this image as the deployment source for a new function app in Azure.

Create a resource group

Create a resource group with the az group create. An Azure resource group is a logical container into which Azure resources like function apps, databases, and storage accounts are deployed and managed.

The following example creates a resource group named myResourceGroup.
If you are not using Cloud Shell, sign in first using az login.

az group create --name myResourceGroup --location westeurope

You generally create your resource group and the resources in a region near you. To see all supported locations for App Service plans, run the az appservice list-locations command.

Create an Azure Storage account

Functions uses a general-purpose account in Azure Storage to maintain state and other information about your functions. Create a general-purpose storage account in the resource group you created by using the az storage account create command.

In the following command, substitute a globally unique storage account name where you see the <storage_name> placeholder. Storage account names must be between 3 and 24 characters in length and may contain numbers and lowercase letters only.

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

After the storage account has been created, the Azure CLI shows information similar to the following example:

{
  "creationTime": "2017-04-15T17:14:39.320307+00:00",
  "id": "/subscriptions/bbbef702-e769-477b-9f16-bc4d3aa97387/resourceGroups/myresourcegroup/...",
  "kind": "Storage",
  "location": "westeurope",
  "name": "myfunctionappstorage",
  "primaryEndpoints": {
    "blob": "https://myfunctionappstorage.blob.core.windows.net/",
    "file": "https://myfunctionappstorage.file.core.windows.net/",
    "queue": "https://myfunctionappstorage.queue.core.windows.net/",
    "table": "https://myfunctionappstorage.table.core.windows.net/"
  },
     ....
    // Remaining output has been truncated for readability.
}

Create a Linux App Service plan

Linux hosting for Functions is currently not supported on consumption plans. You have to host Linux container apps in a Linux App Service plan. To learn more about hosting, see Azure Functions hosting plans comparison.

In the Cloud Shell, create an App Service plan in the resource group with the az appservice plan create command.

The following example creates an App Service plan named myAppServicePlan in the Basic pricing tier (--sku B1) and in a Linux container (--is-linux).

az appservice plan create --name myAppServicePlan --resource-group myResourceGroup --sku B1 --is-linux

When the App Service plan has been created, the Azure CLI shows information similar to the following example:

{ 
  "adminSiteName": null,
  "appServicePlanName": "myAppServicePlan",
  "geoRegion": "West Europe",
  "hostingEnvironmentProfile": null,
  "id": "/subscriptions/0000-0000/resourceGroups/myResourceGroup/providers/Microsoft.Web/serverfarms/myAppServicePlan",
  "kind": "linux",
  "location": "West Europe",
  "maximumNumberOfWorkers": 1,
  "name": "myAppServicePlan",
  < JSON data removed for brevity. >
  "targetWorkerSizeId": 0,
  "type": "Microsoft.Web/serverfarms",
  "workerTierName": null
} 

Create and deploy the custom image

The function app hosts the execution of your functions. Create a function app from a Docker Hub image by using the az functionapp create command.

In the following command, substitute a unique function app name where you see the <app_name> placeholder and the storage account name for <storage_name>. The <app_name> is used as the default DNS domain for the function app, and so the name needs to be unique across all apps in Azure. As before, <docker-id> is your Docker account name.

az functionapp create --name <app_name> --storage-account  <storage_name>  --resource-group myResourceGroup \
--plan myAppServicePlan --deployment-container-image-name <docker-id>/mydockerimage:v1.0.0

After the function app has been created, the Azure CLI shows information similar to the following example:

{
  "availabilityState": "Normal",
  "clientAffinityEnabled": true,
  "clientCertEnabled": false,
  "containerSize": 1536,
  "dailyMemoryTimeQuota": 0,
  "defaultHostName": "quickstart.azurewebsites.net",
  "enabled": true,
  "enabledHostNames": [
    "quickstart.azurewebsites.net",
    "quickstart.scm.azurewebsites.net"
  ],
   ....
    // Remaining output has been truncated for readability.
}

The deployment-container-image-name parameter indicates the image hosted on Docker Hub to use to create the function app. Use the az functionapp config container show command to view information about the image used for deployment. Use the az functionapp config container set command to deploy from a different image.

Configure the function app

The function needs the connection string to connect to the default storage account. When you are publishing your custom image to a private container account, you should instead set these application settings as environment variables in the Dockerfile using the ENV instruction, or something similar.

In this case, <storage_name> is the name of the storage account you created. Get the connection string with the az storage account show-connection-string command. Add these application settings in the function app with the az functionapp config appsettings set command.

storageConnectionString=$(az storage account show-connection-string \
--resource-group myResourceGroup --name <storage_name> \
--query connectionString --output tsv)

az functionapp config appsettings set --name <app_name> \
--resource-group myResourceGroup \
--settings AzureWebJobsDashboard=$storageConnectionString \
AzureWebJobsStorage=$storageConnectionString

Note

If your container is private, you would have to set the following application settings as well

  • DOCKER_REGISTRY_SERVER_USERNAME
  • DOCKER_REGISTRY_SERVER_PASSWORD

You will have to stop and then start your function app for these values to be picked up

You can now test your functions running on Linux in Azure.

Test the function in Azure

Use cURL to test the deployed function. Using the URL that you copied from the previous step, append the query string &name=<yourname> to the URL, as in the following example:

curl https://myfunctionapp.azurewebsites.net/api/httptrigger?code=cCr8sAxfBiow548FBDLS1....&name=<yourname>

using cURL to call the function in Azure.

You can also paste the copied URL in to the address of your web browser. Again, append the query string &name=<yourname> to the URL before you execute the request.

Using a web browser to call the function.

Enable Application Insights

The recommended way to monitor the execution of your functions is by integrating your function app with Azure Application Insights. When you create a function app in the Azure portal, this integration is done for you by default. However, when you create your function app by using the Azure CLI, the integration in your function app in Azure isn't done.

To enable Application Insights for your function app:

Functions makes it simple to add Application Insights integration to a function app from the Azure portal.

  1. In the portal, select All services > Function Apps, select your function app, and then choose the Application Insights banner at the top of the window

    Enable Application Insights from the portal

  2. Create an Application Insights resource by using the settings specified in the table below the image:

    Create an Application Insights resource

    Setting Suggested value Description
    Name Unique app name It's easiest to use the same name as your function app, which must be unique in your subscription.
    Location West Europe If possible, use the same region as your function app, or near to it.
  3. Choose OK. The Application Insights resource is created in the same resource group and subscription as your function app. After creation completes, close the Application Insights window.

  4. Back in your function app, select Application settings, and scroll down to Application settings. When you see a setting named APPINSIGHTS_INSTRUMENTATIONKEY, it means that Application Insights integration is enabled for your function app running in Azure.

To learn more, see Monitor Azure Functions.

Enable continuous deployment

One of the benefits of using containers is being able to automatically deploy updates when containers are updated in the registry. Enable continuous deployment with the az functionapp deployment container config command.

az functionapp deployment container config --enable-cd \
--query CI_CD_URL --output tsv \
--name <app_name> --resource-group myResourceGroup

This command returns the deployment webhook URL after continuous deployment is enabled. You can also use the az functionapp deployment container show-cd-url command to return this URL.

Copy the deployment URL and browse to your DockerHub repo, choose the Webhooks tab, type a Webhook name for the webhook, paste your URL in Webhook URL, and then choose the plus sign (+).

Add the webhook in your DockerHub repo

With the webhook set, any updates to the linked image in DockerHub result in the function app downloading and installing the latest image.

Clean up resources

Other quickstarts in this collection build upon this quickstart. If you plan to continue on to work with subsequent quickstarts or with the tutorials, do not clean up the resources created in this quickstart. If you do not plan to continue, use the following command to delete all resources created by this quickstart:

az group delete --name myResourceGroup

Type y when prompted.

Next steps

In this tutorial, you learned how to:

  • Create a function app and Dockerfile using Core Tools.
  • Build a custom image using Docker.
  • Publish a custom image to a container registry.
  • Create an Azure Storage account.
  • Create a Linux App Service plan.
  • Deploy a function app from Docker Hub.
  • Add application settings to the function app.

Learn how to enable continuous integration functionality built into the core App Service platform. You can configure your function app so that the container is redeployed when you update your image in Docker Hub.