ASP.NET Core with Docker: Simple Guide

Facundo Tripelhorn
12 min readJul 15, 2019

--

What is ASP.NET Core?

A general explanation could be that ASP.NET Core is the open-source version of ASP.NET that, besides running on Windows, runs on Linux, macOS and Docker.

Having said that, let’s start by explaining what is .NET?

This is an extract from Microsoft Docs about .NET:

.NET is a general purpose development platform. It has several key features, such as support for multiple programming languages, asynchronous and concurrent programming models, and native interoperability, which enable a wide range of scenarios across multiple platforms.

The .NET framework can be used to develop all sorts of applications, but this applications will only work on Windows environments. This is where .NET Core comes in to solve this problem:

.NET Core is an open-source, general-purpose development platform maintained by Microsoft and the .NET community on GitHub. It’s cross-platform (supporting Windows, macOS, and Linux) and can be used to build device, cloud, and IoT applications.

Basically, like we said before .NET Core is the open-source and cross-platform version of the .NET Framework.

Since next we will be building a simple web application, let’s focus on ASP.NET which is the web part of .NET:

ASP.NET is a free web framework for building great websites and web applications using HTML, CSS, and JavaScript. You can also create Web APIs and use real-time technologies like Web Sockets.

Why should we use ASP.NET?

There are plenty of good reasons to use ASP.NET when we are developing a website or an application; high speeds, low costs, and vast language support are among the most significant benefits. ASP.NET also allows a developer to choose from many different development environments, its popularity also gives a developer tons of online resources for learning new things or troubleshooting bugs. Also, if you used C# in the past, you could have good reasons to use ASP.NET.

Docker

Before explaining what docker is, let’s talk about a problem we can encounter, so that we can then understand how to solve this problem and why using dockers is important for developers.

Picture yourself having to develop an application in which you don’t know who will use it or where it will be hosted.

There are different kinds of applications that can be run in different operating systems. Maybe you could compile the code for the most popular operating systems and solve the issues. But if you think it through, whenever you want to make changes, you will have to recompile everything again for the different platforms and the maintenance would be a huge problem.

With Docker you can solve this problem because you could put your application and its dependencies inside a box. The operating system should give you support for the box, and it doesn’t matter what it contains.

Now you only need to develop for one platform, you will rest assured that the program runs inside the box, the same box for every operating system.

So, what is Docker?

This is the Docker overview, from the official docs:

Docker is an open platform for developing, shipping, and running applications. Docker enables you to separate your applications from your infrastructure so you can deliver software quickly. With Docker, you can manage your infrastructure in the same ways you manage your applications. By taking advantage of Docker’s methodologies for shipping, testing, and deploying code quickly, you can significantly reduce the delay between writing code and running it in production.

Docker allows users to create independent and isolated environments to launch and deploy their applications. These environments are called containers (the boxes we mentioned before). Containers allow a developer to package up an application with all of the parts it needs and ship it all out as one single package.

In a way, Docker is like a virtual machine without the weight of installing a whole OS just to run your app. These containers share your OS kernel and are really lightweight because shipped things are not already running in the host computer. This gives a significant performance boost and reduces the size of the application.

Virtual Machines vs Containers

Also with Docker you solve a lot of dependencies problems because each container will work no matter what, since it already has everything necessary to run your app.

Getting started

Now that you know what .NET, .NET Core and Docker are, let’s create a simple example.

As our text editor, we will use Visual Studio Code and the .NET Core SDK to develop a simple web application that later will be deployed to a Docker container.

This article is aimed for people with previous experience developing applications that want to check out the .NET environment.

If you want to retrieve the complete code to discover it easily or to execute it, you can have it at your disposal on this GitHub repo.

What you’ll need

Installing Visual Studio Code

You’ll need to download the installer for your platform by visiting the official website.

When the download is complete, run the installer and follow the on-screen instructions. There are no special settings that require extensive knowledge.

Installing .NET Core

You just need to go to Microsoft’s website and download the latest version. Just click “Download .NET Core SDK” and run the execute file.

Installing Docker

First, if you don’t have an account you’ll need to create one and sign in.

You’ll then need to click on “Get Started with Docker Desktop” on the main page and select the download for the OS that corresponds you.

Once you’ve downloaded the executable file, open it. You will see the following screen:

Then click “OK” and it will do the installation process all by itself. Once it’s finished you will be asked to log out of your Windows session:

Once you log back in, you will be asked to enable some features for Docker. Clicking “OK” will restart your computer and enable these features. After that, Docker will be ready to use.

Creating the project

First off, you’ll need to create a new folder in a safe directory to locate your project files. Then start VS Code and once inside, open that folder and bring up the terminal by going into View — Terminal.

Next, we need to create a new MVC type project that we’ll name “FirstProject”. To do this just type in the terminal: dotnet new mvc -o FirstProject.

Now that we have created our project we should execute it. But first we’ll need to move the terminal into the directory that contains the project files by typing cd FirstProject and then execute with dotnet run.

As you can see in the picture above, the project now is running on two different ports: 5001 for the one that has HTTPS, and 5000 for the one that does not. If you try opening a browser window and accessing to https://localhost:5001 you will see the outcome of the template we used on our project.

Now to add some of our own code to this template let’s create a new Products.cshtml file under Views — Home, and we will add an item to the layout (Views — Shared — _Layout.cshtml) so we can access this new view from the main menu of the application.

This tells our program to bind this item to the Home controller and the method Products.

Next up, we need to head into the Controllers folder and open the HomeController to add the Products method so that we can access to the view we just added.

Now what we’re going to do is add some data to display. To do this, we’ll create an EntityFramework repository with an in memory database.

To create the EntityFramework repository we will have to run the following command to install it in our project:

dotnet add package Microsoft.EntityFrameworkCore.InMemory

Having done that, we will create a folder at the root of our project and we will name it Entities. Inside this folder we will create the following classes:

  • Product.cs
  • ApplicationDbContext.cs

The Product class will be accessed by ApplicationDbContext. As you can see we added an override method called OnModelCreating. In this method, what we are doing is adding some Products on the creation of the model so that we already have some data on our test database. This is not necessary if you are going to add the functionality to perform CRUD operations.

Next, we should create a ProductViewModel inside our Models folder that will be used to return data to our view:

Now that we have created the necessary things to return data to our view, let’s include the necessary to our HomeController. What we’re going to include is a constructor that will receive an ApplicationDbContext as a parameter which wil be used by the Products method to fetch data from our database and return it to the view:

If you look at the Products method, you can see that we make a call to EnsureCreated(). What we do with this call is make sure that the database is created and seeded with data.

Having done that, we now need to display what we are retrieving, so we need to edit the Product.cshtml we created at the beginning. We are going to add some HTML code with a bit of Razor syntax to do this.

Now that we have our code ready, we should run it (using dotnet run in the terminal) to see what the data on the Products option in the application menu looks like.

As you can see what we added in the OnModelCreating method is what we see here.

If you want to know more about this you could go to Microsoft Docs, or here to have a more complete tutorial that includes Authorization and Authentication configurations for you app.

Using Docker

Now that we’ve got our application ready, what we need to do is deploy it to a Docker container.

First off, we need to create a DockerFile. This will tell Docker how to configure the container. We do this by creating a new file called “DockerFile” at the root of our project and pasting the following code inside it:

Before we continue let’s analize what this commands in the DockerFile mean:

FROM: Sets the base image to be used for subsequent instructions. This must be the first instruction in a DockerFile. Here we are using dotnet:sdk as our base image.

WORKDIR: Sets the working directory for the subsequent DockerFile commands.

COPY: Copies the specified files from the WORKDIR to the specified directory.

RUN: Executes the following commands at the current WORKDIR.

ENTRYPOINT: Configures the container to be run as an executable.

Our Docker file is ready, the only thing left to do is build our image. But before doing so we will add another file called .dockerignore. It’s strongly recommended to use this file, since in it you can specify which rules to ignore and what exceptions apply from these rules for files and folders. This won’t be included in the build context and thus won’t be packed into an archive and uploaded to the Docker server.

So, let’s create the .dockerignore file at the root of our project. The syntax of this file is based on Go filepath.Match() function and includes some additions.

Here you can have the complete syntax for this file

{ term }term:'*' matches any sequence of non-Separator characters'?' matches any single non-Separator character'[' [ '^' ] { character-range } ']'character class (must be non-empty)c matches character c (c != '*', '?', '\\', '[')'\\' c matches character ccharacter-range:c matches character c (c != '\\', '-', ']')'\\' c matches character clo '-' hi matches character c for lo <= c <= hi
additions:'**' matches any number of directories (including zero)'!' lines starting with ! (exclamation mark) can be used to make exceptions to exclusions'#' lines starting with this character are ignored: use it for comments

Note: Using the ! character is quite tricky. The combination of it and patterns before and after line with the ! character can be used to create more advanced rules.

We will add a simple .dockerignore as an example but in this project it is not really necessary because it’s really simple.

Let’s continue with building the image. To do this, in your terminal, go to the root folder (where the DockerFile is) and run the following commands:

docker build -t firstprojectimage .

This command tells docker “build the image, with the friendly name “firstprojectimage” in the root directory. The friendly name must be lowercase.

In the log you will see how docker runs step by step

PS C:\Users\Facundo Tripelhorn\Desktop\Project\FirstProject> docker build -t firstprojectimage .
Sending build context to Docker daemon 7.783MB
Step 1/10 : FROM microsoft/dotnet:sdk AS build-env
sdk: Pulling from microsoft/dotnet
a4d8138d0f6b: Pull complete
dbdc36973392: Pull complete
f59d6d019dd5: Pull complete
aaef3e026258: Pull complete
f62345fbba0d: Pull complete
373065ab5faf: Pull complete
ade3808af4d7: Pull complete
Digest: sha256:b4c25c26dc73f498073fcdb4aefe167793eb3a8c79effa76df768006b5c345b8
Status: Downloaded newer image for microsoft/dotnet:sdk
---> 52c7fe591881
Step 2/10 : WORKDIR /app
---> Running in 5166b0dcfdfc
Removing intermediate container 5166b0dcfdfc
---> ee3cf1ec39b2
Step 3/10 : COPY *.csproj ./
---> 85239ce4a4cf
Step 4/10 : RUN dotnet restore
---> Running in feeca2fd0622
Restore completed in 10.28 sec for /app/FirstProject.csproj.
Removing intermediate container feeca2fd0622
---> d694dda9a173
Step 5/10 : COPY . ./
---> 052e407e1b47
Step 6/10 : RUN dotnet publish -c Release -o out
---> Running in 3f61172b9e2f
Microsoft (R) Build Engine version 16.1.76+g14b0a930a7 for .NET Core
Copyright (C) Microsoft Corporation. All rights reserved.
Restore completed in 1.63 sec for /app/FirstProject.csproj.
FirstProject -> /app/bin/Release/netcoreapp2.2/FirstProject.dll
FirstProject -> /app/bin/Release/netcoreapp2.2/FirstProject.Views.dll
FirstProject -> /app/out/
Removing intermediate container 3f61172b9e2f
---> 1a20e6c28b47
Step 7/10 : FROM microsoft/dotnet:aspnetcore-runtime
aspnetcore-runtime: Pulling from microsoft/dotnet
fc7181108d40: Pull complete
2c86df27317f: Pull complete
66dd687a6ad1: Pull complete
a7638d93f1fe: Pull complete
Digest: sha256:b18d512d00aff0937699014a9ba44234692ce424c70248bedaa5a60972d77327
Status: Downloaded newer image for microsoft/dotnet:aspnetcore-runtime
---> 318149b63beb
Step 8/10 : WORKDIR /app
---> Running in 74f96967219d
Removing intermediate container 74f96967219d
---> 3a01c17ca6e2
Step 9/10 : COPY --from=build-env /app/out .
---> 618e528e982f
Step 10/10 : ENTRYPOINT ["dotnet", "FirstProject.dll"]
---> Running in 2028bd8a63ae
Removing intermediate container 2028bd8a63ae
---> 1559648726d5
Successfully built 1559648726d5
Successfully tagged firstprojectimage:latest

Once the process is complete you can check if your image was successfully buil by typing docker images in the terminal:

Next we have to run the image and try our app. We do this by executing this command in the terminal:

docker run -d -p 8080:80 --name FirstProject firstprojectimage

This tells docker to run the image “firstprojectimage” and publish to host on port 8080.

Testing your image

Go to http://localhost:8080 and you will see that the application is up and running.

As you can see, deploying a Docker image is really easy.

Thanks for reading, if there’s any help required please comment below.

Keep learning

You can find more information in the underlined words of this article that will be very useful when trying to learn about this topics.

--

--