Running an ASP.NET Core API Project in a Docker Container
Web applications nowadays are commonly making use of the cloud native computing architectural style to deliver highly scalable services. One element in cloud native computing is the use of containers.
Containers allow applications to be deployed in a packaged and isolated manner where all of the dependencies for the application reside only within the container. Currently, Docker is the most popular containerization technology which offers standardized, lightweight and secure containers.
The HomeBrew Project
We are going to work with a simple project to demonstrate the use of Docker for containerization.
HomeBrew is a mini ReST
API for an imaginary beer website made with ASP.NET Core. Its repository is available on Github. It has two main directories: HomeBrewed
, the ASP.NET Core API project, and HomeBrewed.Tests
, the XUnit project which contains the HomeBrewed
test cases.
We’ll just focus on the HomeBrewed
directory for this demonstration. We are going to run the project in a Docker container and we will make use of the VSCode Docker extension to easily set this up.
Using VS Code Extension
Open the HomeBrewed/HomeBrewed
directory in Visual Studio Code. If you do not have it yet, install the Docker extension on VS Code.
Open the command pallete (F1
) and select the Docker: Add Docker Files to Workspace
.
Select .NET: ASP.NET Core
for the application platform, Linux
for the operating system, and 5000
for the port where our app will listen to.
A Dockerfile will be generated in the project directory with the following contents:
FROM mcr.microsoft.com/dotnet/aspnet:3.1 AS base
WORKDIR /app
EXPOSE 5000
FROM mcr.microsoft.com/dotnet/sdk:3.1 AS build
WORKDIR /src
COPY ["HomeBrewed.csproj", "./"]
RUN dotnet restore "HomeBrewed.csproj"
COPY . .
WORKDIR "/src/."
RUN dotnet build "HomeBrewed.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "HomeBrewed.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "HomeBrewed.dll"]
Multi-Stage Builds
The Dockerfile above makes use of multi-stage builds. Instead of using just one base image, we use different images to support the build pipeline our codebase has to go through.
This specific Dockerfile goes through (1) setting up the base image, (2) building the project, (3) publishing the project and (4) running the published project in the final container.
Setting up the Base Image
FROM mcr.microsoft.com/dotnet/aspnet:3.1 AS base
WORKDIR /app
EXPOSE 5000
The first 3 lines (the first stage) in the Dockerfile basically prepares the base image we will be using later on for the container where our project will run. It uses a lightweight image with an ASP.NET 3.1 runtime.
FROM mcr.microsoft.com/dotnet/aspnet:3.1 AS base
It also exposes port 5000 to our host machine where the HomeBrew API will be served.
EXPOSE 5000
Building the Project
FROM mcr.microsoft.com/dotnet/sdk:3.1 AS build
WORKDIR /src
COPY ["HomeBrewed.csproj", "./"]
RUN dotnet restore "HomeBrewed.csproj"
COPY . .
WORKDIR "/src/."
RUN dotnet build "HomeBrewed.csproj" -c Release -o /app/build
The second stage is where the HomeBrewed project is built. Here we use an image with the Dotnet 3.1 SDK. This image is quite bloated, hence why we use multi-stage to keep this phase separate from the image where we’ll actually run the project.
FROM mcr.microsoft.com/dotnet/sdk:3.1 AS build
We use /src
as our working directory and copy the HomeBrewed.csproj
in there. This is the only thing copied because this is the minimal requirement to be able to restore the project. This helps optimize our docker image since Docker will make use of its cache if no changes are made to the HomeBrewed.csproj
file.
WORKDIR /src
COPY ["HomeBrewed.csproj", "./"]
RUN dotnet restore "HomeBrewed.csproj"
After restoring the project, we copy the entire project source code into the /src/.
working directory and create the HomeBrew project’s release build.
COPY . .
WORKDIR "/src/."
RUN dotnet build "HomeBrewed.csproj" -c Release -o /
Publishing the Project
FROM build AS publish
RUN dotnet publish "HomeBrewed.csproj" -c Release -o /app/publish
The third stage published the project. This reuses the image from the second stage which already contains the ASP.NET SDK and project files needed for publishing.
Running the Project
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "HomeBrewed.dll"]
Lastly, we use the image from the first stage as the image that will run our project.
FROM base AS final
WORKDIR /app
We then copy the published files generated from the previous stage.
COPY --from=publish /app/publish .
Finally, we execute the dotnet
command to get the project running.
ENTRYPOINT ["dotnet", "HomeBrewed.dll"]
Host Configuration
We do have to add one more thing to the Dockerfile before running it. We add an environment variable in the first stage as part of host configuration.
FROM mcr.microsoft.com/dotnet/aspnet:3.1 AS base
WORKDIR /app
EXPOSE 5000
ENV ASPNETCORE_URLS=http://*:5000
Our ASP.NET Core API project needs this environment variable to determine the URL where our project will be served. In this case, it is the localhost in the container on port 5000.
Build and Run the Container
The Dockerfile is now setup. It’s time to get it built and running
Build the image with the following command:
docker build -t homebrew .
-t
sets the tag of the image to homebrew
. The .
means we’re using the Dockerfile
file in this directory.
The homebrew
image can now be run with:
docker run -dp 5000:5000 homebrew
-dp
is shorthand for -d
and -p
. -d
runs the container as detached or in the background. p
sets the port that would be exposed in our host machine which is 5000
.
Executing that last command should have our project up and running. Open up a browser and go to localhost:5000
and see the HomeBrew project in action.
Conclusion
Docker is great tool for containerization and offers an easy way for setting up an isolated environment. Applications are more distributable and are quicker to set up with the use of docker containers.
Learn more about docker at https://docs.docker.com/get-started.