DEV Community

Martin
Martin

Posted on

Running Azure Functions on Azure Container Apps

Service Overview

Microsoft recently announced the ability to run Azure Functions on Azure Container Apps (in preview). This is a big improvement over the current serverless option for Azure Functions as it allows us to leverage the benefits of microservices and the features of Azure Container Apps like DAPR and KEDA.

Behind the scenes, KEDA is used as the event driven auto scaler for the Functions. You still need to spin up an Azure Function for managing your functions however they execute within the containers (compute environment). This is also a lot more cost efficient as you can use the Azure Container Apps consumption plan and only pay for what you use.

These are the triggers currently supported whilst in preview that allow dynamic scaling from zero instances:

  • Http
  • Azure Storage Queue
  • Azure Service Bus
  • Azure Event Hubs
  • Kafka Trigger

I'm certain that more triggers will be available down the line as KEDA itself has a huge library of various trigger types.

Demo

I will run through a quick demo which is also available on the Microsoft docs. It's a PowerShell Function that responds to an http request (API, Get) and returns a response if the request is successful. Pretty simple but you can develop whatever function you desire in all the languages currently supported by Azure Functions (.net, Java etc). I also ran some load Tests using Azure Load Testing (JMeter) to see how the scaling performs in real life so you can check that out at the end of the post.

Architecture

Image description

This is what we will be getting up and running today. The exception being that we won't be using virtual network integration or private endpoints just to speed things up but in a production environment you definitely want to enforce all the network traffic to remain within a virtual network in Azure.

Pre-Requisites

You will need the following if you want to run this yourself

Set up local development environment

Before we deploy anything to Azure, let's get our Function set up locally and testing it using Postman (you can also just use a web browser).

1: Initiate the Functions project

func init --worker-runtime powershell --docker
Enter fullscreen mode Exit fullscreen mode

The docker feature flag will generate a .Dockerfile for the project which we will use later to wrap up the code into a Docker image and push it up to the container registry.

2: Let's add a Function to our project.

func new --name HttpTriggerContainerApp --template "HTTP trigger" --authlevel anonymous
Enter fullscreen mode Exit fullscreen mode

3: Once that's done, we need to test it locally to make sure our Function executes properly before we containerise it. To start the Function locally run:

func start  
Enter fullscreen mode Exit fullscreen mode

Image description

You should see the URL for the Function in your terminal: http://localhost:7071/api/HttpTriggerContainerApp

4: Now let's call our API using by suppliying a query parameter in the Http Get request. If you haven't got Postman you can just call pass the parameter via your web browser:

http://localhost:7071/api/HttpTriggerContainerApp?name=Functions

Image description

Now we should have a response if the call was successful:

Image description

Now we know the Function executes using the Http trigger locally we can move on to build our image and getting the same Function executing in Azure Container Apps.

Building our Docker image

Our Docker image was automatically generated when we created our Functions project so all we need to do is build it, tag it and push it up to our container registry.

You will need Docker to do this step. I am using Podman which is an alternative for building OCI images but the commands are the same. Just replace podman with dockerin the command line. Make sure you cd into the root directory of the project.

You will need to replace the acr_name with your registry in Azure. Make sure you have the admin credentials enabled on the registry. You can do this via the CLI az acr update -n <acr_name> --admin-enabled true

1: Build the image:

podman build --tag <acr_name>.azurecr.io/azurefunctionsimage:v1.0.0 .
Enter fullscreen mode Exit fullscreen mode

2: Run the image locally and test it again via podman or your web browser:

podman run -p 8080:80 -it <acr_name>.azurecr.io/azurefunctionsimage:v1.0.0
Enter fullscreen mode Exit fullscreen mode

3: Our container is now listening on port 8080 so here is the new URL to test with: http://localhost:8080/api/HttpTriggerContainerApp?name=Functions

Image description

Now we know our image works when running in a container we can push it our container registry.

4: Log in to your Azure Container Registry:

podman login <acr_name>.azurecr.io -u <acr_username> -p <acr_password)
Enter fullscreen mode Exit fullscreen mode

5: Push the image to the container registry by running the following:

podman push <acr_name>.azurecr.io/azurefunctionsimage:v1.0.0
Enter fullscreen mode Exit fullscreen mode

Now that our image is ready we can proceed to setting up our Azure environment and deploying our Function App on to Azure Contaiener Apps.

Deploy Function on to Azure Container Apps

1: Run the below commands to register the required namespaces and to upgrade the Container Apps CLI extension:

az extension add --name containerapp --upgrade -y
az provider register --namespace Microsoft.Web 
az provider register --namespace Microsoft.App 
az provider register --namespace Microsoft.OperationalInsights
Enter fullscreen mode Exit fullscreen mode

2: Create the resource group for the resources in Azure:

az group create --name AzureFunctionsDemo-rg \
--location australiaeast
Enter fullscreen mode Exit fullscreen mode

3: Create the Container App environment:

az containerapp env create --name azure-functions-demo-cae \
--resource-group AzureFunctionsDemo-rg \
--location australiaeast
Enter fullscreen mode Exit fullscreen mode

4: Create the storage account required for the Function Apps. This is used to store all of the Function code used in the runtime:

az storage account create --name auefunctionsdemo001 \
--location australiaeast \
--resource-group AzureFunctionsDemo-rg \
--sku Standard_LRS
Enter fullscreen mode Exit fullscreen mode

4: Create the Function App. You will notice here we need to provide the container registry details. This is what allows Azure Function to deploy our Function on to the Container App Environment:

az functionapp create --name azure-functions-demo-func \
--storage-account auefunctionsdemo001 \
--environment azure-functions-demo-cae \
--resource-group AzureFunctionsDemo-rg  \
--functions-version 4 --runtime powershell \
--image <acr_name>.azurecr.io/azurefunctionsimage:v1.0.0 \
--registry-server <acr_name>.azurecr.io \
--registry-username <acr_username> \
--registry-password <acr_password>
Enter fullscreen mode Exit fullscreen mode

In a production scenario you would use a managed identity to authenticate to the container registry.

5: Let's retrieve the endpoint of our function so we can call this and test that we get the expected response from the API:

az functionapp function show \
--resource-group AzureFunctionsDemo-rg \
--name azure-functions-demo-func \
--function-name HttpTriggerContainerApp \
--query invokeUrlTemplate
Enter fullscreen mode Exit fullscreen mode

Image description

Now that we have the endpoint, we can test it the same way we did when we deployed it locally. I am using Postman to do this.

Image description

We can now see that we get the exact same response with the Function running on Azure container apps!

This was a relatively simple setup that shows the concept of running Functions on Container Apps. The benefits are that we can use the feature of Container Apps on our Functions like Keda and DAPR. Additionally, it seems it's cheaper to run Azure Container Apps with vNet integration that running a standard App Service Plan or Azure Functions premium which are required in order to integrate into your own virtual network.

P.S You can configure your Container App Environment in your own virtual network allowing your Function app to communicate privately over RFC1918 to any other Azure services you have or private endpoints.

Top comments (0)