8000 Proposal: add support for multiple (named) build-contexts · Issue #37129 · moby/moby · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content
Proposal: add support for multiple (named) build-contexts #37129
Closed
@thaJeztah

Description

@thaJeztah

Add support for multiple (named) build-contexts

Related issues:

Problem statement

Take the following directory structure for a project;

project
 ├── .dockerignore
 ├── Dockerfile
 ├── ginormous
 │   ├── big-file-1
 │   ├── big-file-2
 │   └── big-file-xx
 ├── Makefile
 ├── service1
 │   ├── Dockerfile
 │   └── src
 │       ├── Makefile
 │       ├── source-file-1
 │       ├── source-file-2
 │       └── source-file-xx
 ├── service2
 │   ├── Dockerfile
 │   └── src
 │       ├── Makefile
 │       ├── source-file-1
 │       ├── source-file-2
 │       └── source-file-xx
 ├── common
 │   └── src
 │       ├── 0-various
 │       ├── 1-files
 │       ├── 2-used-by
 │       ├── 3-service-1-and-2
 └── common-2
     └── src
         ├── 0-various
         ├── 1-files
         ├── 2-used-by
         ├── 3-service-1-and-2

In the above;

  • the Dockerfile at the root of the project uses ginormous, and shared
  • service1 has a Dockerfile, and source-files used to build the service in service1/src
  • service2 has a Dockerfile, and source-files used to build the service in service2/src
  • both service1 and service2 share some code/resources, located in common and common-2
  • nor service1, nor service2 use ginormous (a big directory)

Challenges with this example

Building service1 and 2 is a challenge;

  • When building a Dockerfile, all files used have to be within the build-context. This means that in the project structure above, the only context that can be used is the root project directory. Doing so results in the entire project, including ginormous to be sent to the daemon (even though it's not used at all). Relative paths outside of the build-context cannot be used (also see Add with relative path to parent directory fails with "Forbidden path" #2745)
  • Similarly; when contructing/sending the build-context, docker won't resolve symlinks, and copy symlinks as-is, so putting symlinks to common and common-2 inside project1 and project2 will not resolve this problem.
  • Only a single .dockerignore is supported, so it's not possible to "conditionally" exclude files (e.g. when building service1, exclude the ginormous and service2 directories, and vice-versa)

Proposal: allow multiple (named) build-contexts

I propose to add support for multiple build contexts, implemented as a --context flag on COPY and ADD, and a --context <name>=<path> option on the docker image build subcommand.

For example, to build project1, the Dockerfile could look like this:

FROM baseimage

# no --context option set: use the default build-context
COPY . /build/service1/src/

# use the build-context named "common"
COPY --context=common   . /build/common/src/

# use the build-context named "common-2"
ADD --context=common-2 . /build/common-2/src/

RUN cd /build && make && make install

When building the Dockerfile, docker expects two named build-contexts to be provided, in addition to the default (positional) build-context:

From within the project directory:

docker build \
  -f ./service1/Dockerfile \
  --context common=./common/src \
  --context common-2=./common-2/src \
  ./service1

In the above:

  • -f ./service1/Dockerfile is the Dockerfile used to build the image
  • the ./common/src directory is used as build-context "common"
  • the ./common-2/src directory is used as build-context "common-2"
  • the ./service1 directory is used as default build-context

Only the common/src, common-2/src and service1 directories are uploaded to the daemon. All other directories are not part of the build-context, so won't be uploaded.

Similarly, when building from within the project/service1 directory:

docker build \
  --context common=../common/src \
  --context common-2=../common-2/src \
  .
  • No -f is provided, so the Dockerfile in the current directory is used to build the image.
  • the ../common/src directory is used as build-context "common"
  • the ../common-2/src directory is used as build-context "common-2"
  • the current (.) directory is used as default build-context

Both relative and absolute paths can be used to specify the location of a build-context, so all of these are valid:

--context foo=~/go/src/github.com/foobar/foo
--context bar=/dev/sdb/share/bar/
--context bar=./some/dir
--context baz=../../../foobar

Validation

Validation: missing build-contexts

Before building (and sending the build-context), docker validates if all build-contexts are provided. If a context is missing, an error is produced, and the build is aborted. Trying to build the Dockerfile from the example above without specifying any build-context:

docker build .
Error: missing build-context "common"
Error: missing build-context "common-2"

In a multi-stage build, only contexts that are required for the stages that are built should be taken into account. For example:

FROM baseimage AS stage-one
COPY --context=one /subdir/foo /target/dir

FROM busybox AS stage-two
ADD --context=two /foo.tar.gz /target

FROM scratch AS final
COPY --from=stage-one /foo /bar
COPY --from=stage-two /bar /baz
COPY --context=config /config.ini /config.ini

Given the Dockerfile above:

Building just stage-one:

docker build --target=stage-one .
Error: missing build-context "one"

Building up until stage-two:

docker build --target=stage-two .
Error: missing build-context "one"
Error: missing build-context "two"

Building the whole Dockerfile:

docker build .
Error: missing build-context "one"
Error: missing build-context "two"

The default build context (positional argument) is never optional:

docker build --context  --context two=./two
"docker build" requires exactly 1 argument.
See 'docker build --help'.

Usage:  docker build [OPTIONS] PATH | URL | - [flags]

Build an image from a Dockerfile

Validation: unused build-contexts

Similar to unused --build-arg, specifying a build-context that is not used will produce a warning. When determining which contexts are expected/used in

Given the Dockerfile from the previous example:

docker build --target=stage-one --context  --context two=./two .
Sending build context to Docker daemon  2.048kB
Step 1/4 : FROM baseimage AS stage-one
.....
Removing intermediate container edc9bb0f15dc
 ---> a77418a9ddad
[Warning] One or more contexts [two] were not consumed
Successfully built a77418a9ddad

Validation: conflicting options

The --context and --from options cannot be combined. Using both will produce an error:

FROM baseimage AS stage-one
RUN echo foo

FROM busybox
COPY --from=stage-one --context=foo /foo /bar
docker build --context=one .
Sending build context to Docker daemon  2.048kB
Error response from daemon: Dockerfile error line 5: conflicting options '--from' and '--context'

The --context option can be combined with the --chown option.

Validation: context names

Build context names follow these rules:

  • lowercase Alphanumeric characters (a-z, 0-9)
  • punctuation symbols: dashes and underscores
  • must start, and end, with an alphanumberic character
  • consecutive punctuation symbols are not allowed

Specifying an invalid context-name (either on the command-line, or inside a Dockerfile) produces an error.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/builderkind/featureFunctionality or other elements that the project doesn't currently have. Features are new and shiny

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      0