Introducing Docker's multi-stage build capabilities

Cloud-native applications are published in the form of container images. The build process of cloud-native applications is responsible for converting source code into images that can be run directly. This conversion process varies according to the programming language:

  • Interpreted languages, such as JavaScript and Python, generally do not require additional build steps and can be added directly to the source code.

  • Compiled languages, such as Java, Go, and Rust, require the source code to be compiled before adding the result to the image. Go and Rust can be directly compiled into native executable files, while Java can be compiled into byte codes to run on the JVM, and can also be further compiled into native executable files through GraalVM.

For compiled languages, the entire construction process is divided into at least two steps. The compilation process requires more tools and third-party dependencies, while the runtime requires fewer dependencies. Taking Java as an example, the compilation process may require tools such as Git, Maven, Gradle, JDK, and GraalVM, while only JDK or JRE is required at runtime. Java applications compiled into native executables do not even require a JDK or JRE.

In order to solve the differences between compilation and running phases, there are generally two approaches:

  1. The first method is compiled by the continuous integration server, and the compiled result is directly used in the container image. Taking Maven as an example, after the build is complete, you only need to  target copy the JAR files in the generated directory to the mirror image.

  2. The second method is to carry out the compilation process in the container as well.

The second approach is necessary in some cases. For example, some mirror registries only support building mirror images and do not provide services related to continuous integration. The second approach requires the use of Docker's multi-stage build support. In a multi-stage build, each stage can have its own base image, and actions to perform at build time. The results of the previous stage can be used directly by the later stage.

Below is an example of a multi-stage build of a Quarkus application. The build process is divided into two phases:

  1. The first stage uses the Maven image provided by Quarkus, copy the source code to the image, and then use the Maven command to build. FROM After the directive,  AS specify a name for the build phase.

  2. The second stage uses the base image of OpenJDK 11 and copies the files produced in the first stage. COPY The command  --from=build indicates that the source of the copied file is the image generated in the first stage  build , and the path of the copied file is also from  build the image.

FROM quay.io/quarkus/centos-quarkus-maven:21.3.0-java11 AS build

USER root
RUN mkdir /build
COPY src/ /build/src/
COPY pom.xml /build/
RUN cd /build && mvn -B -ntp package

FROM registry.access.redhat.com/ubi8/openjdk-11-runtime:1.10

ENV LANG='en_US.UTF-8' LANGUAGE='en_US:en'

ENV JAVA_OPTIONS="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager"

COPY --from=build --chown=185 /build/target/quarkus-app/lib/ /deployments/lib/
COPY --from=build --chown=185 /build/target/quarkus-app/*.jar /deployments/
COPY --from=build --chown=185 /build/target/quarkus-app/app/ /deployments/app/
COPY --from=build --chown=185 /build/target/quarkus-app/quarkus/ /deployments/quarkus/

EXPOSE 8080
USER 185

ENTRYPOINT [ "java", "-jar", "/deployments/quarkus-run.jar" ]

Through multi-stage construction, the entire construction process is completed by Docker, which simplifies the configuration of application construction.


0307ab56eb2777e54ec2b9b924c938b9.png

Guess you like

Origin blog.csdn.net/cheng_fu/article/details/122207305