Deploy Your First ASP.NET Core 2.0 App In Azure Managed Kubernetes

Microsoft released AKS aka the *new* Azure Container Service aka Azure Kubernetes Service (fully managed) a few months back, it's still in public preview though. This guide will help you to get started with AKS. We are going to deploy our first ASP.NET Core 2.0 App in AKS and then do some cool stuff like scaling up and down.

Following are the steps we are going to perform.

  • Create a docker image (in Linux) from a simple ASP.NET Core 2.0 app
  • Publish the same in Azure Container Registry (ACR)
  • Create a Kubernetes cluster using AKS in Azure
  • Deploy our application in the cluster
  • Scale up/down
  • [Optional] Create a public/private SSH key
Prerequisites

A development computer running,

  • Visual Studio 2017 (mine is v15.5.2)
  • Docker for Windows. Get Docker CE for Windows (stable). After installing and starting Docker, right-click on the tray icon and select Switch to Linux containers (if already not). My current version is v17.12.0-ce-win47 (15139)
  • An Azure subscription
  • Google Cloud SDK for windows. Not mandatory, we can use PowerShell or Bash etc. But let's just use this today.
  • Install kubectl, the Kubernetes command-line tool. This is needed to manage your Kubernetes cluster once it is published on Azure.
  • [Optional] Enable Bash for Windows.
Create a docker image (in Linux) from a simple asp.net core 2.0 app

We are not going to go into details about all the steps here. You can read more if you want to learn about creating an image in another article. Below is my API controller file and the docker file. You can get these files from GitHub too. Just take a note of the Linux image tags in the docker file to be downloaded from docker hub. We are using the latest Linux images here with tag '2.0.5-2.1.4-jessie'.

  1. [Route("api/[controller]")]  
  2. public class QuotesController: Controller {  
  3.     private static readonly string[] ListOfQuotes;  
  4.     static QuotesController() {  
  5.             ListOfQuotes = JsonConvert.DeserializeObject < QuoteList > (System.IO.File.ReadAllText("quotes.json")).Quotes;  
  6.         }  
  7.         [HttpGet]  
  8.     public IActionResult Get() => Ok(ListOfQuotes[new Random().Next(ListOfQuotes.Length)]);  
  9.     [HttpGet("{count}")]  
  10.     public IActionResult GetMany(int count) {  
  11.         if (count < 0 || count > ListOfQuotes.Length) return BadRequest($ "number of quotes must be between 0 and {ListOfQuotes.Length}");  
  12.         var r = new Random();  
  13.         return Ok(ListOfQuotes.OrderBy(_ => r.Next(ListOfQuotes.Length)).Take(count).Select(x => $ "{Environment.MachineName}-{x}"));  
  14.     }  
  15.     private class QuoteList {  
  16.         public string[] Quotes {  
  17.             get;  
  18.             set;  
  19.         }  
  20.     }  
  21. }  
  1. #BUILD PHASE  
  2. FROM microsoft/aspnetcore-build:2.0.5-2.1.4-jessie AS build-env  
  3. WORKDIR /app  
  4. COPY *.csproj .  
  5. RUN dotnet restore  
  6. COPY . .  
  7. RUN dotnet publish -c Release -o out  
  8. #RUN PHASE  
  9. FROM microsoft/aspnetcore:2.0.5-jessie  
  10. WORKDIR /app  
  11. COPY --from=build-env /app/out .  
  12. ENTRYPOINT ["dotnet""DockerWebTestApp.dll"]  
Now, open Google Cloud SDK Shell (for rest of this article we shall be using this only, you can use Bash for Windows, Git Bash, PowerShell or even a CMD shell too). Change directory to your web project's base folder (where the docker file is also located). Now create the docker image by executing the following 'docker build' command. You can take a guess why we are choosing a name like 'sanjaysrepo.azurecr.io/quotesgenerator:linux'. Well, you guessed it right. We are going to create an Azure Container Repository named 'sanjaysrepo' to push this image to the next step. Also, the tag 'linux' is just to identify it. You can leave it blank & default 'latest' tag will be used in that case.

docker build -t sanjaysrepo.azurecr.io/quotesgenerator:linux .

You should be able to see the locally created image now.

Azure

Publish the image in Azure Container Registry (ACR)

Now we can publish this image (which is still in your local dev box) to some container registry like Docker Hub or Google Cloud or ACR. For this article let's choose ACR. In your open Google cloud shell (or any other) execute the following commands one after another to publish the image to Azure. All these steps are self-explanatory.

  1. // Login into your Azure subscription  
  2. az login  
  3. // Change to proper subscription if you have more than one  
  4. az account set --subscription <<subscription id>>  
  5. // Create a resource group called dockerrepo. You can use any existing too as needed  
  6. az group create --name dockerrepo --location eastus  
  7. // Create an ACR called 'sanjaysrepo' under the RG  
  8. az acr create --resource-group dockerrepo --name sanjaysrepo --sku Basic --admin-enabled true  
  9. // Login into the ACR instance once created  
  10. az acr login --name sanjaysrepo  
  11. // Push the locally created image to ACR  
  12. docker push sanjaysrepo.azurecr.io/quotesgenerator:linux  
Create a Kubernetes cluster using AKS in Azure

Let's create a Kubernetes cluster of two VMs in Azure. Please execute the following steps to provision the cluster.

  1. // Enabling AKS preview for your Azure subscription if not done already. Don't worry about the warning after execution, it's actually done already!  
  2. az provider register -n Microsoft.ContainerService  
  3. // Create a RG in East US location (many of the azure DCs don't support AKS fully till now, EUS is still the most reliable :))  
  4. az group create --name kubernetes --location eastus  
  5. // Create the AKS cluster with 2 VM's in the same RG created above. Half of your job is done here :)  
  6. az aks create --resource-group kubernetes --name quotesservice --node-count 2 --generate-ssh-keys  

The last command is going to take some time obviously :) At the end, it should show you a few important details about the cluster as well as the generated SSH keys (with location), The AD App created to generate service principle etc. You should see something like below (click on the images for details).

Azure

Azure

Congrats, your first AKS Linux cluster is deployed. Now let us see how we can communicate with it from my dev machine.

Deploy our application in the cluster

Before we dig into deploying our application, let's take a moment to first understand the deployed cluster & how can we as developers or release managers communicate with the cluster.

If you now open up your Azure portal you will actually see two resource groups created for you. One is 'kubernetes' as you asked for. But there is another one automatically created by Microsoft called 'MC_kubernetes_quotesservice_eastus' for you. The naming conversion used by Microsoft for the 2nd one seems pretty straightforward. MC most probably stands for 'Managed Cluster', and then your specified RG name, cluster name and at last, location to make it unique. If you open up the first RG you will see something like below, it has exactly one resource and it is called 'Container service managed'. This is actually the pointer to the master nodes for Kubernetes, totally managed by Microsoft (like patching, upgrade etc.). You don't have access to the actual Kubernetes control plane or cluster or the master nodes.

Azure

But if you open up the 2nd RG created by Microsoft you should see lots of resources created, like the vNet, node VMs, virtual network interfaces per node, route tables, availability sets, NSG etc. These are the resources you have full access to. Kubernetes clusters are mainly managed by a command line tool called 'kubectl', that you already installed as a prerequisite if you are following till now. So we are going to use this tool to deploy/manage applications/services to these nodes. You also need to understand a bit about YAML file that Kubernetes uses to deploy applications/services. You can read more here.

Azure

To make sure 'kubectl' can communicate to our newly created Azure AKS cluster, let's execute the following command that ideally will configure 'kubectl' to communicate to your cluster securely. You should see an output like 'Merged "quotesservice" as the current context in C:\Users\sanjayd\.kube\config'.

az aks get-credentials --resource-group kubernetes --name quotesservice

Once this is done, we can now execute any 'kubectl' command to validate the cluster. A few examples below.

Azure

Also a few commonly used commands are listed below.

  1. // get all the nodes  
  2. kubectl get nodes  
  3. // get cluster component statuses  
  4. kubectl get cs  
  5. // get all services  
  6. kubectl get svc  
  7. // get all pods  
  8. kubectl get pods  
  9. // get all configs  
  10. kubectl config view  
  11. // get all latest events  
  12. kubectl get events  
  13. // get all deployments  
  14. kubectl get deployments  
  15. // get logs from a pod  
  16. kubectl logs your-pod-name  
  17. // get details about a pod  
  18. kubectl describe pod your-pod-name  
  19. // get details about a node VM  
  20. kubectl describe node your-node-name  
  21. // get overall cluster info  
  22. kubectl cluster-info  
Now as we are good to talk to out nodes, let's see how we can deploy our docker image in the cluster. As we saw earlier 'cubectl' generally uses YAML file to deploy one or more resources inside a cluster. So lets first create one. In the same solution (or any place you want) add a 'quotes.yaml' file and replace the content below.
  1. apiVersion: apps/v1beta1  
  2. kind: Deployment  
  3. metadata:  
  4. name: quotes  
  5. spec:  
  6. replicas: 1  
  7. strategy:  
  8. rollingUpdate:  
  9. maxSurge: 1  
  10. maxUnavailable: 1  
  11. minReadySeconds: 5  
  12. template:  
  13. metadata:  
  14. labels:  
  15. app: quotes  
  16. spec:  
  17. containers:  
  18. - name: quotes  
  19. image: sanjaysrepo.azurecr.io/quotesgenerator:linux  
  20. ports:  
  21. - containerPort: 80  
  22. resources:  
  23. requests:  
  24. cpu: 250m  
  25. limits:  
  26. cpu: 500m  
  27. imagePullSecrets:  
  28. - name: quotesregistrykey  
  29. ---  
  30. apiVersion: v1  
  31. kind: Service  
  32. metadata:  
  33. name: quotes  
  34. spec:  
  35. type: LoadBalancer  
  36. ports:  
  37. - port: 80  
  38. selector:  
  39. app: quotes  
While you can read more about how this works, in short we are creating a template to create a single replica deployment and then a service from that deployment. That deployment we are going to create is going to use an image located at 'sanjaysrepo.azurecr.io/quotesgenerator:linux'. Now to access the image AKS needs a credential. Here we are providing the name of the credential as 'quotesregistrykey'. To create the named credential execute the below command after fetching your ACR's user credentials.

kubectl create secret docker-registry quotesregistrykey --docker-server=https://sanjaysrepo.azurecr.io --docker-username=your-ACR-registry-user-name --docker-password=your-ACR-registry-user-password [email protected]

Once this is done, we are good to deploy our application in the cluster. So, let's go back to Shell & execute this command.

kubectl create -f quotes.yaml

While the service is being created you can watch the status by executing the below command. Wait until a public IP is added in the <EXTERNAL_IP> column (press Ctrl+C to get out of the waiting mode).

kubectl get service quotes --watch

Azure

So ideally, now Azure is creating two new resources for you, one is a load balancer and another one is a public IP to expose your service over port 80 (specified in the YAML file).

Azure

Once you see that an external IP is assigned, we are done :). Go ahead and try to browse/fiddler the URL 'http://<<public-ip>>/api/quotes/12', you should see output with a 200 status.

So we just deployed an instance of our app and it's running a single replica as of now (though we have created 2 nodes for our service). If you execute the URL from fiddler multiple times, you will always see only a single machine name in the output in all the 5 requests that we did below.

Azure

So now let's scale up it a bit.

Scale up/down

The easiest way to scale up the instances is to execute the below command.

  1. // create 5 replicas of the app/pods  
  2. kubectl scale deployment quotes --replicas=5  
  3. // output: deployment "quotes" scaled  
Now, you can check the deployment & see all 5 instances are up & running (after a few moments)

Azure

To verify just go back to fiddler and fetch the same URL quickly multiple times. Ideally, you should see different machine names now.

Azure

Now just for curiosity you can actually use 'kubectl describe pod pod-name' against all 5 pods that we just created & check their distribution among the 2 VM nodes.

Azure

Cool! We are almost done. Last few commands for daily uses.

  1. // scale up/down a deployment to create 2 replicas  
  2. kubectl scale deployment your-deployment-name --replicas=2  
  3. // delete a deployment  
  4. kubectl delete deployments/your-deployment-name  
  5. // delete a service  
  6. kubectl delete services/your-service-name  
  7. // increase/scale the actual VM counts  
  8. az aks scale --resource-group=kubernetes --name=quotesservice --node-count 3  
  9. // deletes the AKS deployment altogether  
  10. az aks delete --name quotesservice --resource-group kubernetes --no-wait  
[Optional] Create a public/private SSH key

If you remember we used '--generate-ssh-keys' param as part of 'az aks create' command. To use your own/previously created SSH, you can make use of '--ssh-key-value' param. Here we shall see how easily we can create an SSH key by using Bash for Windows. So first fire up Bash for Windows and navigate to root directory then simply execute the following command. You can provide a path and secret when prompted else just keep pressing enter.

ssh-keygen -t rsa -C "[email protected]"

Azure

Hope we have learned something about AKS (and a few other things :)) today. Let me know if you face any issues or you have any suggestions/questions.

BTW remember to delete the resources on Azure.