全部產品
Search
文件中心

Container Registry:Java專案容器化構建最佳實務

更新時間:Jun 30, 2024

使用Dockerfile將原始碼構建成容器鏡像,進行鏡像分發、部署。相比於Golang或Python專案,Java專案因企業一般會選擇自建依賴倉庫(如Maven)導致容器化構建難度高,因不熟悉Dockerfile緩衝機制導致構建速度較慢。本文從典型使用者情境(雲上自建GitLab代碼倉庫、自建Maven倉庫)出發,介紹如何利用Dockerfile構建Java專案,如何提速構建過程以及如何利用ACR-EE構建服務來進行自動化鏡像構建。

前提條件

專案介紹

本文使用具有依賴的兩個Java專案進行示範。分別有以下兩個專案:

  • Provider:提供服務,供調用。

    • Core模組:提供公用介面。

    • Service模組:服務實現模組。

  • Consumer:調用Provider服務。

    • Service模組:需要依賴Provide專案裡的Core模組。參考代碼如下:

      .
      ├── 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

構建產物:

  • 基於Provider工程構建出生產者應用鏡像。

  • 基於Consumer工程構建出消費者應用鏡像。

步驟一:確定公用依賴包已上傳

專案引用的公用依賴包需要提前上傳到自建Maven倉庫。以這裡的Provider為例,您可以在Provider目錄下執行以下上傳命令:

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

步驟二:製作專屬Maven基礎鏡像

為了在容器化構建環境內能訪問到自建Maven倉庫,需要把Maven倉庫配置放到基礎鏡像內。這裡建議基於官方Maven鏡像來打造您的企業專屬公用Maven基礎鏡像,應用專案直接引用該基礎鏡像即可訪問Maven倉庫。

  1. 將以下檔案儲存成Dockerfile並和Maven倉庫的《settings.xml》檔案放到同一目錄。

    FROM maven:3.8-openjdk-8 #指定與專案匹配的Maven鏡像,該樣本環境為3.8版本的Maven。
    
    ADD settings.xml /root/.m2/ #將自建Maven倉庫配置放到對應位置。
  2. 執行以下命令構建並推送到遠程鏡像倉庫。

    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

步驟三:構建Consumer應用鏡像(Provider專案略過)

執行以下命令。(建議將構建過程中需要的基礎鏡像全部推送到阿里雲鏡像倉庫。)

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"]

步驟四:最佳化構建速度

步驟三:構建Consumer應用鏡像(Provider專案略過)中,您已經能構建出鏡像,但是當您修改代碼、重複構建時會發現每次都會重複拉取JAR包,而無法利用到JAR包緩衝,構建速度比較緩慢。這是因為Dockerfile有自己的緩衝生效機制,當修改原始碼後,ADD指令內的檔案內容hash後出現了值變化導致RUN指令需要重新構建,沒辦法利用到先前的構建結果緩衝。更多內容,請參見關於緩衝和Dockerfile最佳實務

我們最佳化構建速度的思路是將Maven依賴能緩衝並重複利用:

  1. 首先將專案的《pom.xml》檔案拷貝進容器,下載依賴。只要專案不改動《pom.xml》檔案,後續構建都能利用到緩衝。

  2. 拷貝專案的源碼並編譯。

一個改進的Dockerfile如下所示,專案首次構建耗時43s,後續如果僅僅是改動原始碼構建時間可以縮短到7s。

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"]

步驟五:基於ACR-EE的自動化構建流程

ACR-EE提供了企業級構建服務能力,推薦企業客戶使用,更多內容,請參見使用企業版執行個體構建鏡像

以下將為您介紹幾個在使用過程中會用到的最佳實務。

  • 使用VPC內網構建模式

    針對雲上自建GitLab,建議使用VPC內網安全構建模式來構建鏡像, 避免向公網暴露服務。更多內容,請參見使用VPC安全構建模式構建容器鏡像

  • 使用鏡像版本不可變功能

    推薦倉庫開啟版本不可變功能,以防止線上版本被覆蓋。建立鏡像倉庫

  • 建立基於Commit ID的構建規則

    每次構建產生兩個鏡像版本,一個版本使用代碼Commit ID來表示,方便鏡像版本和代碼版本對應起來;另一個版本使用latest表示最新版本。修改構建規則

    提交代碼並觸發構建。下圖是提交兩次代碼後自動觸發的兩次構建過程,每次都產生了兩個鏡像,並且因為快取命中加速原因,第二次構建更快。1