Docker build patterns - Matthias Noback

  • Автор темы Planet PHP
  • Дата начала

Planet PHP

Guest
The "builder pattern"


As a programmer you may know the Gang-of-Four Builder design pattern. The Docker builder pattern has nothing to do with that. The builder pattern describes the setup that many people use to build a container. It involves two Docker images:

  1. a "build" image with all the build tools installed, capable of creating production-ready application files.
  2. a "service" image capable of running the application.
Sharing files between containers


At some point the production-ready application files need to be copied from the build container to the host machine. There are several options for that.

1. Using docker cp


This is what the Dockerfile for the build image roughly looks like:

# in Dockerfile.build

# take a useful base image
FROM ...

# install build tools
RUN install build-tools

# create a /target directory for the executable
RUN mkdir /target

# copy the source code from the build context to the working directory
COPY source/ .

# build the executable
RUN build --from source/ --to /target/executable


To build the executables, simply build the image:

docker build \
-t build \ # tag the image as "build"
-f Dockerfile.build \ # use Dockerfile.build
. # use current directory as build context


In order to be able to reach in and grab the executable, you should first create a container (not a running one) based on the given image:

docker create \
--name build \ # name the container "build"
build # use the "build" image


You can now copy the executable file to your host machine using docker cp:

docker cp build:/target/executable ./executable

2. Using bind-mount volumes


I don't think making the compile step part of the build process of the container is good design. I like container images to be reusable. In the previous example, when the source files are modified, you need to rebuild the build image itself. But I'd just like to run the same build image again.

This means that the compile step should instead be moved to the ENTRYPOINT or CMD instruction. And that the source/ files shouldn't be part of the build context, but mounted as a bind-mount volume inside the running build container:

# in Dockerfile.build
FROM ...
RUN install build-tools

ENTRYPOINT build --from /project/source/ --to /project/target/executable


This way, we should first build the build image, then run it:

# same build process
docker build \
-t build \
-f Dockerfile.build \
.

# now we *run* the container
docker run \
--name build \
--rm \ # remove the container after running it
-v `pwd`:/project \ # bind-mount the entire project directory
build


Every time you run the build container it will compile the files in /project/source/ and produce a new executable in /project/target/. Since /project is a bind-mount volume, the executable file is automatically available on the host machine in target/ - there's no need to explicitly copy it from the container.

Once the application files are on the host machine, it will be easy to copy them to the service image, since that is done using a regular COPY instruction.

The "multi-stage build"


A feature that has just been introduced to Docker is the "multi-stage build". It aims to solve the issue that for the above build process you need two Dockerfiles, and a (Bash) script to coordinate the build process, and get the files where they need to be, with a short detour via the host filesystem.

With a multi-stage build (see Alex Elis's introductory article on this feature), you can describe the build process in one file:

# in Dockerfile

# these are still

Truncated by Planet PHP, read more at the original (another 4371 bytes)

Читать дальше...
 
Сверху