In this multi-post series I am going to look at what is required to take existing .NET Framework applications and bring them to Windows Containers.
Rather than just dive into the mechanics of the process, first I’d like to take a look at why you might want to move to Windows Containers and what you should be aware of before you start this journey.
Let’s dig in!
Why should I containerise my .NET Framework app?
This is honestly the place you need to start any conversation. A lot of our industry is driven by trends, and the current one is to Containerise everything and then run it on Kubernetes!
My advice is to not blindly decide this is the path for your current applications simply because you read a lot about it and see it at all the conferences you attend!
From my point of view I think the following reasons are why you might consider containerisation:
- Clear fit for purpose. If you have modern .NET Framework 4 applications that fit nicely into the microservices paradigm then it probably makes sense to containerise them and run them on an orchestrator like Kubernetes.
- Consistent deployment and management methods for all platforms. If you are already heavily invested in containerisation for other stacks in your business or team, then why not for your Windows and .NET Framework apps too? Now you have one way to package, deploy, rollback and manage your apps.
- Pushing ops left in your SDLC. I’m amazed that people still remote into production servers and change stuff on the fly. Containers make this a more difficult proposition and force developers to build solutions with immutability in production in mind. This is probably the biggest hidden benefit in adopting Windows Containers though many developers may not agree! 😉
- Better resource utilisation and separation. Containers have less overhead than a full VM and represent much better App Domain separation than running multiple .NET applications on the same Windows VM. As Windows Containers only contain your application and user mode portions of Windows you have a smaller overall footprint versus a traditional VM but benefit from a clearer separation between applications that may run on the same Container host.
Can I bring any .NET Framework app to Windows Containers?
TL;DR – the answer is no. Read on for more detail…
Windows Desktop applications
The first use case that isn’t supported is Windows desktop applications. Some people might look at a Container as a way to provide remote desktop services to end users, but this scenario isn’t supported and from what I’ve read is unlikely to be supported.
If this is a requirement in your environment then you should look at Microsoft’s App-V (Application Virtualisation) that can be used to serve desktop applications to users on demand such that the applications aren’t installed on the users desktop. Alternatively a hosted remote desktop service like Azure’s Windows Virtual Desktop might also suffice.
.NET 1.x and 2.x applications
The minimum requirement you have to meet for .NET on Windows Containers is .NET Framework 3.5. This means if you have codebases (or pre-compiled apps) that rely on either the 1.x or 2.x .NET Framework then you’re out of luck.
Given .NET Framework 3.5 first shipped around 2008 that means you’ve had at least a decade to update your application to it. If in that decade you haven’t, then unfortunately Windows Containers are not a silver bullet for you!
If you’ve come here to figure out how to move forward from .NET 1.1, then I’d recommend reading the official Microsoft documentation on migrating from .NET 1.1 to 4.x.
Dependencies requiring manual installation
If you have dependencies such as third party libraries or software that require you to manually intervene in their installation then you’re going to need to find alternatives.
While it is possible to install software in Windows Containers as part of your Dockerfile, the installation must be totally command-line driven. If you’re unable to execute the installer and provide it with configuration via the command line then it’s a ‘no go’.
Here’s a sample showing how to install an MSI using PowerShell. This is taken from a Dockerfile to install iisnode which enables integrated Node.js support on Windows’ in-built web server IIS.
RUN Write-Host 'Downloading iisnode' ; \ $MsiFile = $env:Temp + '\iisnode.msi' ; \ (New-Object Net.WebClient).DownloadFile('https://github.com/tjanczuk/iisnode/releases/download/v0.2.21/iisnode-full-v0.2.21-x64.msi', $MsiFile) ; \ Write-Host 'Installing iisnode' ; \ Start-Process msiexec.exe -ArgumentList '/i', $MsiFile, '/quiet', '/norestart' -NoNewWindow -Wait
Picking your base image
Now that you’ve finally decided that you want to move to Windows Containers you need to figure out which base image you’ll use.
Microsoft has introduced a new way in which it offers updated Windows Server releases so it’s important to understand this first.
Most of us would be familiar with Windows Server 2016 and Windows Server 2019. These two releases are part of the Long-Term Servicing Channel (LTS) for Windows which follows the fairly traditional ~ 3 year cycle of major releases with regular updates and fixes.
The newer concept is the Semi-Annual Channel (SAC) which is updated with new capabilities much more regularly than the LTS releases. These releases are referred to by their release number rather than a year. So, for example, “Windows Server 20H2” or “Windows Server 1903”.
You can read more about the differences between LTS and SAC on the official Microsoft Docs site.
Which foundation image you choose is up to you, but for my money I’d be more inclined to switch to the Semi-Annual Channel releases because your Container Images probably shouldn’t be long-lived any way, and as you need to rebuild from a base Image on a regular basis it’s best if you’ve got the latest bits, especially if they include Windows Container updates!
But wait, there’s more!
In addition to choosing the Windows Server release to use you will also need to determine which Windows Server image type you want: Windows Sever Core, Windows Nano Server, Windows or Windows IoT Core.
I know this seems like a lot to grok, but the reason you have these choices is really about reducing the size of your base Image.
Nano Server offers the smallest size, but really only supports .NET Core applications. Most .NET Framework applications can run on Windows Server Core, and you only need to consider the full Windows Server Image if you have dependencies on Windows capabilities like GDI+.
There’s also some additional options for base Images that come with either the .NET Framework or ASP.NET pre-loaded.
You can dive into the detail, and how to select the right Image on Microsoft Docs.
In my next post I am going to take an existing .NET Framework application and work through how we get it up and running on Windows Containers. Exciting!!
Until then… 😎