Docker multi-state build allows you to use two (or more) FROM statements in single Dockerfile. Each FROM starts new build stage. The main feature is you can copy files between stages.
Let’s prepare multi-stage build Dockerfile for described in previous posts linkshortener service. Dockerfile will consist of two stages:
- first stage (builder) will pull git repository and will build project,
- second stage will prepare release image, to do this it will use build result of first stage.
Described file might look as following:
FROM adoptopenjdk/openjdk11:centos as builder ARG APP_VERSION ENV CURRENT_VERSION APP_VERSION COPY ssh-key/docker-key /root/.ssh/docker-key COPY ssh-key/docker-key.pub /root/.ssh/docker-key.pub RUN set -eux; \ printf "Host github.com \n Hostname github.com \n IdentityFile ~/.ssh/docker-key \n IdentitiesOnly yes" > /root/.ssh/config; \ yum -y install git; \ ssh-keyscan github.com >> /root/.ssh/known_hosts; \ mkdir /app; \ git clone email@example.com:psstepniewski/linkshortener.git app; \ yum -y install curl; \ curl -L https://www.scala-sbt.org/sbt-rpm.repo > sbt-rpm.repo; \ mv sbt-rpm.repo /etc/yum.repos.d/; \ yum -y install sbt; \ cd app; \ git checkout "$APP_VERSION"; \ sbt dist FROM alpine:3.14 COPY --from=builder /app/target/universal/linkshortener.zip /srv/linkshortener.zip WORKDIR /srv RUN set -eux; \ apk update && apk add --no-cache tcpdump nano tzdata && cp /usr/share/zoneinfo/Europe/Warsaw /etc/localtime && echo "Europe/Warsaw" > /etc/timezone && apk del tzdata && adduser --no-create-home --disabled-password --gecos "" --uid 2727 wheelfred wheelfred; \ apk add --no-cache openjdk11-jre; \ apk add --no-cache bash; \ unzip linkshortener.zip; \ mv linkshortener app; \ rm -rf /srv/linkshortener.zip; \ chown -R wheelfred:wheelfred /srv/* VOLUME ["/srv/logs"] CMD ./app/bin/linkshortener \ -Dconfig.file=/srv/app/conf/linkshortener.conf \ -Ddb.default.migration.auto=true \ -Dhttp.port=13256
Result image consists from layers of second stage. First stage is used only to build application and is not part
of final image. All multi-stage build magic is in line
COPY --from=builder /app/target/universal/linkshortener.zip /srv/linkshortener.zip.
It copies built application from first to second stage. Pay attention you can name Dockerfile stage, in above file
name of first stage is builder
FROM adoptopenjdk/openjdk11:centos as builder.
This simple feature resolves big problem. Before this you had two options:
- Build and run application in one Dockerfile. Downside of this solution is bigger image size which contains
tools needed to build application (like
sbt). In our case it would contain also read key to GitHub repository and project source code. You can prepare Dockerfile which doesn’t pull git repository and uses volumes to access source code, but still you result with Dockerfile of image which need to be able to build project. Additional configuration of host machine is also required (pulling repository and setting volume).
- Build project with one Dockerfile and prepare release image with second Dockerfile. It is analogous solution to
multi-stage build, but it is more complicated. You need to build application in first Dockerfile in
ENTRYPOINT) statement and place result in volume. Then second Dockerfile can access it using
Read more about multi-stage build in Docker documentation.