Dockerfile for dotnet core worker process

Page content

In this article we will talk about creating a dotnet core worker process and we will run it in a docker container. This article is divided into two parts, in the first part we will create a simple worker process and in the second part we will write the Dockerfile to containerise it. If you already know about creating workers in dotnetcore feel free to skip the first part.

Creating the dotnet core worker processs

From your favourite shell, execute the following commands:

$ dotnet new worker -n dotnet-worker

This will create a new directory dotnet-worker, and will create the necessary files. The directory will have some files but the code for the worker is in Worker.cs file.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

namespace dotnet_worker
{
    public class Worker : BackgroundService
    {
        private readonly ILogger<Worker> _logger;

        public Worker(ILogger<Worker> logger)
        {
            _logger = logger;
        }

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            while (!stoppingToken.IsCancellationRequested)
            {
                _logger.LogInformation("Worker running at: {time}", 
                    DateTimeOffset.Now);               
                await Task.Delay(1000, stoppingToken);
            }
        }
    }
}

Lets cd into the directory, and execute dotnet run. You will see the following output.

info: dotnet_worker.Worker[0]
      Worker running at: 11/12/2020 20:36:58 +05:30
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
      Content root path: /home/dinuj/projects/tmp/dotnet-worker
info: dotnet_worker.Worker[0]
      Worker running at: 11/12/2020 20:36:59 +05:30
info: dotnet_worker.Worker[0]
      Worker running at: 11/12/2020 20:37:00 +05:30
^Cinfo: Microsoft.Hosting.Lifetime[0]
      Application is shutting down...

The program just prints Worker running at at every 1000 ms.

Lets now create the dockerfile.

Creating the docker container

Here is a dockerfile for dotnet core

FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build
WORKDIR /source

# copy csproj and restore as distinct layers
COPY *.csproj .
RUN dotnet restore

# copy and publish app and libraries
COPY . .
RUN dotnet publish -c release -o /app

# final stage/image
FROM mcr.microsoft.com/dotnet/core/runtime:3.1
WORKDIR /app
COPY --from=build /app .

ENTRYPOINT ["dotnet", "dotnet-worker.dll"]

Build the docker image.

$ sudo docker build -t dotnet-worker/first:0.1 .
Sending build context to Docker daemon  5.125MB
Step 1/10 : FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build
 ---> c4155a9104a8
Step 2/10 : WORKDIR /source
 ---> Running in 0594fbabbb16
Removing intermediate container 0594fbabbb16
 ---> e8374ef236da
Step 3/10 : COPY *.csproj .
 ---> a6e1083777b9
Step 4/10 : RUN dotnet restore
 ---> Running in ce204321d260
  Determining projects to restore...
  Restored /source/dotnet-worker.csproj (in 1.02 min).
Removing intermediate container ce204321d260
 ---> c0ab89e1c880
Step 5/10 : COPY . .
 ---> a5f09a602a28
Step 6/10 : RUN dotnet publish -c release -o /app
 ---> Running in 4f40b18d2ef4
Microsoft (R) Build Engine version 16.7.0+7fb82e5b2 for .NET
Copyright (C) Microsoft Corporation. All rights reserved.

  Determining projects to restore...
  Restored /source/dotnet-worker.csproj (in 256 ms).
  dotnet-worker -> /source/bin/release/netcoreapp3.1/dotnet-worker.dll
  dotnet-worker -> /app/
Removing intermediate container 4f40b18d2ef4
 ---> bc07532e8269
Step 7/10 : FROM mcr.microsoft.com/dotnet/core/runtime:3.1
 ---> 7be7431e6153
Step 8/10 : WORKDIR /app
 ---> Using cache
 ---> c01148f6dfa2
Step 9/10 : COPY --from=build /app .
 ---> Using cache
 ---> 784032990dd3
Step 10/10 : ENTRYPOINT ["dotnet", "dotnet-worker.dll"]
 ---> Running in 2722bbfe1782
Removing intermediate container 2722bbfe1782
 ---> 5bb0642ca5a8
Successfully built 5bb0642ca5a8
Successfully tagged dotnet-worker/first:0.1

Lets run it.

$ sudo docker run dotnet-worker/first:0.1

The output is similar to the first run that we did after creating the worker.

Now, lets introduce an environment variable and print it from the worker process. Lets name this environment variable as ENVTEST. Add the following line of code to the Worker.cs. Add the line before await Task.Delay(1000, stoppingToken);.

_logger.LogInformation(Environment.GetEnvironmentVariable("ENVTEST"));

Lets now run our code by passing the environment variable. Notice Hello is printed as expected. I am running this in bash shell. The syntax of passing environment variables may differ in your terminal/shell.

$ ENVTEST=Hello dotnet run
info: dotnet_worker.Worker[0]
      Worker running at: 11/12/2020 20:58:21 +05:30
info: dotnet_worker.Worker[0]
      Hello
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
      Content root path: /home/dinuj/projects/dotnet-worker
info: dotnet_worker.Worker[0]
      Worker running at: 11/12/2020 20:58:22 +05:30
info: dotnet_worker.Worker[0]
      Hello
^Cinfo: Microsoft.Hosting.Lifetime[0]
      Application is shutting down...

Lets rebuild the container, since we have changed our code. We will tag it as env-vars.

sudo docker build -t dotnet-worker/env-vars:0.1 .

Run it by passing env vars, uses the passed env vars.

sudo docker run -e "ENVTEST=Hello" dotnet-worker/env-vars:0.1

Lets now make changes to our Dockerfile to default the ENVTEST variable. We will add ENV ENVTEST=THIS_IS_ENV line to our Dockerfile.

FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build
WORKDIR /source

# copy csproj and restore as distinct layers
COPY *.csproj .
RUN dotnet restore

# copy and publish app and libraries
COPY . .
RUN dotnet publish -c release -o /app

# final stage/image
FROM mcr.microsoft.com/dotnet/core/runtime:3.1
WORKDIR /app
COPY --from=build /app .

# default env vars
ENV ENVTEST=THIS_IS_ENV
ENTRYPOINT ["dotnet", "dotnet-worker.dll"]

Now, if we do not pass the environment variable…

$ sudo docker run dotnet-worker/env-vars:0.1
info: dotnet_worker.Worker[0]
      Worker running at: 11/12/2020 15:47:32 +00:00
info: dotnet_worker.Worker[0]
      THIS_IS_ENV
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Production
info: Microsoft.Hosting.Lifetime[0]
      Content root path: /app
info: dotnet_worker.Worker[0]
      Worker running at: 11/12/2020 15:47:33 +00:00
info: dotnet_worker.Worker[0]
      THIS_IS_ENV
info: dotnet_worker.Worker[0]
      Worker running at: 11/12/2020 15:47:34 +00:00
info: dotnet_worker.Worker[0]
      THIS_IS_ENV
^Cinfo: Microsoft.Hosting.Lifetime[0]
      Application is shutting down...

If the env vars needs to be passed from a file then create a env.list file like so:

ENVTEST=This is coming from a file

and use it

sudo docker run --env-file env.list dotnet-worker/env-vars:0.1