ASP.NET Core Series 03: Containerizing a RESTful API and Deploying to Kubernetes Using Azure Container Service

Hi, all!

We have developed a RESTful API in the previous ASP.NET Core blog post series, and then we deployed that to the Azure App Services. In this blog post series, I’ll try to mention how we can containerize a RESTful API, and then how we can deploy it to Kubernetes using Azure Container Service, based my experience in the .NET Core transformation process at the company I’m currently working at.

Topics are:

  1. Building a Docker Image and Push to Docker Hub
  2. Creating a Kubernetes Cluster using Azure Container Service
    1. Creating an SSH RSA Public Key
    2. Creating a Service Principal Client ID and Secret
    3. Creating a Kubernetes Cluster
    4. Connecting to Kubernetes Cluster
    5. Running ASP.NET Core RESTful API inside to Kubernetes Cluster’s Pod
    6. Exposing Kubernetes Pod to Outside with Kubernetes Service
    7. Creating and Exposing Kubernetes Deployment Controller

NOTE: In this article, I won’t describe some topics, such as what the Docker is and why we need that etc. For this article, you need to know some Docker information.

As we know, a container image is a lightweight, stand-alone, executable package of a piece of software. Containers isolate applications in the same infrastructure. Thus, development and staging environment differences are separated from each other, thereby helping to reduce any conflicts that may occur.

We will use the example project that we developed in my previous post titled “ASP.NET Core Series 01: Building RESTful API with Dapper and Deploy to Azure App Services” for containerizing. Let’s download the example project from GitHub, and then unzip to the desktop.

1. Building a Docker Image and Push to Docker Hub

NOTE: Btw, I’m using DockerToolbox on the Windows.

First, we need to create a Dockerfile to building an image. Let’s create a Dockerfile in the root directory of the project as shown below.

FROM microsoft/aspnetcore:1.1
ENTRYPOINT ["dotnet", "aspnetcore-rest-api-with-dapper.dll"]
ARG source=.
WORKDIR /app
EXPOSE 5000
COPY $source .

In the Dockerfile, we defined that we want to use the official compiled ASP.NET Core image and expose on port “5000“. Also as an entrypoint, we specified the “aspnetcore-rest-api-with-dapper.dll” file that we’ll get after publishing the project. Now, let’s build the project using a command-line tool as shown below.

dotnet build

NOTE: Don’t forget to restore packages using the “dotnet restore” command.

After building the project, we need to publish the project using as following command:

dotnet publish -c release

Now, we can build an image using the Dockerfile.

docker build bin\Release\netcoreapp1.1\publish -t aspnetcorerestapionlinux

We built an image called “aspnetcorerestapionlinux” using the “bin\Release\netcoreapp1.1\publish” path. Let’s see the image using the following command:

docker images

There are the “aspnetcorerestapionlinux” image as we can see on the above picture.

Now, let’s run the container to make sure everything is okay.

docker run -d -p 5000:5000 aspnetcorerestapionlinux

We ran the “aspnetcorerestapionlinux” image in a container and exposed it to outside on the port “5000“. Let’s open the “http://192.168.99.100:5000/swaggerURL on the browser.

Yeap, the image is running successfully in the container. Now, we have the containerized ASP.NET Core RESTful API.

We need to push the “aspnetcorerestapionlinux” image to the Docker Hub. Thus, we can pull this image, and then run in a Kubernetes cluster with Azure Container Service. First, we need to have a Docker Hub account. If you don’t have an account, you can create one here.

Let’s add a tag on the image as shown below.

docker tag aspnetcorerestapionlinux gokgokalp/aspnetcorerestapionlinux

NOT: Don’t forget to change “gokgokalp” field with your Docker Hub username.

Now, we need to login.

docker login

After the login process, we can push the image to the Docker Hub.

docker push gokgokalp/aspnetcorerestapionlinux

2. Creating a Kubernetes Cluster using Azure Container Service

Azure Container Service provides solutions to simplify the creation of virtual machines for containerized applications, and to make it easier to configure and manage clusters. Microsoft made the Kubernetes container orchestration service completely available a few months ago. In this way, deploying containerized applications efficiently and scaling them is made easy.

Now, we will create a basic Kubernetes cluster using Azure Container Service. We need the following items to create a cluster:

  • Azure subscription – I mentioned in “ASP.NET Core Series 01” blog post, how we can get a subscription.
  • SSH RSA public key – We need to create this for authenticating to virtual machines.
  • Service Principal client ID and secret – Also, we need to “Azure Active Directory service principal client ID” and a “secret”, for using Kubernetes orchestrator.

2.1 Creating an SSH RSA Public Key

Git for Windows should be installed on your computer. If you need: https://git-for-windows.github.io/

Let’s create a “myPrivateKey” and “myCert” certificate using the “openssl.exe” with Git Bash.

openssl.exe req -x509 -nodes -days 365 -newkey rsa:2048 \-keyout myPrivateKey.key -out myCert.pem

While in this process, we need to fill some information such as “Country Name, State, E-mail, Organization Name“. After this process, the “myPrivateKey” and “myCert” certificate will be created under the related file path.

Now, we will create a public key with the private key that we have created.

openssl.exe rsa -pubout -in myPrivateKey.key -out myPublicKey.key

We have to create an additional key to use PuTTY SSH client as shown below:

openssl rsa -in ./myPrivateKey.key -out myPrivateKey_rsa

After execution the above command, so we got the private key called “myPrivateKey_rsa“.

Now, we will create a public key so that we can create a Linux VM on the Azure portal. First, we need to have PuTTY. If you need, you can download it from here. Let’s run the “PuTTYgen“, and click “Load“. Now we have to choose the “myPrivateKey_rsa” key file that we have created.

Now, we have a public key starting with “ssh-rsa” prefix as you can see on the above picture. We need to keep this public key to use later.

2.2 Creating a Service Principal Client ID and Secret

At this point, we can create an Azure AD (Active Directory) service principal using the portal’s terminal. After the login process to Azure portal, let’s click “Cloud Shell” button at the top right of the portal.

Now let’s create a resource group called “aspnetcore-test-restapi” using the following command.

az group create -n "aspnetcore-test-restapi" -l "westeurope"

After creating the resource group, we need to replace “mySubscriptionID” field of the following command with our subscription ID. After this, let’s run the command.

az ad sp create-for-rbac --role="Contributor" --scopes="/subscriptions/mySubscriptionID/resourceGroups/aspnetcore-test-restapi"

Now, we will see the following output.

We will use “appId” and “password” fields as principal client ID and secret.

2.3 Creating a Kubernetes Cluster

  1. Log in to the Azure Portal.
  2. Click “New” button at the top left of the Portal.
  3. Select “Containers > Azure Container Service” section as shown below.

  4. In the “Basics” section, click “Use existing” button from resource group and select the “aspnetcore-test-restapi” value that we have created, and then fill other fields as shown below.
  5. Now we’ll specify some important information such as orchestrator from the “Master configuration” section. Select “Kubernetes” as an “Orchestrator“, and then fill the “DNS name prefix” and others fields as shown below.

    Then complete the other fields such as “SSH public key” and “Service principal” with the information that we have created earlier.
  6. In the “Agent configuration” section, let’s select “1” as the “Agent count“, and “Standard DS2” as the “Agent virtual machine size“.

Now, we have a Kubernetes cluster.

2.4 Connecting to Kubernetes Cluster

We need to have “kubectl” to be able to connect to the Kubernetes cluster. First, let’s download and install Azure CLI from here. After installation step, open a command tool and install “kubectl” using the following command.

az acs kubernetes install-cli

Now we have to download Kubernetes cluster configuration info. Therefore, login to Azure portal.

az login

Let’s run the following command (don’t forget to change related fields):

az acs kubernetes get-credentials --resource-group=aspnetcore-test-restapi --name=aspnetcore-container-service --ssh-key-file=C:\Users\GOKGOKALP\myPrivateKey_rsa

After the downloading process, let’s see the node list to make sure the cluster configuration info has been successfully downloaded.

kubectl get nodes

2.5 Running ASP.NET Core RESTful API inside to Kubernetes Cluster’s Pod

If I should say something about Pod, as in its documentation:

Pods are the smallest deployable units of computing in Kubernetes.

A Pod can contain one or more application containers. First, we need to create a new YAML file called “aspnetcore-rest-api-pod.yaml” under the root directory of the project to be able to create a pod in the Kubernetes cluster.

apiVersion: v1
kind: Pod
metadata:
 name: aspnetcore-restapi
 labels:
   app: aspnetcore-restapi
spec:
 containers:
 - name: aspnetcore-container
   image: "gokgokalp/aspnetcorerestapionlinux"
   ports:
   - containerPort: 5000

In the “image” section of YAML file, we specified our Docker image that we have pushed to Docker repo. If you remembered, we have exposed the API in a container on the port “5000“.

Now let’s run the following command using a command tool, under the root directory of the project.

kubectl create -f aspnetcore-rest-api-pod.yaml

We created the pod successfully. Let’s see the pod list.

kubectl get pods

As we can see the “aspnetcore-restapi” pod’s status is “running“. In this pod is running ASP.NET Core RESTful API that we have developed. At this point, we just need to expose this pod using Kubernetes Service.

2.6 Exposing Kubernetes Pod to Outside with Kubernetes Service

Basically, Kubernetes Service is an abstraction to creating policies and sets of a logical pod. If you want to reach more information, just look at here.

We need to create a new YAML file called “aspnetcore-rest-api-ks.yaml” for Kubernetes Service.

apiVersion: v1
kind: Service
metadata:
  name: aspnetcore-restapi-service
  labels:
   app: aspnetcore-restapi-service
spec:
  selector:
    app: aspnetcore-restapi
  ports:
    - port: 80
      targetPort: 5000
      protocol: TCP
  type: LoadBalancer

With the above YAML file, we’ll create a Kubernetes Service called “aspnetcore-restapi-service” and target TCP port “5000“. We should also make sure that the “app: aspnetcore-restapi” value is the same with pod label that we have created. Let’s run the following command:

kubectl create -f aspnetcore-rest-api-ks.yaml

Now we can see the service in the service list.

kubectl get services

If we look at the above picture, we can see the “aspnetcore-restapi-service” service has a “52.232.119.105” external IP and exposed to outside.

NOTE: IP assigning operation could take a few minutes.

When we reach to the external IP through the browser, we should see the Swagger UI as shown below.

Finally, we just need to create the Deployment Controller.

2.7 Creating and Exposing Kubernetes Deployment Controller

At this point, we have created a Kubernetes cluster using the Azure Container Service, and we defined a pod as the container of our RESTful API. Then, we defined a Kubernetes Service and exposed it to outside on the port “80” with a Load Balancer.

Well, if you read the topic “Durability of pods (or lack thereof)here, you can see that pods are not durable entities and you can’t reuse them in some situations, such as scheduling failures and node failures. This means when the pod dies, the related service will be unavailable. At this point, we need to use Kubernetes Controller to be production ready. There are a few Controllers such as Deployments and Replication in the Kubernetes. In here, we’ll use the Deployments, which is the new generation of the Replication Controller. Deployments generally provide important operations such as self-healing, rollout management and scaling.

Let’s create a new YAML file called “aspnetcore-rest-api-deployment.yaml“.

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: aspnetcore-restapi-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: aspnetcore-restapi
  template:
    metadata:
      labels:
        app: aspnetcore-restapi
    spec:
      containers:
      - name: aspnetcore-container
        image: "gokgokalp/aspnetcorerestapionlinux"
        ports:
        - containerPort: 5000

We defined the “replicas” section of the YAML file as “3“, and then under the “containers” section, we specified image and port info as they were in the pod creation YAML file. Thus, if any situation occurs such as scheduling failures and node failures, the Deployment will make sure that the number of replicas is 3.

Before creating of the deployment, we need to delete the “aspnetcore-rest-api-pod.yaml” pod and the “aspnetcore-rest-api-ks.yaml” service that we have created.

kubectl delete -f aspnetcore-rest-api-pod.yaml

kubectl delete -f aspnetcore-rest-api-ks.yaml

Now, we can create a deployment using the following command:

kubectl create -f aspnetcore-rest-api-deployment.yaml

Let’s have a look at the pods.

kubectl get pods

We can see on the above picture, the deployment created the 3 pods and they are in “running” statuses. It also possible to automatically scale more loads with a deployment. For more information, click here.

The last thing we only have to do is to create a service and expose again this 3 pods to outside. For this operation, we’ll use again the “aspnetcore-rest-api-ks.yaml” file. Because a few steps before we have deleted the service to be able to create pods with the deployment controller.

apiVersion: v1
kind: Service
metadata:
  name: aspnetcore-restapi-service
  labels:
   app: aspnetcore-restapi-service
spec:
  selector:
    app: aspnetcore-restapi
  ports:
    - port: 80
      targetPort: 5000
      protocol: TCP
  type: LoadBalancer

This service will expose the 3 pods with the label “app: aspnetcore-restapi” to outside from the port “80“.

kubectl create -f aspnetcore-rest-api-ks.yaml

That’s all. We exposed these 3 pods to outside with the Load Balancer. If we want to see the endpoints, just run the following command:

kubectl describe services aspnetcore-restapi-service

After running the command, we can see that the Load Balancer service exposed the “10.244.1.7:5000“, “10.244.1.8:5000” and “10.244.1.9:5000” pods to outside on the “http://52.232.119.105URL.

I know, this is a long article, but it’s a really enjoyable and important topic. Currently, I’m working on Containerizing and Kuberneting topics at my company’s .NET Core transformation process for some projects. I hope, this article would help who needs any information about Containerizing and Kuberneting to .NET Core applications.

In the next .NET Core article series, I’ll try to describe how we can include these operations in the CI pipeline process using Jenkins.

https://github.com/GokGokalp/containerizing-and-kubernetes-sample-files

Some References and Resources

https://github.com/MicrosoftDocs/azure-docs/blob/master/articles/container-service/container-service-tutorial-kubernetes-prepare-app.md
https://github.com/MicrosoftDocs/azure-docs/blob/master/articles/container-service/container-service-tutorial-kubernetes-prepare-acr.md
https://github.com/MicrosoftDocs/azure-docs/blob/master/articles/container-service/container-service-tutorial-kubernetes-deploy-cluster.md
https://github.com/MicrosoftDocs/azure-docs/blob/master/articles/container-service/container-service-tutorial-kubernetes-deploy-application.md
https://www.docker.com/what-container
https://docs.microsoft.com/tr-tr/azure/container-service/container-service-intro
https://kubernetes.io/docs/concepts/overview/what-is-kubernetes
https://docs.microsoft.com/en-us/azure/container-service/container-service-deployment
https://docs.microsoft.com/en-us/azure/virtual-machines/linux/ssh-from-windows
https://docs.microsoft.com/en-us/azure/container-service/container-service-kubernetes-walkthrough
http://www.c-sharpcorner.com/blogs/containerizing-a-net-core-application-using-docker-acs-and-kubernetespart-4
https://kubernetes.io/docs/concepts/services-networking/service/

Gökhan Gökalp

View Comments

Recent Posts

Securing the Supply Chain of Containerized Applications to Reduce Security Risks (Policy Enforcement-Automated Governance with OPA Gatekeeper and Ratify) – Part 2

{:tr} Makalenin ilk bölümünde, Software Supply Chain güvenliğinin öneminden ve containerized uygulamaların güvenlik risklerini azaltabilmek…

6 months ago

Securing the Supply Chain of Containerized Applications to Reduce Security Risks (Security Scanning, SBOMs, Signing&Verifying Artifacts) – Part 1

{:tr}Bildiğimiz gibi modern yazılım geliştirme ortamında containerization'ın benimsenmesi, uygulamaların oluşturulma ve dağıtılma şekillerini oldukça değiştirdi.…

8 months ago

Delegating Identity & Access Management to Azure AD B2C and Integrating with .NET

{:tr}Bildiğimiz gibi bir ürün geliştirirken olabildiğince farklı cloud çözümlerinden faydalanmak, harcanacak zaman ve karmaşıklığın yanı…

1 year ago

How to Order Events in Microservices by Using Azure Service Bus (FIFO Consumers)

{:tr}Bazen bazı senaryolar vardır karmaşıklığını veya eksi yanlarını bildiğimiz halde implemente etmekten kaçamadığımız veya implemente…

2 years ago

Providing Atomicity for Eventual Consistency with Outbox Pattern in .NET Microservices

{:tr}Bildiğimiz gibi microservice architecture'ına adapte olmanın bir çok artı noktası olduğu gibi, maalesef getirdiği bazı…

2 years ago

Building Microservices by Using Dapr and .NET with Minimum Effort – 02 (Azure Container Apps)

{:tr}Bir önceki makale serisinde Dapr projesinden ve faydalarından bahsedip, local ortamda self-hosted mode olarak .NET…

2 years ago