Tips and gotchas for moving traditional applications to Windows containers

With the release of Windows Containers I hope that there will a huge adoption of it. This blog post relies on basic understanding of docker and docker-compose. A good free eBook is available for download here to get started with docker in the Microsoft space.

This is my first blog, hopefully not the last, so apologies in advance. In this post I will share a few key issues that I encountered while trying to run a .NET application which is traditionally run on a Windows server OS to containers. Due to the requirement of the application I was porting, I selected windowsservercore container, which needed full .NET Framework.
The first thing I did was look at what are the different pieces in the software, deciding that each existing windows service will go into its own container and the UI running in IIS will go into another container. One of the windows service was hosting a WCF service and exposing a netTcp endpoint.
The above diagram shows the application that I am porting is built on a collection of .NET class libraries. There were two components directly using that class library. So I decided first to build a base container image with the .NET class library, based on windowsservercore image. Then I created two separate images, one with the “WCF Service” and another with the “Windows Service 4,” based on the previously created base image. The remaining 3 services and the UI all talk through the (WCF) core service. So I ended up with 4 new images based on windowsservercore and on the UI image my dockerfile uses “Install-WindowsFeature” cmdlet to install the necessary IIS features.
The journey looked simple. However, I found a few gotchas and learned a few tips and tricks while doing it.

Running Windows Services

Ideally if possible you would want to run the service from the command line, if it allows. This gives you two things:

  1. In containers you need to have a command which is running, when that command finishes, the container will exit. By running from the command line, you ensure that if your service crashes, this results in the container exiting which docker can detect. Also you do not need to have an artificial process running so that the container does not exit, like it is done with microsoft/iis image.
  2. Docker has support for redirecting logs from console to the daemon, if you are running the service from the console and your application can output the logging information to console, then you can leverage dockers mechanism for aggregating the logs in a central location.
Luckily the windows services that I was running, all could be run from the command line with a debug flag, and then they would actually log the messages in console as well. However, the services kept exiting with message “Press any key to stop the service.”. While running the service in debug mode the as it is blocking, users can press any key to stop the service gracefully, rather than killing it. This nice feature on normal windows caused a headache in my container. The issue is that there is no console input, you can fix it by running the container with the following command:

docker run -it <container_name>

However, this will cause redirect the current console to the container, if you want to keep running it in background you can do so, by running it in detached mode using the following command

docker run -dit <container_name>

While using docker-compose you can use “tty: true” flag to achieve the same.

Multi container application

While using docker-compose to build an application which involves more than one container, you will be referring to other containers using the service name, as the docker daemon resolves the dns name to the ip of the container. However, due to an issue you need to execute the following command while building the container to ensure that DNS resolving with service name works properly.

 RUN set-itemproperty -path 'HKLM:SYSTEMCurrentControlSetServicesDnscacheParameters' -Name ServerPriorityTimeLimit -Value 0 -Type DWord  

Missing VB runtime

While trying to install the application using msi in dockerfile, I kept seeing that the installer failed somewhere halfway. Later, I found that the windows core container is missing VB runtime, which was not a pre-requisite of the software, hence not being installed on the images. While trying to debug the installer issue, I learned two tricks:

  1. Try mounting a volume while running the container and log the installer log to the volume, and use a text editor tool from the host to look into the log.
  2. However, just looking at the logs, I was still not able to pinpoint that it was missing the VB runtime, so, I first tried running the installer on a Windows Server 2016 Standard installation, where it ran fine, then I ran the installer on a Windows Server 2016 Core (without GUI) where it failed with the same issue. Then using procmon I was able to see that it was trying to load the VB runtime. So, I think it is very wise to try to install the application on a Windows Core (without GUI) server, which can help you to find any issues in an environment where you have more control.

Windows Authentication

Windows Containers cannot join a domain, hence if you have application which relies on pure Windows Authentication you are out of luck. For the services that I was migrating there was a hard requirement that, the UI running on IIS talking to a WCF service need to establish a trust relationship, which was based on the windows authentication of the IIS application pool. This trust was needed as that application was allowed to impersonate any user in the system. There are two solutions that can be taken:

  1. Create local user with the same username and password on all the containers and use that.
  2. I decided to rely on SAML and certificate based trust

In summary, the biggest takeaways
  1. Try installing and running the application in Windows Server Core (Without GUI) to ensure that they work and all dependencies are figured out.
  2. Mounting a volume to copy resource and config files back and forth in the container and host, so that you can rely on the hosts text editor capability. Also this allows some adhoc files being copied and or deployed if you find missing, rather that trying to rebuild the entire container image.
  3. Visual Basic runtime is missing, which some legacy application might rely on.
  4. Cannot be added to domain, so either use a token based authentication or local users with same username and password across all containers or other form of trust relationship.
  5. Preferably have a process that you can run in the daemon, otherwise you can use the powershell snippet provided below to keep the container running

     Do {  
    Start-Sleep -s 60
    } while ($true)
  6. Some services when running from the command line uses “Press any key to exit” for those containers you need to run them in interactive mode.
  7. Preferably your application when running from a command outputs all logs in console.

2 thoughts on “Tips and gotchas for moving traditional applications to Windows containers”

  1. I use the Windows Server Core on a Hyper-V to figure out if our application could install. I quickly, as you, figured out the missing Visual Basic runtime. Funny thing is that there is not clear mention about this difference between Core and Desktop Edition, besides the fact that the runtime includes the word "Visual".

    Thanks for sharing

Leave a Reply

Your email address will not be published. Required fields are marked *