This is an easy way to dockerize any C# application based on scheduled tasks.
Change in your application settings
When you decide to move from a console application to a Docker container there are few things to take into account.
- Make sure all the files you need are copied with the build.
- If your logs are saved in a file, change the path to something you can also have on the docker host, C:\logs for example. Mapping the volume between the host and the container will be easier. Best case scenario is to send the logs to a log management platform (i.e.: Graylog)
- Add the SQL credentials to the connection string. Integrated Security is not working in Docker for Windows yet.
- Add the domain to any server names if your application uses connections to other servers. It will avoid any issue with name resolution.
Decide how your application will run
The first solution is to create your container and set up the scheduled tasks on the host to start the container. You will have to start the container with the command. The good thing about this solution is that the container is removed at end of the task.
The second option, the one described below, is to set up the scheduled task in the container itself and have a persistent container. In this case you will keep everything at the same place.
To create a scheduled task in the container you can use Powershell.
- Create a Powershell script like the one below so you can call it in the Dockerfile.
- Add it to you VS solution (in a deploy folder for example) and make sure the content is copied with the build
NET USER DockerUser "dockerpassword123!" /ADD NET LOCALGROUP "Administrators" "DockerUser" /ADD $action = New-ScheduledTaskAction -Execute 'C:\MyApplication.exe' -Argument '/MyParameter' $trigger = New-ScheduledTaskTrigger -Daily -At 07:00:00 Register-ScheduledTask -Action $action -Trigger $trigger -TaskName "DoSomething" -Description "Send Size Translations" -User DockerUser -Password "dockerpassword123!"
Having the scheduled task set up in the container creates another problem. As you might know once the command terminates, the container is stopped by the Docker daemon. We don’t want that to happen and so far there is no “docker” way to do this. The workaround is to call a command that will never end. The most common one is Ping, and you can ping localhost.
This is an example of Dockerfile you can use.
FROM microsoft/windowsservercore ARG CONFIGURATION RUN mkdir C:\logs ADD MyApplicationPath/bin/$CONFIGURATION/ / RUN powershell C:\deploy\CreateScheduledTasks.ps1 CMD ping -t localhost > null
Step by step:
- The base image used is the official Windows Server Core image. It is bigger than the nanoserver image but already contains the .Net Framework.
- We define the variable called CONFIGURATION, this will allow use to pass a parameter when calling the docker build command.
- We create the folder for the logs
- And add all our source file. This wil copy the content directly in the C drive. It is fine as our container is only created to run this application.
- We call our Powershell script that will create the scheduled task to start our application.
- In order to stop docker to free the container we call the ping command. This will run indefinitely and as we don’t want to see any of this line in the docker logs we redirect the output to null.
docker build --no-cache=true --build-arg CONFIGURATION=dev -t myapplicationimage .
docker run -d --name myapplication --restart=on-failure:10 myapplicationimage
Add –v C:\Logs:C:\Logs to map the logs folders between the container and the host.
To go further
There is a better solution which consist in using Hangfire to manage the task, also good when using Docker Swarm. I will write about it in another post soon.