Azure Functions and Docker

Azure Functions v3 in Docker like a Pro

Docker whale and Function Lighting

Since I started writing and deploying Azure Functions a lot have changed. In this article we will create a linux Docker container for an Azure Function v3.

But before a few questions from the skeptical ones:

Why Docker?

Because you can deploy in a robust, comfortable, smart way. 🐳

For example… You put your images into an Azure Container Registry. Then you one-click-deploy an ARM template (with dockerized resources) and boom! You have all the resources up and running.

Why Azure Function Version 3?

Because it’s the new Azure standard for Azure Functions. They work great! Why not migrate now? ⚡

Why Linux?

Beacause a Linux Azure App Service Plan is cheaper! 🐧

Why Azure Functions?

Seriously? Needless to say, they are awesome! 🧡

TLDR: We want the c0de! Feel free to skip all the introduction and go to the last paragraph.

First Naive Approach

I love coding in Visual Studio, and I created all my Azure Functions there. A lot of people prefer VS Code with its internal console. It’s great.

But I’m an uncurable lazy dev. And I love the right-click-publish feature…

That’s why the naive approach was simply to right click the function project and select “Add Docker Support”.

The Docker Nuget was installed this way and a brand-new dockerfile was created.

Great! Article ended! - “Not so quick, Potter…”

Let’s build and publish!

An error has occurred

Build problem:

Step 4/18 : FROM mcr.microsoft.com/dotnet/core/sdk:3.1-stretch AS build
manifest for mcr.microsoft.com/dotnet/core/sdk:3.1-stretch not found: manifest unknown
Docker command failed with exit code 1.

Ok, a little bug I can fix manually. Let’s use another tag from the dotnet core dockerhub page.

Replace mcr.microsoft.com/dotnet/core/sdk:3.1-stretch with mcr.microsot.com/dotnet/core/sdk:3.1.

This time we successfully build, tag and push the function and we get no errors in console. 😎

Pushing my docker image to Azure Container Registry
Pushing my Docker image to Azure Container Registry
  • Publish problem 1: I create a Function in the Azure portal. Then I deploy this container and bind it to the function. In the function blade I can’t see any trace of it!
  • Publish problem 2: My Docker container image is called lftestdockerizingfunctionsfunction:latest. Argh.

Problem 2 is simple

Tag name is taken from the namespace of our function. Then it’s rewritten in lower case and without points. It’s not readable and out of standard.

Let’s add in the .csproj file the following line:

<DockerfileTag>lf.test.dockerizingfunctions.function</DockerfileTag>

Now let’s face problem 1

We already check that every step is correct, we haven’t got any error publishing and we can’t see any function running in the Azure Portal.

Let’s try debugging with Kudu the status of our Azure Function.

In SSH we get a connection error. That’s a shame.

SSH Connection Close — Error: Connect ECONNREFUSED 172.16.0.6:2222 CREDENTIALS
SSH Connection Error

In Bash we can navigate the App Service filesystem. But it’s useless since our function runs in a Docker container.

In the log files we can see that Docker starts our function. But in the function blade not a whiff of our function.

docker run -d -p 3497:8081
--name dockerizingfunctions_1_aacaadc6_middleware
-e WEBSITE_CORS_ALLOWED_ORIGINS =
https://functions.azure.com,
https://functions-staging.azure.com,
https://functions-next.azure.com
-e WEBSITE_CORS_SUPPORT_CREDENTIALS=False
-e WEBSITES_ENABLE_APP_SERVICE_STORAGE=false
-e WEBSITE_SITE_NAME=dockerizingfunctions
-e WEBSITE_AUTH_ENABLED=False
-e WEBSITE_ROLE_INSTANCE_ID=0
-e WEBSITE_HOSTNAME = dockerizingfunctions.azurewebsites.net
-e WEBSITE_INSTANCE_ID = e3a8761ce3056497006ce92f995ddcf6bb4651f4a55edeef03442174d6d35b37 appsvc/middleware:2001061754
/Host.ListenUrl = http://0.0.0.0:8081
/Host.DestinationHostUrl = http://172.16.0.6:80
/Host.UseFileLogging = true

Ok, take a breath and erase all the dockerfile content. 😅

It’s simply wrong.

Second Approach

I start from scratch and this time I use the Azure Functions Core Tools. I create another empty function using the command func init --docker-only --worker-runtime dotnet.

Now we have a completely different and working dockerfile! I get back to Visual Studio and paste this new dockerfile.

We only need to change a few things. First of all replace sdk:3.0 with sdk:3.1 and the other thing that differs is the build context.

  • The Core Tools dockerfile is executed in the same directory of the csproj, so all the files are copied with the command COPY . <destination-path>.
  • In Visual Studio, the build context is the directory where the .sln file is, not the .csproj. So we have to enter a directory and copy all the function files.
FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS installer-env
COPY ["LF.Test.DockerizingFunctions.Function/.", "/src/dotnet-function-app"]
RUN cd /src/dotnet-function-app && \
mkdir -p /home/site/wwwroot && \
dotnet publish *.csproj --output /home/site/wwwroot
FROM mcr.microsoft.com/azure-functions/dotnet:3.0
ENV AzureWebJobsScriptRoot=/home/site/wwwroot \
AzureFunctionsJobHost__Logging__Console__IsEnabled=true
COPY --from=installer-env ["/home/site/wwwroot", "/home/site/wwwroot"]

SSH Connection error

A nice feature to add in our function is SSH support. This could be useful to debug and test.

Let’s change the image mcr.microsoft.com/azure-functions/dotnet:3.0 in the second block of the dockerfile with the image mcr-microsoft.com/azure-functions/dotnet:3.0-appservice.

Now SSH works fine from Kudu

Some mistakes I did that you will avoid

  • If you’re creating a function with ARM template like me, always put some magic words in function app config! Be sure that you have WEBSITES_ENABLE_APP_SERVICE_STORAGE set to false or you won’t see your function!
  • Upgrading is always more painful than writing new code. Remember to set the environment variable FUNCTIONS_EXTENSION_VERSION to ~3.
  • Be aware of the path in dockerfile. Sometimes you have to copy some references before publishing the app.
  • If you need to use DevOps artifacts you should split the dotnet publish into a dotnet restore --configfile NuGet.Config and a dotnet build.
  • Debugging containers isn’t simple at all. You could use SSH for production containers. In a local environment you could use docker exec -it <container> /bin/sh to open a shell inside the container.

Brief Recap

We had a few problems with the Visual Studio generated dockerfile. We fixed them re-writing it completely.

FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS installer-env
COPY ["LF.Test.DockerizingFunctions.Function/.", "/src/dotnet-function-app"]
RUN cd /src/dotnet-function-app && \
mkdir -p /home/site/wwwroot && \
dotnet publish *.csproj --output /home/site/wwwroot
FROM mcr.microsoft.com/azure-functions/dotnet:3.0-appservice
ENV AzureWebJobsScriptRoot=/home/site/wwwroot \
AzureFunctionsJobHost__Logging__Console__IsEnabled=true
COPY --from=installer-env ["/home/site/wwwroot", "/home/site/wwwroot"]

And that’s it… Thanks for reading!

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store