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'.
- [Route("api/[controller]")]
- public class QuotesController: Controller {
- private static readonly string[] ListOfQuotes;
- static QuotesController() {
- ListOfQuotes = JsonConvert.DeserializeObject < QuoteList > (System.IO.File.ReadAllText("quotes.json")).Quotes;
- }
- [HttpGet]
- public IActionResult Get() => Ok(ListOfQuotes[new Random().Next(ListOfQuotes.Length)]);
- [HttpGet("{count}")]
- public IActionResult GetMany(int count) {
- if (count < 0 || count > ListOfQuotes.Length) return BadRequest($ "number of quotes must be between 0 and {ListOfQuotes.Length}");
- var r = new Random();
- return Ok(ListOfQuotes.OrderBy(_ => r.Next(ListOfQuotes.Length)).Take(count).Select(x => $ "{Environment.MachineName}-{x}"));
- }
- private class QuoteList {
- public string[] Quotes {
- get;
- set;
- }
- }
- }
- #BUILD PHASE
- FROM microsoft/aspnetcore-build:2.0.5-2.1.4-jessie AS build-env
- WORKDIR /app
- COPY *.csproj .
- RUN dotnet restore
- COPY . .
- RUN dotnet publish -c Release -o out
- #RUN PHASE
- FROM microsoft/aspnetcore:2.0.5-jessie
- WORKDIR /app
- COPY --from=build-env /app/out .
- 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.
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.
-
- az login
-
- az account set --subscription <<subscription id>>
-
- az group create --name dockerrepo --location eastus
-
- az acr create --resource-group dockerrepo --name sanjaysrepo --sku Basic --admin-enabled true
-
- az acr login --name sanjaysrepo
-
- 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.
-
- az provider register -n Microsoft.ContainerService
-
- az group create --name kubernetes --location eastus
-
- 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).
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.
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.
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.
Also a few commonly used commands are listed below.
-
- kubectl get nodes
-
- kubectl get cs
-
- kubectl get svc
-
- kubectl get pods
-
- kubectl config view
-
- kubectl get events
-
- kubectl get deployments
-
- kubectl logs your-pod-name
-
- kubectl describe pod your-pod-name
-
- kubectl describe node your-node-name
-
- 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.
- apiVersion: apps/v1beta1
- kind: Deployment
- metadata:
- name: quotes
- spec:
- replicas: 1
- strategy:
- rollingUpdate:
- maxSurge: 1
- maxUnavailable: 1
- minReadySeconds: 5
- template:
- metadata:
- labels:
- app: quotes
- spec:
- containers:
- - name: quotes
- image: sanjaysrepo.azurecr.io/quotesgenerator:linux
- ports:
- - containerPort: 80
- resources:
- requests:
- cpu: 250m
- limits:
- cpu: 500m
- imagePullSecrets:
- - name: quotesregistrykey
- ---
- apiVersion: v1
- kind: Service
- metadata:
- name: quotes
- spec:
- type: LoadBalancer
- ports:
- - port: 80
- selector:
- 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
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).
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.
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.
-
- kubectl scale deployment quotes --replicas=5
-
Now, you can check the deployment & see all 5 instances are up & running (after a few moments)
To verify just go back to fiddler and fetch the same URL quickly multiple times. Ideally, you should see different machine names now.
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.
Cool! We are almost done. Last few commands for daily uses.
-
- kubectl scale deployment your-deployment-name --replicas=2
-
- kubectl delete deployments/your-deployment-name
-
- kubectl delete services/your-service-name
-
- az aks scale --resource-group=kubernetes --name=quotesservice --node-count 3
-
- 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]"
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.