Run .NET Core 2 Docker images in Kubernetes using Azure Container Service and Azure Container Registry

This blogpost shows you the bare minimal steps to run .NET Core 2 Docker images in Kubernetes. Kubernetes is hosted in Azure with Azure Container Service and we are using Azure Container Registry as our private Docker Hub.

In this blogpost all steps will be executed manually. Next time we automate the whole process with VSTS.


The next items need to be installed:

Besides this Azure Container Registry and Azure Container Service needs to be provisioned in Azure. For now create them manually within the Azure Portal.

After this make sure the ServiceAccount of Kubernetes can access Azure Container Registry.

Create an Azure Container Registry

To create an Azure Container Registry (ACR) navigate to the Azure Portal and add a new resource:
acr create
Search for Azure Container Registry and click on it.

acr create 2
Choose an available Registry name and Enable the Admin user. (the Admin user can be enabled later on also)

Create a Kubernetes cluster with Azure Container Services

Make sure you have SSH keys. See my blogpost for the easiest way to do this on Windows.
Create a Service Principal and make sure you have the ApplicationId and password.

acs 1
Search for Azure Container Service

acs 2
Give the cluster a name and use an existing (empty) resourcegroup or create a new one

acs 3
Choose for Kubernetes as Orchestrator
Enter the pem public SSH key or the Single line public SSH key
Enter the ApplicationId of the Service Principal
Enter the secret of the Service Principal

acs 4
Choose the number of agents that you like.

Prepare the Kubernetes cluster to have access to Azure Container Registry

When the cluster is succesfully provisioned you are going to change the configuration of the Service Account. Because Azure Container Registry (ACR) is a private Docker Registry also the Kubernetes cluster cannot access it by default.

If you haven’t logged in to Azure then you will need to login with az login
If the cluster is on a different subscription then your default, change subscription with: az account set -s “”

Login to your Kubernetes cluster with:
az acs kubernetes get-credentials –resource-group=pascalnaberacs –name=myacscluster –ssh-key-file “C:\blogpost\opensshprivatekey”
Type the password for the SSH

Add a secret to Kubernetes, this secret contains the credentials to connect to ACR.
kubectl create secret docker-registry acrconnection —docker-server= —docker-username=myacr —docker-password=r/DK=ijNIvTArT1yU1OlXxHiLMXA9UDY —

The name of the secret is: acrconnection
The credentials that you have to pass can be found on the admin tab of Azure Container Registry in the Azure Portal

Get the current configuration of the ServiceAccount
kubectl get serviceaccounts default -o yaml > ./serviceaccount.yml

Add this to the end of the serviceaccount.yml file:

imagePullSecrets:name: acrconnection

So the complete files looks like:

apiVersion: v1
kind: ServiceAccount
  creationTimestamp: 2017-09-12T08:01:14Z
  name: default
  namespace: default
  resourceVersion: "151"
  selfLink: /api/v1/namespaces/default/serviceaccounts/default
  uid: 8ca65600-9790-11e7-83bb-000d3a24e4fe
secrets:name: default-token-j5jzn
imagePullSecrets:name: acrconnection

Replace the current configuration of the ServiceAccount with this new one:
kubectl replace serviceaccount default -f ./serviceaccount.yml

Now when we deploy images located in our Azure Container Registry, the images can be pulled by Kubernetes.

Steps to run your .NET Core 2 WebAPI project as Docker Container in Kubernetes

Now we have all prerequisites in place. Let’s do the actual work to deploy a Docker Container to Kubernetes. The following picture shows the steps that need to be done. Every step is explained below.
k8s manual deployment

1. Create a .NET Core 2 WebAPI project

You can use the command prompt, you can use Visual Studio Code. I’m using Visual Studio 2017:

Create ProjectCreate a new project: choose for an ASP.NET Core Web Application

2 Choose API Type

In the next step in the wizard choose for a Web API project. Note that ASP.NET Core 2.0 is selected, Enable Docker Support is enabled and create a Linux Docker Image.

2. Publish the .NET Core 2 WebAPI project

We are not going to change the behavior of the code in the Web API controller. For now we go with the default implementation.

There are multiple ways to create a Docker Image.
One way is to use the Docker compose file. To use this, build the docker-compose project in Release configuration. After building you will have a Docker image on your machine with the name of your project. Check this in the command prompt with docker images
Using Docker Compose is the easiest way when you have multiple projects in your solution that has to become a Docker Image.

Another way is to use the Dockerfile directly. The Dockerfile references the obj/Docker/publish directory by default. So we are going to publish the project to this directory.
Right click the project and choose for publish. Choose for Folder publish:

Select the same path as the Dockerfile uses. So: obj/Docker/publish
The files are published to this directory.

An alternative to publish the ASP.NET Core 2 WebAPI project is to use the command prompt:
dotnet publish ./MyWebApi.csproj -c Release -o ./obj/Docker/publish

3. Create a Docker image of the .NET Core 2 WebAPI project

This step is already done if you have used Docker compose in the previous step.

Open a command prompt in the directory of the WebApi project.
Execute (where mywebapi is the name of your project) docker build -t mywebapi .
create image

You can see the image on your machine with the following command:
docker images

if you want to run the image on your local machine run:
docker run -it -p 8080:80 mywebapi
Where 8080 is the local port and maps to port 80 of the Docker container.
You can access the WebApi project in the browser via:

If you like, you can in the mean time open a new command prompt and see the Docker container running with: docker ps

4. Push the Docker image to the Azure Container Registry

It’s not possible to deploy a Docker container directly to Kubernetes. Kubernetes needs to get the image from a Docker repository. For example Docker Hub is a public repository. Azure offers a  private Docker repository with Azure Docker Registry (ACR).

To succesfully push the Docker image of the project to ACR, you need to prepare the tags of the image. The tag needs to contain the name of the ACR. So in this sample, where the name of the ACR is myacr, the name of the image will be:
where myservice is only a name to group Container Images that belong together.

For this step multiple ways are possible also:
You can add a tag to an existing image:
docker tag mywebapi
Tag the image with an version also:
docker tag mywebapi

An alternative for adding a tag is to apply multiple tags while building the image.
docker build -t mywebapi -t -t .

Note: it’s not needed to apply a version to the Image, but it’s a best practice to know which version you are using. Because next time when you upload the same image. That will be the latest.

Take a look at the results by executing docker images.
docker images
You can see 3 images with the same IMAGE ID. Two of them have a latest tag and one has tag 1.

Now the images are prepared with the correct tags, you can push the images to ACR.

a. Get the password of the admin user in the Azure Portal:
acr admin
If you forgot to enable the admin account during the creation of ACR, you can do it now.

b. Login to ACR from the command prompt:
docker login -u myacr -p r/DK=ijNIvTArT1yU1OlXxHiLMXA9UDY

c. Push the image to ACR
docker push and
docker push image
Note that uploading the second version of the Image is drastically faster because of the Layers that are already known in ACR.

d. Take a look in the Azure Portal, tab Repositories:
acr repository


if you like, you can now also get the image from ACR on your local machine and run it.
Because you already have this image, delete it from your local machine first.
(the parameter is the first part of the IMAGE ID)
docker rmi fb5

if you get an exception that’s because the image has run.
Check this with docker ps -a
And remove the container with: (the parameter is the first part of the CONTAINER ID)
docker rm 67a 

Check with docker images that the images are not available on your local machine anymore.

Get the image from ACR (you should be logged in already):
docker pull

Run the image.

5. Execute a Deployment file to instruct Kubernetes to host the Docker Container

Create a Deploymentfile which declaratively tells Kubernetes what to do. In this case we want Kubernetes to run 3 containers of the mywebapi image.

apiVersion: extensions/v1beta1
kind: Deployment
  name: mywebapi-deployment
  replicas: 3  
  minReadySeconds: 10
    type: RollingUpdate
      maxUnavailable: 1
      maxSurge: 1 
        app: mywebapi
      - name: mywebapi
        - containerPort: 80
        imagePullPolicy: Always   

Change the file according to your needs and save it as Deploy.yml

Login to your Kubernetes cluster as described in the “Prepare the Kubernetes cluster to have access to Azure Container Registry” section earlier in this blogpost.

Exectute the deployment with (–save-config is to see the history of deployments): kubectl create -f Deploy.yml –save-config

The “create” argument only have to be passed the first time you deploy. Sequential deployments should be executed with: kubectl apply -f Deploy.yml 

Let’s see if the deployment is succeeded:
This can be done in the command line and also in the UI.
a. With the command line
See the deployments:
kubectl get deployments
See the pods that are available now: (you will see 3 pods)
kubectl get pods

b. With the UI
Start the UI proxy:
kubectl proxy
Open the UI in the webbrowser:

Navigate to the Deployments, the pods and you can also see the secret you created.

The pods should be running, but you can’t access the webapi right now. To make a public endpoint do the following: (this has only to be done once)

Create a new Deployment file. This time for a Service. The file looks like this:

apiVersion: v1
kind: Service
  name: myapiservice
    - port: 80
    app: mywebapi
  type: LoadBalancer


Make sure that the selector-app is the same as template-metadata-labels-app name in the Deploy.yml.
Save the file as DeployService.yml
Create the Deployment:
kubectl create -f DeployService.yml

The creation of the Service is fast, but to get a public IP-Address takes a while (3 minutes or so), because Kubernetes is communicating with the load balancer in Azure to get a public IP address.

Check the results in the UI or command line:
kubectl get services
The EXTERNAL-IP is <pending> for now.
services 2
The EXTERNAL-IP is available and now you can access your WebApi:
In my situation the URI is:

Now change the implementation of your WebApi project to return a different value.
Create a Docker Image with a new version tag and push the Docker Image to ACR.
Update the Deploy.yml file with the new version tag and execute it.
See the power of Kubernetes when the Rolling updates takes place.

Next time we are going to automate all this steps with VSTS


2 gedachtes over “Run .NET Core 2 Docker images in Kubernetes using Azure Container Service and Azure Container Registry

  1. Pingback: Automate the deployment of .NET Core 2 Docker containers to Kubernetes with Azure Container Service and Azure Container Registry using VSTS | Pascal Naber

  2. Pingback: Configure Ingress on Kubernetes using Azure Container Service | Pascal Naber

Geef een reactie

Vul je gegevens in of klik op een icoon om in te loggen. logo

Je reageert onder je account. Log uit / Bijwerken )


Je reageert onder je Twitter account. Log uit / Bijwerken )

Facebook foto

Je reageert onder je Facebook account. Log uit / Bijwerken )

Google+ photo

Je reageert onder je Google+ account. Log uit / Bijwerken )

Verbinden met %s