Reputation: 7080
I have created a convenience bash script to create multiple targets directly from a repository.
#/bin/bash
DOCKERBIN=docker
PROJECTNAME=my-favorite-project
ORG=myorg
[email protected]:$ORG/$PROJECTNAME.git
"$DOCKERBIN" build --rm=false --target docserver -t "$ORG/$PROJECTNAME-docs" "$REPO"
"$DOCKERBIN" build --rm=false --target demo -t "$ORG/$PROJECTNAME" "$REPO"
"$DOCKERBIN" build --target dashboard -t "$ORG/$PROJECTNAME-dashboard" "$REPO"
As you can see, there are three different targets made from the same repository. The Dockerfile in the base directory of the git repository uses Docker's uses the multistage-build capability to use a single common base builder that creates three independent images. I have used --no-rm
to try to speed up the build, but what I'd really rather do is to issue a single docker command to create all three tagged images. I tried interleaving the --target
and -t
arguments, but the unfortunate result is that it does the entire build three times and applies all three tags to the last target built.
Is there a better way to do this?
Upvotes: 0
Views: 1300
Reputation: 263469
This is the goal of docker buildx bake
. It's still considered experimental, so the final syntax may change. You may also need to export DOCKER_CLI_EXPERIMENTAL=enabled
to get access to this within the docker CLI. Looking at the HCL syntax in the docs, I believe you'd have an HCL file that looks like:
group "default" {
targets = ["docserver", "demo", "dashboard"]
}
target "docserver" {
dockerfile = "Dockerfile.docserver"
tags = ["docker.io/myorg/my-favorite-project-docs"]
}
target "demo" {
dockerfile = "Dockerfile.demo"
tags = ["docker.io/myorg/my-favorite-project"]
}
target "dashboard" {
dockerfile = "Dockerfile.dashboard"
tags = ["docker.io/myorg/my-favorite-project-dashboard"]
}
Like David mentions, if the images build independently of each other, putting them into a single multi-stage Dockerfile is only going to cause problems since older build tools go through previous stages to get to the target making the builds take longer for no value.
Upvotes: 1
Reputation: 158647
docker build
always builds exactly one image. If you need to get three images out, you need to run docker build
three times as you've shown.
If you need to build three independent images, I'd suggest using three separate Dockerfiles rather than trying to force Docker to only run some stages of a multi-stage build. If there are common parts you need repeated, and you have a script like this anyways, you can consider adding a fourth Dockerfile that builds the common parts.
For example, let's say you need to add a custom TLS CA root certificate to every image you build, and you've settled on an Ubuntu base as a standard for all of your containers. You might build a common base image for this like
# projectname/Dockerfile.base
FROM ubuntu:18.04
RUN apt-get update \
&& DEBIAN_FRONTEND=noninteractive \
apt-get install --assume-yes --no-install-recommends \
ca-certificates
COPY local-root.crt /usr/local/share/ca-certificates
RUN update-ca-certificates
Your per-component Dockerfiles can then take the exact image name as an ARG
, and use that in the FROM
line:
# projectname/Dockerfile.dashboard
ARG base
FROM $base
RUN apt-get update \
&& DEBIAN_FRONTEND=noninteractive \
apt-get install --assume-yes --no-install-recommends \
nodejs
...
CMD ["./myapp"]
Then when you go to build this, you need to tell your build script to first build the base image, then the images on top of that.
#!/bin/sh
# projectname/build-docker.sh
TAG=$(date +%Y%m%d.%H%M%S)
docker build -t "me/base:$TAG" -f Dockerfile.base .
docker build -t "me/dashboard:$TAG" -f Dockerfile.dashboard \
--build-arg "base=me/base:$TAG" .
docker push "me/dashboard:$TAG"
Potentially accomplishing this is just a matter of splitting up your single multi-stage Dockerfile into a separate Dockerfile per application. The COPY --from=...
option can reference another image by name, if you do wind up doing something like building an image of static assets that need to be copied into multiple places.
Upvotes: 1