Let's talk!

Maciej Szymczuk
Regional Director
USA

(+1) 929 262 9149

maciej.szymczuk@softwarehut.com


contact form

Let's talk

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:

localhost 5000 screen name

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.

localhost5000 screen

Now, use docker 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.

Kubernetes tab screen

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 exposes port 80 and contains one instance replicas: 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
screen code 1

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.

deployment name

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:

  1. Create an Azure Resource Group to have everything in one place.
  2. Create Azure Container Register (ACR) to push and share our local image.
  3. Add Azure Service Principal to be able to use our ACR with Azure Kubernetes Service (AKS).
  4. Create AKS.
  5. 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": "cfed7ab3-8db9-4bba-86c9-2311d505d2ad", 
"password": "7193c36f-3123-4453-8ee7-2aa7bf071007",

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 "cfed7ab3-8db9-4bba-86c9-2311d505d2ad" --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 "cfed7ab3-8db9-4bba-86c9-2311d505d2ad" ` 
 --client-secret "7193c36f-3123-4453-8ee7-2aa7bf071007"

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
screen code 2

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

Open shkubedeploy.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.

code example azure 1

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
screen code 3

Copy an IP and check it out in a browser.

deployment name

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
image-10

Or change the number of replicas:

kubectl get deployment 
kubectl scale --replicas=5 deployment/shkube-deployment 
kubectl get deployment
image-12

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:

code example azure

And apply all changes.

kubectl apply -f .\shkubedeploy.yml

Refresh your browser. Now, you should see a new message!

hello Azure 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$!

Fresh software development tips delivered straight to your inbox

Subscribe to our monthly newsletter with useful information about building valuable software products.
Don't worry, we value your privacy and won't spam you with any bussines enquiries!

szczepan_thumbnail

Software Developer

I’m a .NET-driven full stack developer. I love the idea behind Docker and I’m happy to share it.

As part of our website we use cookies to provide you with services at the highest level, including in a manner tailored to individual needs. Using the site whithout changing the cookies settings means that they will be stored in your device. You can changes settings at any time. Accept