In this post I starting a series on getting started with Docker; here, I’m going to expand to give some tips on how to debug a docker build when even the build itself is failing.
Imagine that you’re trying to debug a docker build script, and you keep getting build errors. The script never completes, and so you can’t attach to the running container (as shown here).
In this case, there are still a few options available. Let’s consider the following build script:
FROM mcr.microsoft.com/dotnet/runtime:6.0 AS base WORKDIR /app FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build WORKDIR /src COPY ["pcm-test.csproj", "pcm-test/"] RUN dotnet restore "pcm-test/pcm-test.csproj" COPY . . WORKDIR "/src/pcm-test" RUN dotnet build "pcm-test.csproj" -c Release -o /app/build FROM build AS publish RUN dotnet publish "pcm-test.csproj" -c Release -o /app/publish FROM base AS final WORKDIR /app COPY --from=publish /app/publish . ENTRYPOINT ["dotnet", "pcm-test.dll"]
When I run this (it’s a console app, btw), I get the following build error:
=> ERROR [build 10/10] RUN dotnet build "pcm-test.csproj" -c Release -o /app/build #18 0.790 Microsoft (R) Build Engine version 17.0.0-preview-21501-01+bbcce1dff for .NET #18 0.790 Copyright (C) Microsoft Corporation. All rights reserved. #18 0.790 #18 1.351 Determining projects to restore... #18 1.623 All projects are up-to-date for restore. #18 1.703 You are using a preview version of .NET. See: https://aka.ms/dotnet-core-preview #18 2.897 CSC : error CS5001: Program does not contain a static 'Main' method suitable for an entry point
I can absolutely assure you that the program does have a main method.
When a build goes wrong, I’ve determined two ways to debug - the first is by simply executing debug commands inside the build, and the second is to reduce the build until it works, and then inspect the image.
Executing Debug Commands
Since you can run any command inside a build file, you can simply do something like this:
RUN ls -lrt
Remember that if you do this, you’ll need to change a couple of settings:
This can be set to auto (default), plain, and tty.
tty (or interactive terminal) and auto will compress the output; whereas plain will show all the container output (including these kind of debug messages.
Docker tries to be clever, and cache the commands that have already executed; however, for debug statements, this is going to mean that they’ll only ever fire the first time. It tells you when it’s executing these from the cache:
docker build -t pcm-test --no-cache --progress=plain .
Sometimes, executing these statements is enough; however, sometimes, it helps to build a partial image.
Removing the breaking parts of the build
When I first started writing software, on a ZX Spectrum - I’d frequently copy (BASIC) code out from a magazine. I didn’t really know what I was writing, so if it didn’t work it would give me an error that I didn’t understand; however, it would tell me the offending line number, and so I’d simply remove that line. Obviously, subsequent lines would then start to fail, and typically, I’d end up with a program that ended at the first typing (or printing) error. This didn’t make for a very good game.
This isn’t true in docker - if you remove the offending code, you can still create an environment, explore it, and even manually execute the rest of the build inside the environment, to see what’s going wrong!
FROM mcr.microsoft.com/dotnet/runtime:6.0 AS base WORKDIR /app FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build WORKDIR /src COPY ["pcm-test.csproj", "pcm-test/"] RUN dotnet restore "pcm-test/pcm-test.csproj" COPY . . WORKDIR "/src/pcm-test" #RUN dotnet build "pcm-test.csproj" -c Release -o /app/build # #FROM build AS publish #RUN dotnet publish "pcm-test.csproj" -c Release -o /app/publish # #FROM base AS final #WORKDIR /app #COPY --from=publish /app/publish . #ENTRYPOINT ["dotnet", "pcm-test.dll"]
This will now create an image, and so we can run and attach to that image:
docker run -it pcm-test sh
I can now have a look around the image:
As you can see, this looks a bit strange - there’s no code there.
In this post, I’ve covered two techniques to work out why your docker build may be failing.