使用jlink工具模塊化Java應用,結合Docker優化容器鏡像(上)

如果你曾經嘗試使用Java的模塊,你可能已經發現模塊化並不容易。第一個障礙可能就是將你自己的應用模塊化,但更多的問題來自於第三方庫模塊化的現行狀態。這是不幸的,因爲一個應用被模塊化一次,就可以當作一個可執行文件被分佈到經過裁剪的JDK版本上運行。於現在的容器化時代來說,這就是一個小型的Docker鏡像。

通過這篇文章,我會解釋如何使用jlink。這是一個從Java9就可用的命令行工具,可以用來創建一個可執行的Java容器。我將通過對模型的概述快速開啓,然後展示如何使用jlink創建可執行文件,最後講述如何使jlink與Docker容器完美的融合。

這篇文章完整的源碼和文件都提交在GitHub,文中更大的項目源碼和配置文件也可以在GitHub上找到。

jlink和模塊化

一個類A在編譯或者運行的時候可能會使用其它的類,比如java.util.List。

存在一個問題,就是JRE包含了很多的類文件,其中很多都不會被程序所使用,但它們總是會被捆綁到程序中。舉個例子,一個運行中的服務型應用仍然綁定了如javax.swing的圖形包。

另一個問題是,Java應用依賴於JRE是如何管理類可見性的。爲了使ch.frankel.a中的類A對ch.frankel.b中的類B是可見的,class A就必須具有公共的可見性。記住,第三方的JAR庫是無法將他們的API類文件和內部類文件從不同的包中徹底的分離。從歷史的觀點來講,包就是內部依賴的隱式命名,比如ch.frankel.c.internal。但是,之前是沒有如何技術手段強制執行這個約束的。

Java9嘗試通過提供另一種方法(模塊)管理包的可見性,從而解決這個問題。以下是幾種模塊:

  • 系統模塊:這個模塊通過JVM提供。
  • 應用模塊:一個應用通過提供一個它根目錄下的module-info文件使之變爲一個應用模塊。
  • 自動模塊:在JAR的MANIFEST.MF中添加一個Automatic-Module-Name的實體,這個指定的模塊將會被處理爲一個自動模塊。
  • 無命名模塊:除了系統、應用、自動模塊之外的JAR。

一個Java9(或之後)的應用是使用位於根目錄下的一個module-info文件進行模塊化得來的。這是一個表單文件,包含了模塊的名字和聲明所有需要的依賴模塊。運行應用的時候,加載器讀取表單文件,只加載所有必要的模塊。

通過這個設計,使得可以排除JDK中不必要的部分模塊,這也是jlink的任務。正如官方文檔所描述的,“你可以通過jlink工具對一組模塊和它們的依賴組裝和優化進一個定製化的運行時鏡像。”

jlink使你能夠使用應用下的模塊配置實現一個定製化的JRE服務於應用。使用同一個機器,jlink允許你無需多餘的操作就能創建一個可執行文件,所以可交付的應用完全自給自足,並且不依賴目標系統是否擁有一個兼容的JRE環境。

Docker基礎

讓我們使用最簡單的應用:Hello World,來開始學習jlink,這是它無比榮耀的地方:

// Main.java
public class Main{
	public static void main(String[] args){
		System.out.println(“Hello world”);
	}
}

不可否認的是,Docker是當今最受歡迎的容器分配渠道。使用Docker對這個“Hello World”程序進行分配是相當受益的。因爲我的意圖是創建一個獨立的Dockerfile並使得最終的鏡像儘可能地小,所以需要對它進行多級構建。

提醒一下,Docker的多級地構建允許你在之後的構建階段複用之前階段構建的結果形成鏈式構建。總之,每一個階段可以繼承不同的基礎鏡像,並且你可以對每個階段進行命名。因爲這會使得通過名字比使用索引對每個階段進行引用時更容易。最大的好處是,多級構建可以在每個階段中儘可能地使用相關的鏡像,所以在構建過程的最後得到一個作爲結果的最小鏡像。

這是一個示例,用於展示Dockerfile是如何使用Maven爲Hello World創建一個鏡像的。假設項目架構兼容Maven:

Dockerfile
FROM maven:3.6-jdk-12-alpine as build

WORKDIR /app

COPY pom.xml
COPY src src

RUN mvn package

FROM openjdk:12-alpine

COPY --from=build /app/target/jlink-ground-up-1.0-SNAPSHOT.jar \
		  /app/target/jlink-ground-up.jar

ENTRYPOINT [“java”,”-jar”]
CMD [“/app/target/jlink-ground-up.jar”]

在這個文件中,第二行標誌着多級構建中的第一個階段,就是使用Maven的鏡像並標記爲構建(build)。然後,使用mvn package命令生成JAR,並使用默認命名,即jlink-ground-up-1.0-SNAPSHOT.jar。

接下來以FROM開頭的一行,即爲構建過程的第二階段也是最後一個階段。這一行會盡可能地在Linux發行的Alpine地JDK鏡像中選擇一個最小的鏡像。Alpine鏡像不提供Java11的版本,但有一個是支持Java12的。接下來的COPY命令則聲明接下來使用的JAR文件是第一個階段(被命名爲build)的輸出文件。接下來你就可以創建自己的鏡像了:

$ docker build –t jlink:1.0

最主要的問題是,這個方法生成的Docker鏡像是很大的,因爲它嵌入了整個JDK,然而這只是個Hello World程序。事實上,這個程序本身和OpenJDK12相比是微不足道的:

$ docker images

 

因爲沒有優化,所以Docker生成的Hello World鏡像與JDK地大小基本一致。爲了減小一個鏡像的尺寸,你可以利用系統模塊以及只分配JDK中被使用的模塊。(之後的文章會介紹)

關於jlink的入門基礎知識的講解就是這些了,在之後的文章中,我們將系統的解決以上出現的問題。比如,如何構建一個定製化的啓動器、如何將jlink運用於開發產品中、如何添加模塊依賴和非模塊依賴等等。

使用jlink工具模塊化Java應用,結合Docker優化容器鏡像(中)

使用jlink工具模塊化Java應用,結合Docker優化容器鏡像(下)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章