Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I cache C# dotnet core package in Dockerfile properly?

I'm trying to build efficient dockerfile, so package restore will be triggered only when package removed/added/updated. This is what I tried (based on the official sample):

FROM microsoft/dotnet:2.0-sdk-stretch AS build-env
WORKDIR /app

# Copy csproj and restore as distinct layers
COPY ./src/myapp.csproj ./
RUN dotnet restore && \
    dotnet add package ILLink.Tasks -v 0.1.4-preview-981901 -s https://dotnet.myget.org/F/dotnet-core/api/v3/index.json

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

# Build runtime image
FROM microsoft/dotnet:2.0-runtime-deps
RUN useradd -d /home/dotnet -ms /bin/bash dotnet
USER dotnet
WORKDIR /home/dotnet/app
ENV ASPNETCORE_URLS=http://+:9999
COPY --from=build-env /app/out ./
ENTRYPOINT ["./myapp"]

I'm copying the csprog, running dotnet restore, and then copy the rest of files, and build. This should have the expected behavior - restoring packages only when needed. But this is not what happened - from some reason (couldn't find anything about it on the documentation) dotnet publish trigger restore, although the packages are already cached:

Sending build context to Docker daemon  131.3MB
Step 1/13 : FROM microsoft/dotnet:2.0-sdk-stretch AS build-env
 ---> 17fc4fa98e0b
Step 2/13 : WORKDIR /app
 ---> Using cache
 ---> 9b13d975844b
Step 3/13 : COPY ./src/myapp.csproj ./
 ---> Using cache
 ---> fed39192abce
Step 4/13 : RUN dotnet restore &&     dotnet add package ILLink.Tasks -v 0.1.4-preview-981901 -s https://dotnet.myget.org/F/dotnet-core/api/v3/index.json
 ---> Using cache
 ---> efcdaf201661
Step 5/13 : COPY ./src ./
 ---> a50fd8fa6106
Removing intermediate container 9eadb5543dbe
Step 6/13 : RUN dotnet publish -c Release -o out -r linux-x64
 ---> Running in 3bf17790a376
Microsoft (R) Build Engine version 15.7.177.53362 for .NET Core
Copyright (C) Microsoft Corporation. All rights reserved.

  Restoring packages for /app/myapp.csproj...
  Restore completed in 1.4 sec for /app/Hamuste.csproj.
  Installing <redacted>
  Installing <redacted>
  Installing <redacted>

What am I missing? How can I improve this docker file?

like image 488
Omer Levi Hevroni Avatar asked Jun 19 '18 05:06

Omer Levi Hevroni


2 Answers

Add --no-restore to your dotnet publish command.

If you are building a multi-project solution with more than one published project and shared local dependencies, additionally add a build step: RUN dotnet build -c Release --no-restore PROJECT_NAME, then publish in a separate command RUN dotnet publish -c Release -o out --no-restore --no-build PROJECT_NAME. This way the build layer can be reused and cached to publish subsequent projects.

like image 38
spikyjt Avatar answered Oct 20 '22 18:10

spikyjt


In general it looks correct. dotnet publish (or dotnet build as well) will trigger package restore - this is something you cannot omit. But it should just make sure that everything is in place and not install anything.

However I suspect, that the dotnet add package is causing the issue. I never used that command, but from https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-add-package I understand, it just adds the package to dependencies and then you should run dotnet restore. Doc says, that you don't have to, because new build run (and obviously publish) will run restore anyway.

My suggestion is that you revert the Step 4/13 command. First, do dotnet add package and then execute dotnet restore. or add another dotnet restore afterwards. See if this helps.

If it not helps, try to trace which packages are installed during publish. Perhaps the Step 5/13 (COPY . .) overwrites the entry that dotnet add package created and restore in publish is simply bringing back things that add package removed.

like image 92
Miq Avatar answered Oct 20 '22 18:10

Miq