How to write a multi-purpose Dockerfile for .Net Core Projects?
Let’s say you have an application created by .Net Core 3.1 and you wish to create a Dockerfile for it.
I know there are many Dockerfiles ready on the internet that you can choose from, but I am going to explain how to create a suitable one for development and a more efficient one for production.
Here, I am going to declare three stages for this purpose
- Developer stage
- Builder stage
- Final stage
And the full Dockerfile content is something like the following
ARG REPO=mcr.microsoft.com/dotnet/core/sdk
FROM $REPO:3.1-bionic AS developer# Prepare Dotnet Entity Framework
RUN dotnet tool install --global dotnet-ef
ENV PATH="${PATH}:/root/.dotnet/tools"# # #
# Placeholder for any dev tools
# # ## Copy csproj and restore as distinct layers
COPY ./src/Myapplication.Web.Host/Myapplication.Web.Host.csproj \
./host/Myapplication.Web.Host/
COPY ./src/Myapplication.Web.Core/Myapplication.Web.Core.csproj \
./host/Myapplication.Web.Core/
COPY ./src/Myapplication.Core/Myapplication.Core.csproj \
./host/Myapplication.Core/
COPY ./src/Myapplication.Application/*.csproj \
./host/Myapplication.Application/
COPY ./src/Myapplication.EntityFrameworkCore/*.csproj \
./host/Myapplication.EntityFrameworkCore/
RUN dotnet restore \
./host/Myapplication.Web.Host/Myapplication.Web.Host.csprojWORKDIR /src
COPY ./runapp.sh ./runapp.sh
RUN chmod +x ./runapp.shFROM developer AS builderARG BUILDCONFIG=RELEASE
ARG VERSION=1.0.0RUN export ASPNETCORE_ENVIRONMENT=Staging# copy everything else and build
COPY ./src/ ./host/
RUN dotnet publish \
host/Myapplication.Web.Host/Myapplication.Web.Host.csproj \
--runtime linux-musl-x64 -c $BUILDCONFIG -o /out \
/p:Version=$VERSION -p:PublishTrimmed=trueRUN dotnet publish \
host/Myapplication.Migrator/Myapplication.Migrator.csproj \
--runtime linux-musl-x64 -c $BUILDCONFIG -o /out/Migrator \
/p:Version=$VERSION -p:PublishTrimmed=true# Final runtime image
FROM mcr.microsoft.com/dotnet/core/runtime-deps:3.1-alpine
RUN apk add icu-libs
WORKDIR /app
COPY --from=builder /out ./ENV ASPNETCORE_URLS=http://0.0.0.0:80
ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=falseENTRYPOINT ["./Myapplication.Web.Host"]
Developer stage
Myapplication is a .Net Core solution with multiple projects inside. In the first stage named developer
I am using Ubuntu bionic based provided .Net Core SDK.
This is important to use Ubuntu here if you wish to have a remote connection to the container and you could be able to debug via VSCode. Somehow, I can’t manage to install OmniSharp properly on the Alpine image. Although Ubuntu's image seems to be heavy, in action there are not so many differences for a developer.
In developer
stage, dotnet tool install
helps to install the Entity framework (dotnet-ef
).
Next, will just copy project files to run a restore command on dotnet
, so, you will have all the required NuGet packages installed on the image.
Need to mention here, I have provided a shell file runapp.sh
to automated some required commands which is a command to migrate the database by Entity Framework, and a command to execute dotnet run
command.
Builder stage
Basically, the builder stage is a replacement for build pipelines for me here. I mean it’s an easy way to implement a build procedure to validate build and finally publish a prod version of the application.
Here, the build stage is starting from the developer stage. Setting the environment variables, and running the publish command by dotnet
. Because I have two applications to be published I have two dotnet publish
commands here. My prod environment identification is Staging
which is set by environment variable ASPNETCORE_ENVIRONMENT and it takes appsettings.Staging.json
as the main configuration file while running.
Final stage
Finally, it’s time to create a production version of the application image.
By taking the Alpine runtime version of .Net Core, we can manage to create a very efficient version of our .Net Core application which uses less memory and space than Debian images.
Remember, keep ASPNETCORE_URLS
as 0.0.0.0
to accept all incoming IP’s. And, the image is ready to be published wherever on production Docker host or Kubernetes.
Other resources:
And later I will discuss the following topics as well
- Docker-compose Development Environment
- Using Azure DevOps Deploy to GKE (Google Kubernetes Engine)