Microservice architecture and Docker are becoming more and more popular these days. And there are many reasons for that, as it makes running apps locally and on production easier and more efficient.
In this article, I will shortly brief you through some main terminologies and show you how to:
- Dockerise an ASP .NET Core application.
- Create and run Docker image.
- Use this image in your local Kubernetes cluster.
- Create Azure Container Register and push your image there.
- Setup Azure Kubernetes Service and connect it to ACR.
- Scale you cluster and change number of replicas.
- And the most important: how to clean all this mess.
What do you need to know about Docker and Kubernetes?
Microservices, also known as microservice architecture, are being used to split an application into small, single responsible services so they can be developed and scaled separately instead of one, big monolith application.
Container, on the other hand, is a nano-server which we can use as a host for an app instead of virtual machines. To store containerised apps’ images, we can use container register. So how do we create, deploy, and run applications using containers? One way to do this is by using Docker, an open-sourced online tool.
Another thing we need, is a tool for managing containerised workloads and services. In this case, we’ll use Kubernetes, as it facilitates both declarative configuration and automation. By default, Docker uses Docked Swarm for an orchestration but in a new version of Docker Desktop, available for both MacOS and Windows, you can change this to use Kubernetes.
Pre-requirements
To follow the example explained in this article, you will need a computer running on macOS or Windows. You can use Linux, too, but I won’t discuss this in this example.
Then, if you’re a Windows user, enable Hyper-V on your machine if you haven’t done it yet.
The next thing to do is to install or update Docker Desktop and Azure CLI, which you will need a little bit later.
Run the app
In the next few steps we will try four different approaches to run the app:
- locally,
- via local Docker instance,
- via your local Kubernetes instance,
- via Kubernetes on Azure.
Let’s get started!
It will be much simpler to have the same codebase so open your PowerShell and clone my repository.
Here, you can find the simplest possible asp.net core application which should display a message from appsettings.json and your machine name. If you have installed .NET Core Runtime, you can build and run the app locally without Docker:
- Navigate to project folder SHKube.
- Run the application dotnet run.
Application is listening on port 5000 so open your browser and navigate to http://localhost:5000. You should see the name of our machine:
You should see the name of our machine:
Now, it’s time to dockerise our app
There are at least two ways to build and run an app inside Docker: via command line only or by using Dockerfile. I think that the second approach is more clear, so I will use it in this example. Take a look at the Dockerfile inside the project’s folder.
FROM mcr.microsoft.com/dotnet/core/aspnet:2.2-stretch-slim AS base WORKDIR /app FROM mcr.microsoft.com/dotnet/core/sdk:2.2-stretch AS build WORKDIR /src COPY ["*.csproj", "./"] RUN dotnet restore COPY . ./ WORKDIR /src RUN dotnet build -c Release -o /app FROM build AS publish RUN dotnet publish -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . ENTRYPOINT ["dotnet", "SHKube.dll"]
What we have here is a multi-stage build. We are using two images from DockerHub.
-
mcr.microsoft.com/dotnet/core/sdk:2.2-stretch
is used to build an app – it contains .NET Core SDK which is necessary to build our app -
mcr.microsoft.com/dotnet/core/aspnet:2.2-stretch-slim
which is lighter and contains only .NET Core Runtime to run our app
Let’s build a new image with our app.
docker build . -t shkube:local
We have named the image shkube and tagged it local. Check out if the image is on a list of local images:
docker image list | select -First 2
You can also use docker image list
only to see all images. Finally, we can run
this image:
docker run -d -p 5000:80 shkube:local
-
-d
means detached mode – run in a background and print container ID -
-p
binds port 80 of a container to port 5000 of a host
Open the browser and navigate to http://localhost:5000. You should now see the ID of a container.
Now, usedocker stop
to stop and docker rm
to remove the container. You can also use:
docker container rm -f $(docker ps -aq)
to clean everything.
We can now move to Kubernetes
Open Docker settings and open Kubernetes tab. Select two checkboxes (see the screen below) and wait for an installation to finish. It should look very similar for macOS users.
As with the Dockerfile, you can use yml file to have all configuration necessary for Kubernetes in one place.
Open shkubedeploy.yml
apiVersion: apps/v1 kind: Deployment metadata: name: shkube-deployment labels: app: shkube spec: replicas: 1 template: metadata: name: shkube labels: app: shkube spec: containers: - name: shkube image: shkube:local imagePullPolicy: IfNotPresent restartPolicy: Always selector: matchLabels: app: shkube --- apiVersion: v1 kind: Service metadata: name: shkube-service spec: selector: app: shkube ports: - port: 80 type: NodePort
You have just created a new
Deployment for Kubernetes. It contains one service named shkube-service
, which exposesport 80
and contains one instancereplicas: 1
of our container image shkube:local
.
Containers are ephemeral. It could just die. Kubernetes will run a new instance of our container if it happens. It is recommended to have at least three instances. We will scale it up a later.
Now we must run our deployment:
kubectl create -f .\shkubedeploy.yml
And check out if the service is still pending or not:
kubectl get svc --watch
Grab a port number and open your browser. In my case it is http://localhost:31192/ . Now you should see the name of the deployment.
Let’s clean this mess:
kubectl delete -f .\shkubedeploy.yml
Simple as that!
Finally, welcome to Azure!
With Azure things are a little bit more complicated but don’t worry – you can handle this! You need to follow a few additional steps before you can continue with the deployment:
- Create an Azure Resource Group to have everything in one place.
- Create Azure Container Register (ACR) to push and share our local image.
- Add Azure Service Principal to be able to use our ACR with Azure Kubernetes Service (AKS).
- Create AKS.
- And then, spin out the deployment.
Log in to Azure from PowerShell.
az login
Create a Resource Group
az group create -n shkuberg -l westeurope
-
-n
stands for name -
-l
stands for region (I have picked up Western Europe because it’s the closest one, but it doesn’t matter in this case)
Next, create Azure Container Register.
az acr create -n shkubeacr -g shkuberg --sku standard
-
-g
stands for resource group -
--sku
stands for Stock Keeping Unit (available options: Basic, Classic, Premium, Standard)
Find loginServer
, we will need that in a moment (mine is shkubeacr.azurecr.io
).
Check out if the ACR is ready:
az acr list -o table
And log in to ACR:
az acr login -n shkubeacr
Now, let’s tag our existing image to match the ACR
name. Use loginServer
that you
have stored before:
docker tag shkube:local shkubeacr.azurecr.io/shkube:v1
Check out all local images:
docker image list
You can now see two images with the same ID but different tags. Push an image to the ACR:
docker push shkubeacr.azurecr.io/shkube:v1
And check if it is there:
az acr repository list -n shkubeacr -o table
Good job!
Add Azure Service Principal
Create a service principal and configure its access to Azure resources.
az ad sp create-for-rbac --skip-assignment
Remember the appId
and password
? -You need those now. Mine are:
"appId": "d8fb053c-6ecc-4513-a8b8-9dcaed99d752", "password": "8a1298ba-bd64-407c-a805-d15b7c2f1cac",
Create a new role assignment (use your appId after
--assignee
):
$acrId = az acr show --name shkubeacr --resource-group shkuberg --query "id" --output tsv az role assignment create --assignee "d8fb053c-6ecc-4513-a8b8-9dcaed99d752" --role Reader --scope $acrId
And you are done with permissions!
Azure Kubernetes Service
az aks create ` --name shkubeakscluster ` --resource-group shkuberg ` --node-count 1 ` --generate-ssh-keys ` --service-principal "d8fb053c-6ecc-4513-a8b8-9dcaed99d752" ` --client-secret "8a1298ba-bd64-407c-a805-d15b7c2f1cac"
Use appId
for service-principal
and password
for client-secret
.
It’s a good moment for a short break, so grab yourself a cup of coffee and wait.
You have just created a single, brand new ASK.
Check where your kube context point to.
cat C:\Users\{yourLocalUserName}\.kube\config | more
You should see the localhost here, but you have to add the one from Azure.
az aks get-credentials --name shkubeakscluster --resource-group shkuberg
Check again:
cat C:\Users\{yourLocalUserName}\.kube\config | sls "shkubeakscluster"
Let’s check a list of nodes:
kubectl get nodes
And grab ACR Login server to update our shkubedeploy.yml
file.
az acr list --resource-group shkuberg --query "[].{acrLoginServer:loginServer}" --output table
Openshkubedeploy.yml
file and change image to the one from ACR instead of local
image and change the service spec type to LoadBalancer instead of NodePort because we are on Azure now.
And you are ready to go. Upload your config file and wait for it to be ready.
kubectl apply -f .\shkubedeploy.yml kubectl get service --watch
Copy an IP and check it out in a browser.
We can now increase a number of Kubernetes clusters:
kubectl get nodes az aks scale --name shkubeakscluster --resource-group shkuberg ` --node-count 3 kubectl get nodes
Or change the number of replicas:
kubectl get deployment kubectl scale --replicas=5 deployment/shkube-deployment kubectl get deployment
How to update our application now?
Let’s change a Message in appsettings.json
to **Hello azure: “. As before, we have to
build a new image and push it to the register.
docker build . -t shkubeacr.azurecr.io/shkube:v2 docker push shkubeacr.azurecr.io/shkube:v2
We have to update our deployment file to point to
the new image and change the number of replicas to have the same value as it is on Azure now. Open and edit shkubedeploy.yml
:
And apply all changes.
kubectl apply -f .\shkubedeploy.yml
Refresh your browser. Now, you should see a new message!
This is it! You are ready to clean all the mess you have made:
1. Remove a deployment
kubectl delete -f .\shkubedeploy.yml
2. Remove Kubernetes clusters
az aks delete --name shkubeakscluster --resource-group shkuberg
3. Remove container register
az acr delete -n shkubeacr
4. Remove resource group
az group delete --name shkuberg
5. And switch back to your local Kubernetes context
kubectl config use-context docker-for-desktop kubectl config unset contexts.shkubeakscluster
Summary
Do you remember the times when your app was working well locally, but not on production and how hard it was to debug? And this strange behaviour when your single-node was scaled horizontally and you couldn’t figure out what was wrong?
We went a long journey to prove that now it can be done better.
- We have run the same ASP.NET Core application locally and inside a Docker container.
- We have used the same Docker container image with and without Kubernetes.
- We have used the same Kubernetes deployment configuration on our workstation and then on Azure.
And we have made it for less than 5$!