从源主机复制应用程序源代码到镜像的临时构建目录
采用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镜像
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 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将从缓存层中取出,而不必再次下载和构建。镜像大小
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。可以发现这个镜像只包括了运行应用所必须的组件,是一个非常不错的轻量级镜像。运行容器
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)
应用测试
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"}
本方法的局限性
https://github.com/briansjavablog/build-and-run-spring-boot-with-docker
![2523c5b4ce92bd1f8e55d1844f530810.png](https://img-blog.csdnimg.cn/img_convert/2523c5b4ce92bd1f8e55d1844f530810.png)