Docker COPY (frustration level 500)

Hello

I have spendt the last 8 hours trying to accomplish a very simple task, I by no means consider myself less intelligent than the average human, yet it is proving extremely difficult to copy a folder into a Docker container.

I have a very simple Dockerfile, and a folder named TEST containing a file called TESTFILE1234, the TEST folder is in the same folder as my Dockerfile

I Have tried every combination of source and destination, leading slash, leading dot slash, etc. etc. nothing works, the folder is never copied into my container. I get no errors when running docker build . or when running the container interactively. The folder is just never copied

FROM ... /redhat-docker-remote/ubi8/dotnet-70:7.0-12 as build
WORKDIR /src

COPY TEST /TEST

FROM ... redhat-docker-remote/ubi8/dotnet-70-runtime:7.0-12

RUN chown -R 1001:0 /opt/app-root && fix-permissions /opt/app-root

USER root

RUN true \
    && microdnf clean all \
    && microdnf update --nodocs \
    && microdnf clean all \
    && true

USER root
RUN microdnf install -y libxml2 

USER root
RUN microdnf clean all -y && \
	rm -rf /var/cache/yum/*

ENV TZ=Europe/Copenhagen
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

WORKDIR /app
COPY --from=build /opt/app-root .
HEALTHCHECK --interval=30s CMD [ -e /tmp/.lock ] || exit 1

ENV LD_LIBRARY_PATH="/app/clidriver/lib/"
ENV PATH=$PATH:"/app/clidriver/bin:/app/clidriver/lib"

USER 1001

What makes you think that it is never copied?

You have a multi-staged build. Each FROM instruction creates a new stage, everything following a FROM instruction up to the line before the next FROM instruction, are the instructions for that stage.

In your first stage starts with setting a WORKDIR and finishes after the COPY instruction that copies TEST from your build context into the image path /TEST. You actually don’t do anything with it.

In your second stage, amongst other things, you have A COPY instruction to copy the folder /opt/app-root from the builder stage to your WORKDIR (/app).

So you either expect /opt/app-root to be provided by the base image you use for the build stage, or your Dockerfile is incomplete and does not show the actual steps that lead from copying TEST into the image to whatever makes the file exist in /opt/app-root, or you actually need to implement them.

Thank you for your quick reply.

using WORKDIR /opt/app-root/src
If I COPY /TEST /opt/app-root/src the file IS copied, but the file ends op in /app. Im not sure I completely understand why. I have removed the first FROM redhat-docker-remote/ubi8/dotnet-70:7.0-12, as im not sure it’s actually used for anything we can’t get from the other FROM with redhat-docker-remote/ubi8/dotnet-70-runtime:7.0-12.

Please paste the complete changed Dockerfile. I am not able to make any sense of it otherwise.

Its working now, the application source (dotnet) are copied to the right destination folder, the files end up in /opt/app-root/src

FROM ... /redhat-docker-remote/ubi8/dotnet-70-runtime:7.0-12
WORKDIR /opt/app-root/src

COPY /TEST ./

USER root
RUN chown -R 1001:0 /opt/app-root && fix-permissions /opt/app-root

USER root

RUN true \
    && microdnf clean all \
    && microdnf update --nodocs \
    && microdnf clean all \
    && true

USER root
RUN microdnf install -y libxml2 

USER root
RUN microdnf clean all -y && \
	rm -rf /var/cache/yum/*

ENV TZ=Europe/Copenhagen
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

HEALTHCHECK --interval=30s CMD [ -e /tmp/.lock ] || exit 1

ENV LD_LIBRARY_PATH="/app/clidriver/lib/"
ENV PATH=$PATH:"/app/clidriver/bin:/app/clidriver/lib"

USER 1001

I wrapped your Dockerfile content in Preformat Text blocks, please take a look at how its done by editing your post. Please use it next time when you paste code blocks.

Of course it works now: you removed the mutli-staged build and directly copy the files where you expect them to be.

You might want to consider putting the first four instructions at the bottom of your Dockerfile, otherwise you will not benefit from the build cache whenever the /TEST file changes. Also: there is no need to use the USER instruction over and over for the same user again, without switching to another user.

I ended up with this:

# -------------------- Dotnet build --------------------
FROM ... /redhat-docker-remote/ubi8/dotnet-70:7.0-12 as build
WORKDIR /opt/app-root/xkt/src
COPY src ./

ARG NUGET_USER
ARG NUGET_PASSWORD
ARG DOCKERVERSION=1.0

ENV NUGET_USER=$NUGET_USER
ENV NUGET_PASSWORD=$NUGET_PASSWORD

ENV PATH="$PATH:/opt/app-root/.dotnet/tools"

USER root

RUN dotnet tool install --global dotnet-reportgenerator-globaltool --configfile ./Nuget.Config
RUN dotnet restore ./XKT.ExternalAccountsService.UnitTests --disable-parallel --configfile ./Nuget.Config
RUN dotnet test ./XKT.ExternalAccountsService.UnitTests/XKT.ExternalAccountsService.UnitTests.csproj --collect:"XPlat Code Coverage" -c Release --results-directory /testresults --logger "trx;LogFileName=test_results.xml" /p:CollectCoverage=true --settings ./XKT.ExternalAccountsService.UnitTests/runsettings.xml --no-restore
RUN dotnet publish ./XKT.ExternalAccountsService/XKT.ExternalAccountsService.csproj -c Release /p:Version=$DOCKERVERSION -o /opt/app-root/xkt/build --no-build

# Clean-up
WORKDIR /opt/app-root/xkt/build
RUN rm -r /opt/app-root/src

# ------------------- Dotnet runtime -------------------
# /opt/app-root is the standard directory for third-party (us) applications in Redhat Linux
# 

FROM ... /redhat-docker-remote/ubi8/dotnet-70-runtime:7.0-12
ARG NUGET_USER
ARG NUGET_PASSWORD

# See: https://www.ibm.com/docs/en/db2/11.5?topic=configuring-environment-variables
ENV DB2_CLI_DRIVER_INSTALL_PATH=/opt/app-root/xkt/clidriver
ENV LD_LIBRARY_PATH=./clidriver/lib/
ENV PATH=$PATH:/opt/app-root/xkt/clidriver/bin:/opt/app-root/xkt/clidriver/lib

USER root
RUN chown -R 1001:0 /opt/app-root && fix-permissions /opt/app-root

RUN microdnf install -y pam 

RUN true \
    && microdnf clean all \
    && microdnf update --nodocs \
    && microdnf clean all \
    && true

RUN microdnf clean all -y && \
	rm -rf /var/cache/yum/*

ENV TZ=Europe/Copenhagen
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

# Transfer application from "build step" above
WORKDIR /opt/app-root/xkt/
COPY --from=build /opt/app-root/xkt/build .

HEALTHCHECK --interval=30s CMD [ -e /tmp/.lock ] || exit 1

USER 1001
ENTRYPOINT ["dotnet", "XKT.ExternalAccountsService.dll"]

Everything works, and there is no clutter left behind, there is probably room for improvement. Thanks!