One of the challenges that enterprise organizations face in today’s world is migrating their legacy applications to cloud. Although these apps still work, they can become unstable due to compatibility issues with existing Operating Systems (OS) and Infrastructure (Infra). If this problem is not addressed in time, organizations may have to replace these apps, requiring a huge investment of money, manpower, and time. In such scenarios, containers can offer a real advantage. Containerizing an app can help with a smooth transition from existing OS & Infra to modern day OS & Infra. It can also help streamline software development and facilitate application mobility.
Recently, our team was challenged to help a financial services company to modernize its existing .NET Framework applications to fit the world of cloud and Kubernetes. The .NET Framework is a software framework developed by Microsoft that runs primarily on Microsoft Windows.
All this meant that we had to find a solution that could help us containerize the app inside a Windows server core image, then help run these containers on top of a Windows server and automate the deployment, scaling, and management of these containerized applications using Kubernetes.
At the time of this ask, Kubernetes had just released the Windows support for general availability (GA). However, there were still solutions available at Microsoft’s site to orchestrate a custom Kubernetes cluster with Linux-based master and Windows-based minion nodes (to host the pods). And we got hammering at this with the following:
- Containerize the .NET Framework applications
- Deploy the custom cluster
- Develop a continuous integration/continuous delivery (CI/CD) pipeline to help push updates to these containerized apps
- Deploy the containerized apps as pods
Containerize the .NET Framework applications
We first used the Windows server core container images from Microsoft as the base image for setting up our other individual images. Following are the images we built:
- .NET Framework SDK image
- Code Build image (building the code inside the Docker container)
- ASP.NET image
- Code Deploy image
Once we had all the necessary images, we tagged them and uploaded to our Docker repository.
Deploy the custom cluster for Kubernetes
Next, we stood up our Custom K8S cluster using an Overlay network configuration. We used AWS to host the virtual machines (VM). We used the following two VM’s:
- Ubuntu 16.04 for the K8S Master
- Windows Server 2019 for the K8S Minions (to host the pods)
Develop a CI/CD pipeline to help push updates to these containerized apps
Next, we developed a CI/CD pipeline using the following:
- CodeCommit as our code repo
- Jenkins to deploy a pipeline, that:
- picks up new commits in CodeCommit master branch
- uses the pre-built images to do a Code Build.
- ships the code build image to our Docker registry
- Docker registry for storing our images with tag scheme
- Spinnaker to detect new image in the registry and deploy to pods
Once we had built the necessary images and deployed the required infrastructure, we updated the application code locally using the Visual Studio Code and pushed the updates to our CodeCommit repository, which triggered our pipeline and built new container images (with appropriate tagging) and pushed them to Docker Hub. Spinnaker found the new image and instantaneously deployed the same to our pods in a rolling update pattern. The deployment successfully allowed the client to modernize its existing .NET Framework applications for the cloud and helped the developers concentrate on developing the code instead of focusing on the underlying plumbing.
There can be many benefits to this approach. The developers can concentrate on working with the same app frameworks, instead of changing and re-writing most of the app. With the help of the architecture and supported tools (mostly Open Source), there’s no need to worry about moving out of a legacy system and getting into a vendor lock-in. And finally, we can increase the speed to market by providing a clean and seamless approach for migrating legacy workloads to the cloud.