Best practices for building containerized Java projects

更新时间:
复制 MD 格式

You can use a Dockerfile to build source code into a container image for distribution and deployment. Compared with Golang or Python projects, Java projects are more difficult to containerize and often have slower build speeds. These challenges arise because enterprises typically use self-hosted dependency repositories, such as Maven, and developers may be unfamiliar with Dockerfile caching mechanisms. This topic uses a typical user scenario that involves a self-hosted GitLab code repository and a self-hosted Maven repository on the cloud to describe how to build a Java project by using a Dockerfile, accelerate the build process, and use the build service of Alibaba Cloud Container Registry Enterprise Edition (ACR EE) for automated image building.

Prerequisites

Project overview

This topic demonstrates a build process using two interdependent Java projects. The projects are as follows:

  • Provider: Provides a callable service.

    • Core module: Provides public interfaces.

    • Service module: The service implementation module.

  • Consumer: Calls the Provider service.

    • Service module: Depends on the Core module in the Provider project. The following code provides an example:

      .
      ├── consumer
      │ ├── Dockerfile
      │ ├── consumer.iml
      │ ├── pom.xml
      │ └── service
      │     ├── pom.xml
      │     ├── src
      │     └── target
      └── provider
          ├── Dockerfile
          ├── core
          │ ├── pom.xml
          │ ├── src
          │ └── target
          ├── pom.xml
          ├── provider.iml
          └── service
              ├── pom.xml
              ├── src
              └── target

Build artifacts:

  • A producer application image built from the Provider project.

  • A consumer application image built from the Consumer project.

Step 1: Ensure public dependencies are uploaded

Upload the public dependency packages that the project references to your self-managed Maven repository. For the Provider project, run the following upload command in the Provider directory:

mvn clean install org.apache.maven.plugins:maven-deploy-plugin:2.8:deploy -DskipTests

Step 2: Create a custom Maven base image

To access your self-managed Maven repository in the containerized build environment, add the Maven repository configuration to the base image. Create a custom public Maven base image for your enterprise based on the official Maven image. Application projects can then reference this base image to access the Maven repository.

  1. Save the following content as a Dockerfile and place it in the same directory as the settings.xml file of the Maven repository.

    FROM maven:3.8-openjdk-8 # Use a Maven image that matches the project. This example uses Maven 3.8.
    ADD settings.xml /root/.m2/ # Add the self-managed Maven repository configuration to the specified path.
  2. Run the following commands to build the image and push it to the remote image repository.

    ls
    Dockerfile   settings.xml
    docker build -t demo-registry-vpc.cn-beijing.cr.aliyuncs.com/demo/maven-base:3.8-openjdk-8 -f Dockerfile .
    Sending build context to Docker daemon   7.68kB
    Step 1/2 : FROM maven:3.8-openjdk-8
     ---> a3f42bfde036
    Step 2/2 : ADD settings.xml /root/.m2/
     ---> db0d5a5192e3
    Successfully built db0d5a5192e3
    Successfully tagged demo-registry-vpc.cn-beijing.cr.aliyuncs.com/demo/maven-base:3.8-openjdk-8
    docker push demo-registry-vpc.cn-beijing.cr.aliyuncs.com/demo/maven-base:3.8-openjdk-8

Step 3: Build the Consumer application image (skip for the Provider project)

Run the following command. (Push all base images required during the build process to an Alibaba Cloud image repository.)

FROM demo-registry-vpc.cn-beijing.cr.aliyuncs.com/demo/maven-base:3.8-openjdk-8 AS builder
# add pom.xml and source code
ADD ./pom.xml pom.xml
ADD ./service service/
# package jar
RUN mvn clean package
# Second stage: minimal runtime environment
From demo-registry-vpc.cn-beijing.cr.aliyuncs.com/demo/openjdk:8-jre-alpine
# copy jar from the first stage
COPY --from=builder service/target/service-1.0-SNAPSHOT.jar service-1.0-SNAPSHOT.jar
EXPOSE 8080
CMD ["java", "-jar", "service-1.0-SNAPSHOT.jar"]

Step 4: Optimize the build speed

In Step 3: Build the Consumer application image (skip for the Provider project), you built an image. However, if you modify the code and rebuild, you will notice that the build process is slow because it downloads the JAR packages again instead of using the cache. Docker invalidates the cache for a layer when its content changes. After you modify the source code, the hash of the files in the ADD instruction changes. This change forces the RUN instruction to execute again, ignoring the cache from the previous build. For more information, see Dockerfile best practices.

To optimize the build speed, cache and reuse the Maven dependencies:

  1. First, copy the project's pom.xml file into the container to download the dependencies. As long as the pom.xml file does not change, subsequent builds can use the cache.

  2. Copy the project's source code and compile it.

The following is an improved Dockerfile. The first build takes 43 s. If you only change the source code, subsequent builds take only 7 s.

FROM demo-registry-vpc.cn-beijing.cr.aliyuncs.com/demo/maven-base:3.8-openjdk-8 AS builder
# To resolve dependencies in a safe way (no re-download when the source code changes)
ADD ./pom.xml pom.xml
ADD ./service/pom.xml service/pom.xml
RUN  mvn install
ADD ./service service/
# package jar
RUN mvn clean package
# Second stage: minimal runtime environment
From demo-registry-vpc.cn-beijing.cr.aliyuncs.com/demo/openjdk:8-jre-alpine
# copy jar from the first stage
COPY --from=builder service/target/service-1.0-SNAPSHOT.jar service-1.0-SNAPSHOT.jar
EXPOSE 8080
CMD ["java", "-jar", "service-1.0-SNAPSHOT.jar"]

Step 5: Automated build process with Container Registry Enterprise Edition

Container Registry Enterprise Edition (ACR-EE) provides enterprise-level build services and is recommended for enterprise customers. For more information, see Build images using an Enterprise Edition instance.

The following sections outline several best practices.

  • Use the VPC build mode

    For self-managed GitLab instances on the cloud, use the secure VPC build mode to build images. This prevents exposing services to the public internet. For more information, see Build container images in secure VPC mode.

  • Use the immutable image tag feature

    Enable the immutable tag feature for the repository to prevent production versions from being overwritten. On the Create Image Repository page, in the Repository Information step, set Image Version to Immutable by selecting the checkbox. After you select this option, no image tag in the repository, except for latest, can be overwritten. This ensures container image version consistency.

  • Create a building rule based on commit IDs

    Each build generates two image tags. One tag is the code commit ID, which helps map image versions to code versions. The other tag is latest, which indicates the most recent version. In the Modify Building Rule dialog box, in the Image Version step, the Commit ID and latest image tags are configured.

    Commit the code to trigger a build. The following example shows two builds automatically triggered by two code commits. Each build generates two images, and the second build is faster due to cache hits. On the build settings page, the Automatically Build Images upon Code Change switch is enabled. In the building rule settings, Branch is set to branches:master, the build context directory is /, and the Dockerfile name is Dockerfile. The image version includes two tags, Configuration 1 and Configuration 2, which correspond to the commit ID version and the latest version. The build log shows that both builds completed successfully, taking 85 s and 106 s, respectively.

好的,我将作为一位追求极致简洁的文案本地化专家,开始处理这份 HTML 文档。 我的处理流程如下: 1. 逐行分析 HTML,锁定