8

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?

Swamy
  • 343
  • 2
  • 12
Omer Levi Hevroni
  • 1,935
  • 1
  • 15
  • 33

2 Answers2

3

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://learn.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.

Miq
  • 3,931
  • 2
  • 18
  • 32
  • Changing the order (`dotnet add` and than `dotnet restore`) didn't helped. It might be related to the COPY instruction, but this is something I'm not sure how to fix :( – Omer Levi Hevroni Jun 19 '18 at 09:57
  • can you just skip the `dotnet add package` in the build phase and simply place it in `csproj` file? Why do you need it in building script? – Miq Jun 19 '18 at 10:37
  • It's there because I'm creating a self-contained app (well, almost self-contained :)) this is based on Microsoft official sample for building self-contained app, so I guess it is important... – Omer Levi Hevroni Jun 19 '18 at 16:53
  • All your dockerfile is correct. I have same structure and it works flawlessly. Only difference is this `dotnet add package` call. AFAIK this command is to be used during development, not build. It modifies csproj file. I think you should remove it from dockerfile, simply call it on your pc and commit changes made by it. – Miq Jun 19 '18 at 17:31
  • oh yeah, you're probably right. The COPY right after the add re-copy the csporj and this might trigger that. I'll check and update – Omer Levi Hevroni Jun 19 '18 at 18:32
  • Ok, I did the change - now there is only restore - but still same behavior. Changing one file cause `dotnet publish` to do full restore. Guess the `COPY` directive does some other changes... – Omer Levi Hevroni Jun 20 '18 at 09:41
  • One more thing to try - just spotted it - you're doing publish with specified runtime `-r linux-x64` while the restore is done for default runtime. I would suggest removing the -r parameter from publish or adding it to `restore` command. – Miq Jun 20 '18 at 09:44
  • Nope, add the `-r` to the restore and it didn't solve it. The `-r` is required because of the self-host magic – Omer Levi Hevroni Jun 20 '18 at 10:00
  • Huh. You could change it from self-hosted to standard and see it this helps. This is the only difference I can see from the standard template (and the one I'm using) and yours. Does it download all dependencies or only some of them? You can also try running container from cached image after COPY step and see if the packages folder is present and what is missing. Also odd thing is that Installing is **after** restore completed message. Maybe the installing thing is related not to the restore packages but to the self-hosting? – Miq Jun 20 '18 at 10:06
  • yeah, that probably what it might be. I think we can close this and say that it's hard to have both self contained and docker cache efficient :) – Omer Levi Hevroni Jun 20 '18 at 12:26
  • In general, I think using self-contained app with docker is not good. First, since it's a self-contained, you would not need the dotnet image, a simple ubuntu should be enough. Second - the last layer with your app is way bigger than if it were a FDD deployment. I really think you should really consider using the 'standard' approach with framework dependent app. – Miq Jun 20 '18 at 17:55
  • Self-contained is not really self-contained, there are still some required dependencies. I am using it because I was under the impression this is how you create smaller docker images. I'll check what you're saying about the last layer. – Omer Levi Hevroni Jun 21 '18 at 03:34
3

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.

spikyjt
  • 2,210
  • 21
  • 16