.NET Core and Devops Part 3: Running ASP.NET Core Services in Docker

Welcome to the third in a series of articles focused on .NET Core from a devops perspective where I hope to demonstrate how we can practically start developing and deploying .NET Core applications on Linux.

In this article I will walk through how we can use Docker to containerise our ASP.NET Core services. This process will form the basis of our final end to end build and deployment process.

Part 1: Running our first .NET Core application on Linux

Part 2: Running our first ASP.NET Core service on Linux

Part 3: Running ASP.NET Core Services on Linux in Docker (This Article)

Part 4: Setting up a CI build and deploy pipeline for our ASP.NET Core Service using Docker

If you have experience of using Docker on Linux then great. If not then just follow along, you will start to see the possibilities that Docker can bring to your build and release process. I am going to be using the same Downcount ASP.NET Core Web API that I introduced in Part 2.

I will demonstrate two approaches.

  1. Creating an ASP.NET Core Docker Image from Scratch – I will show how to containerize our application from scratch. This is useful if you are completely new to Docker
  2. Building an ASP.NET Core Docker Image using a Dockerfile – I will show how to create docker images using Dockerfiles.

The first step is to install docker on your Linux machine following these instructions (I’m using Ubuntu). From now on I will refer to this machine (the machine on which Docker is installed) as the host.

Be sure that docker is installed and working correctly by running

$ sudo docker run hello-world

Creating an ASP.NET Core Docker Image from Scratch

Pull down the latest Docker Ubuntu image.

$ sudo docker pull ubuntu

Run a new container from this image and launch the bash console in the container

$ sudo docker run --name testubuntu -it ubuntu /bin/bash

Now you should see the command prompt running from within a basic Ubuntu container. Its that easy, you are now using Docker!

The aim now is to repeat all the steps we did in Part 2 to configure ASP.NET Core but do them in this container. From within this container,  install .NET Core

apt-get install apt-transport-https
sh -c 'echo "deb [arch=amd64] https://apt-mo.trafficmanager.net/repos/dotnet-release/ xenial main" > /etc/apt/sources.list.d/dotnetdev.list'
apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 417A0893
apt-get update
apt-get install dotnet-dev-1.0.0-preview2-003131

Install GIT and get the latest the code base

apt-get install git-all
mkdir Downcount
cd Downcount
git clone https://github.com/shiningdragon/Downcount.git
cd Downcount/DotNetCore/Downcount.Service

Build and publish the application

dotnet restore
dotnet build
dotnet publish -o /var/downcount

Install Nginx

apt-get install nginx
apt-get install vim
service nginx start

Repeat the steps from part 2 to configure Nginx and create the startup file needed to launch the dotnet process (there is no need to create the systemd stuff in the container, it wont be needed). Once that is done you can exit from the container back into the host by running

$ exit

Create an Image from the Container

Now we will commit this container as a reusable image

$ sudo docker commit testubuntu downcount_from_scratch_img

Once this completes we can run

$ sudo docker images

to verify the image was created successfully.

Run your Image

Now we are in a position to run our new image and host our ASP.NET Core service. We will use port 80 on the host so please make sure no other process is using port 80 on the host. For example, if Apache of Nginx is installed on the host, make sure these services are stopped.

$ sudo docker run --name downcount_from_scratch_cont1 -it -p 80:80 downcount_from_scratch_img /bin/bash

This command will run a new container created from our image. It will map port 80 in the container to port 80 on the host and also run the container in interactive mode presenting you with the bash prompt.

From within this prompt we can run the startup file that you should have created in the container (as in Part 2)

/var/downcount/startup

And now browsing to http://localhost:80/swagger/ui on the host, you should see the Downcount service up and running from within the container.

Alternatively, instead of running the container in interactive mode you could run it in  background mode using

$ sudo docker run --name downcount_from_scratch_cont2 -d -p 80:80 downcount_from_scratch_img /var/downcount/startup

This has the same effect, our service is still running in the container, but the container runs in the background and not interactively.

Building an ASP.NET Core Docker Image using a Dockerfile

The above approach is not how we would practically create a Docker Image for our service in the real world. In the real world we would use a Dockerfile. Dockerfiles are scripts containing commands declared successively which are to be executed in that order by docker to automatically create a new docker image.

It is these Dockerfiles that we would run during our build process, this will give us a Docker image for each code change that we can push to our test and production environments. We will cover the core Dockerfile commands but you can read more detail in the official Dockerfile documentation.

First create a working directory, dockerdowncount, and create a file named Dockerfile in there

sudo mkdir dockerdowncount
cd dockerdowncount
vim Dockerfile

Breaking down the Dockerfile

Here is the contents of my Dockerfile, lets go through it line by line

The lines

# Set the base image to Ubuntu
FROM microsoft/aspnetcore:lts

# File Author / Maintainer
MAINTAINER sd software

tell the docker to build this image using the official Microsoft/aspcore image as the base image. As you can see from the Docker Hub documentation this is the official image for running ASP.NET Core applications and contains all the .NET Core prerequisites we need. I’m using version 1.0.1 (lts) but please replace the tag to use whichever version you are targeting.

Next we install Vim and Nginx

# Update the repository
RUN apt-get update

# Install VIM (useful when debugging issues)
RUN apt-get install -y vim

# Download and Install Nginx
RUN apt-get install -y nginx

# Edit Nginx configuration
COPY default /etc/nginx/sites-available/default

The final COPY command configures Nginx just like we did in the previous article. It does this by copying the file named default from the current directory on the host to the /etc/nginx/sites-available/ in the image. To support this make sure your  dockerdowncount folder contains a file named default with contents as follows

server {
    listen 80;
    location / {
        proxy_pass http://localhost:5000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection keep-alive;
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
}

The next copy command copies the precompiled Downcount assemblies from the host machine into the a folder on the image. You can see how we could set this up to run on a build machine and copy in our newly built application dlls.

# Copy downcount assemblies and files
RUN mkdir /var/downcount
COPY downcount /var/downcount

In order for this to work please ensure all your application assemblies and files are present in a folder named downcount in your working directory.

You also need to make sure you create a file named startup in this directory on the host. This is the same startup file we used in Part 2 when manually running our ASP.NET Core service. This the script that Docker will run when starting our container. The contents should be as follows

#!/bin/bash
service nginx start
dotnet /var/downcount/DownCount.Service.dll

The next Dockefile step makes sure the startup file has execute permissions.

# Make startup file excutable
RUN chmod 755 /var/downcount/startup

Finally, we instruct dotnet core to host the application on port 5000, ensure port 80 is exposed to the host and set the default command to run when this image starts up.

# SET base url
ENV ASPNETCORE_URLS http://+:5000

# Expose ports
EXPOSE 80

# Set the default command to execute when creating a new container
CMD ["/var/downcount/startup"]

Building our ASP.NET Core Docker Image

Finally we are in a position to build our image.

$ sudo docker build -t downcount_img .

This will execute the Dockefile and create a new image for us. The base image (microsoft/aspcore) will be pulled from Docker Hub if it isn’t already present and all steps in the Dockerfile will be applied against it. Once the build command has completed, running

$ sudo docker images

should show our new image ready for use.

docker2

Running our ASP.NET Core Docker Image

To run this image execute

$ sudo docker run --name downcount_cont -d -p 80:80 downcount_img

What docker will do here is create a new container from the image and map port 80 on the container to port 80 on the host. We already set the image to run the startup file on starting so we don’t have to specify any command to run. Running

$ sudo docker ps

You should see the container running and ports mapped.

docker1

If you browse to http://localhost:80/swagger/ui on the host you will see the Downcount service up and running and hosted inside a Docker container you built with Docker build!

N.B. Once again, be sure no other process is running on the Host that is using port 80. (e.g. stop any existing Nginx or Apache web server)

Advertisements

One thought on “.NET Core and Devops Part 3: Running ASP.NET Core Services in Docker”

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 )

Twitter picture

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

Facebook photo

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

Google+ photo

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

Connecting to %s