Google Jib 容器化構建工具

一、前言

隨着近些年的技術發展,Java 領域微服務已經成爲主流的技術方向。隨着微服務化,雲原生的概念也逐漸火熱起來,不瞭解雲原生彷彿就是一個原始人。而在雲原生中,應用容器化 是其核心屬性之一。

應用容器化,用抽象的話來說就是:將軟件容器中的應用程序和進程作爲獨立的應用程序部署單元運行,並作爲實現高級別資源隔離的機制。從總體上改進開發者的體驗、促進代碼和組件重用,而且要爲雲原生應用簡化運維工作。通俗點說,就是藉助於 Docker 等容器化技術,將一個個的微服務打包成鏡像,在容器中獨立部署運行。

二、背景

我司目前採用的是基於 GitLab + Jenkins + Rancher 這套 CI/DI 體系。在這套體系中微服務的容器化依賴於 Jenkins 去實現。現在假設我們有一個項目,其組織結構如下:

parentPro
    |-- moduleA
    |-- moduleB
    |-- rest    [rest 模塊爲 spring boot 啓動入口,並依賴 moduleA、moduleB]

對於 SpringBoot 項目,Maven 的默認構建工具是 Spring-boot-maven-plugin,構建出產物爲 Fat JarFat jar 中包含有 rest 模塊中的 classes,及 rest 所依賴的 moduleAmoduleB 及其他第三方 jar 庫。最終,通過 Jenkins 的 Dockerfile 文件將 Fat jar 基於 JDK 基礎鏡像層構建,產生一個新的應用鏡像。

每次應用構建新版本鏡像時,因爲 Maven 構建產出物是 Fat jar,當 restmoduleAmoduleB 模塊中任意一處發生變化時,都會產出一個新的 Fat jar。構建鏡像時都要將整個 Fat jar 重新寫入到鏡像層,並將整個鏡像層推送到鏡像倉庫中,大大降低了鏡像構建和推送的性能,並導致同一個應用鏡像的多個 Tag 佔用大量的存儲空間。

三、Google Jib

3.1 介紹

Jib 是谷歌公司推出的開源 Java 鏡像構建工具,它可以將一個 Java 應用構建成 OCI 鏡像或者是 Docker 鏡像,目前最新的 Relaese 版本爲 1.8.0。

JIB 具有以下特點:

  1. Jib 使用 Java 開發,並作爲 Maven 或 Gradle 的一部分運行。你不需要編寫 Dockerfile 或 Docker 環境,甚至無需創建包含所有依賴的大 JAR 包,就可以構建出鏡像,並將鏡像推送到鏡像倉庫。因爲 Jib 與 Java 構建過程緊密集成,所以它可以訪問到打包應用程序所需的所有信息。在後續的容器構建期間,它將自動選擇 Java 構建過的任何變體。
  2. JIB 構建出的應用鏡像,具有分層結構, 利用鏡像分層和註冊表緩存來實現快速、增量的構建,提高構建鏡像、推送鏡像的性能,減少鏡像存儲空間。
  3. 冪等性,Jib 支持根據 Maven 和 Gradle 的構建元數據進行聲明式的容器鏡像構建,只要輸入保持不變,就可以通過配置重複創建相同的鏡像。

下圖爲某微服務開啓 Jib 構建後在 Jenkins 中的構建過程,可以看出構建速度的提升主要在 package 和 push 階段。

3.2 原理

Jib 在編譯 Java 應用時,會將 Java 項目內的資源及所依賴的資源,基於變化頻率不同分成多個部分,並將每個部分都單獨作爲一個鏡像層存在,這樣其中一部分資源發生變化時,只需要重新構建該部分所屬鏡像層即可。以第二節的應用爲例,rest 應用鏡像將被分爲以下鏡像層:

  • Classes: rest 模塊中的 class 信息,這部分信息變化頻率最高,處於最上層鏡像層;

  • Resources: rest 模塊中的配置文件,這部分信息變化頻率較低,處於第二層鏡像層;

  • Project Dependencies: rest 模塊的項目依賴信息,在當前示例中爲 moduleAmoduleB,這部分內容比依賴第三方 Jar 庫更容易變化,所以也單獨做爲一個鏡像層存在;

  • Snapshot Dependencies:rest 模塊所依賴的 SnapShot Jar 庫

  • All other Dependencies: rest 模塊所依賴的其他類型 Jar 庫;

  • Each extra directory:其他所依賴額外資源目錄;

基於 Jib 插件構建出的鏡像,與使用以下 Dockerfile 所構建出的鏡像相同:

四、簡單上手

源碼地址:https://github.com/jitwxs/blog_sample

4.1 基礎配置

創建一個全新的 SpringBoot 項目,依賴只包含 spring-boot-starter-web 這一個即可。編寫一個 Controller 類,用於測試:

@RestController
public class DemoController {
    @GetMapping("/hello")
    public String hello() {
        return "hello world!";
    }
}

然後,在 POM 文件中添加 JIB 插件:

<plugin>
  <groupId>com.google.cloud.tools</groupId>
  <artifactId>jib-maven-plugin</artifactId>
  <version>1.8.0</version>
  <configuration>
    <from>
      <image>harbor.jitwxs-inc.com/base/java:8-jdk-alpine</image>
    </from>
    <to>
      <image>harbor.jitwxs-inc.com/sample/${artifactId}:v1</image>
    </to>
    <allowInsecureRegistries>true</allowInsecureRegistries>
  </configuration>
</plugin>

介紹一下含義:

  • <from> 基礎鏡像信息,即構建本鏡像所基於的根鏡像

  • <to> 輸出鏡像信息, 表示本鏡像構建完成後,要發佈到哪裏去

  • <allowInsecureRegistries> 允許使用 HTTP 協議連接 Registry 倉庫

  • <image> 鏡像名,命名格式爲:Registry 倉庫地址/屬組/鏡像名:Tag名

由於 Docker Hub 的速度實在是太感人了,開着梯子都 push 不上去,因此我使用了私服倉庫。如果使用 Docker Hub,那麼 image 標籤內容形如:docker.io/jitwxs/image_name:tag,其中 jitwxs 爲你的 DockerHub 唯一ID,一般是用戶名。

配置完畢後,使用如下命令編譯,並自動 push 到倉庫中:

mvn clean package -DskipTests jib:build

核心就是 jib:build,更多命令見文檔: https://github.com/GoogleContainerTools/jib/tree/master/jib-maven-plugin#build-your-image

4.2 鑑權

運行後,發現拋瞭如下的錯誤。根據錯誤日誌可知連接 Registry 倉庫時需要鑑權。

4.2.1 命令行

第一種方式也是最粗暴的,在執行 maven 命令時傳遞 Registry 倉庫的用戶名密碼。

mvn clean package -DskipTests jib:build \
    -Djib.from.auth.username=admin \
    -Djib.from.auth.password=admin \
    -Djib.to.auth.username=admin \
    -Djib.to.auth.password=admin

由於 <from><to> 中的鏡像可能不是來自於同一個 Registry 倉庫,因此既要配置 from 的用戶名密碼,也要配置 to 的用戶名密碼。

執行完畢後,通過命令行,或者可視化工具,查看是否被 push 上去(此處我使用的工具是 Harbor)。

4.2.2 配置文件

使用命令行方式每次執行都要輸入那麼長一串命令,這樣實在是不方便。另一種方法是在 pom.xml 文件直接將用戶名密碼配置進去,形如:

<plugin>
  <groupId>com.google.cloud.tools</groupId>
  <artifactId>jib-maven-plugin</artifactId>
  <version>1.8.0</version>
  <configuration>
    <from>
      <image>harbor.jitwxs-inc.com/base/java:8-jdk-alpine</image>
      <auth>
        <username>my_username</username>
        <password>my_password</password>
      </auth>
    </from>
    <to>
      <image>harbor.jitwxs-inc.com/sample/${artifactId}:v1</image>
      <auth>
        <username>my_username</username>
        <password>my_password</password>
      </auth>
    </to>
    <allowInsecureRegistries>true</allowInsecureRegistries>
  </configuration>
</plugin>

即給 from 和 to 標籤都加上 <auth> 標籤,但是這種方式實在是不夠優雅,因爲將用戶名密碼硬編碼在代碼中會帶來安全性問題。

合適的方法是配置在 Maven 的 settings.xml 配置文件中,在 <servers> 標籤中,新增一個 <server> 節點,配置 Registry 倉庫的用戶名密碼。

<servers>
    ...
    <server>
      <id>harbor.jitwxs-inc.com</id>
      <username>admin</username>
      <password>admin</password>
      <configuration>
        <email>[email protected]</email>
      </configuration>
    </server>
</servers>

配置完畢後,讓我們把項目的 tag 從 v1 修改爲 v2,再執行次命令驗證下:

mvn clean package -DskipTests jib:build

可以看到正常被 push 上去了。最後官方文檔詳細介紹了各種鑑權方式的使用,參見:https://github.com/GoogleContainerTools/jib/tree/master/jib-maven-plugin#authentication-methods

4.3 本地構建

下面試下在本地進行構建,首先使用 docker 命令將鏡像拉取下來:

> ~: docker pull harbor.jitwxs-inc.com/sample/springboot_jib:v2
v2: Pulling from sample/springboot_jib
53478ce18e19: Pull complete
d1c225ed7c34: Pull complete
887f300163b6: Pull complete
471ae92a2408: Pull complete
286e54d31846: Pull complete
4f4af7a6fe32: Pull complete
Digest: sha256:dfb6628201b1c5fec5eaca00deec157d437559356043043e636fe11b6f3ce1fe
Status: Downloaded newer image for harbor.jitwxs-inc.com/sample/springboot_jib:v2

然後基於該鏡像,創建容器,並後臺運行在 8080 端口:

docker run -d --name jib_test -p 8080:8080 harbor.jitwxs-inc.com/sample/springboot_jib:v2

打開瀏覽器,請求接口 http://127.0.0.1:8080/hello,正確輸出。

4.4 綁定到生命週期

如果你不想單獨輸入 jib:build,你可以把 jib 綁定到 Maven 命令中,在插件中添加如下的 <executions> 標籤即可。

<plugin>
  <groupId>com.google.cloud.tools</groupId>
  <artifactId>jib-maven-plugin</artifactId>
  ...
  <executions>
    <execution>
      <phase>package</phase>
      <goals>
        <goal>build</goal>
      </goals>
    </execution>
  </executions>
</plugin>

然後通過 Maven 的 mvn package 命令就會自動構建鏡像了。

五、驗證

這裏推薦一個工具 dive, dive 能夠通過文件目錄的形式直觀地顯示一個鏡像中的每個鏡像層內的內容,便於查看鏡像的分層信息。

./dive harbor.okcoin-inc.com/sample/springboot_jib:v1

六、參考文獻

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