Skip to content

Instantly share code, notes, and snippets.

@adrianord
Last active September 14, 2021 23:53
Show Gist options
  • Save adrianord/5c51bd0f087cca2818ac15b076a72727 to your computer and use it in GitHub Desktop.
Save adrianord/5c51bd0f087cca2818ac15b076a72727 to your computer and use it in GitHub Desktop.
Generic DotNet Dockerfile using BuildKit `RUN --mount` directive to keep csproj structure.
# This is an example of what you might have in your .dockerignore. Some items can be omitted like `!/test/**` or added
# like `**/node_modules` if you have a node/frontend project.
*
!/*.sln
!/nuget.config
!/src/**
**/bin
**/obj
#syntax=docker/dockerfile:1.2
#######################################
## This Dockerfile requires BuildKit ##
#######################################
## Build Stage
FROM mcr.microsoft.com/dotnet/sdk:5.0 as build
## Build stage arguments
ARG CONFIG_PROFILE=Release
ARG PROJECT_DIR
ARG PROJECT_NAME
ENV PROJECT=${PROJECT_DIR}/${PROJECT_NAME}.csproj
WORKDIR /app
COPY nuget.config* ./
COPY *.sln ./
## Copy .csproj files into the correct file structure
SHELL ["/bin/bash", "-O", "globstar", "-c"]
RUN --mount=target=docker_build_context \
cd docker_build_context;\
cp **/*.csproj ../ --parents;
RUN rm -rf docker_build_context
SHELL ["/bin/sh", "-c"]
## Restore project
RUN dotnet restore ${PROJECT}
## Copy all files if restore succeeds
COPY . ./
## Publish project without restoring
RUN dotnet publish --no-restore -c ${CONFIG_PROFILE} -o out ${PROJECT}
## New stage used to reduce the size of the final image ---
FROM mcr.microsoft.com/dotnet/aspnet:5.0
## Final stage arguments
ARG PROJECT_NAME
WORKDIR /app
COPY --from=build /app/out .
ENV ASPNETCORE_URLS=http://+:80
## Create a symlink so we can use exec form entrypoint
RUN ln -s ${PROJECT_NAME}.dll Entrypoint.dll
ENTRYPOINT [ "dotnet", "Entrypoint.dll" ]
@adrianord
Copy link
Author

After attempting to use the old version of this Dockerfile (the non-buildkit version) in production, I hit some issues. The regex for the .sln file wasn't working great and I had a misunderstanding about how Docker handles "globstar", in that it does not at all.

#This does not do a glob star, it is the equivalent to COPY */*/*.csproj
COPY */**/*.csproj

This caused an issue when the projects were not a flat structure on the filesystem.

So I decided to go down the rabbit hole of BuildKit and the new RUN --mount directive and produced a much more reliable Dockerfile. Along the way I also discovered you could symlink the assembly that needed to run so that you can always use EXEC form which allows the dotnet process to be PID 1 in the container. This helps with shutdown times and signal processing since SHELL form work eat the signals and not send it to the dotnet process.

It still takes advantage of all that layer caching goodness as well.

@nickcox
Copy link

nickcox commented May 17, 2021

It blew my mind that Docker build doesn't support globstar. I ended up using a combination of find and cpio.

find . -name *.csproj | cpio -pdm [wherever]

Do you think there's any advantage to the BuildKit + mount approach?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment