Give Codeship’s CI/CD Platform a Try

Want to learn more?

In this article we will see how we can deploy a ASP.NET framework application as Docker containers on Windows Server. We will adopt a “lift and shift” model where we will treat our web application as a blackbox and only change how it’s deployed and made available to our users.

Although a standalone Docker container running Microsoft Internet Information Services (IIS) server is sufficient for making the web application available to the world, we will instead create a reverse-proxy setup using Traefik. This will give us some nice features such as being able to route requests to a different IIS site, automatic SSL certificates using LetsEncrypt, SSL termination including Server Name Indication (SNI) and aim to achieve zero-downtime deployments.

One point worth noting here is that site1.echorand.me and site2.echorand.me are valid publicly available DNS records that I have setup for the purpose of this article. This is relevant because LetsEncrypt needs the DNS records to be public and resolve correctly to issue SSL certificates. On Amazon AWS, I have created two record sets in my Route53 zone and pointed them to the public IP address of my EC2 instance.

During the course of this article, we will be working with Docker on Windows (Windows Server 1803), Traefik, nssm and PowerShell.

Software setup

We will be using Windows Server 1803 as our base operating system both for building our container and deploying our application. Windows Server 1803 is available in Amazon Web Services marketplace (search for Windows_Server-1803-English-Core-Containers). These Amazon Machine Images (AMIs) come with docker engine installed.

We will be using the latest release of Traefik which at the time of writing is 1.7.4.

The sample web application along with other helper PowerShell scripts are available in the demo1 sub-directory of this repo. In the rest of this article, I will assume that you have a local clone of this repository on the Windows server system.

Sample web application

Our sample web application is composed of two projects site1 and site2 – each an ASP.NET Framework Web API site.

If we build and view the sites locally, their homepages look as follows:

Each site in turn has a number of API endpoints which can be browsed via clicking on Help on the respective site:

The Visual Studio solution can be found in the demo1/AspNetFrameworkDemo sub-directory.

Setting up Docker

If you are using an AMI from the AWS market place, we will already have Docker installed:

If, however, you need to install the latest Docker engine on Windows server, you can follow the instructions from Docker.com.

Building a Docker image

Each site will be deployed as a separate Docker container. In the same directory as the solution file, we have Dockerfile.site1 and Dockerfile.site2 corresponding to each site. The Dockerfile.site1 is as follows:

We use Docker multi-stage build to create the final image such that doesn’t have build tools installed. We configure a healthcheck using the HEALTHCHECK instruction which will be used by Traefik to determine whether to forward requests to a container or not.

Ideally, we will be using a continuous integration (CI) service to build our Docker images and push them to a Docker registry. However, at the time of this writing, I could not find a free hosted CI service which supports Windows Server 1803 container building. Hence, we will build the images manually on the same host as the one we will deploy our application on.

The result of this step should be two Docker images amitsaha/aspnetframework-demo-site1 and amitsaha/aspnetframework-demo-site2 each tagged with the Git commit hash of the HEAD.

Brief overview of Traefik

Traefik is an open source reverse proxy with a massive feature list. It is deployable as a single binary which makes the deployment experience simple. At the highest level, we set up Traefik with a list of frontends, a list of backends and then define rules which map the frontend to the backends. Traefik can discover backends via a number of providers including docker. The Docker integration makes use of Docker labels to provide a native integration experience and is straightforward to setup.

Setting up Traefik as a Windows service

We will download Traefik and use nssm to set up a Traefik windows service using the PowerShell script – TraefikSetup.ps1.

The script also copies a configuration file for Traefik which is a TOML file configuring various aspects of a Traefik deployment. The documentation demonstrates various examples.

In our case, the Traefik configuration file is available here. Let’s go over the main sections:

We set the log level to DEBUG since it may be useful to look at the logs especially when things go wrong. Then, we setup the http and https entrypoints on port 80 and 443 respectively. We also setup a default redirect from http to https.

Next, we setup up LetsEncrypt configuration and specify that we want to use the http challenge:

Running our web applications

Once the script exits, from a browser on our local system, we should be able to see our site 1 at https://site1.echorand.me and site 2 at https://site2.echorand.me. You may need to check your cloud provider’s network access control lists and security group rules to allow incoming traffic on port 443 and port 80 (for LetsEncrypt HTTP validation). You will also likely need to update our Windows firewall setting to allow traffic:

It’s a good idea to check if the new containers have been registered by Traefik and the old containers have been deregistered before terminating the old ones. One limitation of using the Docker native integration is due to a limitation of the Docker engine not supporting updating labels at runtime. Hence, for example even though Traefik supports weighted load distribution, we cannot vary the weight at runtime for a Docker container. In such a case, using the Traefik file backend and writing a custom utility to render this file with the backend configuration may be required.

Traefik doesn’t yet support native blue-green deployment, but using the above approach we can achieve something close to it.

Conclusion

In this article, we saw how we can deploy a ASP.NET framework application on Windows server as Docker containers. To enable straightforward request routing and seamless deployments, we used Traefik as a reverse proxy for our setup.

Additional resources

Subscribe via Email

Over 60,000 people from companies like Netflix, Apple, Spotify and O'Reilly are reading our articles. Subscribe to receive a weekly newsletter with articles around Continuous Integration, Docker, and software development best practices.

We promise that we won't spam you. You can unsubscribe any time.

Join the Discussion

Leave us some comments on what you think about this topic or if you like to add something.