為Java應(yīng)用創(chuàng)建Docker鏡像的3種方式總結(jié)
為Java應(yīng)用創(chuàng)建Docker鏡像的三種方式
在 Dockerfiles 出現(xiàn)的很久之前,Java 開發(fā)者大多使用單體應(yīng)用方式部署(WARs, JARs, EARs, 等等)。現(xiàn)在如你所知,最好的做法是為每個小業(yè)務(wù)單獨部署的微服務(wù)方式。你構(gòu)建的不是一個巨大的單體應(yīng)用程序,而是使多個可以獨立運行的小服務(wù)。
這正是 Docker 的用武之地。如果你想升級一個服務(wù),只需要針對需要升級的服務(wù)單元重新構(gòu)建一個新的 Docker 鏡像,二不是像之前那樣針對整個應(yīng)用重新部署 jar/war/ear 包。
這篇文章我將介紹3種不同的方式為 Java 應(yīng)用創(chuàng)建 Docker 鏡像。如果你希望跟著介紹體驗這3種方式,請在這里 clone 的我倉庫代碼 https://github.com/annabaker/docker-with-java-demos 。
先決條件
- Docker 已安裝
- Maven 已安裝(針對第一種方式)
- 一個簡單的 Spring Boot 應(yīng)用(我已經(jīng)使用 Spring Initializr創(chuàng)建了一個 Spring Web 項目)
方式一:只構(gòu)建部署包
這種方式我們會先使用 Maven(或者其他構(gòu)建工具)控制部署包的過程。
首先解壓之前使用 Spring Initializr 工具生成的構(gòu)建包。在 Spring Boot 應(yīng)用的項目目錄中,創(chuàng)建一個 Dockerfile 文件。在控制臺中執(zhí)行以下命令:
$ unzip demo.zip $ cd demo $ nano Dockerfile
復(fù)制以下內(nèi)容并保存:
# we will use openjdk 8 with alpine as it is a very small linux distro FROM openjdk:8-jre-alpine3.9 # copy the packaged jar file into our docker image COPY target/demo-0.0.1-SNAPSHOT.jar /demo.jar # set the startup command to execute the jar CMD ["java", "-jar", "/demo.jar"]
FROM
語句指明我們使用的父鏡像COPY
語句會把本地 Maven 構(gòu)建的的 jar 包復(fù)制到鏡像中CMD
語句告訴 Docker 一旦容器啟動后內(nèi)部執(zhí)行這個命令
現(xiàn)在,我們先使用 Maven 構(gòu)建我們的 .jar 包:
mvn clean package
然后開始構(gòu)建 Docker 鏡像。下面的命令告訴 Docker 在當(dāng)前目錄去獲取 Dockerfile。雖然不是強制的,但是作為慣例我們使用用戶名/鏡像名
的方式命名。-t
標識符表示一個 Docker 標簽(tag),這里是 1.0-SNAPSHOT
。如果不指定一個 tag, Docker 會使用默認的 tag:latest
。
$ docker build -t anna/docker-package-only-build-demo:1.0-SNAPSHOT .
通過剛才構(gòu)建的鏡像來運行一個容器:
$ docker run -d -p 8080:8080 anna/docker-package-only-build-demo:1.0-SNAPSHOT
-d :在后臺運行容器,-p 映射本地 8080 端口到容器中的 8080 端口。
訪問 localhost:8080,你應(yīng)該會看到下面頁面的內(nèi)容:
當(dāng)你對測試結(jié)果感到滿意后,關(guān)閉容器:
$ docker stop <container_id>
優(yōu)點
- 產(chǎn)生了一個輕量級的鏡像
- 在 Docker 鏡像中不需要依賴 Maven
- 在 Docker 鏡像中不依賴我們應(yīng)用的任何依賴
- 當(dāng)應(yīng)用層面修改后,仍然可以使用本地 Maven 倉庫的緩存,這在后面的方式2、方式3中再繼續(xù)討論
缺點
- 宿主機需要依賴 Maven 和 Jdk 環(huán)境
- 如果 Maven 構(gòu)建失敗或者沒有事先執(zhí)行構(gòu)建,那么 Docker 也將構(gòu)建失敗——如果你希望與其他工具集成,用當(dāng)前 Dockerfile 來自動構(gòu)建鏡像時將成為一個問題
方式二:普通 Docker 構(gòu)建
在“普通” Dcoker 構(gòu)建中,Docker 會控制構(gòu)建打包過程。
修改之前的 Dockerfile 文件為如下內(nèi)容:
# select parent image FROM maven:3.6.3-jdk-8 # copy the source tree and the pom.xml to our new container COPY ./ ./ # package our application code RUN mvn clean package # set the startup command to execute the jar CMD ["java", "-jar", "target/demo-0.0.1-SNAPSHOT.jar"]
現(xiàn)在,我們和之前一樣來構(gòu)建一個鏡像:
$ docker build -t anna/docker-normal-build-demo:1.0-SNAPSHOT .
然后,運行一個容器:
$ docker run -d -p 8080:8080 anna/docker-normal-build-demo:1.0-SNAPSHOT
繼續(xù)驗證容器運行結(jié)果,訪問 localhost:8080。驗證成功后再停止容器。
優(yōu)點
- Docker 控制了打包過程,所以這種方式不需要宿主機事先安裝構(gòu)建工具和 Jdk 環(huán)境
- 使用其他工具集成時很友好,可以直接執(zhí)行 Dockerfile 來構(gòu)建鏡像
缺點
- 在這三種方式中,生成的鏡像文件最大
- 不僅包含了部署包,還包含了所有的代碼依賴和部署工具,這在運行時候是不需要的
- 如果應(yīng)用層需要重新構(gòu)建,maven打包時會從遠程倉庫重新下載依賴包(不能使用maven的本地緩存)
方式三:多階段構(gòu)建(理想方式)
在多階段構(gòu)建方式中,我們給每個階段使用一個 FROM
語句。每個 FROM
語句會創(chuàng)建一個新的基本層,而且會拋棄之前 FROM
階段我們不需要的所有東西。
修改你的 Dockerfile 為如下內(nèi)容:
在這三種方式中,生成的鏡像文件最大 不僅包含了部署包,還包含了所有的代碼依賴和部署工具,這在運行時候是不需要的 如果應(yīng)用層需要重新構(gòu)建,maven打包時會從遠程倉庫重新下載依賴包(不能使用maven的本地緩存)
構(gòu)建鏡像:
$ docker build -t anna/docker-multi-stage-build-demo:1.0-SNAPSHOT .
然后運行容器:
$ docker run -d -p 8080:8080 anna/docker-multi-stage-build-demo:1.0-SNAPSHOT
優(yōu)點
- 生成一個輕量級的 Docker 鏡像
- 不需要宿主機事先安裝構(gòu)建工具和 Jdk 環(huán)境(Docker 控制了打包過程)
- 可以方便集成自動部署工具自動執(zhí)行當(dāng)前的 Dockerfile來構(gòu)建鏡像
- 從第一個階段到第二個階段我們只復(fù)制了需要的部署包(例如:和方案二相比,使用這種方式,應(yīng)用的依賴沒有被打包到最終的鏡像中)
- 可以按照需求創(chuàng)建足夠多的階段
- 可以使用 –target 標簽在任何特定的階段停止構(gòu)建,例如:
docker build — target MAVEN_BUILD -t anna/docker-multi-stage-build-demo:1.0-SNAPSHOT .
缺點
如果應(yīng)用層需要需要重新構(gòu)建,maven打包時會從遠程倉庫重新下載依賴包(不能使用maven的本地緩存)
驗證:鏡像有多大
在命令行,運行:
docker image ls
你會看到類似下面信息:
如你所見,多階段構(gòu)建的鏡像最小,普通構(gòu)建的鏡像最大。這和預(yù)料的一樣,因為普通構(gòu)建包含了我們的應(yīng)用代碼,所有依賴包,以及打包工具;但是都階段構(gòu)建只包含我們需要的東西。
結(jié)論
按照上面介紹的三種 Docker 構(gòu)建鏡像的方法,多階段構(gòu)建是最理想的。你可以兩全其美——Dockerr控制打包代碼,但是你只提取需要的最終部署包。當(dāng)在云空間存儲容器時,這一點變得尤為重要。
你可以花費更少的時間去構(gòu)建和傳輸容器,因為鏡像更小費用——鏡像越小,占用的存儲越小越便宜更小的表面積,也就是從我們的鏡像中移除額外的依賴,使它更不容易受到攻擊
謝謝你看到這里,希望這邊文章對你有所幫助!你可以在這里查看三種構(gòu)建方式的源碼:https://github.com/annabaker/docker-with-java-demos
原文地址(需要梯子):https://medium.com/containers-101/three-ways-to-create-docker-images-for-java-e139805ecb7f
總結(jié)
到此這篇關(guān)于為Java應(yīng)用創(chuàng)建Docker鏡像的3種方式總結(jié)的文章就介紹到這了,更多相關(guān)Java應(yīng)用創(chuàng)建Docker鏡像內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
try catch finally的執(zhí)行順序深入分析
首先執(zhí)行try,如果有異常執(zhí)行catch,無論如何都會執(zhí)行finally,當(dāng)有return以后,函數(shù)就會把這個數(shù)據(jù)存儲在某個位置,然后告訴主函數(shù),我不執(zhí)行了,接下來你執(zhí)行吧,所以函數(shù)就會推出2013-09-09springboot定時任務(wù)SchedulingConfigurer異步多線程實現(xiàn)方式
這篇文章主要介紹了springboot定時任務(wù)SchedulingConfigurer異步多線程實現(xiàn)方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-04-04java中實現(xiàn)map與對象相互轉(zhuǎn)換的幾種實現(xiàn)
這篇文章主要介紹了java中實現(xiàn)map與對象相互轉(zhuǎn)換的幾種實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07