javascript
docker build -t_在Docker环境构建、打包和运行Spring Boot应用
從源主機復制應用程序源代碼到鏡像的臨時構建目錄
采用Maven完成應用的編譯和打包,生成可執行的JAR文件
采用JRE運行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鏡像如下內容是Dockerfile中定義的鏡像文件,盡管內容不多,但包含了很多步的工作。我將在下面詳細解釋每一行。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構建第一步采用的基礎鏡像,Docker將首先在本地查找鏡像,本地不存在后,將從DockerHub拉取。Maven會在最后階段被剔除掉(后續COPY命令介紹)考慮下載快速和鏡像大小控制的原因,選擇Alpine版的Maven鏡像。MAINTAINERBrianHannaway非必選項,但是為映像作者提供一個接觸點可提高可維護性。(本實驗應用驗證的點)COPY pom.xml/build/在鏡像中創建一個build目錄, 并拷入pom.xml文件。COPY src/build/src/拷入src目錄到鏡像中build目錄。WORKDIR/build/設置build為工作目錄。后續任何命令都在此目錄中運行。RUN mvnpackage執行mvn包來運行編譯和打包應用,生成成可執行的JAR文件。在第一次構建鏡像時,Maven將從公共Maven庫拉取所有需要的依賴項,并將它們緩存在鏡像的本地。后續的構建將使用這個緩存版的鏡像層,這意味著依賴項將在本地引用,而不必再次從外部拉取。至此,已經完成了鏡像定義,只需等其構建成一個可執行的JAR文件。這是多階段構建的第一部分。下一階段將獲取JAR并運行它。FROM openjdk:8-jre-alpine告知Docker多階段構建的下一步采用openjdk:8-jre-alpine的基礎鏡像。再次使用Java 8 JRE的Alpine版本,這一步的選擇其實比前面的Maven版本選擇更為重要,因為存在于最終版的鏡像只是openjdk:8-jre-alpine,因此如果要盡可能控制最終鏡像大小的話,選擇輕量級JRE鏡像就非常重要。WORKDIR/app告知Docker在鏡像內創建另一個/app工作目錄,后續任何命令都在此目錄中運行。COPY--from=MAVEN_BUILD/build/target/docker-boot-intro-0.1.0.jar/app/告知Docker從MAVEN_BUILD階段的/build/target目錄復制ocker-boot-intro-0.1.0.jar到/app目錄。如前文所述,多階段構建的優勢就是允許用戶將特定的內容從一個構建階段復制到另一個構建階段,并丟棄其他所有的內容。如果需要保留從MAVENBUILD階段開始的所有內容,那最終鏡像會包含Maven(包括Maven本地庫)工具,以及目標目錄中生成的所有類文件。通過從MAVENBUILD階段選擇必須要的內容,那最終得到的鏡像會小很多。ENTRYPOINT["java","-jar","app.jar"]告知Docker在容器運行本鏡像時,運行哪些命令。本部分用冒號進行多命令的隔離。本案例中,需要把執行JAR文件復制到/app目錄運行。構建鏡像完成Docker鏡像定義后,就可以著手構建。打開包含Dockerfile(根目錄)的目錄。運行以下命令構建鏡像:docker image build -t docker-boot-intro
-t參數為指定名稱和可選標簽。如果不指定標簽,Docker會自動標記為最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)
運行構建時,Docker將逐條執行Docker文件中的每個命令。為每個步驟創建一個帶有唯一ID的層。例如,步驟1創建的層的ID為293423a981a7。第一次構建圖像時,Docker將從DockerHub獲取它需要的任何外部圖像,然后在此之上開始構建新的層。這會使得第一次構建速度非常慢。在構建過程中,Docker在嘗試構建層之前會檢查緩存,看看是否已經有所構建層的緩存版本。如果該層的緩存版本可用,Docker將直接使用它而不是從頭開始構建。這意味著一旦構建了一個鏡像層,后續的構建就是重用,速度會快很多。你可以在上面的構建輸出中通過Docker緩存輸出的hash值看到使用了緩存層。以上面第6步所發生的為例:作為RUN mvn包命令的一部分,Docker將從公共Maven庫獲取所有POM依賴項,構建成一個可執行JAR,并將所有這些內容存儲在ID為c48659e0197e的層中。下一次構建這個鏡像時,Maven依賴項和應用程序JAR將從緩存層中取出,而不必再次下載和構建。鏡像大小運行docker image ls命令將羅列出所有的本地鏡像??砂l現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)
我在前文中提到過盡可能保持鏡像大小的最佳實踐,接下來讓我們細探一下docker-boot-intro鏡像的105MB由什么組成的。運行如下命令:docker image history boot-docker-intro
將看到鏡像中各個層的內容情況。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基礎鏡像處于第一層。在之上的幾層配置了一系列的環境變量,然后是大小為79.4 MB的JRE文件。最后的3層是我們在Dockerfile中定義的層,并包含了20.1 MB的應用JAR??梢园l現這個鏡像只包括了運行應用所必須的組件,是一個非常不錯的輕量級鏡像。運行容器鏡像構建好后,可以使用以下命令運行一個容器:docker container run -p 8080:8080 docker-boot-intro
run命令包括一個可選的-p參數,作用是允許用戶將容器應用的端口映射到主機的端口。熟悉Spring Boot的人都知道,應用程序的默認啟動端口就是8080。運行一個容器時,Docker將運行可執行JAR文件來啟動應用,使用容器的8080端口。但如果要訪問容器中的應用,需要通過主機的端口訪問,通過端口映射去到容器端口。-p 8080:8080參數就是將容器端口8080映射到主機端口8080。如果沒有異常的話,應該可以看到應用程序在端口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)
應用測試如果看到類似于上面顯示的信息輸出,那表示容器已經順利啟動。接下來就可以測試應用。如果你在Windows或Mac上運行Docker,需要使用的工具是一個Linux虛擬機Docker Toolbox。需要通過運行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來調用應用的健康檢查點來測試應用情況。如果應用程序啟動并運行正常,即可獲得HTTP 200的響應,響應內容為{“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緩存層以減少構建時間。雖然這是事實,但是在構建Java應用時需要考慮存在的例外。每當對Java源代碼或POM文件進行更改后,Docker將會發現變更差異,從而忽略緩存的副本層,重新構建所需的層。這是正常的,但問題是這個變化會導致緩存中的Maven依賴項丟失。因此,當使用mvn包命令重新構建這個層時,所有Maven依賴項將再次從遠程庫中拉取一次,導致顯著減慢了構建的速度,成為開發過程中真正的痛點。而且這個問題在構建沒有Docker的Java應用程序時完全不存在,僅僅發生在使用Docker構建應用層時發生。解決方案是什么?目前解決這個問題的方法是使用主機上的本地Maven存儲庫作為Maven依賴項的源。通過卷告訴Docker去訪問主機本地的Maven庫,而非從公共庫中拉取依賴項。這種方法可以解決這個問題。但也是有利有弊。從好的方面看,你使用的是主機緩存的Maven依賴項,可以在更改源代碼后,快速重新構建,節省了構建時間。但不利的方面是Docker鏡像的管理因此而失去了一些自主性。使用Docker的主要初衷之一就是不必擔心在其運行的環境中的軟件配置。理想情況下,Docker鏡像應該是自我構建且擁有構建和運行所需的一切元素,而不必存在主機依賴。而這個方法恰好違背了這個初衷,讓Docker構建失去了部分自主性。在下一篇文章中,我們將介紹Docker卷,并展示如何使用它們訪問主機上的Maven庫。結束語在本文中,我們定義了一個Docker鏡像來構建和運行一個Spring Boot應用程序。我們討論了讓鏡像保持盡可能小的重要性,可以通過使用超級小的Alpine基礎鏡像和在多階段構建過程中進行內容剔除的方式來實現。我們還討論了使用Docker構建Java應用程序的局限性和可能的解決方案。用戶可以從GitHub[1]獲取文章中的測試完整源代碼。相關鏈接:https://github.com/briansjavablog/build-and-run-spring-boot-with-docker
總結
以上是生活随笔為你收集整理的docker build -t_在Docker环境构建、打包和运行Spring Boot应用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: sublimetext3插件安装_sub
- 下一篇: 中奖程序