javascript
8s 使用本地打包镜像_在Docker环境构建、打包和运行Spring Boot应用
從源主機(jī)復(fù)制應(yīng)用程序源代碼到鏡像的臨時(shí)構(gòu)建目錄
采用Maven完成應(yīng)用的編譯和打包,生成可執(zhí)行的JAR文件
采用JRE運(yùn)行JAR文件
package com.blog.samples.docker;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
定義Docker鏡像如下內(nèi)容是Dockerfile中定義的鏡像文件,盡管內(nèi)容不多,但包含了很多步的工作。我將在下面詳細(xì)解釋每一行。FROM maven:3.5.2-jdk-8-alpine AS MAVEN_BUILD
MAINTAINER Brian Hannaway
COPY pom.xml /build/
COPY src /build/src/
WORKDIR /build/
RUN mvn package
FROM openjdk:8-jre-alpine
WORKDIR /app
COPY --from=MAVEN_BUILD /build/target/docker-boot-intro-0.1.0.jar /app/
ENTRYPOINT ["java", "-jar", "docker-boot-intro-0.1.0.jar"]
代碼備注:FROM maven:3.5.2-jdk-8-alpine AS MAVEN_BUILD告知Docker采用Maven編譯器。maven:3.5.2-jdk-8-alpine構(gòu)建第一步采用的基礎(chǔ)鏡像,Docker將首先在本地查找鏡像,本地不存在后,將從DockerHub拉取。Maven會在最后階段被剔除掉(后續(xù)COPY命令介紹)考慮下載快速和鏡像大小控制的原因,選擇Alpine版的Maven鏡像。MAINTAINERBrianHannaway非必選項(xiàng),但是為映像作者提供一個(gè)接觸點(diǎn)可提高可維護(hù)性。(本實(shí)驗(yàn)應(yīng)用驗(yàn)證的點(diǎn))COPY pom.xml/build/在鏡像中創(chuàng)建一個(gè)build目錄, 并拷入pom.xml文件。COPY src/build/src/拷入src目錄到鏡像中build目錄。WORKDIR/build/設(shè)置build為工作目錄。后續(xù)任何命令都在此目錄中運(yùn)行。RUN mvnpackage執(zhí)行mvn包來運(yùn)行編譯和打包應(yīng)用,生成成可執(zhí)行的JAR文件。在第一次構(gòu)建鏡像時(shí),Maven將從公共Maven庫拉取所有需要的依賴項(xiàng),并將它們緩存在鏡像的本地。后續(xù)的構(gòu)建將使用這個(gè)緩存版的鏡像層,這意味著依賴項(xiàng)將在本地引用,而不必再次從外部拉取。至此,已經(jīng)完成了鏡像定義,只需等其構(gòu)建成一個(gè)可執(zhí)行的JAR文件。這是多階段構(gòu)建的第一部分。下一階段將獲取JAR并運(yùn)行它。FROM openjdk:8-jre-alpine告知Docker多階段構(gòu)建的下一步采用openjdk:8-jre-alpine的基礎(chǔ)鏡像。再次使用Java 8 JRE的Alpine版本,這一步的選擇其實(shí)比前面的Maven版本選擇更為重要,因?yàn)榇嬖谟谧罱K版的鏡像只是openjdk:8-jre-alpine,因此如果要盡可能控制最終鏡像大小的話,選擇輕量級JRE鏡像就非常重要。WORKDIR/app告知Docker在鏡像內(nèi)創(chuàng)建另一個(gè)/app工作目錄,后續(xù)任何命令都在此目錄中運(yùn)行。COPY--from=MAVEN_BUILD/build/target/docker-boot-intro-0.1.0.jar/app/告知Docker從MAVEN_BUILD階段的/build/target目錄復(fù)制ocker-boot-intro-0.1.0.jar到/app目錄。如前文所述,多階段構(gòu)建的優(yōu)勢就是允許用戶將特定的內(nèi)容從一個(gè)構(gòu)建階段復(fù)制到另一個(gè)構(gòu)建階段,并丟棄其他所有的內(nèi)容。如果需要保留從MAVENBUILD階段開始的所有內(nèi)容,那最終鏡像會包含Maven(包括Maven本地庫)工具,以及目標(biāo)目錄中生成的所有類文件。通過從MAVENBUILD階段選擇必須要的內(nèi)容,那最終得到的鏡像會小很多。ENTRYPOINT["java","-jar","app.jar"]告知Docker在容器運(yùn)行本鏡像時(shí),運(yùn)行哪些命令。本部分用冒號進(jìn)行多命令的隔離。本案例中,需要把執(zhí)行JAR文件復(fù)制到/app目錄運(yùn)行。構(gòu)建鏡像完成Docker鏡像定義后,就可以著手構(gòu)建。打開包含Dockerfile(根目錄)的目錄。運(yùn)行以下命令構(gòu)建鏡像:docker image build -t docker-boot-intro
-t參數(shù)為指定名稱和可選標(biāo)簽。如果不指定標(biāo)簽,Docker會自動標(biāo)記為最latest。$ docker image build -t docker-boot-intro .
Sending build context to Docker daemon 26.56MB
Step 1/10 : FROM maven:3.5.2-jdk-8-alpine AS MAVEN_BUILD
---> 293423a981a7
Step 2/10 : MAINTAINER Brian Hannaway
---> Using cache
---> db354a426bfd
Step 3/10 : COPY pom.xml /build/
---> Using cache
---> 256340699bc3
Step 4/10 : COPY src /build/src/
---> Using cache
---> 65eb0f98bb79
Step 5/10 : WORKDIR /build/
---> Using cache
---> b16b294b6b74
Step 6/10 : RUN mvn package
---> Using cache
---> c48659e0197e
Step 7/10 : FROM openjdk:8-jre-alpine
---> f7a292bbb70c
Step 8/10 : WORKDIR /app
---> Using cache
---> 1723d5b9c22f
Step 9/10 : COPY --from=MAVEN_BUILD /build/target/docker-boot-intro-0.1.0.jar /app/
---> Using cache
---> d0e2f8fbe5c9
Step 10/10 : ENTRYPOINT ["java", "-jar", "docker-boot-intro-0.1.0.jar"]
---> Using cache
---> f265acb14147
Successfully built f265acb14147
Successfully tagged docker-boot-intro:latest
SECURITY WARNING: You are building a Docker image from Windows against a non-Windows Docker host. All files and directories added to build context will have '-rwxr-xr-x' permissions. It is recommended to double check and reset permissions for sensitive files and directories.
Brians Computer@DESKTOP-077OUJ8 MINGW64 /c/dev/docker-boot-intro (master)
運(yùn)行構(gòu)建時(shí),Docker將逐條執(zhí)行Docker文件中的每個(gè)命令。為每個(gè)步驟創(chuàng)建一個(gè)帶有唯一ID的層。例如,步驟1創(chuàng)建的層的ID為293423a981a7。第一次構(gòu)建圖像時(shí),Docker將從DockerHub獲取它需要的任何外部圖像,然后在此之上開始構(gòu)建新的層。這會使得第一次構(gòu)建速度非常慢。在構(gòu)建過程中,Docker在嘗試構(gòu)建層之前會檢查緩存,看看是否已經(jīng)有所構(gòu)建層的緩存版本。如果該層的緩存版本可用,Docker將直接使用它而不是從頭開始構(gòu)建。這意味著一旦構(gòu)建了一個(gè)鏡像層,后續(xù)的構(gòu)建就是重用,速度會快很多。你可以在上面的構(gòu)建輸出中通過Docker緩存輸出的hash值看到使用了緩存層。以上面第6步所發(fā)生的為例:作為RUN mvn包命令的一部分,Docker將從公共Maven庫獲取所有POM依賴項(xiàng),構(gòu)建成一個(gè)可執(zhí)行JAR,并將所有這些內(nèi)容存儲在ID為c48659e0197e的層中。下一次構(gòu)建這個(gè)鏡像時(shí),Maven依賴項(xiàng)和應(yīng)用程序JAR將從緩存層中取出,而不必再次下載和構(gòu)建。鏡像大小運(yùn)行docker image ls命令將羅列出所有的本地鏡像。可發(fā)現(xiàn)docker-boot-intro鏡像大小為105 MB。Brians Computer@DESKTOP-077OUJ8 MINGW64 /c/dev/docker-boot-intro (master)
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
docker-boot-intro latest 823730301d60 15 minutes ago 105MB
853d42b823c3 15 minutes ago 136MB
39ac5e9e9562 19 minutes ago 105MB
dfda2356bd36 19 minutes ago 136MB
Brians Computer@DESKTOP-077OUJ8 MINGW64 /c/dev/docker-boot-intro (master)
我在前文中提到過盡可能保持鏡像大小的最佳實(shí)踐,接下來讓我們細(xì)探一下docker-boot-intro鏡像的105MB由什么組成的。運(yùn)行如下命令:docker image history boot-docker-intro
將看到鏡像中各個(gè)層的內(nèi)容情況。Brians Computer@DESKTOP-077OUJ8 MINGW64 /c/dev/docker-boot-intro/target (master)
$ docker image history docker-boot-intro
IMAGE CREATED CREATED BY SIZE COMMENT
823730301d60 19 minutes ago /bin/sh -c #(nop) ENTRYPOINT ["java" "-jar"... 0B
7e43d899f02f 19 minutes ago /bin/sh -c #(nop) COPY file:05f3666306f8c7af... 20.1MB
1723d5b9c22f 6 days ago /bin/sh -c #(nop) WORKDIR /app 0B
f7a292bbb70c 4 months ago /bin/sh -c set -x && apk add --no-cache o... 79.4MB
4 months ago /bin/sh -c #(nop) ENV JAVA_ALPINE_VERSION=8... 0B
4 months ago /bin/sh -c #(nop) ENV JAVA_VERSION=8u212 0B
4 months ago /bin/sh -c #(nop) ENV PATH=/usr/local/sbin:... 0B
4 months ago /bin/sh -c #(nop) ENV JAVA_HOME=/usr/lib/jv... 0B
4 months ago /bin/sh -c { echo '#!/bin/sh'; echo 'set... 87B
4 months ago /bin/sh -c #(nop) ENV LANG=C.UTF-8 0B
4 months ago /bin/sh -c #(nop) CMD ["/bin/sh"] 0B
4 months ago /bin/sh -c #(nop) ADD file:a86aea1f3a7d68f6a... 5.53MB
Brians Computer@DESKTOP-077OUJ8 MINGW64 /c/dev/docker-boot-intro/target (master)
如上所顯示5.53 MB的Alpine基礎(chǔ)鏡像處于第一層。在之上的幾層配置了一系列的環(huán)境變量,然后是大小為79.4 MB的JRE文件。最后的3層是我們在Dockerfile中定義的層,并包含了20.1 MB的應(yīng)用JAR。可以發(fā)現(xiàn)這個(gè)鏡像只包括了運(yùn)行應(yīng)用所必須的組件,是一個(gè)非常不錯(cuò)的輕量級鏡像。運(yùn)行容器鏡像構(gòu)建好后,可以使用以下命令運(yùn)行一個(gè)容器:docker container run -p 8080:8080 docker-boot-intro
run命令包括一個(gè)可選的-p參數(shù),作用是允許用戶將容器應(yīng)用的端口映射到主機(jī)的端口。熟悉Spring Boot的人都知道,應(yīng)用程序的默認(rèn)啟動端口就是8080。運(yùn)行一個(gè)容器時(shí),Docker將運(yùn)行可執(zhí)行JAR文件來啟動應(yīng)用,使用容器的8080端口。但如果要訪問容器中的應(yīng)用,需要通過主機(jī)的端口訪問,通過端口映射去到容器端口。-p 8080:8080參數(shù)就是將容器端口8080映射到主機(jī)端口8080。如果沒有異常的話,應(yīng)該可以看到應(yīng)用程序在端口8080成功啟動的信息。Brians Computer@DESKTOP-077OUJ8 MINGW64 /c/dev/docker-boot-intro/target (master)
$ docker container run -p 8080:8080 docker-boot-intro
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.1.7.RELEASE)
5436 [main] INFO com.blog.samples.docker.Application - Starting Application v0.1.0 on 934a1d731576 with PID 1 (/app/docker-boot-intro-0.1.0.jar started by root in /app)
5466 [main] INFO com.blog.samples.docker.Application - No active profile set, falling back to default profiles: default
16585 [main] INFO o.s.b.w.e.tomcat.TomcatWebServer - Tomcat initialized with port(s): 8080 (http)
16742 [main] INFO o.a.coyote.http11.Http11NioProtocol - Initializing ProtocolHandler ["http-nio-8080"]
16886 [main] INFO o.a.catalina.core.StandardService - Starting service [Tomcat]
16892 [main] INFO o.a.catalina.core.StandardEngine - Starting Servlet engine: [Apache Tomcat/9.0.22]
17622 [main] INFO o.a.c.c.C.[Tomcat].[localhost].[/] - Initializing Spring embedded WebApplicationContext
17628 [main] INFO o.s.web.context.ContextLoader - Root WebApplicationContext: initialization completed in 11614 ms
21399 [main] INFO o.s.s.c.ThreadPoolTaskExecutor - Initializing ExecutorService 'applicationTaskExecutor'
23347 [main] INFO o.s.b.a.e.web.EndpointLinksResolver - Exposing 2 endpoint(s) beneath base path '/actuator'
23695 [main] INFO o.a.coyote.http11.Http11NioProtocol - Starting ProtocolHandler ["http-nio-8080"]
23791 [main] INFO o.s.b.w.e.tomcat.TomcatWebServer - Tomcat started on port(s): 8080 (http) with context path ''
23801 [main] INFO com.blog.samples.docker.Application - Started Application in 21.831 seconds (JVM running for 25.901)
應(yīng)用測試如果看到類似于上面顯示的信息輸出,那表示容器已經(jīng)順利啟動。接下來就可以測試應(yīng)用。如果你在Windows或Mac上運(yùn)行Docker,需要使用的工具是一個(gè)Linux虛擬機(jī)Docker Toolbox。需要通過運(yùn)行docker-machine ip命令可以獲得Linux VM的IP地址。本案例中的Linux VM IP是192.168.99.100。Brians Computer@DESKTOP-077OUJ8 MINGW64 /c/dev/docker-boot-intro (master)
$ docker-machine ip
192.168.99.100
獲得IP后,可以使用cURL命令cURL 192.168.99.100:8080/actuator/health來調(diào)用應(yīng)用的健康檢查點(diǎn)來測試應(yīng)用情況。如果應(yīng)用程序啟動并運(yùn)行正常,即可獲得HTTP 200的響應(yīng),響應(yīng)內(nèi)容為{“status”:“up”}。Brians Computer@DESKTOP-077OUJ8 MINGW64 /c/dev/docker-boot-intro (master)
$ curl 192.168.99.100:8080/actuator/health
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 15 0 15 0 0 937 0 --:--:-- --:--:-- --:--:-- 937{"status":"UP"}
本方法的局限性我在前文提到過,可以重用Docker緩存層以減少構(gòu)建時(shí)間。雖然這是事實(shí),但是在構(gòu)建Java應(yīng)用時(shí)需要考慮存在的例外。每當(dāng)對Java源代碼或POM文件進(jìn)行更改后,Docker將會發(fā)現(xiàn)變更差異,從而忽略緩存的副本層,重新構(gòu)建所需的層。這是正常的,但問題是這個(gè)變化會導(dǎo)致緩存中的Maven依賴項(xiàng)丟失。因此,當(dāng)使用mvn包命令重新構(gòu)建這個(gè)層時(shí),所有Maven依賴項(xiàng)將再次從遠(yuǎn)程庫中拉取一次,導(dǎo)致顯著減慢了構(gòu)建的速度,成為開發(fā)過程中真正的痛點(diǎn)。而且這個(gè)問題在構(gòu)建沒有Docker的Java應(yīng)用程序時(shí)完全不存在,僅僅發(fā)生在使用Docker構(gòu)建應(yīng)用層時(shí)發(fā)生。解決方案是什么?目前解決這個(gè)問題的方法是使用主機(jī)上的本地Maven存儲庫作為Maven依賴項(xiàng)的源。通過卷告訴Docker去訪問主機(jī)本地的Maven庫,而非從公共庫中拉取依賴項(xiàng)。這種方法可以解決這個(gè)問題。但也是有利有弊。從好的方面看,你使用的是主機(jī)緩存的Maven依賴項(xiàng),可以在更改源代碼后,快速重新構(gòu)建,節(jié)省了構(gòu)建時(shí)間。但不利的方面是Docker鏡像的管理因此而失去了一些自主性。使用Docker的主要初衷之一就是不必?fù)?dān)心在其運(yùn)行的環(huán)境中的軟件配置。理想情況下,Docker鏡像應(yīng)該是自我構(gòu)建且擁有構(gòu)建和運(yùn)行所需的一切元素,而不必存在主機(jī)依賴。而這個(gè)方法恰好違背了這個(gè)初衷,讓Docker構(gòu)建失去了部分自主性。在下一篇文章中,我們將介紹Docker卷,并展示如何使用它們訪問主機(jī)上的Maven庫。結(jié)束語在本文中,我們定義了一個(gè)Docker鏡像來構(gòu)建和運(yùn)行一個(gè)Spring Boot應(yīng)用程序。我們討論了讓鏡像保持盡可能小的重要性,可以通過使用超級小的Alpine基礎(chǔ)鏡像和在多階段構(gòu)建過程中進(jìn)行內(nèi)容剔除的方式來實(shí)現(xiàn)。我們還討論了使用Docker構(gòu)建Java應(yīng)用程序的局限性和可能的解決方案。用戶可以從GitHub[1]獲取文章中的測試完整源代碼。相關(guān)鏈接:https://github.com/briansjavablog/build-and-run-spring-boot-with-docker
總結(jié)
以上是生活随笔為你收集整理的8s 使用本地打包镜像_在Docker环境构建、打包和运行Spring Boot应用的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 偏移出来的数据不准_独家解读!京东高可用
- 下一篇: layer.open子页面调用父页面的方