In my previous post, I talked about my learnings of getting started with Docker in Visual studio and a brief intro into the Docker ecosystem.

Visual Studio can get you up and running very fast with Docker, without having to create Docker files and using the Docker command lines. However, I am going to try and explain what a Docker file is, Docker build, Docker run and also Docker Compose and how it all comes together.

Tl;dr:

  • Docker files describe how to build a Docker image.
  • The Docker build command builds images based on Docker files
  • The Docker run command starts a Docker image
  • Docker compose files combine docker building and running in a single file.

So…What is a Docker file?

A Docker file is in the form of a YAML file that contains a set of instructions that describes what the application needs in order to build it. If you are new to YAML, here’s a nice intro.

Taking the sample docker file from this sample which was generated by Visual Studio, it can be broken into 4 stages.

Let’s try and break it down.

Stage 1: Specify the runtime

FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base
WORKDIR /app
EXPOSE 80

The first line uses the FROM syntax to define what base docker image to use. In this case, it specifies the dotnet 2.1 runtime.

The next line uses the WORKDIR command which sets the working directory.

The final line of the first step uses the EXPOSE feature which exposes port 80.

Stage 2: Building

The next stage is more involved.

FROM microsoft/dotnet:2.1-sdk AS build
WORKDIR /src
COPY DockerAspNetCoreDemo/DockerAspNetCoreDemo.csproj DockerAspNetCoreDemo/
RUN dotnet restore DockerAspNetCoreDemo/DockerAspNetCoreDemo.csproj
COPY . .
WORKDIR /src/DockerAspNetCoreDemo
RUN dotnet build DockerAspNetCoreDemo.csproj -c Release -o /app

Similar to the first stage, it again uses the FROM syntax to define an image. Notice this time it specifies the dotnet 2.1 sdk rather than the runtime. This is so that it can build the app.

The next line sets the working directory to src. It then uses the copy syntax to copy the csproj file into a new folder within docker called DockerAspNetCoreDemo.

It runs dotnet restore using the copied proj file. It then copies everything and from the current source folder to the docker folder of src. Finally, it runs dotnet build with the release parameter and outputs the files into the app directory which was created in stage 1.

Stage 3: Publishing

The penultimate step below executes the dotnet publish command and has a parameter that specifies the output to the app folder.

FROM build AS publish
RUN dotnet publish DockerAspNetCoreDemo.csproj -c Release -o /app

Stage 4: Setting the entry point

Finally, these steps instruct Docker where the entry point of how to start the application. I suppose it’s equivalent to the dotnet-run command.

FROM base AS final
WORKDIR /app
COPY --from=publish /app .
ENTRYPOINT ["dotnet", "DockerAspNetCoreDemo.dll"]

Docker build

Now that we have a Docker file, one can run the docker build command to build a docker image.

Now, if navigate to the root directory of the demo application and run:

docker build DockerAspNetCoreDemo -f DockerAspNetCoreDemo/Dockerfile -t aspnetdemo

You may get an error like this:

C:\Users\chlee\Documents\Github\DockerAspNetCoreDemo>docker build DockerAspNetCoreDemo -f DockerAspNetCoreDemo/Dockerfile -t aspnetdemo
Sending build context to Docker daemon  4.209MB
Step 1/16 : FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base
 ---> f8297fe48f0c
Step 2/16 : WORKDIR /app
 ---> Using cache
 ---> c16c3d21ce14
Step 3/16 : EXPOSE 80
 ---> Using cache
 ---> 6fd93b472bcc
Step 4/16 : FROM microsoft/dotnet:2.1-sdk AS build
 ---> f4bc69f831aa
Step 5/16 : WORKDIR /src
 ---> Using cache
 ---> 782546a1fbc5
Step 6/16 : COPY DockerAspNetCoreDemo/DockerAspNetCoreDemo.csproj DockerAspNetCoreDemo/
COPY failed: GetFileAttributesEx \\?\C:\WINDOWS\TEMP\docker-builder185318480\DockerAspNetCoreDemo\DockerAspNetCoreDemo.csproj: The system cannot find the path specified.

Not great. Why? Well after some research and discovering this git issue comment it turns out that its the way Visual Studio generates the docker file. However, running this in Visual Studio will have no issues.

A Simpler Docker file

With the above docker file specific to Visual Studio, below is a simpler version. This was taken and modified from the docker site

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

# Copy csproj and restore as distinct layers
COPY *.csproj ./
RUN dotnet restore

# Copy everything else and build
COPY . ./
RUN dotnet publish -c Release -o out

# Build runtime image
FROM microsoft/dotnet:2.1-aspnetcore-runtime
WORKDIR /app
COPY --from=build-env /app/out .
ENTRYPOINT ["dotnet", "DockerAspNetCoreDemo.dll"]

Now if you run:

docker build DockerAspNetCoreDemo -f DockerAspNetCoreDemo/Dockerfile-alternative -t aspnetdemo
  • -f is the filename of the new simpler docker file.
  • -t specifies a tag for the image, in our case its aspnetdemo.

Then the output will be:

C:\Users\chlee\Documents\Github\DockerAspNetCoreDemo>docker build DockerAspNetCoreDemo -f DockerAspNetCoreDemo/Dockerfile-alternative -t aspnetdemo
Sending build context to Docker daemon  4.209MB
Step 1/11 : FROM microsoft/dotnet:2.1-sdk AS build-env
 ---> f4bc69f831aa
Step 2/11 : WORKDIR /app
 ---> Using cache
 ---> 9025461ed217
Step 3/11 : EXPOSE 80
 ---> Using cache
 ---> 0e3c072cc161
Step 4/11 : COPY *.csproj ./
 ---> Using cache
 ---> 3eadd1126142
Step 5/11 : RUN dotnet restore
 ---> Using cache
 ---> 481f505ae094
Step 6/11 : COPY . ./
 ---> Using cache
 ---> 570fb6690fb4
Step 7/11 : RUN dotnet publish -c Release -o out
 ---> Using cache
 ---> 382bad5ea05e
Step 8/11 : FROM microsoft/dotnet:2.1-aspnetcore-runtime
 ---> f8297fe48f0c
Step 9/11 : WORKDIR /app
 ---> Using cache
 ---> c16c3d21ce14
Step 10/11 : COPY --from=build-env /app/out .
 ---> Using cache
 ---> 07c15508103a
Step 11/11 : ENTRYPOINT dotnet DockerAspNetCoreDemo.dll
 ---> Using cache
 ---> 0eeff78b4f71
Successfully built 0eeff78b4f71
Successfully tagged aspnetdemo:latest

This should have created a docker image, and to confirm this, you can run docker images to list all the images.

You should get something like this:

REPOSITORY          TAG                      IMAGE ID            CREATED             SIZE
aspnetdemo          latest                   0eeff78b4f71        18 minutes ago      506MB
microsoft/dotnet    2.1-aspnetcore-runtime   f8297fe48f0c        45 hours ago        503MB

Docker run

Now that we have an image, we can run the docker run command to start the image and load it into a container.

docker run -d -p 8080:80 --name aspnetapp aspnetdemo

The docker run parameters used can be described below:

  • -d specifies it’s in detach mode, which means it will run in the background.
  • -p specifies the external port 8080 and the internal port of the image which is 80
  • --name specifies the name of the container
  • last parameter, aspnetdemo is the image name that we want to run.

Run the command docker ps to verify it is running. You should see something like this:

CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                  NAMES
5a04e20efb39        aspnetdemo          "dotnet DockerAspN..."   38 seconds ago      Up 34 seconds       0.0.0.0:8080->80/tcp   aspnetapp

Finally, if you load in a browser https://localhost:8080 you should see something like this:

docker-aspnetdemo-localhost

What is Docker Compose?

Docker compose is another command where it can be used in conjunction with Docker files. A Docker compose file will tend to have instructions on how to start the application. In general, it will just refer to the Docker file or an existing docker image and also include any app-specific parameters. For instance, in a web app, we can specify ports and any environment variables.

Here is an example of a docker-compose file:

version: '3.4'

services:
  dockeraspnetcoredemo:
    build:
      context: .
      dockerfile: DockerAspNetCoreDemo/Dockerfile
    ports:
        - "8080:80"

Notice that it specifies the docker file DockerAspNetCoreDemo/Dockerfile. If we wanted to specify an image, the compose file will look something like this:

version: '3.4'

services:
  dockeraspnetcoredemo:
    image: aspnetdemo
    ports:
        - "8080:80"

Now, if we run:

docker-compose -f docker-compose.yml up -d
  • -f specifies the filename
  • up is the command to “start” the image
  • -d is the detach mode which runs it in the background

You should see the demo site again if you navigate to https://localhost:8080

Summary

I talked about what is a Docker file is, how to build a docker image from a docker file. Then I walked through how to start a docker image either via the docker run command or via a docker compose file.

I hope this has been helpful and some insight into the Docker world.

You can grab the code from https://github.com/ch-lee/DockerAspNetCoreDemo