為Java應(yīng)用創(chuàng)建Docker鏡像的三種方式
在 Dockerfiles 出現(xiàn)的很久之前,Java 開(kāi)發(fā)者大多使用單體應(yīng)用方式部署(WARs, JARs, EARs, 等等)?,F(xiàn)在如你所知,最好的做法是為每個(gè)小業(yè)務(wù)單獨(dú)部署的微服務(wù)方式。你構(gòu)建的不是一個(gè)巨大的單體應(yīng)用程序,而是使多個(gè)可以獨(dú)立運(yùn)行的小服務(wù)。
這正是 Docker 的用武之地。如果你想升級(jí)一個(gè)服務(wù),只需要針對(duì)需要升級(jí)的服務(wù)單元重新構(gòu)建一個(gè)新的 Docker 鏡像,二不是像之前那樣針對(duì)整個(gè)應(yīng)用重新部署 jar/war/ear 包。
這篇文章我將介紹3種不同的方式為 Java 應(yīng)用創(chuàng)建 Docker 鏡像。如果你希望跟著介紹體驗(yàn)這3種方式,請(qǐng)?jiān)谶@里 clone 的我倉(cāng)庫(kù)代碼 https://github.com/annabaker/docker-with-java-demos 。
先決條件
- Docker 已安裝
- Maven 已安裝(針對(duì)第一種方式)
- 一個(gè)簡(jiǎn)單的 Spring Boot 應(yīng)用(我已經(jīng)使用 Spring Initializr創(chuàng)建了一個(gè) Spring Web 項(xiàng)目)
方式一:只構(gòu)建部署包
這種方式我們會(huì)先使用 Maven(或者其他構(gòu)建工具)控制部署包的過(guò)程。
首先解壓之前使用 Spring Initializr 工具生成的構(gòu)建包。在 Spring Boot 應(yīng)用的項(xiàng)目目錄中,創(chuàng)建一個(gè) Dockerfile 文件。在控制臺(tái)中執(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
語(yǔ)句指明我們使用的父鏡像 -
COPY
語(yǔ)句會(huì)把本地 Maven 構(gòu)建的的 jar 包復(fù)制到鏡像中 -
CMD
語(yǔ)句告訴 Docker 一旦容器啟動(dòng)后內(nèi)部執(zhí)行這個(gè)命令
現(xiàn)在,我們先使用 Maven 構(gòu)建我們的 .jar 包:
mvn clean package
然后開(kāi)始構(gòu)建 Docker 鏡像。下面的命令告訴 Docker 在當(dāng)前目錄去獲取 Dockerfile。雖然不是強(qiáng)制的,但是作為慣例我們使用用戶名/鏡像名
的方式命名。-t
標(biāo)識(shí)符表示一個(gè) Docker 標(biāo)簽(tag),這里是 1.0-SNAPSHOT
。如果不指定一個(gè) tag, Docker 會(huì)使用默認(rèn)的 tag:latest
。
$ docker build -t anna/docker-package-only-build-demo:1.0-SNAPSHOT .
通過(guò)剛才構(gòu)建的鏡像來(lái)運(yùn)行一個(gè)容器:
$ docker run -d -p 8080:8080 anna/docker-package-only-build-demo:1.0-SNAPSHOT
-d :在后臺(tái)運(yùn)行容器,-p 映射本地 8080 端口到容器中的 8080 端口。
訪問(wèn) localhost:8080,你應(yīng)該會(huì)看到下面頁(yè)面的內(nèi)容:
當(dāng)你對(duì)測(cè)試結(jié)果感到滿意后,關(guān)閉容器:
$ docker stop <container_id>
優(yōu)點(diǎn)
- 產(chǎn)生了一個(gè)輕量級(jí)的鏡像
- 在 Docker 鏡像中不需要依賴 Maven
- 在 Docker 鏡像中不依賴我們應(yīng)用的任何依賴
- 當(dāng)應(yīng)用層面修改后,仍然可以使用本地 Maven 倉(cāng)庫(kù)的緩存,這在后面的方式2、方式3中再繼續(xù)討論
缺點(diǎn)
- 宿主機(jī)需要依賴 Maven 和 Jdk 環(huán)境
- 如果 Maven 構(gòu)建失敗或者沒(méi)有事先執(zhí)行構(gòu)建,那么 Docker 也將構(gòu)建失敗——如果你希望與其他工具集成,用當(dāng)前 Dockerfile 來(lái)自動(dòng)構(gòu)建鏡像時(shí)將成為一個(gè)問(wèn)題
方式二:普通 Docker 構(gòu)建
在“普通” Dcoker 構(gòu)建中,Docker 會(huì)控制構(gòu)建打包過(guò)程。
修改之前的 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)在,我們和之前一樣來(lái)構(gòu)建一個(gè)鏡像:
$ docker build -t anna/docker-normal-build-demo:1.0-SNAPSHOT .
然后,運(yùn)行一個(gè)容器:
$ docker run -d -p 8080:8080 anna/docker-normal-build-demo:1.0-SNAPSHOT
繼續(xù)驗(yàn)證容器運(yùn)行結(jié)果,訪問(wèn) localhost:8080。驗(yàn)證成功后再停止容器。
優(yōu)點(diǎn)
- Docker 控制了打包過(guò)程,所以這種方式不需要宿主機(jī)事先安裝構(gòu)建工具和 Jdk 環(huán)境
- 使用其他工具集成時(shí)很友好,可以直接執(zhí)行 Dockerfile 來(lái)構(gòu)建鏡像
缺點(diǎn)
- 在這三種方式中,生成的鏡像文件最大
- 不僅包含了部署包,還包含了所有的代碼依賴和部署工具,這在運(yùn)行時(shí)候是不需要的
- 如果應(yīng)用層需要重新構(gòu)建,maven打包時(shí)會(huì)從遠(yuǎn)程倉(cāng)庫(kù)重新下載依賴包(不能使用maven的本地緩存)
方式三:多階段構(gòu)建(理想方式)
在多階段構(gòu)建方式中,我們給每個(gè)階段使用一個(gè) FROM
語(yǔ)句。每個(gè) FROM
語(yǔ)句會(huì)創(chuàng)建一個(gè)新的基本層,而且會(huì)拋棄之前 FROM
階段我們不需要的所有東西。
修改你的 Dockerfile 為如下內(nèi)容:
# the first stage of our build will use a maven 3.6.1 parent image
FROM maven:3.6.1-jdk-8-alpine AS MAVEN_BUILD
# copy the pom and src code to the container
COPY ./ ./
# package our application code
RUN mvn clean package
# the second stage of our build will use open jdk 8 on alpine 3.9
FROM openjdk:8-jre-alpine3.9
# copy only the artifacts we need from the first stage and discard the rest
COPY --from=MAVEN_BUILD /docker-multi-stage-build-demo/target/demo-0.0.1-SNAPSHOT.jar /demo.jar
# set the startup command to execute the jar
CMD ["java", "-jar", "/demo.jar"]
構(gòu)建鏡像:
$ docker build -t anna/docker-multi-stage-build-demo:1.0-SNAPSHOT .
然后運(yùn)行容器:
$ docker run -d -p 8080:8080 anna/docker-multi-stage-build-demo:1.0-SNAPSHOT
優(yōu)點(diǎn)
- 生成一個(gè)輕量級(jí)的 Docker 鏡像
- 不需要宿主機(jī)事先安裝構(gòu)建工具和 Jdk 環(huán)境(Docker 控制了打包過(guò)程)
- 可以方便集成自動(dòng)部署工具自動(dòng)執(zhí)行當(dāng)前的 Dockerfile來(lái)構(gòu)建鏡像
- 從第一個(gè)階段到第二個(gè)階段我們只復(fù)制了需要的部署包(例如:和方案二相比,使用這種方式,應(yīng)用的依賴沒(méi)有被打包到最終的鏡像中)
- 可以按照需求創(chuàng)建足夠多的階段
- 可以使用
–target
標(biāo)簽在任何特定的階段停止構(gòu)建,例如:
docker build — target MAVEN_BUILD -t anna/docker-multi-stage-build-demo:1.0-SNAPSHOT .
缺點(diǎn)
如果應(yīng)用層需要需要重新構(gòu)建,maven打包時(shí)會(huì)從遠(yuǎn)程倉(cāng)庫(kù)重新下載依賴包(不能使用maven的本地緩存)
驗(yàn)證:鏡像有多大
在命令行,運(yùn)行:
docker image ls
你會(huì)看到類似下面信息:
如你所見(jiàn),多階段構(gòu)建的鏡像最小,普通構(gòu)建的鏡像最大。這和預(yù)料的一樣,因?yàn)槠胀?gòu)建包含了我們的應(yīng)用代碼,所有依賴包,以及打包工具;但是都階段構(gòu)建只包含我們需要的東西。
結(jié)論
按照上面介紹的三種 Docker 構(gòu)建鏡像的方法,多階段構(gòu)建是最理想的。你可以兩全其美——Dockerr控制打包代碼,但是你只提取需要的最終部署包。當(dāng)在云空間存儲(chǔ)容器時(shí),這一點(diǎn)變得尤為重要。
- 你可以花費(fèi)更少的時(shí)間去構(gòu)建和傳輸容器,因?yàn)殓R像更小
- 費(fèi)用——鏡像越小,占用的存儲(chǔ)越小越便宜
- 更小的表面積,也就是從我們的鏡像中移除額外的依賴,使它更不容易受到攻擊
謝謝你看到這里,希望這邊文章對(duì)你有所幫助!你可以在這里查看三種構(gòu)建方式的源碼:https://github.com/annabaker/docker-with-java-demos文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-426331.html
原文地址(需要梯子):https://medium.com/containers-101/three-ways-to-create-docker-images-for-java-e139805ecb7f文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-426331.html
到了這里,關(guān)于為Java應(yīng)用創(chuàng)建Docker鏡像的三種方式的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!