Featured image for: Containerize .NET for Red Hat OpenShift: Windows containers and .NET Framework.

Developers who use and target Microsoft's .NET Framework are no longer outsiders looking in when it comes to developing container-based applications. Whether porting an existing application (for example, a website running in IIS) or creating a new microservice, or somewhere in between, it is now possible—thanks to Windows containers—to deploy .NET Framework applications to your Kubernetes or Red Hat OpenShift clusters. This article explores the option of running .NET Framework applications in Windows containers in OpenShift clusters.

Note: This article is part of a series introducing three ways to containerize .NET applications on Red Hat OpenShift. The previous article introduced Linux containers for .NET Core.

Windows containers?

A quick history lesson is in order. While Linux containers can trace their origins all the way back to the chroot system call created in 1979 (Yes, you read that right: Nineteen seventy-nine), the first generally-recognized full implementation of Linux containers began in 2008 with LXC— LinuX Containers. Two LXC implementations, Warden and LMCTFY, had mild success, but Linux containers really took off with the introduction of Docker in 2013. Following that, issues such as security, scaling, networking, and more, blossomed and continue to improve with age. Linux containers have reached the level of acceptance and maturity such that they are arguably becoming the new standard in software development.

Windows containers were introduced to the .NET ecosystem with the release of Windows Server 2016, allowing developers to build, manage, and treat .NET Framework applications just like Linux containers. Commands such as docker build and docker run were identical on Windows and Linux. The only difference was the underlying operating system.

Red Hat announced general availability (GA) and support for Windows containers in OpenShift in late December 2020. This means—I'm repeating myself because it is just so amazing—that you can build an image of your .NET Framework application (such as a website running on IIS) on your Windows PC and run it in your OpenShift cluster.

There is just one consideration: You need a Windows node in your cluster.

Running Windows containers in OpenShift

Operations folks, take note: In order to run Windows containers in OpenShift, you'll need a cluster that includes a Windows node capable of running Windows containers. That's the "magic sauce" for running Windows containers in OpenShift. Currently, Windows Server 2019 is the best choice for running Windows containers.

Developers, you have it easy. As a builder of bits, you won't really see much difference; you'll create your application, build an image, and it will be deployed to OpenShift. A nice thing is that you won't have to worry about applications running on the same port. OpenShift is built on Kubernetes, and Kubernetes automagically assigns ports and keeps track of the mapping between what it (Kubernetes) exposes and what your application uses.

A recipe for success

Once you have a cluster capable of running Windows containers, I have created a GitHub repository (repo) with code and instructions for you to give this exciting technology a go. Until then, you can read and follow along here. Sort of like reading a recipe before you actually make that wonderful dinner.

Super awesome bonus material alert: The repo includes instructions, scripts, and data for creating and building a Microsoft SQL Server database inside your OpenShift cluster, because why not get dessert with dinner?

Building the Windows container image

To build a Windows container image, we need the following ingredients:

  1. A compiled .NET Framework application—in this case, a website to run in IIS
  2. A configuration file to build the image, "Dockerfile," that puts everything together
  3. A command to build the image
  4. An image registry where we can store the image, which we'll eventually pull into our OpenShift cluster

The compiled application

We're building a website called "Net Candy Store," the MVP (minimal viable project) that our startup needs to get up and running ASAP. At this point, the application is not fully functional, but we want to start building and deploying right away and fine-tune things as we move along.

Using the Git repo I mentioned earlier, we'll use the solution (netcandystore.sln) file in Visual Studio, as shown in Figure 1. Once there, we can use the Publish option to create the bits needed—the netcandystore.dll binary.

visual studio publish dialog box
Figure 1
Figure 1: The Publish Dialog box in Visual Studio.

That Target location is copied and pasted into our image build configuration file, "Dockerfile."

The Dockerfile

Now, we need the instructions for the docker build command, which are stored in the file, "Dockerfile." Typically, grammar aside, this is just called "the Dockerfile," so we'll continue with that. Here are the contents of the Dockerfile:

# The `FROM` instruction specifies the base image. You are
# extending the `microsoft/aspnet` image.

FROM mcr.microsoft.com/dotnet/framework/aspnet:4.8

# The final instruction copies the site you published earlier into the container.
COPY ./bin/app.publish/ /inetpub/wwwroot

Basically, we have just two things to do: Use the base image from Microsoft, and copy our binary to the newly-built image. This is literally the simplest Dockerfile I've ever seen.

A command to build the image

With all that in place, we use the docker build command to build the image. For the name and tag of our image, I will use a fully-qualified name that points to the image registry where I'll later push the image. The commands I send to OpenShift will pull from that registry. If you use the instructions in the git repo I mentioned earlier, you'll be using the same image.

With that in mind, here's how to build the Windows container image that will run in OpenShift:

docker build -t quay.io/donschenck/netcandystore:2021mar8.1 .

An image registry to store the image

Now, after logging into my quay.io account, I can run the following command to make the image registry available:

docker push quay.io/donschenck/netcandystore:2021mar8.1

What about licensing? To put it simply: Licensing relies on the host machine where you are running the containers. You can find more information on this Microsoft FAQ page.

Deploying the image in your Windows container

It's go time. We are ready to deploy this image to our Windows container host on OpenShift and start enjoying the fruits of our .NET Framework labors. But there's a small catch: If you try to deploy to your cluster, the deployment could crash with a timeout error. There's a simple workaround, and I've included a sample of it in my Git repo. The trick is to docker pull the somewhat large Windows server image (5.25 GB) to your cluster's Windows node from within the node itself. As a bonus, if you run other Windows containers in your cluster, on the same Windows node, they can use that same server image. In other words, you probably only need to do this "preload" once.

The details of this step are on my Git repo, so I won't repeat them here. The overview is this: Find the name of the Windows node and use SSH to run the docker pull command inside of it. Once that is done—it takes two or three minutes—the rest is typical OpenShift operations: Create a deployment that points to your application image, an associated service, and a route to publicly expose it.

Conclusion: A guide to follow

If you want to follow a step-by-step guide, including code for a Windows container, an installation of SQL Server on OpenShift, and the installation of a .NET 5 (.NET Core) application running in a Linux container, follow this repo: https://github.com/redhat-developer-demos/netcandystore.

Last updated: October 14, 2022