Using Startup Command to Pass Command Line Arguments to Azure App Service for Linux

Azure App Service on Linux has several prefabricated Docker images that support applications written in languages such as .NET core, PHP, and Node.js. App Service also supports using your own Docker image to spin up a container for your application. A useful configuration feature of App Service on Linux is the Startup File configuration that you can set as part of configuring the runtime stack. The value that you specify for the configuration overrides the CMD instruction of the Dockerfile that creates the runtime of the application. If you are not aware of this configuration option, we will soon deploy an application that uses this configuration option soon, so keep reading.

The Docker documentation states that if your Dockerfile has both CMD and ENTRYPOINT instructions, then CMD arguments are appended to the end of the command generated by the ENTRYPOINT instruction. A necessary condition for this feature to work is that you must use the exec form of the ENTRYPOINT instruction in your Dockerfile. In simple terms, assume that your Dockerfile has the following instructions.

ENTRYPOINT["sed","-i","s/Windows/Linux/g"]
CMD["file-to-update"]

When the Docker daemon executes the instructions of the previous code listing, it will generate and run the command sed -i 's/Windows/Linux/g' file-to-update by combining the arguments of the ENTRYPOINT and CMD instructions in the container. In general, we specify the command that will launch the application process as arguments of the ENTRYPOINT instruction.

If you combine this feature of Docker with the ability to override CMD instruction in App Service for Linux, you can enable the scenario of passing command-line arguments to a containerized ASP.NET Core web application. Also, you can change the runtime arguments of a live app and affect the web application immediately. Although I will only discuss this feature in the context of Azure App Service, you can use the same approach with Kubernetes and Docker Compose.

To demonstrate the concept, I created a simple ASP.NET Core MVC web application that paints the homepage with the color that it receives from a command-line argument.

Source Code

You can download the source code of the sample application code from GitHub.

The structure of the project is straightforward. The solution Colors contains a single project named ColoredWeb which is an ASP.NET Core MVC web application. The application contains a Dockerfile which mostly contains standard instructions except for the ENTRYPOINT and CMD instructions that supply command line parameters to the application.

Command Line Arguments to The Application

To understand how we can instruct Docker to pass arguments to the application, let’s explore the Dockerfile of the ColoredWeb project.

FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build
WORKDIR /src
COPY ["ColoredWeb/ColoredWeb.csproj", "ColoredWeb/"]
RUN dotnet restore "ColoredWeb/ColoredWeb.csproj"
COPY . .
WORKDIR "/src/ColoredWeb"
RUN dotnet build "ColoredWeb.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "ColoredWeb.csproj" -c Release -o /app/publish

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "ColoredWeb.dll"]
CMD ["Color=Blue"]

I want to draw your attention to the last two instructions of the Dockerfile. With the combination of the ENTRYPOINT and CMD instructions, Docker will try to launch the application with the following command.

dotnet ColoredWeb.dll Color=Blue

In the previous command, note that we assigned the value Blue to the key Color, which is a command-line argument passed to the ColoredWeb executable. The ConfigureAppConfiguration extension method in the class Program adds the command line to the application configurations. You can pass more than one argument to the applications as well.

public static IHostBuilder CreateHostBuilder(string[] args)
{
    return Host.CreateDefaultBuilder(args)
        .ConfigureAppConfiguration((hostingContext, config) => { config.AddCommandLine(args); })
        .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); });
}

If you try to debug the application in local Docker container and navigate to the home page, you will see that it is painted in color red rather than blue.

Debugging ColoredWeb Locally
Debug ColoredWeb locally

The application picks the default color because Visual Studio executes a command like the following to launch the container in debug mode.

docker run -dt -v "C:\Users\rahul\vsdbg\vs2017u5:/remote_debugger:rw" `
-v "D:\Projects\PaintMe\ColoredWeb:/app" `
-v "D:\Projects\PaintMe:/src"`
-v "C:\Users\rahul\AppData\Roaming\Microsoft\UserSecrets:/root/.microsoft/usersecrets:ro"`
-v "C:\Users\rahul\AppData\Roaming\ASP.NET\Https:/root/.aspnet/https:ro"`
-v "C:\Users\rahul\.nuget\packages\:/root/.nuget/fallbackpackages2"`
-v "C:\Program Files\dotnet\sdk\NuGetFallbackFolder:/root/.nuget/fallbackpackages"`
-e "DOTNET_USE_POLLING_FILE_WATCHER=1" -e "ASPNETCORE_ENVIRONMENT=Development"`
-e "ASPNETCORE_URLS=https://+:443;http://+:80"`
-e "NUGET_PACKAGES=/root/.nuget/fallbackpackages2" -e "NUGET_FALLBACK_PACKAGES=/root/.nuget/fallbackpackages;/root/.nuget/fallbackpackages2"`
-P --name ColoredWeb --entrypoint tail coloredweb:dev -f /dev/null

In the previous command, you can see that Visual Studio overrides both the ENTRYPOINT and CMD instructions with the tail -f /dev/null command. Hence, Docker does not execute the ENTRYPOINT and CMD instructions of the Dockerfile. However, you can build the container image yourself (docker build) and execute it (docker run) to make the application execute expectedly.

Let’s now switch to Azure App Service. I have published the image of the ColoredWeb application to my DockerHub repository here. Let’s now use this image for creating an Azure App Service.

Create Azure App Service

Although you may (and should) use the Azure CLI to create and configure your application, I will walk you through the steps to set up the application on the Azure portal as it is more visually intuitive than the CLI.

On the Azure portal, create a new Azure App Service. In the first step of the wizard, select Docker Container as the mode of publishing the application and set the language toggle to Linux. Select a name and an appropriate App Service Plan that suits your requirements.

Colored Web App Service - Basics
ColoredWeb App Service – Basics

In the next step of the wizard, select Docker Hub as the source of the application image. Since I have hosted the image of the application in public Docker repository, select the value Public in the Access Type dropdown. Next, in the Image and tag field, enter the name and tag of the image, rahulrai/coloredweb:latest. Finally, in the Startup Command field, enter the value Color=Yellow, which will override the CMD instruction specified in the Dockerfile of the application.

Colored Web App Service - Docker
ColoredWeb App Service – Docker

Click the Review + Create button that will take you to the last step of the wizard. Click on the Create button to create the application. After the application is ready, navigate to the URL of the application, which will render as follows.

Colored Web with CMD Override
ColoredWeb with CMD override

You can change the command line arguments of the application on the fly which will trigger a rolling update of the application. To change the command line arguments, navigate to the Container Settings blade of the application. In the following image, I demonstrate how to change the command line argument from Yellow to Red.

Update The Color of The Application
Update the color of the application

I hope you found this exercise interesting and discovered another way to configure your web applications on Azure.  You can reach out to me on Twitter @rahulrai_in to share your feedback.