Building Docker images for ASP.NET Core Application

docker_dotnetcore

This one is one of the crucial part of my DotNet Core Migration strategy series which I had been working for so long. With our initial posts of this series, we got introduced with Cosmos DB, created a sample Web API in .NET framework 4.6.1 and then migrated the same API to .NET core 2.1. Now we would like to deploy the core app.

We all know that every solution or design have their own deployment strategy which differs based on requirement and technical debts.  My kind of strategy is purely for educational purpose and might not be applicable for your case. However, you can take this as a reference if you want to implement containerization using docker technology.

Ideally, I want to deploy my core app into Azure and I know we can simply do that by deploy the API as Azure API Service. However, my intention for this series is to deploy the API to Azure Container Instances as docker images. Why I want to do that, is because I am new to containerization and I know there are many benefits of it. If you would like to know what containerization is, I would recommend to check out some of the good tutorials and online documentations. I will share some links at the bottom of this post for your reference.

Let’s get started.


Step 1: Accessing about Containerizing our ASP.NET Core app

Just to let you know that Visual Studio 2017, does provide enabling docker and containerization support to your .NET core app.

Enabling docker support for either linux or windows platform can be achievable by right clicking the .NET core project and choosing Docker support from the context menu as shown below.

vs_docker_support

This will create a dockerfile to the root of the application which has all the commands to be executed which helps to create the docker image. We will go through the details of dockerfile a bit later.

 

VS 2017 also support container orchestration using either Docker Compose and Service Fabric. For this post we are going to deal with Docker Compose is a YAML (human friendly data serialization standard) file that defines configuration of services for running multi-container docker applications. You can add orchestration support to the project by right-clicking the project properties and selecting Container Orchestrator Support from the context menu.

image

 

However, I have decided not to use VS 2017 to generate dockerfile or add container orchestration support. This is because I did faced few issues while doing so. Instead, I am going create the dockerfile and docker-compose.yaml file separately and also will share some inputs of how these files work and where to place these files in your solution.

 

Before that, please do keep in mind that in order to run docker, you need to install docker for either windows or Mac.

Considering the pre-requisites of installing docker to my Windows, I am not meeting the requirement as Docker can only be installed in Windows 10 professional or Enterprise edition and unfortunately I have Windows 10 Home. Well, if you facing similar constraints as mine, there is nothing to worry since we do have a solution here.


Step 2: My solution for running docker in Windows 10 Home

Docker Toolbox is a solution for Mac and Windows which doesn’t meet the requirement for Docker Community or Enterprise edition. It is used to launch the Docker environment for incompatible versions of Windows and Mac. Here are the installers for Windows and Mac. The installer will add a Docker GUI named Kitematic, an Oracle Virtual Box and Docker Quickstart for running docker commands.

image_thumb81

Before you start Docker Quickstart, ensure that Virtualization is enabled at BIOS and don’t forget to uncheck Windows hypervisor program from Windows features.

image_thumb41

The only reason we need to do this is because, if the hypervisor is enabled then you might encounter an error while running Docker Quickstart.

image_thumb101

After all these checks are done, restart the machine and then run Docker Quickstart.

image_thumb5

Once you get the interactive shell of docker, run hello-world to see if docker commands are working.

image

All good. Now our next step is to add dockerfile and docker-compose.yaml to our solution.


Step 3: Adding docker files to solution

Alright, here I want to share some insights about where these two files should be placed in your solution. If you use VS 2017 capability to add docker support or container orchestration support, by default the dockerfile will be placed inside the API project folder and the docker-compose.yaml will be placed in the solution folder. At later stage I am going to use Jenkins to build the docker images based on my github repo, which will create issue building and running the docker images. Hence, you should ideally place the docker-compose.yaml in the root folder of your github repo and dockerfile in the solution folder.

image

Here is how the dockerfile looks like

 
FROM microsoft/dotnet:2.1-sdk AS build
WORKDIR /app

COPY EmployeeManagement.Core/*.sln .
COPY EmployeeManagement.Core/EmployeeManagement.Api/*.csproj ./EmployeeManagement.Api/
COPY EmployeeManagement.Core/EmployeeManagement.Model/*.csproj ./EmployeeManagement.Model/
COPY EmployeeManagement.Core/EmployeeManagement.Provider/*.csproj ./EmployeeManagement.Provider/
COPY EmployeeManagement.Core/EmployeeManagement.Repository/*.csproj ./EmployeeManagement.Repository/
RUN dotnet restore

COPY EmployeeManagement.Core/EmployeeManagement.Api/. ./EmployeeManagement.Api/
COPY EmployeeManagement.Core/EmployeeManagement.Model/. ./EmployeeManagement.Model/
COPY EmployeeManagement.Core/EmployeeManagement.Provider/. ./EmployeeManagement.Provider/
COPY EmployeeManagement.Core/EmployeeManagement.Repository/. ./EmployeeManagement.Repository/
WORKDIR /app/EmployeeManagement.Api
RUN dotnet publish -c Release -o /app

FROM microsoft/dotnet:2.1-aspnetcore-runtime AS runtime
WORKDIR /app
COPY --from=build /app .
ENTRYPOINT ["dotnet", "EmployeeManagement.Api.dll"]

Let me explain you what this file has.

FROM microsoft/dotnet:2.1-sdk AS build 

Build the image with dotnet 2.1-sdk that is required for .NET core applications

WORKDIR /app  

Set the working directory to execute the commands like RUN, COPY, ENTRYPOINT, CMD and ADD against it which are specified in the docker file

COPY EmployeeManagement.Core/*.sln .
COPY EmployeeManagement.Core/EmployeeManagement.Api/*.csproj ./EmployeeManagement.Api/
COPY EmployeeManagement.Core/EmployeeManagement.Model/*.csproj ./EmployeeManagement.Model/
COPY EmployeeManagement.Core/EmployeeManagement.Provider/*.csproj ./EmployeeManagement.Provider/
COPY EmployeeManagement.Core/EmployeeManagement.Repository/*.csproj ./EmployeeManagement.Repository/

This will copy the files with .csproj from the source to the current working directory

RUN dotnet restore

Run the command from dotnet CLI to install all the dependencies to the container file system.

COPY EmployeeManagement.Core/EmployeeManagement.Api/. ./EmployeeManagement.Api/
COPY EmployeeManagement.Core/EmployeeManagement.Model/. ./EmployeeManagement.Model/
COPY EmployeeManagement.Core/EmployeeManagement.Provider/. ./EmployeeManagement.Provider/
COPY EmployeeManagement.Core/EmployeeManagement.Repository/. ./EmployeeManagement.Repository/
WORKDIR /app/EmployeeManagement.Api

This will copy remaining files of the project

RUN dotnet publish -c Release -o /app

This is going to build everything using Release configuration

FROM microsoft/dotnet:2.1-aspnetcore-runtime AS runtime
WORKDIR /app
COPY –from=build /app .
ENTRYPOINT [“dotnet”, “EmployeeManagement.Api.dll”]

Here we are going to use the base image aspnetcore-runtime from Microsoft, to run the compiled app, set the working directory, copy the compiled version to the working directory and then create the entry point which will instruct the docker, how to start the app using ASPNETCore runtime.

Now we need to add the docker-compose.yaml file to our root folder. Here is how the YAML file looks like.

 

 

 

version: '3.4'

services:
employeemanagement.api:
 image: employeemanagementapi
 container_name: acrnetmigration.azurecr.io
 build:
  context: .
    dockerfile: EmployeeManagement.Core/Dockerfile

Here we need to provide the path of the Dockerfile, container name which ideally will be our Azure Container Registry where the images will be stored and name of the image which will ideally be uploaded to the container

We are done with docker configuration. Oh, we forgot to add a .dockerignore file in our root folder which will ideally help to increase build performance and exclude files that are not required for the build.


.dockerignore
.env
.git
.gitignore
.vs
.vscode
docker-compose.yml
docker-compose.*.yml
*/bin
*/obj
*/.vs


Step 4: Run Docker Compose to build the image and container

In previous steps we did two major things –

Dockerfile – Created the file where we have stored definitions of our app environment

docker-compose.yaml – Created this file which define the services for the app that will run together in isolated environment

Our next step is to run the docker command that will compose, build, create images and container while starting the container and keep it running in the background. Open command prompt in Administrator mode and go to your directory where the docker-compose.yaml file is stored.

Run the docker command docker-compose up –d

image

If everything goes well, you should be able to see that the images and container has been created.

 

In order to view the images created, you can use the docker command docker images

image

 

In order to view the container created, use the docker command docker ps

image

 

Every time you run the docker-compose up –d command, it will check if there are any existing containers and if the image has been changed or modified, it will stop the container and recreate the container.


Step 5: Get the App/website running

In order to run the run the the app, we need to execute the following docker command docker run –p 8888:80 employeemanagementapi. Here “employeemanagementapi” is the latest image.

image

 

Verify that you are able to browse the app pasting the Url http://192.168.99.100:8888 in the browser window

image


Few Tips and Tricks

Based on my experience while performing this exercise, I did face few issues which I would like to share here. If you are new to docker, then this might be a good start.

Ensure that your dockerfile and docker-compose.yaml are placed in proper location. If you have a complex solution with multiple projects, place the the docker-compose.yaml file in the root folder and dockerfile in the solution folder and not the individual project folder.

Add .dockerignore file to the root folder too.

If you are using XML documentation for your API project, then ensure that you have set the XML documentation file path for both DEBUG and RELEASE. When the files are published to build the image, if Output of XML documentation file is not present, then API documentation will not work. This issue will  happen if you have missed setting up XML documentation for RELEASE mode

image

 

You might encounter various issues while running the dockerfile, which are almost similar to the below screenshots. If you are new to docker, then remember to follow the format of the dockerfile code that I have shared in this post.

image

image

image

image

 

If you want to delete all the images and containers, then use the docker command docker rmi –f <image id>. You can also use docker prune images to delete all the images or docker prune ps to delete all the containers

image

 

To view the IP address of the container, use the docker command docker inspect <image name>

image

In our next post, we are going to provision a Jenkins resource which will be used to build our CI/CD pipeline.

Next in this post, we are going to learn the process of how to publish docker images in ACR (Azure Container Registry) and deploy to Azure Container Instances (AKS), both in Manual and through Automation.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.