Search code examples
c#docker.net-coredockerfilekaniko

How can I reduce the build time for a .NET Core application using Docker and Kaniko?


I have following Dockerfile in my .NET Core 2.2 console application.

FROM mcr.microsoft.com/dotnet/core/runtime:2.2-stretch-slim AS base
WORKDIR /app
FROM mcr.microsoft.com/dotnet/core/sdk:2.2-stretch AS build
WORKDIR /src
COPY ["TaikunBillerPoller.csproj", ""]
RUN dotnet restore "TaikunBillerPoller.csproj"
COPY . .
WORKDIR "/src/"
RUN dotnet build "TaikunBillerPoller.csproj" -c Release -o /app
FROM build AS publish
RUN dotnet publish "TaikunBillerPoller.csproj" -c Release -o /app
FROM base AS final
WORKDIR /app
COPY --from=publish /app .
ENTRYPOINT ["dotnet", "TaikunBillerPoller.dll"]

My .dockerignore file looks like

**/.dockerignore
**/.env
**/.git
**/.gitignore
**/.vs
**/.vscode
**/*.*proj.user
**/azds.yaml
**/charts
**/bin
**/obj
**/Dockerfile
**/Dockerfile.develop
**/docker-compose.yml
**/docker-compose.*.yml
**/*.dbmdl
**/*.jfm
**/secrets.dev.yaml
**/values.dev.yaml
**/.toolstarget

We are using GitLab and Kaniko for building gitlab-ci.yml file.

This console application takes 7 minutes to build, but another application written in the Go language takes 40 seconds.

How might I reduce the build time for this application?


Solution

  • Your first FROM line is completely unused. Instead change your FROM base line to FROM mcr.microsoft.com/dotnet/core/runtime:2.2-stretch-slim

    This issue may be due to the fact that Kaniko **/someDir .dockerignore patterns are not properly observed. I'm noticing that /obj, /bin, .idea (rider) and .git folders are all being copied.

    https://github.com/GoogleContainerTools/kaniko/issues/1396

    You are also not using the alpine based sdk and runtime images.

    In the dotnet restore command you can use the --no-cache flag because docker layer cacheing will take care of that.

    dotnet publish does a build so you can skip calling dotnet build. If you want to perform testing you can call dotnet test then

    You are explicitly calling dotnet restore so in all subsequent dotnet commands you can use the --no-restore option.

    FROM mcr.microsoft.com/dotnet/core/sdk:2.2-alpine AS base
    #Add whatever tools you need to the base image
    RUN apk add --update --no-cache git bash curl zip; \
        export PATH="$PATH:/root/.dotnet/tools"; \
        dotnet tool install --global dotnet-xunit-to-junit --version 1.0.2
    
    FROM base AS restore
    WORKDIR /src
    COPY ["TaikunBillerPoller.csproj", ""]
    RUN dotnet restore --no-cache "TaikunBillerPoller.csproj"
    COPY . .
    
    FROM restore as publish
    ARG VERSION="0.0.0"
    RUN dotnet test "TaikunBillerPoller.csproj" --configuration Release --no-restore
    RUN dotnet publish "TaikunBillerPoller.csproj" --output /app --configuration Release --no-restore /p:Version=$VERSION
    
    FROM mcr.microsoft.com/dotnet/core/runtime:2.2-alpine AS final
    WORKDIR /app
    COPY --from=publish /app .
    ENTRYPOINT ["dotnet", "TaikunBillerPoller.dll"]
    

    On a 2015 Mac I have an asp.net microservice that builds, tests, publishes and creates a beanstalk_bundle zip using a normal docker build with the following times:

    • 51s No cache
    • 22s Code change
    • <1s No code change (pipeline yml change)

    Kaniko adds overhead because layer caching is done remotely to some repository (typically). This time is going to depend a lot on how you have your Kaniko cache and mounted volumes configured. Here is something I use on my local machine for debugging.

    #!/bin/bash
    # Assuming this is either not an ephemeral machine, or the ephemeral machine
    # maps the cache directory to permanent volume.
    # We cache images into the local machine
    # so that the Kaniko container, which is ephemeral, does not have to pull them each time.
    docker run -v $(pwd):/workspace gcr.io/kaniko-project/warmer:latest \
            --cache-dir=/workspace/cache \
            --image=mcr.microsoft.com/dotnet/core/sdk:2.2-alpine \
            --image=mcr.microsoft.com/dotnet/core/aspnet:2.2-alpine
    docker run -it --rm \
            -v `pwd`:/workspace \
            -v `pwd`/kaniko-config.json:/kaniko/.docker/config.json:ro \
            -v `pwd`/reports:/reports \
            -v `pwd`/beanstalk_bundle:/beanstalk_bundle \
            gcr.io/kaniko-project/executor:latest \
            --dockerfile "buildTestPublish.Dockerfile" \
            --destination "registry.gitlab.com/somePath/theImageName:theVersion" \
            --skip-unused-stages \
            --cache \
            --cache-dir=/workspace/cache \
            --verbosity=trace