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.
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.
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.
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.
Before you start Docker Quickstart, ensure that Virtualization is enabled at BIOS and don’t forget to uncheck Windows hypervisor program from Windows features.
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.
After all these checks are done, restart the machine and then run Docker Quickstart.
Once you get the interactive shell of docker, run hello-world to see if docker commands are working.
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.
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
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/
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
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
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
In order to view the container created, use the docker command docker ps
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.
Verify that you are able to browse the app pasting the Url http://192.168.99.100:8888 in the browser window
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
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.
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
To view the IP address of the container, use the docker command docker inspect <image name>
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.