Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dockerfile COPY and RUN in one layer

Tags:

docker

I have a script used in the preapration of a Docker image. I have this in the Dockerfile:

COPY my_script /
RUN bash -c "/my_script"

The my_script file contains secrets that I don't want in the image (it deletes itself when it finishes).

The problem is that the file remains in the image despite being deleted because the COPY is a separate layer. What I need is for both COPY and RUN to affect the same layer.

How can I COPY and RUN a script so that both actions affect the same layer?

like image 823
starfry Avatar asked Jul 04 '19 10:07

starfry


People also ask

What does && mean in Dockerfile?

Each RUN command in a Dockerfile creates a new layer to the Docker image. In general, each layer should try to do one job and the fewer layers in an image the easier it is compress. This is why you see all these '&& 's in the RUN command, so that all the shell commands will take place in a single layer.

Can Dockerfile have multiple Workdir?

you can have multiple WORKDIR in same Dockerfile. If a relative path is provided, it will be relative to the previous WORKDIR instruction. If no WORKDIR is specified in the Dockerfile then the default path is / . The WORKDIR instruction can resolve environment variables previously set in Dockerfile using ENV.

What is multi stage Dockerfile?

A multistage build allows you to use multiple images to build a final product. In a multistage build, you have a single Dockerfile, but can define multiple images inside it to help build the final image.

What is difference between CMD and ENTRYPOINT?

Differences between CMD & ENTRYPOINT CMD commands are ignored by Daemon when there are parameters stated within the docker run command while ENTRYPOINT instructions are not ignored but instead are appended as command line parameters by treating those as arguments of the command.


4 Answers

take a look to multi-stage:

Use multi-stage builds

With multi-stage builds, you use multiple FROM statements in your Dockerfile. Each FROM instruction can use a different base, and each of them begins a new stage of the build. You can selectively copy artifacts from one stage to another, leaving behind everything you don’t want in the final image. To show how this works, let’s adapt the Dockerfile from the previous section to use multi-stage builds.

Dockerfile:

FROM golang:1.7.3
WORKDIR /go/src/github.com/alexellis/href-counter/
RUN go get -d -v golang.org/x/net/html  
COPY app.go .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .

FROM alpine:latest  
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=0 /go/src/github.com/alexellis/href-counter/app .
CMD ["./app"]  
like image 60
LinPy Avatar answered Sep 18 '22 09:09

LinPy


As of 18.09 you can use docker build --secret to use secret information during the build process. The secrets are mounted into the build environment and aren't stored in the final image.

RUN --mount=type=secret,id=script,dst=/my_script \
    bash -c /my_script
$ docker build --secret id=script,src=my_script.sh

The script wouldn't need to delete itself.

like image 38
John Kugelman Avatar answered Sep 21 '22 09:09

John Kugelman


This can be handled by BuildKit:

# syntax=docker/dockerfile:experimental
FROM ...
RUN --mount=type=bind,target=/my_script,source=my_script,rw \
    bash -c "/my_script"

You would then build with:

DOCKER_BUILDKIT=1 docker build -t my_image .

This also sounds like you are trying to inject secrets into the build, e.g. to pull from a private git repo. BuildKit also allows you to specify:

# syntax=docker/dockerfile:experimental
FROM ...
RUN --mount=type=secret,target=/creds,id=cred \
    bash -c "/my_script -i /creds"

You would then build with:

DOCKER_BUILDKIT=1 docker build -t my_image --secret id=creds,src=./creds .

With both of the BuildKit options, the mount command never actually adds the file to your image. It only makes the file available as a bind mount during that single RUN step. As long as that RUN step does not output the secret to another file in your image, the secret is never injected in the image.

For more on the BuildKit experimental syntax, see: https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/experimental.md

like image 45
BMitch Avatar answered Sep 21 '22 09:09

BMitch


I guess you can use a workaround to do this:

Put my_script in a local http server which for example using python -m SimpleHTTPServer, and then the file could be accessed with http://http_server_ip:8000/my_script

Then, in Dockerfile use next:

RUN curl http://http_server_ip:8000/my_script > /my_script && chmod +x /my_script && bash -c "/my_script"

This workaround assure file add & delete in same layer, of course, you may need to add curl install in Dockerfile.

like image 27
atline Avatar answered Sep 22 '22 09:09

atline