Build once, run everywhere with .NET Core and Docker
One of the biggest conundrum when developing software that the industry is slowing trying to overcome is how to move code that has dependencies on other code, from one machine to another, without losing pieces of it along the way.
.NET took a big step forward in the past as it provided a runtime that could be easily installed on any Windows machine and would easily provide all the dependencies needed for the code to run.
Before it, the industry was reliant on COM Components. COM Components were heavily dependent on the machine they were running on. They were creating folders, modifying registries. They did not provide an easy way to specify a version.
That was not enough and became evident that something more was needed.
Not too long ago I remember creating my first .NET Core API. Unfortunately, the production server we were using did not have the .NET Core runtime installed.
Permissions are another example that comes in my mind. Forgetting to configure them could make for a long bug to be fixed in production.
The industry is trying to take another big step forward and we are shifting to “build once, run everywhere” by providing an easy way to create images that may be run inside “lightweight virtual machines” referred to as containers.
In this article, I will create a .NET Core API from scratch and I will dockerize it without using Visual Studio.
That will mean creating a Dockerfile manually that will result in a container running a simple .NET Core API.
The API
The API that I will use is the default one created when running the following command:
dotnet new api
The builder container
The first task that Docker will need to perform will be instantiating a container with the .NET Core SDK inside and build the source code.
In order to that, the image we need will have to come with .NET Core SDK shipped into it.
This image contains the .NET Core SDK which is comprised of three parts:
.NET Core CLI
.NET Core
ASP.NET Core
Use this image for your development process (developing, building and testing applications)
The first section out Dockerfile will look like follows:
FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build
WORKDIR /source# restore nuget packages
COPY *.csproj .
RUN dotnet restore# copy source code
COPY . .# build the source code using the SDK
RUN dotnet publish -c release -o /executable --no-restore
At the end of it, nuget will have restored the package folder and our executables will be stored inside the “executable” folder.
The runtime container
The second step will be instructing Docker to take the executable, move it into the runner container and start the dotnet CLI using it.
# runs the deployable using a separate image
# that is shipped with the .NET RuntimeFROM mcr.microsoft.com/dotnet/core/aspnet:3.1
WORKDIR /app
COPY --from=build /executable .
ENTRYPOINT ["dotnet", "backend.dll"]
The image used at this stage is:
Running the container
The image defined by the Dockerfile needs to be built and then run into a container.
It can be built using the following command:
docker build . -t backend
This will generate an image that will be stored on your local machine. You may see a list of all images installed by running:
docker image ls
docker run -it -p 5000:80 backend
This will launch the container in interactive mode as if you were logged in on terminal.
The API will be running binding to port 80 of the container, listening to the IP address 0.0.0.0, and will be reachable from the local machine from the address “http://localhost:5000/weatherforecast”.
Summary
Learning Docker takes time and requires an important upfront investment.
It comes with the promise of making code easier to move across machines, being them those used by developers or those used in different environments.
A great article with a lot of use cases for containers may be found here:
https://www.airpair.com/docker/posts/8-proven-real-world-ways-to-use-docker