本篇內(nèi)容介紹了“Java SpringBoot項目怎么構建Docker鏡像調(diào)優(yōu)”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠?qū)W有所成!
創(chuàng)新互聯(lián)公司服務熱線:13518219792,為您提供成都網(wǎng)站建設網(wǎng)頁設計及定制高端網(wǎng)站建設服務,創(chuàng)新互聯(lián)公司網(wǎng)頁制作領域10多年,包括PE包裝袋等多個方面擁有豐富的網(wǎng)站營銷經(jīng)驗,選擇創(chuàng)新互聯(lián)公司,為網(wǎng)站保駕護航。
前言:
以前的 SpringCloud 微服務時代以 “Jar包" 為服務的基礎,每個服務都打成 Jar 供服務間相互關聯(lián)與調(diào)用。而 現(xiàn)在隨著 Kubernetes 流行,已經(jīng)變遷到一個鏡像一個服務,依靠 Kubernetes 對鏡像的統(tǒng)一編排進行對服務進行統(tǒng)一管理。在對 Kubernetes 微服務實踐過程中,接觸最多的肯定莫過于 Docker 鏡像。由于本人使用的編程語言是 Java,所以對 Java SpringBoot 項目接觸比較多,所以比較關心如何更好的通過 Dockerfile 編譯 Docker 的鏡像。
Kubernetes 微服務簡單說就是一群鏡像間的排列組合與相互間調(diào)的關系,故而如何編譯鏡像會使服務性能更優(yōu),使鏡像構建、推送、拉取速度更快,使其占用網(wǎng)絡資源更少這里優(yōu)化,更易使用成為了一個重中之重的事情,也是一個非常值得琢磨的問題。這里我將對 SpringBoot 項目打包 Docker 鏡像如何寫 Dockerfile 的探究進行簡單敘述。
系統(tǒng)環(huán)境:
Docker 版本:18.09.3
Open JDK 基礎鏡像版本:openjdk:8u212-b04-jre-slim
測試用的鏡像倉庫:阿里云 Docker Hub
項目 Github:https://github.com/my-dlq/blog-example/tree/master/springboot-dockerfile
這里將用常規(guī) SpringBoot 編譯 Docker 鏡像的 Dockerfile 寫法,感受下這種方式編譯的鏡像用起來如何。
這里準備一個經(jīng)過 Maven 編譯后的普通的 springboot 項目來進行 Docker 鏡像構建,項目內(nèi)容如下圖所示,可以看到要用到的就是里面的應用程序的 Jar 文件,將其存入鏡像內(nèi)完成鏡像構建任務。
jar 文件大?。?0.86mb
構建 Docker 鏡像需要提前準備 Dockerfile 文件,這個 Dockerfile 文件中的內(nèi)容為構建 Docker 鏡像執(zhí)行的指令。下面是一個常用的 SpringBoot 構建 Docker 鏡像的 Dockerfile,將它放入 Java 源碼目錄(target 的上級目錄),確保下面設置的 Dockerfile 腳本中設置的路徑和 target 路徑對應。
FROM openjdk:8u212-b04-jre-slim VOLUME /tmp ADD target/*.jar app.jar RUN sh -c 'touch /app.jar' ENV JAVA_OPTS="-Duser.timezone=Asia/Shanghai" ENV APP_OPTS="" ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /app.jar $APP_OPTS" ]
通過 Docker build 命令構建 Docker 鏡像,觀察編譯的時間。
由于后續(xù)需要將鏡像推送到 Aliyun Docker 倉庫,所以鏡像前綴用了 Aliyun。
time:此參數(shù)會顯示執(zhí)行過程經(jīng)過的時間
$ time docker build -t registry.cn-beijing.aliyuncs.com/mydlq/springboot:0.0.1 .
構建過程
Sending build context to Docker daemon 148.7MB Step 1/7 : FROM openjdk:8u212-b04-jre-slim 8u212-b04-jre-slim: Pulling from library/openjdk 743f2d6c1f65: Already exists b83e581826a6: Pull complete 04305660f45e: Pull complete bbe7020b5561: Pull complete Digest: sha256:a5bcd678408a5fe94d13e486d500983ee6fa594940cbbe137670fbb90030456c Status: Downloaded newer image for openjdk:8u212-b04-jre-slim --->; 7c6b62cf60ee Step 2/7 : VOLUME /tmp --->; Running in 13a67ab65d2b Removing intermediate container 13a67ab65d2b --->; 52011f49ddef Step 3/7 : ADD target/*.jar app.jar --->; 26aa41a404fd Step 4/7 : RUN sh -c 'touch /app.jar' --->; Running in 722e7e44e04d Removing intermediate container 722e7e44e04d --->; 7baedb10ec62 Step 5/7 : ENV JAVA_OPTS="-Duser.timezone=Asia/Shanghai" --->; Running in 2681d0c5edac Removing intermediate container 2681d0c5edac --->; 5ef4a794b992 Step 6/7 : ENV APP_OPTS="" --->; Running in 5c8924a2a49d Removing intermediate container 5c8924a2a49d --->; fba87c19053a Step 7/7 : ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /app.jar $APP_OPTS" ] --->; Running in c4cf97009b3c Removing intermediate container c4cf97009b3c ---> d5f30cdfeb81 Successfully built d5f30cdfeb81 Successfully tagged registry.cn-beijing.aliyuncs.com/mydlq/springboot:0.0.1 real 0m13.778s user 0m0.078s sys 0m0.153s
看到這次編譯在 14s內(nèi)完成。
將鏡像推送到 Aliyun 倉庫,然后查看并記錄推送時間
$ time docker push registry.cn-beijing.aliyuncs.com/mydlq/springboot:0.0.1
執(zhí)行過程
The push refers to repository [registry.cn-beijing.aliyuncs.com/mydlq/springboot] cc1a2376d7c0: Pushed 2b940d07e9e7: Pushed 9544e87fb8dc: Pushed feb5d0e1e192: Pushed 8fd22162ddab: Pushed 6270adb5794c: Pushed 0.0.1: digest: sha256:dc60d304383b1441941ca4e9abc08db775d7be57ccb7c534c929b34ff064a62f size: 1583 real 0m24.335s user 0m0.052s sys 0m0.059s
看到這次在 25s內(nèi)完成。
這里切換到另一臺服務器上進行鏡像拉取操作,觀察鏡像拉取時間。
$ time docker pull registry.cn-beijing.aliyuncs.com/mydlq/springboot:0.0.1
拉取過程
0.0.1: Pulling from mydlq/springboot 743f2d6c1f65: Already exists b83e581826a6: Pull complete 04305660f45e: Pull complete bbe7020b5561: Pull complete 4847672cbfa5: Pull complete b60476972fc4: Pull complete Digest: sha256:dc60d304383b1441941ca4e9abc08db775d7be57ccb7c534c929b34ff064a62f Status: Downloaded newer image for registry.cn-beijing.aliyuncs.com/mydlq/springboot:0.0.1 real 0m27.528s user 0m0.033s sys 0m0.192s
看到這次拉取總共用時 28s內(nèi)完成。
這里將源碼的 JAVA 文件內(nèi)容修改,然后重新打 Jar 包,這樣再次嘗試編譯、推送、拉取過程,由于 Docker 在執(zhí)行構建時會采用分層緩存,所以這是一個執(zhí)行較快過程。
(1)、編譯
$ time docker build -t registry.cn-beijing.aliyuncs.com/mydlq/springboot:0.0.2 . Sending build context to Docker daemon 148.7MB Step 1/7 : FROM openjdk:8u212-b04-jre-slim --->; 7c6b62cf60ee Step 2/7 : VOLUME /tmp --->; Using cache --->; 52011f49ddef Step 3/7 : ADD target/*.jar app.jar --->; c67160dd2a23 Step 4/7 : RUN sh -c 'touch /app.jar' --->; Running in 474900d843a2 Removing intermediate container 474900d843a2 --->; 3ce9a8bb2600 Step 5/7 : ENV JAVA_OPTS="-Duser.timezone=Asia/Shanghai" --->; Running in f48620b1ad36 Removing intermediate container f48620b1ad36 --->; 0478f8f14e5b Step 6/7 : ENV APP_OPTS="" --->; Running in 98485fb15fc8 Removing intermediate container 98485fb15fc8 --->; 0b567c848027 Step 7/7 : ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /app.jar $APP_OPTS" ] --->; Running in e32242fc6efe Removing intermediate container e32242fc6efe --->; 7b223b23ebfd Successfully built 7b223b23ebfd Successfully tagged registry.cn-beijing.aliyuncs.com/mydlq/springboot:0.0.2 real 0m3.190s user 0m0.039s sys 0m0.403s
可以看到在編譯鏡像過程中,前1、2層用的緩存,所以速度非???。總編譯過程耗時 4s內(nèi)完成。
(2)、推送
$ time docker push registry.cn-beijing.aliyuncs.com/mydlq/springboot:0.0.2 The push refers to repository [registry.cn-beijing.aliyuncs.com/mydlq/springboot] d66a2fec30b5: Pushed f4da2c7581aa: Pushed 9544e87fb8dc: Layer already exists feb5d0e1e192: Layer already exists 8fd22162ddab: Layer already exists 6270adb5794c: Layer already exists real 0m20.816s user 0m0.024s sys 0m0.081s
可以看到只推送了前兩層,其它四次由于遠程倉庫未變化,所以沒有推送。整個推送過程耗時 21s內(nèi)完成。
(3)、拉取
$ time docker pull registry.cn-beijing.aliyuncs.com/mydlq/springboot:0.0.2 0.0.2: Pulling from mydlq/springboot 743f2d6c1f65: Already exists b83e581826a6: Already exists 04305660f45e: Already exists bbe7020b5561: Already exists d7e364f0d94a: Pull complete 8d688ada35b1: Pull complete Digest: sha256:7c13c40fa92ec2fdc3a8dfdd3232be1be9c1a1a99bf123743ff2a43907ee03dc Status: Downloaded newer image for registry.cn-beijing.aliyuncs.com/mydlq/springboot:0.0.2 real 0m23.214s user 0m0.053s sys 0m0.097s
本地以及緩存前四層,只拉取有變化的后兩層。這個過程耗時 24s內(nèi)完成。
通過這種方式對 SpringBoot 項目構建 Docker 鏡像來使用,給我的感受就是只要源碼中發(fā)生一點點變化,那么 SpringBoot 項目就需要將項目經(jīng)過 Maven 編譯后再經(jīng)過 Docker 鏡像構建,每次都會將一個 70M+ 的應用 Jar 文件存入 Docker 中,有時候明明就改了一個字母,可能又得把整個程序 Jar 重新存入 Docker 鏡像中,然后在推送和拉取過程中,每次都得推一個大的鏡像或者拉取一個大的鏡像來進行傳輸,感覺非常不方便。
Docker 為了節(jié)約存儲空間,所以采用了分層存儲概念。共享數(shù)據(jù)會對鏡像和容器進行分層,不同鏡像可以共享相同數(shù)據(jù),并且在鏡像上為容器分配一個 RW 層來加快容器的啟動順序。
在構建鏡像的過程中 Docker 將按照 Dockerfile 中指定的順序逐步執(zhí)行 Dockerfile 中的指令。隨著每條指令的檢查,Docker 將在其緩存中查找可重用的現(xiàn)有鏡像,而不是創(chuàng)建一個新的(重復)鏡像。
Dockerfile 的每一行命令都創(chuàng)建新的一層,包含了這一行命令執(zhí)行前后文件系統(tǒng)的變化。為了優(yōu)化這個過程,Docker 使用了一種緩存機制:只要這一行命令不變,那么結果和上一次是一樣的,直接使用上一次的結果即可。
為了充分利用層級緩存,我們必須要理解 Dockerfile 中的命令行是如何工作的,尤其是RUN,ADD和COPY這幾個命令。
參考 Docker 文檔了解 Docker 鏡像緩存:https://docs.docker.com/develop/develop-images/dockerfile_best-practices/
SpringBoot 編譯成鏡像后,底層會是一個系統(tǒng),如 Ubantu,上一層是依賴的 JDK 層,然后才是 SpringBoot 層,最下面兩層我們無法操作,考慮優(yōu)化只能是 SpringBoot 層琢磨。
從上面實驗中了解到之所以每次編譯、推送、拉取過程中較為緩慢,原因就是龐大的鏡像文件。了解到 Docker 緩存概念后就就產(chǎn)生一種想法,如果不經(jīng)常改變的文件緩存起來,將常改動的文件不進行緩存。由于 SpringBoot 項目是經(jīng)常變換的,那么應該怎么利用緩存機制來實現(xiàn)呢?如果強行利用緩存那么每次打的鏡像不都是緩存中的舊的程序內(nèi)容嗎。
所以就考慮一下應用 Jar 包里面都包含了什么文件, Java 的哪些文件是經(jīng)常變動的,哪些不經(jīng)常變動,對此,下面將針對 SpringBoot 打的應用 Jar 包進行分析。
顯示解壓后的列表,查看各個文件夾大小
$ tree -L 3 --si --du . ├── [ 74M] BOOT-INF │ ├── [2.1k] classes │ └── [ 74M] lib ├── [ 649] META-INF │ ├── [ 552] MANIFEST.MF │ └── [ 59] maven └── [ 67] org └── [ 38] springframework
可以看到最大的文件就是 lib 這個文件夾,打開這個文件夾,里面是一堆相關依賴 Jar,這其中一個 Jar 不大,但是一堆 Jar 組合起來就非常大了,一般 SpringBoot 的項目依賴 Jar 大小維持在 40MB ~ 160MB。
在看看 org 文件夾,里面代碼加起來才幾百 KB。故此 SpringBoot 程序 Jar 包就是這些 Classes 文件和依賴的 Jar 組成,這些依賴 Jar 總共 74 MB,幾乎占了這個應用 Jar 包的全部大小。
如果一個 Jar 包只包含 class 文件,那么這個 Jar 包的大小可能就幾百 KB?,F(xiàn)在要探究一下,如果將 lib 依賴的 Jar 和 class 分離,設置應用的 Jar 包只包含 class 文件,將 lib 文件夾下的 Jar 文件放在 SpringBoot Jar 的外面。
當我們寫一個程序的時候,常常所依賴的 Jar 不會經(jīng)常變動,變動多的是源代碼程序,依賴的 Jar 包非常大而源代碼非常小。仔細思考一下,如果在打包成 Docker 鏡像的時候?qū)靡蕾嚨?Jar 包單獨設置一層緩存,而應用 Jar 包只包含 Class 文件,這樣在 Docker 執(zhí)行編譯、推送、拉取過程中,除了第一次是全部都要執(zhí)行外,再往后的執(zhí)行編譯、推送、拉取過程中,只會操作改動的那個只包含 Class 的 Jar 文件,就幾百 KB,可以說是能夠瞬間完成這個過程。所以思考一下,如何將 lib 文件夾下的依賴 Jar 包和應用 Jar 包分離開來。
經(jīng)過查找很多相關資料,發(fā)現(xiàn) SpringBoot 的 Maven 插件在執(zhí)行 Maven 編譯打 Jar 包時候做了很多事情,如果改變某些插件的打包邏輯,致使打應用 Jar 時候?qū)?lib 文件夾下所有的 Jar 包都拷貝到應用 Jar 外面,只留下編譯好的字節(jié)碼文件。
將這幾個 Maven 工具引入到項目 pom.xml 中
org.apache.maven.plugins maven-jar-plugin true lib/ org.springframework.boot spring-boot-maven-plugin nothing nothing org.apache.maven.plugins maven-dependency-plugin copy-dependencies prepare-package copy-dependencies ${project.build.directory}/lib
執(zhí)行 Maven 命令打包 Jar
$ mvn clean install
當 Maven 命令執(zhí)行完成后,查看 target 目錄如下圖:
然后測試下這個 Jar 文件是否能正常運行
$ java -jar springboot-helloworld-0.0.1.jar
然后看到運行日志,OK!下面將繼續(xù)進行 Dockerfile 改造工作。
> 項目 Github 地址:https://github.com/my-dlq/blog-example/tree/master/springboot-dockerfile
這里修改上面的 Dockerfile 文件,需要新增一層指令用于將 lib 目錄里面的依賴 Jar 復制到鏡像中,其它保持和上面 Dockerfile 一致。
FROM openjdk:8u212-b04-jre-slim VOLUME /tmp COPY target/lib/ ./lib/ ADD target/*.jar app.jar RUN sh -c 'touch /app.jar' ENV JAVA_OPTS="-Duser.timezone=Asia/Shanghai" ENV APP_OPTS="" ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /app.jar $APP_OPTS" ]
> 這里新增了一層指令,作用為將 lib 文件夾復制到鏡像之中,由于 Docker 緩存機制原因,這層一定要在復制應用 Jar 之前,這樣改造后每次只要 lib/ 文件夾里面的依賴 Jar 不變,就不會新創(chuàng)建層,而是復用緩存。
在執(zhí)行編譯、推送、拉取之前,先將服務器上次鏡像相關的所有資源都清除掉,然后再執(zhí)行。
(1)、編譯
$ time docker build -t registry.cn-beijing.aliyuncs.com/mydlq/springboot:0.0.1 . Sending build context to Docker daemon 223.2MB Step 1/8 : FROM openjdk:8u212-b04-jre-slim 8u212-b04-jre-slim: Pulling from library/openjdk 743f2d6c1f65: Already exists b83e581826a6: Pull complete 04305660f45e: Pull complete bbe7020b5561: Pull complete Digest: sha256:a5bcd678408a5fe94d13e486d500983ee6fa594940cbbe137670fbb90030456c Status: Downloaded newer image for openjdk:8u212-b04-jre-slim --->; 7c6b62cf60ee Step 2/8 : VOLUME /tmp --->; Running in 529369acab24 Removing intermediate container 529369acab24 --->; ad689d937118 Step 3/8 : COPY target/lib/ ./lib/ --->; 029a64c15853 Step 4/8 : ADD target/*.jar app.jar --->; 6265a83a1b90 Step 5/8 : RUN sh -c 'touch /app.jar' --->; Running in 839032a58e6b Removing intermediate container 839032a58e6b --->; 5d877dc35b2b Step 6/8 : ENV JAVA_OPTS="-Duser.timezone=Asia/Shanghai" --->; Running in 4043994c5fed Removing intermediate container 4043994c5fed --->; 7cf32beb571f Step 7/8 : ENV APP_OPTS="" --->; Running in b7dcfa10458a Removing intermediate container b7dcfa10458a --->; b6b332bcf0e6 Step 8/8 : ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /app.jar $APP_OPTS" ] --->; Running in 539093461b59 Removing intermediate container 539093461b59 --->; d4c095c4ffec Successfully built d4c095c4ffec Successfully tagged registry.cn-beijing.aliyuncs.com/mydlq/springboot:0.0.1 real 0m22.983s user 0m0.051s sys 0m0.540s
(2)、推送
$ time docker push registry.cn-beijing.aliyuncs.com/mydlq/springboot:0.0.1 The push refers to repository [registry.cn-beijing.aliyuncs.com/mydlq/springboot] c16749205e05: Pushed 7fef1a146748: Pushed a3bae74bbdf2: Pushed 9544e87fb8dc: Pushed feb5d0e1e192: Pushed 8fd22162ddab: Pushed 6270adb5794c: Pushed 0.0.1: digest: sha256:e2f4db740880dbe5338b823112ba9467fedf8b27cd75572611d0d3837c80f157 size: 1789 real 0m30.335s user 0m0.052s sys 0m0.059s
(3)、拉取
$ time docker pull registry.cn-beijing.aliyuncs.com/mydlq/springboot:0.0.1 0.0.1: Pulling from mydlq/springboot 743f2d6c1f65: Already exists b83e581826a6: Pull complete 04305660f45e: Pull complete bbe7020b5561: Pull complete de6c4f15d75b: Pull complete 7066947b7d89: Pull complete e0742de67c75: Pull complete Digest: sha256:e2f4db740880dbe5338b823112ba9467fedf8b27cd75572611d0d3837c80f157 Status: Downloaded newer image for registry.cn-beijing.aliyuncs.com/mydlq/springboot:0.0.1 real 0m36.585s user 0m0.024s sys 0m0.092s
(1)、編譯
$ time docker build -t registry.cn-beijing.aliyuncs.com/mydlq/springboot:0.0.2 . Sending build context to Docker daemon 223.2MB Step 1/8 : FROM openjdk:8u212-b04-jre-slim --->; 7c6b62cf60ee Step 2/8 : VOLUME /tmp --->; Using cache --->; ad689d937118 Step 3/8 : COPY target/lib/ ./lib/ --->; Using cache --->; 029a64c15853 Step 4/8 : ADD target/*.jar app.jar --->; 563773953844 Step 5/8 : RUN sh -c 'touch /app.jar' --->; Running in 3b9df57802bd Removing intermediate container 3b9df57802bd --->; 706a0d47317f Step 6/8 : ENV JAVA_OPTS="-Duser.timezone=Asia/Shanghai" --->; Running in defda61452bf Removing intermediate container defda61452bf --->; 742c7c926374 Step 7/8 : ENV APP_OPTS="" --->; Running in f09b81d054dd Removing intermediate container f09b81d054dd --->; 929ed5f8b12a Step 8/8 : ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /app.jar $APP_OPTS" ] --->; Running in 5dc66a8fc1e6 Removing intermediate container 5dc66a8fc1e6 --->; c4942b10992c Successfully built c4942b10992c Successfully tagged registry.cn-beijing.aliyuncs.com/mydlq/springboot:0.0.2 real 0m2.524s user 0m0.051s sys 0m0.493s
可以看到,這次在第 3 層直接用的緩存,整個編譯過程才花了 2.5秒時間
(2)、推送
$ time docker push registry.cn-beijing.aliyuncs.com/mydlq/springboot:0.0.2 The push refers to repository [registry.cn-beijing.aliyuncs.com/mydlq/springboot] d719b9540809: Pushed d45bf4c5fb92: Pushed a3bae74bbdf2: Layer already exists 9544e87fb8dc: Layer already exists feb5d0e1e192: Layer already exists 8fd22162ddab: Layer already exists 6270adb5794c: Layer already exists 0.0.2: digest: sha256:b46d81b153ec64321caaae7ab28da0e362ed7d720a7f0775ea8d1f7bef310d00 size: 1789 real 0m0.168s user 0m0.016s sys 0m0.032s
可以看到在 0.2s內(nèi)就完成了鏡像推送
(3)、拉取
$ time docker pull registry.cn-beijing.aliyuncs.com/mydlq/springboot:0.0.2 0.0.2: Pulling from mydlq/springboot 743f2d6c1f65: Already exists b83e581826a6: Already exists 04305660f45e: Already exists bbe7020b5561: Already exists de6c4f15d75b: Already exists 1c77cc70cc41: Pull complete aa5b8cbca568: Pull complete Digest: sha256:b46d81b153ec64321caaae7ab28da0e362ed7d720a7f0775ea8d1f7bef310d00 Status: Downloaded newer image for registry.cn-beijing.aliyuncs.com/mydlq/springboot:0.0.2 real 0m1.947s user 0m0.017s sys 0m0.042s
可以看到在 2s內(nèi)就完成了鏡像拉取
由于網(wǎng)絡波動和系統(tǒng)變化,所以時間只能當做參考,不過執(zhí)行編譯、推送、拉取過程的確快了不少,大部分用文件都進行了緩存,只有幾百 KB 的流量交互自然速度比幾十 MB 甚至幾百 MB 速度要快很多。
最后說明一下,這種做法只是提供了一種參考,現(xiàn)在的微服務服務 Docker 鏡像化以來,維護的是整個鏡像而不是一個服務程序,所以關心的是 Docker 鏡像能否能正常運行,怎么構建鏡像會使構建的鏡像更好用。
在生產(chǎn)環(huán)境下由于版本變化較慢,不會動不動就更新,所以在生產(chǎn)環(huán)境下暫時最好還是按部就班,應用原來 SpringBoot 鏡像編譯方式以確保安裝(除非已大量實例驗證該構建方法)。
“Java SpringBoot項目怎么構建Docker鏡像調(diào)優(yōu)”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關的知識可以關注創(chuàng)新互聯(lián)網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實用文章!