Deploy ASP.NET Core App on Kubernetes

Deploy ASP.NET Core App on Kubernetes

In this ASP.NET Core Kubernetes Tutorial we will be deploying an ASP.NET Core app to Kubernetes from start till end. I will start with Docker and Minikube then I will take the topic of Kubernetes Objects which are Pods, Deployments and Services. Finally, I will use these objects to host the ASP.NET Core app on Kubernetes. This tutorial will give you a solid foundation of K8s so make sure you go through the whole tutorial. Let us start it with any further delay.

Docker and Minikube

The working of Minikube with Docker containers is slightly different. Minikube actually creates a Virtual Machine (VM) inside docker and within this Virtual machine it runs the docker containers. Check the below diagram which explains this:

minikube virtual machine docker

To learn about docker check our Docker Series for ASP.NET Core Developers – link.

This also means minikube only access the docker containers running inside the VM and not which are outside of VM. So, when working with Kubernetes you have to first enter this VM and there you create docker containers. Then you do other tasks like creating Pods to run these containers inside them.

Now I will tell you how to enter this VM. If you are in windows then enter the following command on your command prompt window and press enter.

minikube docker-env

This command will show you environment variables created by minikube. You copy and paste the last one (which is @FOR /f "tokens=*" %i IN ('minikube -p minikube docker-env') DO @%i) on the command prompt and press enter. This will take you inside the VM created by Minikube. Check the below image:

minikube docker env command

Now you can enter docker ps command which will show you all the containers running inside this VM.

For linux and macOS you can just enter the command which is given below on the terminal to enter minikube’s VM.

Eval $(minikube docker-env)

So just remember that when we will create ASP.NET Core app which will be hosted on Kubernetes pods then we will be doing these steps:

  • 1. Enter the minikube’s VM.
  • 2. Create docker container that will contain our asp.net core app.
  • 3. Create k8s Pods that will hold these containers.
After this tutorial I recommend you to learn an advanced topic which is Hosting Multi-Container ASP.NET Core app to Single Pod in Kubernetes.

Imperative Commands and Declarative Object Configuration

There are 2 techniques/approach to manage k8s objects these are:

  • 1. Imperative Commands.
  • 2. Declarative Object Configuration.
Imperative Commands

In imperative commands, we provide operations to kubectl that it needs to perform in a single step. For example, see the below command which is creating a deployment called mydep and uses nginx image.

kubectl create deployment mydev --image nginx
Declarative Object Configuration

In Declarative Object configuration, we define objects in a configuration file and tell kubectl to create the object based on this configuration file. The configuration file is a YAML file with extension .yaml or .yml. The YAML file has key: value expressions to specify our needs.

For example, I am telling kubectl to create an object which is defined in the configuration file called myobj.yaml.

kubectl create -f myobj.yaml

This myobj.yaml file would contain the below instructions/codes:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80

Notice that I specified kind: Deployment which means the configuration file is asking kubectl to create a Deployment. Declarative Object Configuration approach is mostly used in k8s development.

Don’t worry about what exactly is a K8S Deployment. The coming section will explain it also you will learn how to write the configuration files.

Kubernetes Objects

Kubernetes objects are entities that describe the states of various components running on k8s cluster. Some examples of what they describe are:

  • What containerized applications are running and on which worker nodes.
  • The resources available to those applications.
  • The policies such as restart policies, upgrades, and fault-tolerance.

The important Kubernetes objects are:

  • 1. Pods
  • 2. Deployments
  • 3. Services
  • 4. Ingress

Let us take each of them one by one.

What are Pods in Kubernetes

A Pod in Kubernetes is the smallest execution unit. A Pod can run single docker container or multiple docker containers.

kubernetes pods

We will be deloying an ASP.NET Core app to a Kubernetes Pod in just a moment.

Actually, Kubernetes is not limited to docker, k8s also supports other docker like container runtime software. Some examples are containerd & CRI-O.

You can create Pods by using workload resources such as Deployment. The deployment will manage the pods and if any pod goes down due to some error or anything, then the deployment will either restart it or create a new pod and run it.

Pods Networking and Data Sharing

Pods can also communicate with one another and with the k8s cluster. Each pod is assigned it’s own IP address and with this IP address they communicate with one another.

A Pod can specify a set of shared storage volumes and the containers running inside the Pods can access the shared volumes. Volumes also allow persistent data and survives in case one of the containers needs to be restarted.

Important commands for Pods

To see the list of all the Pods running in a k8s cluster run the kubectl get pods command which is given below.

kubectl get pods

If you run the above command you will be going to see no result as you don’t have any pods in your system. We will create our first pod in just a moment.

You can see a detailed description about your pods by running the describe pod command given below.

kubectl describe pod podname

What are Deployments in kubernetes

A Deployment provides Kubernetes about needed states for an app. In deployment we describe to k8s things such as:

  • The images to be used for the app.
  • The container states needed for running the image.
  • The number of Pods needed to run the containers and the way the pods should update or rollback.
  • How to scale up and scale down.

To create a deployment in k8s we will create a YAML configuration file with any name of our choice and then ask k8s to create a deployment according to this YAML file. In short, we will use Declarative Object Configuration approach.

Deployment Configuration File

The configuration file given below is creating a deployment object in k8s. I have named it as mydev.yaml. You can give it any name of your choice.

Look for the indentations as they are very necessary in a YAML file. If the indentations are not correct then you will receive errors when creating an object from this file.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: first-dep
  labels:
    app: aspnet-core-app
spec:
  replicas: 1
  selector: 
    matchLabels:
      component: web
  template:
    metadata: 
      labels:
        component: web
    spec:
      containers:
        - name: csimpleweb
          image: simpleweb
          imagePullPolicy: Never
          ports:
            - containerPort: 80

Notice it has key: value expressions in it. Let us understand what it is saying. From the top:

apiversion & kind

The apiVersion: apps/v1 tells the api version to be used. Different types of configuration files have different version. If you are creating a deployment than it will remain same for your file to.

In the second line I have kind: Deployment which tells k8s that this configuration is for deployment object.

metadata

The metadata field specifies meta data for the deployment. I have added 2 fields to it which are:

  • a. name
  • b. labels
metadata:
  name: first-dep
  labels:
    app: aspnet-core-app

I have named the deployment as first-dep by using the name field. Then I have provided labels field for the deployment which is:

labels:
  app: aspnet-core-app

Here app: aspnet-core-app can be anything.

With labels, we can select an object. For example, if I want to delete an object (which can be anything like a deployment, pod, service, ingress, etc) then I can use these labels for selecting the object and then deleting them. The below command will delete an object with this way.

kubectl delete -l app=aspnet-core-app

More than one labels can be used for an object. Example:

labels:
  app: aspnet-core-app
    stack: test

So, to delete this object the command will be:

kubectl delete -l app=aspnet-core-app, stack=test
spec

Now coming to the spec section which is quite big and tells deployment how it should create and manage the pods, what these pods will do and the container which will be contained by these pods.

spec:
  replicas:1
  selector: 
    matchLabels:
      component: web
  template:
    metadata: 
      labels:
        component: web
    spec:
      containers:
        - name: csimpleweb
          image: simpleweb
          imagePullPolicy: Never
          ports:
            - containerPort: 80

The spec contains 3 fields which are:

  • replicas
  • selector
  • template

I specified 1 pods with replicas:1, and this pod will be going to run the ASP.NET Core A;p. You can also run more than 1 Pods like 3 by setting replicas:3.

Then there is selector.matchLabels field which serves as a selector for the deployment to apply to the pod.

selector: 
    matchLabels:
      component: web

The matchLabels tell the deployment to apply itself to only those Pods which have the labels component: web. The Pods labels will be defined next.

Then comes the template field for the Pods. The section which we are talking is:

template:
  metadata: 
    labels:
      component: web
  spec:
    containers:
      - name: csimpleweb
        image: simpleweb
        imagePullPolicy: Never
        ports:
          - containerPort: 80

With the metadata field I describe the meta data for the Pods. I provided the labels for the Pods which is:

labels:
  component: web

Check that this is actually the same which the matchLabels field instruct the deployment to target those pods that have the label as component: web.

matchLabels:
  component: web

I have shown this thing in the below image.

deployment matchlabels working

You can have more than one label for the Pods and then in your matchLabels field you will need to specify all of these labels for the Pods to be targeted by the deployment.

Next, there is a spec for the Pods and it is given under the template field. See the below code:

spec:
  containers:
    - name: csimpleweb
      image: simpleweb
      imagePullPolicy: Never
      ports:
        - containerPort: 80

The spec field contains the containers field that describes the containers which the Pods will contain.

The name of the container is provided as “cssimpleweb”.

- name: csimpleweb

Next the image name for the container is specified as “simpleweb”.

image: simpleweb

Also note that you will have to create this docker image inside the minikube’s VM or you can instruct k8s to download the image from a registry like Docker Hub, Azure Container Registry, Microsoft Container Registry and so on.

The next field imagePullPolicy tells whether to download the image from a registry or not. I have set it to “Never” as I will be building the docker image in the minikube VM so the deployment will directly use the image from there.

imagePullPolicy: Never

Suppose the image resides in docker hub then I can set it to “Always” and this will force the deployment to preform image pull from the registry every time it is created. Note that “Always” is the default value and if you want then you can simply omit the imagePullPolicy field.

Finally, there is ports field which specifies the container port that will be opened. In my case I specified the port 80 to be opened inside the container.

Note that port 80 is the default port of the ASP.NET Core app and that is the reason for choosing this port. If you want a different port then you will have to add that port using environment variables.

ports:
  - containerPort: 80

Enough of the theory part, let the apply it on the system. So I will create a new ASP.NET Core app then create it’s docker image inside the minikube VM. Next I will host my app on Kubernetes using the deployment which I just described.

Create a new ASP.NET Core App

First open Visual Studio and create a new Web Application.

asp.net core web application kubernetes k8s

Give a name to your app, I named the app as FirstKubeApp, and make sure to check the option that says – Place solution and project in the same directory.

configure project kubernetes

Now select the template called ASP.NET Core Web App this will create a basic ASP.NET Core Razor Pages based app.

ASP.NET Core Razor Pages

Run the app in visual studio. You can see it’s a basic app with just 2 pages – “home” and “privacy”. I have shown this in the below given video.

Next add docker support to this app, so right click the app name in the solution explorer then select Add ➤ Docker Support. This will create a Dockerfile for the app.

create dockerfile visual studio

Now you will be asked to select the target OS. Select Linux as it is the King of OS.

target os docker

Build the Image inside Minikube’s Virtual Machine

Let us build a new image for our app using the Dockerfile. Recall, you will have to build the image inside the minikube’s VM. This you can do by running the minikube docker-env command.

This command will show you environment variables created by minikube. You copy and paste the last one (which is @FOR /f "tokens=*" %i IN ('minikube -p minikube docker-env') DO @%i) on the command prompt and press enter. This will take you inside the VM created by Minikube. Check the below image:

minikube docker env command

Now, in your command prompt, go to the directory of the Dockerfile. You can do this by the cd command. Then run the “dir” and confirm that the Dockerfile is shown.

dir command

Now build the image by the docker build command given below.

docker build -t simpleweb -f Dockerfile .

Notice the “.” at the end of the build command. The image called simpleweb will be build inside the minikube’s VM.

You can now run the docker images command which will show all the images inside the minikube’s VM. You can also see the “myfirstimage” image there to. See the below image where I have shown this.

docker images minikube vm

Exiting Minikube VM

The procedure to exit minikube vm is very simple. Just close the command prompt window and open a new one. The new command prompt window will be outside the minikube vm.

To test it run docker ps which will show you the containers that are inside the local docker instance and not the ones that are inside the minikube docker instance.

Apply the Deployment

Now save the deployment file with any name of your choice and with a .yaml extension. I have named this deployment file as mydev.yaml.

You can save this file in any directory of your pc. This configuration file’s code is the same which I discussed with your earlier and is given below.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: first-dep
  labels:
    app: aspnet-core-app
spec:
  replicas: 1
  selector: 
    matchLabels:
      component: web
  template:
    metadata: 
      labels:
        component: web
    spec:
      containers:
        - name: csimpleweb
          image: simpleweb
          imagePullPolicy: Never
          ports:
            - containerPort: 80

Now go to the directory of this configuration file and run the kubectl apply command and is given below.

kubectl apply -f mydev.yaml

The deployment will be created with a message deployment.apps/first-dep.

Now you can see this deployment by running the kubectl get deployments command.

kubectl get deployments

Next run the kubectl get pods command which will show you 1 pod running as I set replicas:1 in the configuration file.

Check the below image where I have shown the outputs of these command.

kubectl apply

Congratulations our deployment is applied to k8s cluster and your app is running in a docker container which is contained inside a Pod.

Notice the Ready column against the deployment and pods. It takes a little while for the object to come to the ready state. So, till that time you will see 0/1 for it. Run the kubectl get deployments or kubectl get pods command after a few seconds and you will see 1/1 for it which specifies that the object is now ready.

Next, we will see how to expose the Pods by using a Kubernetes Service.

What are Services in Kubernetes

A Kubernetes Service assigns a unique IP address to the Pods so that they are exposed to the outside. Service also gives a single DNS name for a set of Pods running an app.

Why service comes to picture ? Pods have IP address and you can communicate with them using their IP addresses. A Deployment manages Pods and may destroys old pods and creates new pods for your app. So, the IP address of the Pods running the app will change. Therefore, communication with IP addresses will break sometime in the future.

Here comes the service which connects with the pods not with their IP address but with selector (recall I discussed selector during the time of “Deployment” in the above section). So, you use a service to communicate with the Pods and this communication will never break.

kubernetes service

Services do the following works:

  • A label selector that locates pods.
  • Creates a clusterIP IP address that assigns port number and port definition to itself.
  • Mapping of incoming ports to a targetPort of the Pods
Types of Services

Services are of 4 types:

ClusterIP – this is the default service type. This service is only reachable from within the cluster and not from outside.

NodePort – this type of service is reachable from outside the cluster. The path for this service would be <NodeIP>:<NodePort>. I will be creating this type of service since I want my app to be accessible on the browser with a url.

LoadBalancer – this type of service is used in the cloud like Azure. The cloud provider provisions a load balancer for your Service.

ExternalName – this service is mapped to a DNS name

Creations of a service is done through YAML configuration file. Each of the 4 service configuration file is different.

How to create a Service in Kubernetes

Let us now create a Service in Kubernetes using YAML configuration file. My app is already running inside a Pod and I want to access it through a URL in the browser. Therefore a NodePort Service will be an ideal candidate for this situation.

So create a new YAML file and name it myservice.yaml or anything of your choice. The only necessity is that it’s extension should be .yaml or .yml.

Add the following configurations to this file.

apiVersion: v1
kind: Service
metadata:
  name: first-service
spec:
  type: NodePort
  selector:
    component: web
  ports:
    - port: 8080
      targetPort: 80

Most of the fields are the same which I have already discussed when I created a deployments configuration file (see above).

I defined the field called apiVersion and given it’s value to “v1”. All the services of type NodePort much have this api version.

apiVersion: v1

Next, I defined the kind field as Service. This tells k8s that the object being created by the configuration file is a Service.

kind: Service

Next, I gave the service name as first-service.

metadata:
  name: first-service

Next, coming to the spec section where I defined the service type as NodePort.

spec:
  type: NodePort

After that there is a selector that tells the service should target all the Pods that have the label component: web. Check the deployment config file where I have provided the pods the same label.

selector:
  component: web

Finally, there is ports field that specifies how the ports of the service will be mapped to that of the container running inside of a Pod. There are 2 fields:

  • 1. port – the port of the service.
  • 2. targetPort – the port of the container which the service will target. It should be the same port which is given by containerPort field in the deployment config file.
ports:
  - port: 8080
    targetPort: 80

I specified that the 8080 port of the service should be mapped to port 80 of the container.

Now let us apply this configuration which will create the service in Kubernetes.

So, in the command prompt, go to the directory where this configuration is kept and run the following command.

Kubectl apply -f myservice.yaml

This will apply the configuration and create the service called “first-service”. Run the command kubectl get services and you will see your newly created service. Now let us access this service on the browser.

More professionally it would be wise to use Kubernetes Ingress to expose your services to outside world. See my tutorial How to use Kubernetes Ingress on an ASP.NET Core app to learn more.
Access the K8s Service on the Browser

To access a k8s service on the browser run the minikube service servicename command on command prompt. It will open a tunnel and then a new tab is opened on the browser where you you will see your ASP.NET Core app.

Since the service name is “first-service”, therefore the command to run will be:

minikube service first-service

Check the below image where I have shown these commands.

accessing kubernetes services browser

Your app will open on the browser and you can access it. To exit from the tunnel press “ctrl+c” on the command prompt and it will close the tunnel and your app will now become in-accessible. Check the below video where I have shown this procedure.

You can now download the source codes of this tutorial.

Download

Conclusion

Congrats, you completed this very long ASP.NET Core Kubernetes Tutorial which explains how to host your app in Kubernetes by using Pods, Deployments and Services. Now it’s your time to deploy your ASP.NET Core app on Kubernetes.

SHARE THIS ARTICLE

  • linkedin
  • reddit
yogihosting

ABOUT THE AUTHOR

I hope you enjoyed reading this tutorial. If it helped you then consider buying a cup of coffee for me. This will help me in writing more such good tutorials for the readers. Thank you. Buy Me A Coffee donate

Comments

  1. MaticDiba says:

    Hi,
    first, thank you for these articles, they are really helpful. But I’m having issues with the last step. When I try to create a tunnel, browser is opened, but it times out. I went through all the steps. The only difference is the version:
    minikube version: v1.25.2

    1. yogihosting says:

      Well I think there is some pod problem or the app may be getting an error. Kindly try debugging to find out any cause. I would suggest you to read my tutorial Managing ASP.NET Core app hosted on Kubernetes where you can understand debugging, bash session etc.

  2. Rolle says:

    Just a comment to add to MaticDiba: I get the same problem/error following this tutorial. I’m running Windows, not sure if that is the problem. To get it to work I used an Ingress and “minikube tunnel”. This makes it work in Windows also. (Running minikube v1.25.2) See here also (you don’t have to use ssh, just run “minkube tunnel” and it will work): https://www.yogihosting.com/kubernetes-ingress-aspnet-core/

    Also yogihosting, thanks for nice tutorials!

    1. yogihosting says:

      Thank you friend for the update on Ingress. This will help other readers who are implementing it on windows.

Leave a Reply

Your email address will not be published. Required fields are marked *