使用 Google Jib 構(gòu)建 Java 容器
隨著近些年的技術(shù)發(fā)展,Java 領(lǐng)域微服務(wù)已經(jīng)成為主流的技術(shù)方向。隨著微服務(wù)化,云原生的概念也逐漸火熱起來,不了解云原生仿佛就是一個(gè)原始人。而在云原生中,應(yīng)用容器化 是其核心屬性之一。
應(yīng)用容器化,用抽象的話來說就是:將軟件容器中的應(yīng)用程序和進(jìn)程作為獨(dú)立的應(yīng)用程序部署單元運(yùn)行,并作為實(shí)現(xiàn)高級別資源隔離的機(jī)制。從總體上改進(jìn)開發(fā)者的體驗(yàn)、促進(jìn)代碼和組件重用,而且要為云原生應(yīng)用簡化運(yùn)維工作。通俗點(diǎn)說,就是借助于 Docker 等容器化技術(shù),將一個(gè)個(gè)的微服務(wù)打包成鏡像,在容器中獨(dú)立部署運(yùn)行。
背景
我司目前采用的是基于 GitLab + Jenkins + Rancher 這套 CI/CD 體系。在這套體系中微服務(wù)的容器化依賴于 Jenkins 去實(shí)現(xiàn)。現(xiàn)在假設(shè)我們有一個(gè)項(xiàng)目,其組織結(jié)構(gòu)如下:
parentPro
????|--?moduleA
????|--?moduleB
????|--?rest????[rest?模塊為?spring?boot?啟動入口,并依賴?moduleA、moduleB]
對于 SpringBoot 項(xiàng)目,Maven 的默認(rèn)構(gòu)建工具是 Spring-boot-maven-plugin,構(gòu)建出產(chǎn)物為 Fat Jar。Fat jar 中包含有 rest 模塊中的 classes,及 rest 所依賴的 moduleA、moduleB 及其他第三方 jar 庫。最終,通過 Jenkins 的 Dockerfile 文件將 Fat jar 基于 JDK 基礎(chǔ)鏡像層構(gòu)建,產(chǎn)生一個(gè)新的應(yīng)用鏡像。
每次應(yīng)用構(gòu)建新版本鏡像時(shí),因?yàn)?Maven 構(gòu)建產(chǎn)出物是 Fat jar,當(dāng) rest、moduleA、moduleB 模塊中任意一處發(fā)生變化時(shí),都會產(chǎn)出一個(gè)新的 Fat jar。構(gòu)建鏡像時(shí)都要將整個(gè) Fat jar 重新寫入到鏡像層,并將整個(gè)鏡像層推送到鏡像倉庫中,大大降低了鏡像構(gòu)建和推送的性能,并導(dǎo)致同一個(gè)應(yīng)用鏡像的多個(gè) Tag 占用大量的存儲空間。
Google Jib
介紹
Jib 是谷歌公司推出的開源 Java 鏡像構(gòu)建工具,它可以將一個(gè) Java 應(yīng)用構(gòu)建成 OCI 鏡像或者是 Docker 鏡像,目前最新的 Relaese 版本為 1.8.0。
JIB 具有以下特點(diǎn):
Jib 使用 Java 開發(fā),并作為 Maven 或 Gradle 的一部分運(yùn)行。你不需要編寫 Dockerfile 或 Docker 環(huán)境,甚至無需創(chuàng)建包含所有依賴的大 JAR 包,就可以構(gòu)建出鏡像,并將鏡像推送到鏡像倉庫。因?yàn)?Jib 與 Java 構(gòu)建過程緊密集成,所以它可以訪問到打包應(yīng)用程序所需的所有信息。在后續(xù)的容器構(gòu)建期間,它將自動選擇 Java 構(gòu)建過的任何變體。 JIB 構(gòu)建出的應(yīng)用鏡像,具有分層結(jié)構(gòu), 利用鏡像分層和注冊表緩存來實(shí)現(xiàn)快速、增量的構(gòu)建,提高構(gòu)建鏡像、推送鏡像的性能,減少鏡像存儲空間。 冪等性,Jib 支持根據(jù) Maven 和 Gradle 的構(gòu)建元數(shù)據(jù)進(jìn)行聲明式的容器鏡像構(gòu)建,只要輸入保持不變,就可以通過配置重復(fù)創(chuàng)建相同的鏡像。
下圖為某微服務(wù)開啟 Jib 構(gòu)建后在 Jenkins 中的構(gòu)建過程,可以看出構(gòu)建速度的提升主要在 package 和 push 階段。
原理
Jib 在編譯 Java 應(yīng)用時(shí),會將 Java 項(xiàng)目內(nèi)的資源及所依賴的資源,基于變化頻率不同分成多個(gè)部分,并將每個(gè)部分都單獨(dú)作為一個(gè)鏡像層存在,這樣其中一部分資源發(fā)生變化時(shí),只需要重新構(gòu)建該部分所屬鏡像層即可。以第二節(jié)的應(yīng)用為例,rest 應(yīng)用鏡像將被分為以下鏡像層:
Classes: rest 模塊中的 class 信息,這部分信息變化頻率最高,處于最上層鏡像層; Resources: rest 模塊中的配置文件,這部分信息變化頻率較低,處于第二層鏡像層; Project Dependencies: rest 模塊的項(xiàng)目依賴信息,在當(dāng)前示例中為 moduleA、moduleB,這部分內(nèi)容比依賴第三方 Jar 庫更容易變化,所以也單獨(dú)做為一個(gè)鏡像層存在; Snapshot Dependencies:rest 模塊所依賴的 SnapShot Jar 庫; All other Dependencies: rest 模塊所依賴的其他類型 Jar 庫; Each extra directory:其他所依賴額外資源目錄;
基于 Jib 插件構(gòu)建出的鏡像,與使用以下 Dockerfile 所構(gòu)建出的鏡像相同:
簡單上手
源碼地址:https://github.com/jitwxs/blog_sample
基礎(chǔ)配置
創(chuàng)建一個(gè)全新的 SpringBoot 項(xiàng)目,依賴只包含 spring-boot-starter-web 這一個(gè)即可。編寫一個(gè) Controller 類,用于測試:
@RestController
public?class?DemoController?{
????@GetMapping("/hello")
????public?String?hello()?{
????????return?"hello?world!";
????}
}
然后,在 POM 文件中添加 JIB 插件:
<plugin>
??<groupId>com.google.cloud.toolsgroupId>
??<artifactId>jib-maven-pluginartifactId>
??<version>1.8.0version>
??<configuration>
????<from>
??????<image>harbor.jitwxs-inc.com/base/java:8-jdk-alpineimage>
????from>
????<to>
??????<image>harbor.jitwxs-inc.com/sample/${artifactId}:v1image>
????to>
????<allowInsecureRegistries>trueallowInsecureRegistries>
??configuration>
plugin>
介紹一下含義:
基礎(chǔ)鏡像信息,即構(gòu)建本鏡像所基于的根鏡像輸出鏡像信息, 表示本鏡像構(gòu)建完成后,要發(fā)布到哪里去允許使用 HTTP 協(xié)議連接 Registry 倉庫鏡像名,命名格式為:Registry 倉庫地址/屬組/鏡像名:Tag名
“由于 Docker Hub 的速度實(shí)在是太感人了,開著梯子都 push 不上去,因此我使用了私服倉庫。如果使用 Docker Hub,那么 image 標(biāo)簽內(nèi)容形如:
”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
鑒權(quán)
運(yùn)行后,發(fā)現(xiàn)拋了如下的錯誤。根據(jù)錯誤日志可知連接 Registry 倉庫時(shí)需要鑒權(quán)。
命令行
第一種方式也是最粗暴的,在執(zhí)行 maven 命令時(shí)傳遞 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
由于 和 中的鏡像可能不是來自于同一個(gè) Registry 倉庫,因此既要配置 from 的用戶名密碼,也要配置 to 的用戶名密碼。
執(zhí)行完畢后,通過命令行,或者可視化工具,查看是否被 push 上去(此處我使用的工具是 Harbor)。
配置文件
使用命令行方式每次執(zhí)行都要輸入那么長一串命令,這樣實(shí)在是不方便。另一種方法是在 pom.xml 文件直接將用戶名密碼配置進(jìn)去,形如:
<plugin>
??<groupId>com.google.cloud.toolsgroupId>
??<artifactId>jib-maven-pluginartifactId>
??<version>1.8.0version>
??<configuration>
????<from>
??????<image>harbor.jitwxs-inc.com/base/java:8-jdk-alpineimage>
??????<auth>
????????<username>my_usernameusername>
????????<password>my_passwordpassword>
??????auth>
????from>
????<to>
??????<image>harbor.jitwxs-inc.com/sample/${artifactId}:v1image>
??????<auth>
????????<username>my_usernameusername>
????????<password>my_passwordpassword>
??????auth>
????to>
????<allowInsecureRegistries>trueallowInsecureRegistries>
??configuration>
plugin>
即給 from 和 to 標(biāo)簽都加上 標(biāo)簽,但是這種方式實(shí)在是不夠優(yōu)雅,因?yàn)閷⒂脩裘艽a硬編碼在代碼中會帶來安全性問題。
合適的方法是配置在 Maven 的 settings.xml 配置文件中,在 標(biāo)簽中,新增一個(gè) 節(jié)點(diǎn),配置 Registry 倉庫的用戶名密碼。
<servers>
????...
????<server>
??????<id>harbor.jitwxs-inc.comid>
??????<username>adminusername>
??????<password>adminpassword>
??????<configuration>
????????<email>[email protected]email>
??????configuration>
????server>
servers>
配置完畢后,讓我們把項(xiàng)目的 tag 從 v1 修改為 v2,再執(zhí)行次命令驗(yàn)證下:
mvn clean package -DskipTests jib:build
可以看到正常被 push 上去了。最后官方文檔詳細(xì)介紹了各種鑒權(quán)方式的使用,參見:https://github.com/GoogleContainerTools/jib/tree/master/jib-maven-plugin#authentication-methods
本地構(gòu)建
下面試下在本地進(jìn)行構(gòu)建,首先使用 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
然后基于該鏡像,創(chuàng)建容器,并后臺運(yùn)行在 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,正確輸出。
綁定到生命周期
如果你不想單獨(dú)輸入 jib:build,你可以把 jib 綁定到 Maven 命令中,在插件中添加如下的 標(biāo)簽即可。
<plugin>
??<groupId>com.google.cloud.toolsgroupId>
??<artifactId>jib-maven-pluginartifactId>
??...
??<executions>
????<execution>
??????<phase>packagephase>
??????<goals>
????????<goal>buildgoal>
??????goals>
????execution>
??executions>
plugin>
然后通過 Maven 的 mvn package 命令就會自動構(gòu)建鏡像了。
驗(yàn)證
這里推薦一個(gè)工具 dive, dive 能夠通過文件目錄的形式直觀地顯示一個(gè)鏡像中的每個(gè)鏡像層內(nèi)的內(nèi)容,便于查看鏡像的分層信息。
./dive harbor.okcoin-inc.com/sample/springboot_jib:v1
“原文鏈接:https://blog.csdn.net/yuanlaijike/article/details/103606999
”
CKA 認(rèn)證培訓(xùn)
?點(diǎn)擊屏末?|?閱讀原文?|?即刻學(xué)習(xí)








