Docker multi-stage build mechanism

Posted by mikosiko on Wed, 29 May 2019 23:03:28 +0200

With the release of version 17.05, Docker has also made an important update to the image building area, namely multi-stage build (multi-stage build), which is a real help to the long-term partners who have been troubled by the large image.

Multi-phase construction is not supported

Before version 17.05, we used to build Docker images in two ways:

  • Writing all the build processes in the same Docker file, including the compilation, testing, packaging of the project and its dependency libraries, may bring some problems:
    1. Dockerfile is particularly bulky
    2. Mirror layers are particularly deep
    3. There is a risk of source leakage
  • A slightly elegant way is to package the project and its dependency libraries compilation and testing outside before copying them into the build directory. Although this avoids the risks of the first way, we still need to write two sets of Dockerfile s or some scripts to integrate the two phases automatically.

Here, a simple example of JAVA project mirror construction is given to illustrate the two approaches. The project contains only one App.java class:

public class App {

    public static void main(String[] args) {
        System.out.println("Hello, multi-stage build...");
    }

}
  • Take the simplest approach
FROM dojomadness/alpine-jdk8-maven:latest

# add pom.xml and source code
ADD ./pom.xml pom.xml
ADD ./src src/

# package jar and remove source code and temporary class files
RUN mvn clean package && cp -f target/msb-1.0.jar msb.jar && rm -rf pom.xml src/ target/

# run jar
CMD ["java", "-jar", "msb.jar"]

This is where all the build processes are written in the same Dockerfile, but to minimize the mirror level, we merge multiple execution commands into one RUN At the same time, we need to clean up the source directory files and compiled temporary directory files to prevent source code leakage.

  • In a slightly elegant way

Here we need to divide into two stages:
1) Compile and package Docker file.2.1

FROM maven:3.5.0-jdk-8-alpine

# add pom.xml and source code
ADD ./pom.xml pom.xml
ADD ./src src/

# package jar
RUN mvn clean package

2) Building mirror Docker file.2.2

From openjdk:8-jre-alpine

# copy jar
COPY ./msb.jar msb.jar

# run jar
CMD ["java", "-jar", "msb.jar"]

3) Integrating the two construction phases of build.sh

#!/bin/bash

# First stage: complete build environment
docker build -t msb:build -f Dockerfile.2.1 .
# create temporary container
docker create --name extract msb:build
# extract jar
docker cp extract:/target/msb-1.0.jar ./msb.jar
# remove temporary container
docker rm -f extract

# Second stage: minimal runtime environment
docker build --no-cache -t msb:build-2 -f Dockerfile.2.2 .

# remove local jar
rm -rf ./msb.jar

Here we have written two Dockerfiles and one Shell script to build the final image in two phases, but one predictable problem is that if multiple projects are related and dependent on each other, we need to maintain multiple Dockerfiles, or we need to write more complex build.sh scripts, resulting in high maintenance costs in the later period.

Support multi-stage construction

After the launch of Docker 17.05 multi-phase build, we can easily avoid the problems we encountered before, and only need to maintain a Docker file:

# First stage: complete build environment
FROM maven:3.5.0-jdk-8-alpine AS builder
# add pom.xml and source code
ADD ./pom.xml pom.xml
ADD ./src src/
# package jar
RUN mvn clean package

# Second stage: minimal runtime environment
From openjdk:8-jre-alpine
# copy jar from the first stage
COPY --from=builder target/msb-1.0.jar msb.jar
# run jar
CMD ["java", "-jar", "msb.jar"]

Are you familiar with the Docker file above, which merges two Docker files in the second way into the same Docker file and automatically completes the process of building. sh for you during the construction process?

For multi-stage build ing, there are two key points:

  • At the preceding stage FROM An additional instruction is added after the instruction. AS Parameters can be named for this build phase, which can be easily referenced in the subsequent build phase. The format is as follows:

    FROM image[:tag | @digest] AS stage
    
  • At a later stage COPY The -- from parameter is added after the instruction to indicate which previous build phase is referenced in the following format:

    COPY --from=stage ...
    

Similarly, multi-stage construction can also easily build the desired container image through a Dockerfile for multiple interdependent projects without worrying about the risks of too large a mirror and source code leakage.

Container Mirror Service

Topics: Docker xml Java Maven