Running ASP.NET Applications (Core) in Kubernetes — A Detailed Step By Step Approach

Bruno Terkaly
14 min readNov 26, 2018

--

It would have been unthinkable just a few years ago to imagine an ASP.NET application running in a Kubernetes cluster in Linux. But here we are — the world has changed. After all, without change there would be no innovation, creativity, or incentive for improvement.

There are a ton of great reasons to be running ASP.NET applications in a Kubernetes cluster. First, you have the most powerful and feature laden IDE ever created for web development. Visual Studio includes an amazing computer and cross-compiler, supporting Windows, Android, Apache Cordova, iOS, Linux — to name a few. It is extremely extensible and includes almost all of the tooling you’ll ever need to build, debug, and deploy your web applications. Second, the world is on fire with adopting Kubernetes. The ability of Kubernetes to deploy and run self-healing applications at scale has convinced many companies to move forward aggressively with adoption.

This post is about running ASP.NET applications in a Kubernetes cluster. This post will guide you through all the steps to get there.

There are 1,000’s and 1,000’s of ASP.NET Web Apps trapped inside enterprises ready to move to the cloud and run at scale in large clusters.

The end game is getting utility-grade reliability and performance through service level agreements (SLAs). Let’s try to get you a little further along in this post.

You can see that the workflow begins with the developer building an ASP.NET MVC Web App and including it into a DevOps Pipeline. Typically, a code check in would trigger the workflow all these steps would take place. But we will do the step manually. The docker build/push would bring you to the green circle Step 3 below, whereby the image is publicly available at hub.docker.com.

The Admin would then do a kubectl create -f [mydeploy.yaml and myservice.yaml] make Step 4 happen below. kubectl would then download from hub.docker.com or Azure Container Registry, depending on whether you are hosting closer to your running application.

The Azure Container Registry can take full advantage of advanced capabilities with partners like TwistLock and Aqua. Yourimage-building process will produce secure images as they get deployed to the Azure Container Registry, as well as protect your deployment environments like your Kubernetes cluster by securing each node in the cluster.

Goal of this Post

This post is about learning .NET Core. Specifically, we will show how to get a new or existing ASP.NET MVC Web application into Kubernetes.

Steps in this post

Twelve steps to Kubernetes

.NET Core is a general purpose development platform maintained by Microsoft and the .NET community on GitHub. It is cross-platform, supporting Windows, macOS and Linux, and can be used in device, cloud, and embedded/IoT scenarios. ASP.NET applications run inside a Linux-based container, inside of a Kubernetes cluster..

Getting Ready

Viewing the jump box and using it to create the Kubernetes cluster

You will need a few things to get started and to follow along exactly as I did. Clearly, there are alternate paths.

To begin, you will need full access to a Linux VM (including RDP). We also need a Kubernetes cluster.

The Big Picture — What you will need

The image above depicts a bunch of VMs in an Azure Data Center. Azure has more global regions than any other cloud provider, with 54 regions worldwide, available in 140 countries (as of Dec 1, 2018).

Let’s begin with App User, who uses a browser to interact with an ASP.NET application running in a Kubernetes cluster (dotted blue box).

The Admin is the developer, the engineer building the cluster and deploying the ASP.NET app. The Admin has a Linux/Ubuntu VM that acts as a jump box (green), as a sort of Developer VM. A jump box is a computer on a network used to access and manage cloud resources and applications in a separate security zone.

Separately, I am using a Windows 10 machine with Visual Studio 2017. You will see some screen shots.

Throughout this post I will be on two systems (Windows 10 for the ASP.NET App and Ubuntu 16.04 for the Kubernetes and Container piece). On the Windows 10 box it is about developing the app with Visual Studio. Ubuntu is about building, running, and deploying containers in Kubernetes.

We will make the assumption that you have your ASP.NET application running in Windows with Visual Studio. There are a few moving pieces here, so here is a brief list:

  • Kubernetes
  • kubectl
  • Visual Studio
  • ASP.net MVC Web application
  • Docker
  • Azure CLI (Optional)
  • Dockerfile
  • hub.docker.com
  • Jump Box

Setting Up Your Linux Environment

For Linux jump box VM it is a simple Ubuntu version 16.04 VM that runs in Azure.

Simply put I will be using a few VMs in Azure (maybe less if you want to experiment with a really small Kubernetes cluster):

(1) One VM to be your jump box.

(2) Four VMs for my Kubernetes cluster (I want to run the app replicated on multiple agent nodes)

For the Jump Box, you can get help here:
https://docs.microsoft.com/en-us/azure/virtual-machines/linux/quick-create-portal

Although we won’t use be using it, Azure does include a managed service, known as AKS, the Azure Kubernetes service if you want Microsoft to help manage the cluster for you. All the code in this post would work great in AKS.

You will need Docker

On your jump box VM you will also need Docker CE (or equivalent) setup so you can follow these directions:
https://docs.docker.com/install/linux/docker-ce/ubuntu/. We obviously will be messing with containers so Docker command line tooling is needed:

(1) docker run

(2) docker build

(3) docker push

You will want the Azure CLI

Another useful component to add to your jump box is the Azure CLI, which allows you to manage your entire Azure infrastructure from the command line. The Azure CLI is a command-line tool providing a great experience for managing Azure resources. The CLI is designed to make scripting easy, query data, support long-running operations, and more.

Mac and Windows environments are also supported for the Azure Command Line Tooling. For Windows it is Powershell and for Linux it is a Python a based library that is executable from the command line. To install, see this link:
https://docs.microsoft.com/en-us/cli/azure/install-azure-cli?view=azure-cli-latest

Another capability that is really important is Remote Desktop:
https://docs.microsoft.com/en-us/azure/virtual-machines/linux/use-remote-desktop. This allows me to run http://localhost in a browser to access my locally running container (the jump box).

Installing Frameworks And SDKs

You will need to download a number of SDKs, Frameworks, Github Repo’s.

There are actually two core image types for .NET Core:

(1) .NET Core Runtime

(2) .NET Core SDK

What is .NET Core?

At a high-level, .NET Core is focused on server applications that can run on Windows, Linux and Mac. There is no support for creating desktop applications with a user interface. That means that there is not any support for Windows Forms and WPF applications. Language support includes VB.NET, C# and F#.

Installing these two versions of the .NET Core technology stack is simple. This Microsoft post describes the installation and prerequisites for the .NET Core tooling.
https://docs.microsoft.com/en-us/dotnet/core/linux-prerequisites?tabs=netcore21

Here are the instructions for installing .NET Core 2.1 Runtime on Ubuntu:
https://www.microsoft.com/net/download/linux-package-manager/ubuntu18-04/runtime-2.1.2

To install .NET Core 2.1 SDK on Linux Ubuntu 18.04:
https://www.microsoft.com/net/download/linux-package-manager/ubuntu18-04/sdk-2.1.302. There are also different versions available to install.

My Sample Code at Github

All the sample code and examples I use in this blog can be found at the link below:
https://github.com/brunoterkaly/dotnetlinux-make-container. We’ll cover the pieces as we progress through this post.

This repo has some important elements:
(1) All of the source code to the ASP.NET MVC Project
(2) The Dockerfile we will use to build and containerize our ASP.NET MVC Application.
(3) myservice.yaml and mydeploy.yaml — both of these Kubernetes yaml files allow you to run and get access to the ASP.NET MVC Application running in a container.

Here are some of the commands. I encourage you to read the docs. Just to be clear, these are the commands I run on my Linux Ubuntu jump box.

# .NET Core SDKwget -q https://packages.microsoft.com/config/ubuntu/16.04/packages-microsoft-prod.deb
sudo dpkg -i packages-microsoft-prod.deb
sudo apt-get install apt-transport-https -y
sudo apt-get update -y
sudo apt-get install dotnet-sdk-2.1 -y

Let’s discuss the Kubernetes cluster setup.

Setting Up Your Kubernetes Cluster

As mentioned earlier in this post, it is possible to run all the examples on either using Azure’s managed service (AKS) or self-managed using ACS Engine, as explained here:

https://medium.com/@bterkaly/acs-engine-how-to-create-a-highly-customizable-version-of-kubernetes-4aed76e4e55f

The point to emphasize is that you will need a Kubernetes cluster to run these examples. In Kubernetes “kubectl” is the command line tool you use to deploy apps to the Kubernetes cluster. That is all explained in the Medium post above.

Developing your ASP.Net Application on Windows

The purpose of this post is to take an ASP.NET MVC web application and get it to run inside of a Kubernetes cluster. Before getting into the Kubernetes aspect, let’s quickly discuss the Windows development experience.

You will need some app that you want to migrate to make full use of this post. If you do not have a sample app, see this post:

https://www.asp.net/mvc/overview

Showing some typical Visual Studio screens

Below are some simple examples of the built-in templates that you can draw from can be seen with a simple File/New Project menu selection.

There are different types of ASP.NET web applications that you can create, as seen by the screen snapshot below:

*Notice it is all part of .NET Core.

File, New Project in Visual Studio

The next dialog box allows you to refine the project type you would like to create:

Selecting web application (MVC)

There are a number of options here and they will all work in the context of an ASP.NET Core application. MVC is a Model-View-Controller programming model, which I believe is the best option for web dev.

Obviously Angular, React.js, and a Standard API are also options.

I have put a red dotted box around the modifications I made the default project:

  • css/images — I changed the graphics inside of banner1, 2, etc.
  • HomeController.cs — they made some modifications here that contain logic to display the stock price for three stocks.
  • ModelHelper (DataDownloader.cs) — contains the code to perform the actual download of financial information for three stocks.
  • StockViewModel.cs — represents the data model of the financial information that is diplayed as a table in the ASP.NET web application.

The source code to the ASP.NET MVC web application

The key method that I implemented is the GetData() method. It is somewhat orthogonal to the article, but just wanted to show something other than the default new project. You can use NuGet to add any variety of dependencies that end up inside our running container.

public List<StockViewModel> GetData()
{
string url = "https://www.quandl.com/api/v3/datasets/" +
"EOD/{0}?limit=1&api_key={1}";
// Choose any tickers you want
List<string> tickers =
new List<string> { "MSFT", "IBM", "AAPL" };
// This holds all the data
List<StockViewModel> objList = new List<StockViewModel>();
using (WebClient wc = new WebClient())
{
// Loop through all ticker symbols
foreach (var item in tickers)
{
string currUrl = String.Format(url, item,apiKey);
var json = wc.DownloadString(currUrl);
RegexOptions options = RegexOptions.IgnoreCase |
RegexOptions.Singleline;
Regex regx = new Regex(
"<code data-language=\"ruby\">(?<theBody>.*)</code>",
options);
Match match = regx.Match(json);
if (match.Success)
{
string theBody = match.Groups["theBody"].Value;
dynamic parsedData =
JsonConvert.DeserializeObject(theBody);
dynamic d1 = parsedData.dataset.data;
DateTime date = DateTime.Parse(d1[0][0].ToString());
double closeprice =
Convert.ToDouble(d1[0][4].ToString());
double volume = Convert.ToDouble(
d1[0][5].ToString());
// We have our data. Add it to the list
objList.Add(new StockViewModel
{ dateTime = date.ToShortDateString(),
ticker = item,
close = closeprice.ToString(),
volume = volume.ToString() });
}
}
}
return objList;
}

As mentioned previously, all the source code can be found here:

https://github.com/brunoterkaly/dotnetlinux-make-container.

It goes without saying that the Visual Studio experience is what makes ASP.NET MVC web applications much easier to write. The integrated debugger experience is exceptional:

The debugging experience in Visual Studio

Copying Your ASP.NET MVC Project To Linux

We are done with development. Assume you now have an ASP.NET Web App.

We can now begin the process of containerization, which first means we need to move the ASP.NET MVC app to the Linux environment (our Jump Box).

I have taken the approach of copying all of the project to a folder on my Linux jump box.

Copying the project from Windows to Linux

There are a number of tools available to simplify the copying of your ASP.NET MVC application to Linux. you can see below that I have copied the files to a temporary folder on Linux (/tmp/AspNetCoreWebApp/AspNetCoreWebApp/AspNetCoreWebApp/).

You will begin by copying the files over to a temporary folder (because of permission issues) to the Linux environment. From there you can copy those files to the main repo folder.

The main repo folder, [/home/azureuser/dotnetlinux-make-container], looks like this. As you can see, it comes with the source code to the ASP.net MVC web application, as well as a Dockerfile as well as 2 yaml files. You can see the Views, Models, Controllers folder from our ASP.NET MVC Web Application.

root-> ls -latr
total 84
drwxr-xr-x 92 azureuser azureuser 4096 Nov 27 00:51 ..
drwxr-xr-x 4 root root 4096 Nov 27 00:51 Views
-rw-r--r-- 1 root root 5765 Nov 27 00:51 README.md
drwxr-xr-x 2 root root 4096 Nov 27 00:51 Properties
-rw-r--r-- 1 root root 141 Nov 27 00:51 myservice.yaml
-rw-r--r-- 1 root root 340 Nov 27 00:51 mydeploy.yaml
drwxr-xr-x 3 root root 4096 Nov 27 00:51 Models
-rw-r--r-- 1 root root 157 Nov 27 00:51 getYaml.sh
-rw-r--r-- 1 root root 15 Nov 27 00:51 .dockerignore
-rw-r--r-- 1 root root 371 Nov 27 00:51 Dockerfile
drwxr-xr-x 2 root root 4096 Nov 27 00:51 Controllers
-rw-r--r-- 1 root root 604 Nov 27 00:51 bundleconfig.json
drwxr-xr-x 8 root root 4096 Nov 27 00:51 .
drwxr-xr-x 6 root root 4096 Nov 27 00:51 wwwroot
drwxr-xr-x 8 root root 4096 Nov 27 00:51 .git
-rw-r--r-- 1 root root 2061 Nov 27 00:51 Startup.cs
-rw-r--r-- 1 root root 638 Nov 27 00:51 Program.cs
-rw-r--r-- 1 root root 242 Nov 27 00:51 AspNetCoreWebApp.csproj
-rw-r--r-- 1 root root 105 Nov 27 00:51 appsettings.json
-rw-r--r-- 1 root root 146 Nov 27 00:51 appsettings.Development.json

We are now ready to explore the next step in the process, which is working with the Dockerfile.

Building Out Your Docker File

The Dockerfile is a fundamental file that allows us to build out a docker container for our ASP.NET MVC web application. It looks like this:

FROM microsoft/dotnet:sdk AS build-env
WORKDIR /app
# Copy csproj and restore as distinct layers
COPY *.csproj ./
RUN dotnet restore
# Copy everything else and build
COPY . ./
RUN dotnet publish -c Release -o out
# Build runtime image
FROM microsoft/dotnet:aspnetcore-runtime
WORKDIR /app
COPY --from=build-env /app/out .
ENTRYPOINT ["dotnet", "AspNetCoreWebApp.dll"]

The Dockerfile above uses the .NET Core uses the SDK to compile the ASP.NET MVC Web App and the .NET Core Runtime to be the image that gets deployed to the Kubernetes cluster. It is a Debian based Linux image.

There are a few important things to notice about this file. First of all notice that there are two containers here that are being used to build the final image. The first is the .NET SDK and the second is the ASP.NET Core Runtime. The SDK is used to do the actual building of the image in the preparation before the final executable is placed in the ASP.NET Core Runtime container.

The dotnet restore command uses NuGet to restore dependencies as well as project-specific tools that are specified in the project file.

Project properties for our ASP.NET MVC web application

Notice that the Assembly name (AspNetCoreWebApp) is the same one being referenced in the Dockerfile above (see ENTRYPOINT) for the exact line.

Building And Pushing Your Image

All the pieces are in place for us to be ready to issue the docker build command. We will also issue the docker push command, assuming that the build executes correctly.

When your application finally gets deployed and run inside of the Kubernetes cluster, it will simply be running on top of the Debian guest operating system with all the .NET dependencies packaged within the running container.

Our Running application
# From the key folder [/home/azureuser/dotnetlinux-make-container]
root-> docker build . -t brunoterkaly/dotnetlinux
The output from the docker build command

Now that the image has been built, it is time to upload it to hub.docker.com.

root-> docker push brunoterkaly/dotnetlinux

After the image has been PUSH-ED to hub.docker.com, you can verify its existence.

Running The MVC Application Locally In A Container

Before running our web app in the Kubernetes cluster, let’s run it locally on our jump box VM.

The command is quite simple.

root-> docker run -d -p 8080:80 --name webapp brunoterkaly/dotnetlinux
871964c5c666e6477c9e48030c917d905aebd0a65977eb9d87a3ecfb54082dcd

The browser can verify it is running under localhost. Notice port 8080 being used.

Running ASP.NET MVC Web App

Migrating The Work To A Kubernetes Cluster

We have verified the container (ASP.NET MVC Web Application) works properly on our local jump box and development computer. We are now ready to deploy the application to the Kubernetes cluster by creating to text files, or YAML files, which provide a declarative syntax to create services, public IP addresses, and replicas of our running ASP.NET MVC Web Application.

The application running

The two files below (mydeploy.yaml and myservice.yaml) are how we deploy running containers into Kubernetes clusters.

mydeploy.yaml

apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: dotnetlinux
spec:
replicas: 2
template:
metadata:
labels:
app: dotnetlinux
spec:
containers:
- image: "brunoterkaly/dotnetlinux"
imagePullPolicy: Always
name: dotnetlinux
ports:
- containerPort: 80

myservice.yaml

apiVersion: v1
kind: Service
metadata:
name: dotnetlinux
spec:
type: LoadBalancer
ports:
- port: 80
selector:
app: dotnetlinux

To deploy our application and expose a public IP address for it we are going to go to the command line on our jump box. We will use the kubectl command to run both of these declarative files.

kubectl apply -f mydeploy.yaml
kubectl apply -f myservice.yaml

You will need to wait for the pods to come up and the services to be available via a public IP address.

kubectl get svc
root-> kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
dotnetlinux LoadBalancer 10.0.128.57 52.247.208.75 80:32754/TCP 5d
kubernetes ClusterIP 10.0.0.1 <none> 443/TCP 16d

Using the Kubernetes Dashboard to validate the app is running

You will need to start the proxy from your Jump Box so that we can use our browser to connect to the Kubernetes dashboard. We can then use localhost.

The Kubernetes dashboard shows us the deployment succeeded.

Validating the Deployment
Validating the 2 replicas are running

Testing the Application using the Public IP address

The ASP.NET MVC Web Application running

Summary

This concludes the post. It was incredibly straightforward to take our ASP.NET MVC Web App and getting it to run in a Kubernetes cluster.

--

--

Bruno Terkaly
Bruno Terkaly

Written by Bruno Terkaly

I lead a team within the Microsoft Commercial Software Engineering team. We solve the most difficult problems our customers face around a wide variety of tech.

Responses (2)