Description
We have Multistage docker build that creates rpm in each stages. if one rpm is there in folder i dont want to build that particular stage.
So how to skip one stage from multistage docker
is there any condition in dockerfile like if, to skip one stage or few docker commands
Describe the results you expected:
Skip one stage from multi-stage docker build
Output of docker version:
[root@localhost consul]# docker -v
Docker version 18.03.0-ce, build 0520e24
Output of docker info:
Containers: 5
Running: 5
Paused: 0
Stopped: 0
Images: 105
Server Version: 18.03.0-ce
Storage Driver: overlay2
Backing Filesystem: xfs
Supports d_type: true
Native Overlay Diff: true
Logging Driver: json-file
Cgroup Driver: cgroupfs
Plugins:
Volume: local
Network: bridge host macvlan null overlay
Log: awslogs fluentd gcplogs gelf journald json-file logentries splunk syslog
Swarm: inactive
Runtimes: runc
Default Runtime: runc
Init Binary: docker-init
containerd version: cfd04396dc68220d1cecbe686a6cc3aa5ce3667c
runc version: 4fc53a81fb7c994640722ac585fa9ca548971871
init version: 949e6fa
Security Options:
seccomp
Profile: default
Kernel Version: 3.10.0-693.el7.x86_64
Operating System: CentOS Linux 7 (Core)
OSType: linux
Architecture: x86_64
CPUs: 2
Total Memory: 3.859GiB
Name: localhost.localdomain
ID: QSFU:ODKS:LJGZ:GC34:KXTP:6B7Y:5UMB:Q7WT:V2X3:4K6M:DFLQ:I7WS
Docker Root Dir: /var/lib/docker
Debug Mode (client): false
Debug Mode (server): false
Registry: https://index.docker.io/v1/
Labels:
Experimental: false
Insecure Registries:
127.0.0.0/8
Live Restore Enabled: false
Additional environment details (AWS, VirtualBox, physical, etc.):
Running docker in Centos7 VirtualBox
There is no way to skip stages per se.
You can build specific stages: docker build --target=<stage>.
Why are you creating the same thing in multiple stages? You can have a stage just for creating that thing and then copy it into the stage that requires it.
Thanks for your quick response.
Each stage has performs different build. Suppose in one stage we are creating rpm, we dont wanna recreate rpm using that build if rpm is already there, in that case we want to skip that stage .
Apart from that i have couple of issues
we dont wanna recreate rpm using that build if rpm is already there, in that case we want to skip that stage .
Not sure I understand the "if rpm is already there" part. Docker build will cache Dockerfile steps where possible.
BuildKit (which will be included as an experimental feature in the next docker release) will further optimize that, and skip stages that are not needed for the --target stage
unit and integration test imagesdocker build from running a specific stage that's not meant to be run.I have the following Dockerfile with multiple stages. For efficiency I'm running different tasks in different stages so I want to skip some stages depending on the needs of the final stage.
# Defining environment
ARG APP_ENV=dev
# Building the base image
FROM alpine as base
RUN echo "running BASE commands"
# Building de pre-install Prod image
FROM base as prod-preinstall
RUN echo "running PROD pre-install commands"
# Building the Dev image
FROM base as dev-preinstall
RUN echo "running DEV pre-install commands"
# Installing the app files
FROM ${APP_ENV}-preinstall as install
COPY app ./app
RUN echo "running install commands"
FROM install as prod-postinstall
RUN echo "running PROD post-install commands"
FROM install as dev-postinstall
RUN echo "running DEV post-install commands"
FROM ${APP_ENV}-postinstall as final
RUN echo "running final commands"
If APP_ENV=PROD I want to skip all the stages that not depend on the PROD stages, not only for efficiency but because the DEV stages will break at some point due to missing stages. Now I know that skipping stages in not currently supported in Dockerfiles, and this scenario can be achieved using multiple Dockerfiles (with duplicated code) or adding some wrapper scripts to check the APP_ENV argument.
This can be a pretty good feature to build more complex and efficient Docker images.
@mauricios yes; BuildKit does exactly that; it will only build stages that are needed to build the final stage (or stage that's specified through --target). BuildKit is integrated in Docker 18.06 as an experimental feature (release candidates for 18.06 are already available; Docker for Mac/Windows releases will be available soon).
Docker 18.06 has been released, and includes experimental BuildKit support; to use this;
{"experimental":true}) in the daemon.json configuration fileDOCKER_BUILDKIT=1 environment variable to use buildkit (remove the environment variable, or set it to 0 to disable)Here's what it looks like;
with APP_ENV=prod:
DOCKER_BUILDKIT=1 docker build -t prod --build-arg APP_ENV=prod -<<'EOF'
# Defining environment
ARG APP_ENV=dev
# Building the base image
FROM alpine as base
RUN echo "running BASE commands"
# Building de pre-install Prod image
FROM base as prod-preinstall
RUN echo "running PROD pre-install commands"
# Building the Dev image
FROM base as dev-preinstall
RUN echo "running DEV pre-install commands"
# Installing the app files
FROM ${APP_ENV}-preinstall as install
RUN echo "running install commands"
FROM install as prod-postinstall
RUN echo "running PROD post-install commands"
FROM install as dev-postinstall
RUN echo "running DEV post-install commands"
FROM ${APP_ENV}-postinstall as final
RUN echo "running final commands"
EOF
Only the PROD build-stages are executed:
[+] Building 2.1s (9/9) FINISHED
=> local://context (.dockerignore) 0.0s
=> => transferring context: 02B 0.0s
=> local://dockerfile (Dockerfile) 0.0s
=> => transferring dockerfile: 705B 0.0s
=> docker-image://docker.io/library/alpine:latest 0.0s
=> => resolve docker.io/library/alpine:latest 0.0s
=> /bin/sh -c echo "running BASE commands" 0.5s
=> /bin/sh -c echo "running PROD pre-install commands" 0.4s
=> /bin/sh -c echo "running install commands" 0.4s
=> /bin/sh -c echo "running PROD post-install commands" 0.4s
=> /bin/sh -c echo "running final commands" 0.4s
=> exporting to image 0.0s
=> => exporting layers 0.0s
=> => writing image sha256:57048e9c9845084931f74490091563aadc50 0.0s
=> => naming to docker.io/library/prod 0.0s
with APP_ENV=dev:
DOCKER_BUILDKIT=1 docker build -t dev -<<'EOF'
# Defining environment
ARG APP_ENV=dev
# Building the base image
FROM alpine as base
RUN echo "running BASE commands"
# Building de pre-install Prod image
FROM base as prod-preinstall
RUN echo "running PROD pre-install commands"
# Building the Dev image
FROM base as dev-preinstall
RUN echo "running DEV pre-install commands"
# Installing the app files
FROM ${APP_ENV}-preinstall as install
RUN echo "running install commands"
FROM install as prod-postinstall
RUN echo "running PROD post-install commands"
FROM install as dev-postinstall
RUN echo "running DEV post-install commands"
FROM ${APP_ENV}-postinstall as final
RUN echo "running final commands"
EOF
Only the DEV build-stages are executed:
[+] Building 1.8s (9/9) FINISHED
=> local://dockerfile (Dockerfile) 0.0s
=> => transferring dockerfile: 705B 0.0s
=> local://context (.dockerignore) 0.0s
=> => transferring context: 02B 0.0s
=> docker-image://docker.io/library/alpine:latest 0.0s
=> CACHED /bin/sh -c echo "running BASE commands" 0.0s
=> /bin/sh -c echo "running DEV pre-install commands" 0.4s
=> /bin/sh -c echo "running install commands" 0.5s
=> /bin/sh -c echo "running DEV post-install commands" 0.4s
=> /bin/sh -c echo "running final commands" 0.4s
=> exporting to image 0.0s
=> => exporting layers 0.0s
=> => writing image sha256:1cf6caaaab4689f6b11ea4d61b5b6175e54074 0.0s
=> => naming to docker.io/library/dev 0.0s
Being able to interpolate vars on the build directives is great, but maybe we could have something more seamless, like building only the depended-upon stages for each target.
The dependency could be inferred both from COPY --from=<stage> directives and FROM <stage> directives. So for example, with this Dockerfile:
# Compile module Foo
FROM gcc as foo
RUN compile-foo.sh
# Install runtime dependencies
FROM alpine as base
RUN apk install <dependencies>
# Building img flavor A
FROM base as versionA
RUN [...]
# Building img flavor B using module Foo
FROM versionA as versionB
COPY --from=foo /lib/foo /lib/foo
We could build the dependency graph based on the target:
versionB --> versionA --> base
`--> foo
So building with versionA as target would trigger the base and versionA stages.
Likewise, building with versionB as target would also trigger the base and versionA stages, but additionally it would trigger the foo stage, as the dependency is implied by the COPY directive.
@bwowk this is exactly what buildkit does.
@cpuguy83
Oh, my bad, I completely misinterpreted the example.
I guess @thaJeztah meant to write ARG APP_ENV=prod in the first sample Dockerfile:
with
APP_ENV=prod:DOCKER_BUILDKIT=1 docker build -t prod --build-arg APP_ENV=prod -<<'EOF' # Defining environment ARG APP_ENV=dev
That's great :)
Can't wait to see it included as a stable feature.
I guess @thaJeztah meant to write ARG APP_ENV=prod in the first sample Dockerfile:
No, that's not needed; the ARG APP_ENV=dev line sets the _default_ value for APP_ENV. That means that someone building the dockerfile, without setting a --build-arg will build a dev-image by default. To _override_ the default, provide a --build-arg flag.
Hey @thaJeztah ... Thanks for that example. Quick question: Is it possible that you mean --target instead of -t in the following command?
DOCKER_BUILDKIT=1 docker build -t prod --build-arg APP_ENV=prod -<<'EOF'
I thought -t was for --tag...
That's correct; -t is shorthand for --tag. In my example, both the prod and dev image build the final stage (target), but the --build-arg sets the value for APP_ENV. Therefore the final stage will use a different stage as base image
Did this ever get made a stable feature? I noticed the issue is still open
Buildkit is available on current versions of docker (but does still require you to enable it with with the DOCKER_BUILDKIT=1 environment variable)
Let me close this issue, because this is addressed in buildkit, and not something that can be addressed in the classic builder
@ thaJeztah Wait this feature to be released formally, it's a really helpful.
@njleonzhang the feature has been released; BuildKit is included in Docker 18.09 and up (including the current 19.03 release); it's not enabled by default (see https://github.com/moby/moby/issues/40379), but it's ready to be used for building images (and generally recommended to use instead of the classic builder); enable buildkit by setting DOCKER_BUILDKIT=1 in your environment
Most helpful comment
Docker 18.06 has been released, and includes experimental BuildKit support; to use this;
{"experimental":true})in thedaemon.jsonconfiguration fileDOCKER_BUILDKIT=1environment variable to use buildkit (remove the environment variable, or set it to0to disable)Here's what it looks like;
with
APP_ENV=prod:Only the PROD build-stages are executed:
with
APP_ENV=dev:Only the DEV build-stages are executed: