GoogleContainerTools / kaniko

Build Container Images In Kubernetes
Apache License 2.0
14.9k stars 1.44k forks source link

Support bind Build Mounts (RUN --mount=type=bind / RUN --mount) like BuildKit and Buildah do #1568

Open guillaume-d opened 3 years ago

guillaume-d commented 3 years ago

For more details see https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/syntax.md

That would help avoiding contortions in the Dockerfile due to COPY's silly "partial flattening" of file hierarchies...

Also for performance some of the other types would be nice:

carnott-snap commented 3 years ago

We are looking to use a single Dockerfile and need to inject credentials for local builds. Our expectation is that koniko would never need these, since it manages things internally, but it looks like even parsing these flags is unsupported:

error building image: parsing dockerfile: Dockerfile parse error line 6: Unknown flag: mount

As a start, we could simply parse and ignore the ones that are not supported, like secret or ssh, and only fail when required or for other types, like bind, cache or tmpfs.

Also the above error is pretty generic, and it would make more sense to do a check of the syntax directive, and fail there:

# syntax = docker/dockerfile:1.2
agowa commented 3 years ago

any update on this? I currently use RUN --mount=type=bind to have the working directory accessible within the container without adding it to the layer. I use this to install a very large deb file. Copying the deb file into the container and then installing it would double the size.

alchemistake commented 2 years ago

Congratulations on 1 year anniversary of this issue. Will it be every added?

kvaps commented 2 years ago

The problem of this issue is that mount operation requires extra permissions. Actually we have two options how we can to implement it without them: 1) Copy layer data into build context during the run 2) Use mv or ln -s to "mount" layer data to build context, but in this way the layer content can be modified by the RUN command itself

I think the first option is better, because this will not violate the Dockerfile specification. Or we can make this behavior configurable via executor flag, eg: --bind-mount-method=<copy|symlink|mount>, where mount method will require additional CAP_SYS_ADMIN permissions.

agowa commented 2 years ago

The intend is to have files in a folder, from a previous layer (or a special device from /dev) available while building, but to not include it within the layer. Anything that makes this possible will be appreciated. At the end of the day it's irrelevant if it's a symlink, a mount or (at least for files) a copy. As long as it doesn't get included in the layer.

hermanbanken commented 2 years ago

A cache could typically be a few gigabytes. I think it would be advisable to at least have the option of symlink/mount instead of a plain copy.

guillaume-d commented 2 years ago

We are looking to use a single Dockerfile and need to inject credentials for local builds. Our expectation is that koniko would never need these, since it manages things internally, but it looks like even parsing these flags is unsupported:

error building image: parsing dockerfile: Dockerfile parse error line 6: Unknown flag: mount

Starting from kaniko 1.8.0, the mere syntax for the credentials is supported, as well as all other syntax added by syntax=docker/dockerfile:1.2.0. Cause (AFAICU): GH-1866 / GH-1885, where for some reason the image builder got bumped from an ancient Docker version to BuildKit v0.8.3, which happens to support that much syntax (but alas not dockerfile >= 1.3 (like for example 1.4+'s heredocs), as requested in #1712 and #1713 (heredocs)). Proof: see some compatibility tests' results for kaniko, especially those for dockerfile:1.2 using kaniko v1.8.0.

As a start, we could simply parse and ignore the ones that are not supported, like secret or ssh, and only fail when required or for other types, like bind, cache or tmpfs.

As said above, parsing those already works, but this does not offer much more, there are few things to be gained for just the syntax being accepted, even WRT compatibility with BuildKit: only few useful things require only syntax and/or are completely backward-compatible:

# syntax=docker/dockerfile:1.2

# purely syntactic, so it indeed works,
# see <https://gitlab.com/iguillaumed/playground/dockerfile-standard/-/jobs/2395435878>:
ARG k1=v1 k2=v2

# purely syntactic, so it indeed works,
# see <https://gitlab.com/iguillaumed/playground/dockerfile-standard/-/jobs/2395435878>:
ADD --chown=$owner:$group ARG...

# when purely syntactic, the key will never be available, so
# required=true will not work as expected / could be insecure: 
RUN --mount=type=ssh CMD

# when purely syntactic, the secret will never be available, so
# required=true will not work as expected / could be insecure: 
RUN --mount=type=secret CMD

In the future (some kaniko version after 1.8.1):

# syntax=docker/dockerfile:1.3

# the default:
RUN --network=default CMD

# insecure when purely syntactic, but functionally equivalent to no --network:
RUN --network=none CMD
# syntax=docker/dockerfile:1.4

# in most cases functionally equivalent to no --link
# (just make sure by testing with a full implementation like BuildKit that --link may really be used),
# but no performance gain of course when purely syntactic:
COPY --link ARG...

# to be explicit when/if --link cannot be used:
COPY --link=false ARG...

# the same as above but with ADD instead of COPY

# just speculating here, but heredocs should be purely syntactic:
RUN <<EOT
  apt-get update
  apt-get install -y vim
EOT

Also the above error is pretty generic, and it would make more sense to do a check of the syntax directive, and fail there:

# syntax = docker/dockerfile:1.2

As one can see in my compatibility tests' result for dockerfile:1.3 using kaniko v1.8.1, the error messages have not gotten better (this time for RUN --network=none):

error building image: parsing dockerfile: dockerfile parse error line 5: Unknown flag: network

However they might come directly from BuildKit, so maybe they could(/must?) be improved there instead, for example as suggested below:

error building image: parsing dockerfile: dockerfile parse error line 5: Unknown flag with syntax = docker/dockerfile:1.2: network

Even more precise error messages WRT the needed syntax would go against BuildKit's syntax modularity idea (and against the flow of time itself :wink:): one syntax's parser would have to know about all other syntaxes' parsers or future syntaxes!

guillaume-d commented 2 years ago

Concerning RUN --mount=type=secret, it should be less difficult to implement than all other types:

Also now we know the syntax already works, see my previous comment.

guillaume-d commented 2 years ago

FYI Buildah >= 1.24 (which is shipped with Podman >= 4) also supports RUN --mount=type=bind.

NfNitLoop commented 1 year ago

At least if these options aren't supported, they should cause a Kaniko build to fail so the user doesn't think they're getting these advantages (cacheing) or go off on wild goose chases trying to figure out why files are missing when in reality --mount=from=previousStage just failed silently. 😢

rd-danny-fleer commented 4 months ago

Any news on this topic? Support for build mounts would be very useful. Currently I have to COPY some files to the image to install them. This makes them persist in the final image. Build mounts would be a perfect solution to make the files temporary available without adding them to the image.

agowa commented 4 months ago

@rd-danny-fleer Look at this and the following sections within the docs https://docs.docker.com/reference/dockerfile/#run---mount besides that your options are using a different tool to create the images. By now there are a lot of tools that support the OCI spec.

And if all of these aren't enough you can always opt for a multi stage build to squash all layers into one:

FROM someImage:someTag AS WithManyDirtyLayers
COPY bigFileInto container
RUN dpkg -i bigFileInContainer.deb
RUN do stuff

FROM scratch
COPY --from=WithManyDirtyLayers / /
# ENTRYPOINT
# CMD

HOWEVER instead of this approach that looses all of the layer information I'd suggest first trying to statically link your application and have a container with only your applications binary copied into (aka without any linux user land)