Jump to content

Recommended Posts

Guest justinyoo
Posted

In my previous post, I discussed different containerising options for .NET developers. Now, let's slightly move the focus to Azure Functions app. How can you containerise the Azure Functions app? Fortunately, Azure Functions Core Tools natively support it with [iCODE]Dockerfile[/iCODE]. But is the [iCODE]Dockerfile[/iCODE] the only option for containerising your .NET-based Azure Functions apps? Throughout this post, I'm going to discuss how Azure Functions apps can be containerised both with and without [iCODE]Dockerfile[/iCODE].

 

 

 

 

You can find a sample code from this GitHub repository.

 

 

 

 

[HEADING=1]Prerequisites[/HEADING]

 

 

 

There are a few prerequisites to containerise .NET-based function apps effectively.

 

 

 

 

 

 

[HEADING=1]Containerise with [iCODE]Dockerfile[/iCODE][/HEADING]

 

 

 

With the Azure Functions Core Tools, you can create a new Azure Functions app with the following command:

 

 

 

func init FunctionAppWithDockerfile \
   --worker-runtime dotnet-isolated \
   --docker \
   --target-framework net8.0

 

 

 

You may notice the [iCODE]--docker[/iCODE] option in the command. This option creates a [iCODE]Dockerfile[/iCODE] in the project directory. The [iCODE]Dockerfile[/iCODE] looks like this:

 

 

 

FROM mcr.microsoft.com/dotnet/sdk:8.0 AS installer-env

COPY . /src/dotnet-function-app
RUN cd /src/dotnet-function-app && \
mkdir -p /home/site/wwwroot && \
dotnet publish *.csproj --output /home/site/wwwroot

# To enable ssh & remote debugging on app service change the base image to the one below
# FROM mcr.microsoft.com/azure-functions/dotnet-isolated:4-dotnet-isolated8.0-appservice
FROM mcr.microsoft.com/azure-functions/dotnet-isolated:4-dotnet-isolated8.0
ENV AzureWebJobsScriptRoot=/home/site/wwwroot \
   AzureFunctionsJobHost__Logging__Console__IsEnabled=true

COPY --from=installer-env ["/home/site/wwwroot", "/home/site/wwwroot"]

 

 

 

There are a few points on [iCODE]Dockerfile[/iCODE] to pick up:

 

 

 

  • The base image for the runtime is [iCODE]mcr.microsoft.com/azure-functions/dotnet-isolated:4-dotnet-isolated8.0[/iCODE]. There are two other options available, [iCODE]mcr.microsoft.com/azure-functions/dotnet-isolated:4-dotnet-isolated8.0-slim[/iCODE] and [iCODE]mcr.microsoft.com/azure-functions/dotnet-isolated:4-dotnet-isolated8.0-mariner[/iCODE]. You can choose one of them based on your requirements.
  • The function app is running on the [iCODE]/home/site/wwwroot[/iCODE] directory.
  • It has two environment variables, [iCODE]AzureWebJobsScriptRoot[/iCODE] and [iCODE]AzureFunctionsJobHost__Logging__Console__IsEnabled[/iCODE].
  • There's no [iCODE]ENTRYPOINT[/iCODE] instruction defined.

 

 

 

These points will be used later on this post.

 

 

 

Let's add a new function to the Azure Functions app with the following command:

 

 

 

func new -n HttpExampleTrigger -t HttpTrigger -a anonymous

 

 

 

Once it's added, open the [iCODE]HttpExampleTrigger.cs[/iCODE] file and modify it as follows:

 

 

 

// Before
return new OkObjectResult("Welcome to Azure Functions!");

// After
return new OkObjectResult("Welcome to Azure Functions with Dockerfile!");

 

 

 

Now, you can build the container image with the following command:

 

 

 

docker build . -t funcapp:latest-dockerfile

 

 

 

You have the container image for the Azure Functions app. You can run the container with the following command:

 

 

 

docker run -d -p 7071:80 --name funcappdockerfile funcapp:latest-dockerfile

 

 

 

Your function app is now up and running in a container. Open the browser and navigate to [iCODE]http://localhost:7071/api/HttpExampleTrigger[/iCODE] to see the function app running.

 

 

 

Open your Docker Desktop and check the running container.

 

 

 

[ATTACH type=full" alt="Container running Azure Functions app #1]63666[/ATTACH]

 

 

 

Can you see the [iCODE]Path[/iCODE] and [iCODE]Args[/iCODE] value? The [iCODE]Path[/iCODE] value is [iCODE]/opt/startup/start_nonappservice.sh[/iCODE] and the [iCODE]Args[/iCODE] value is an empty array. In other words, the shell script, [iCODE]start_nonappservice.sh[/iCODE] is run when the container starts, and it takes no arguments. Keep this information in mind. You'll use it later in this post.

 

 

 

Once you're done, stop and remove the container with the following commands:

 

 

 

docker stop funcappdockerfile && docker rm funcappdockerfile

 

 

 

So far, we've containerised the Azure Functions app with the [iCODE]Dockerfile[/iCODE]. But, as I mentioned before, there is another way to containerise the Azure Functions app without having [iCODE]Dockerfile[/iCODE]. Let's move on.

 

 

 

[HEADING=1]Containerise with [iCODE]dotnet publish[/iCODE][/HEADING]

 

 

 

Because MSBuild natively supports containerisation, you can make use of the [iCODE]dotnet publish[/iCODE] command to build the container image for your function app. If you want to dynamically set the base container image, this option will be really useful. To do this, you might need to update your [iCODE].csproj[/iCODE] file to include the containerisation settings. First of all, create a new function app without the [iCODE]--docker[/iCODE] option.

 

 

 

func init FunctionAppWithMSBuild \
   --worker-runtime dotnet-isolated \
   --target-framework net8.0

 

 

 

Let's add a new function to the Azure Functions app with the following command:

 

 

 

func new -n HttpExampleTrigger -t HttpTrigger -a anonymous

 

 

 

Once it's added, open the [iCODE]HttpExampleTrigger.cs[/iCODE] file and modify it as follows:

 

 

 

// Before
return new OkObjectResult("Welcome to Azure Functions!");

// After
return new OkObjectResult("Welcome to Azure Functions with MSBuild!");

 

 

 

Now, the fun part begins. Open the [iCODE]FunctionAppWithMSBuild.csproj[/iCODE] file and add the following node:

 

 

 

<ItemGroup>
 <ContainerEnvironmentVariable
     Include="AzureWebJobsScriptRoot" Value="/home/site/wwwroot" />
 <ContainerEnvironmentVariable
     Include="AzureFunctionsJobHost__Logging__Console__IsEnabled" Value="true" />
</ItemGroup>

 

 

 

Did you find that these two environment variables are the same as the ones in the [iCODE]Dockerfile[/iCODE]? Let's add another node to the [iCODE].csproj[/iCODE] file:

 

 

 

<ItemGroup Label="ContainerAppCommand Assignment">
 <ContainerAppCommand Include="/opt/startup/start_nonappservice.sh" />
</ItemGroup>

 

 

 

This node specifies the command to run when the container starts. This is the same as the [iCODE]Path[/iCODE] value in the [iCODE]Docker Desktop[/iCODE] screenshot. Now, you can build the container image with the following command:

 

 

 

dotnet publish ./FunctionAppWithMSBuild \
   -t:PublishContainer \
   --os linux --arch x64 \
   -p:ContainerBaseImage=mcr.microsoft.com/azure-functions/dotnet-isolated:4-dotnet-isolated8.0 \
   -p:ContainerRepository=funcapp \
   -p:ContainerImageTag=latest-msbuild \
   -p:ContainerWorkingDirectory="/home/site/wwwroot"

 

 

 

 

NOTE: You can set the base container image with [iCODE]mcr.microsoft.com/azure-functions/dotnet-isolated:4-dotnet-isolated8.0-slim[/iCODE] or [iCODE]mcr.microsoft.com/azure-functions/dotnet-isolated:4-dotnet-isolated8.0-mariner[/iCODE], depending on your requirements.

 

 

 

 

This command takes four properties – [iCODE]ContainerBaseImage[/iCODE], [iCODE]ContainerRepository[/iCODE], [iCODE]ContainerImageTag[/iCODE], and [iCODE]ContainerWorkingDirectory[/iCODE]. With this [iCODE]dotnet publish[/iCODE] command, you have the same development experience as building the container image with the [iCODE]docker build[/iCODE] command, without having to rely on [iCODE]Dockerfile[/iCODE].

 

 

 

Run the following command to run the container:

 

 

 

docker run -d -p 7071:80 --name funcappmsbuild funcapp:latest-msbuild

 

 

 

Your function app is now up and running in a container. Open the browser and navigate to [iCODE]http://localhost:7071/api/HttpExampleTrigger[/iCODE] to see the function app running.

 

 

 

Open your Docker Desktop and check the running container.

 

 

 

[ATTACH type=full" alt="Container running Azure Functions app #2]63667[/ATTACH]

 

 

 

Can you see the [iCODE]Path[/iCODE] and [iCODE]Args[/iCODE] value? The [iCODE]Path[/iCODE] value is [iCODE]/opt/startup/start_nonappservice.sh[/iCODE] and the [iCODE]Args[/iCODE] value has [iCODE]dotnet[/iCODE] and [iCODE]FunctionAppWithMSBuild.dll[/iCODE]. But these arguments are ignored because the shell script doesn't take any argument.

 

 

 

Once you're done, stop and remove the container with the following commands:

 

 

 

docker stop funcappmsbuild && docker rm funcappmsbuild

 

 

 

Now, you've got the Azure Functions app containerised without [iCODE]Dockerfile[/iCODE]. With this [iCODE]dotnet publish[/iCODE] approach, you can easily change the base container image, repository, tag, and working directory without relying on [iCODE]Dockerfile[/iCODE].

 

 

 


 

 

 

So far, I've walked through how .NET developers can containerise their .NET-based Azure Functions apps with two different options. You can either write [iCODE]Dockerfile[/iCODE]s or use [iCODE]dotnet publish[/iCODE] to build container images. Which one do you prefer?

 

 

 

[HEADING=1]More about MSBuild for Containers?[/HEADING]

 

 

 

If you want to learn more options about containers with MSBuild, the following links might be helpful.

 

 

 

 

 

 

This article was originally published on

Dev Kimchi.

 

Continue reading...

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...