Spring Boot 创建 Docker 镜像
随着越来越多的组织转向容器和虚拟服务器,Docker正成为软件开发工作流程中一个更重要的部分。为此,Spring Boot 2.3中最新的功能之中,提供了为Spring Boot应用程序创建 Docker 镜像的能力。
这篇文章的目的,就是为了给大家介绍如何为 Spring Boot 应用程序创建 Docker 镜像。
1. 传统Docker构建
使用Spring Boot 构建 Docker 镜像的传统方法是使用 Dockerfile 。下面是一个简单的例子:
FROM openjdk:8-jdk-alpine
EXPOSE 8080
ARG JAR_FILE=target/demo-app-1.0.0.jar
ADD ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
然后我们可以使用 docker build 命令来创建 Docker
映像。这对大多数应用程序都很好,但也有一些缺点。
首先,我们使用的是 Spring Boot
创建的 fat jar。这会影响启动时间,尤其是在集装箱环境中。我们可以通过添加jar文件的分解内容来节省启动时间。
其次,Docker镜像是分层构建的。Spring Boot fat jar 的特性使得所有的应用程序代码和第三方库都放在一个层中。这意味着即使只有一行代码更改,也必须重新构建整个层。
通过在构建之前分解 jar ,应用程序代码和第三方库各自获得自己的层。这样,我们便可以利用Docker的缓存机制。现在,当某一行代码被更改时,只需要重新构建相应的层。
考虑到这一点,让我们看看Spring Boot 如何改进创建Docker镜像的过程。
2. Buildpacks
BuildPacks 是一种提供框架和应用程序依赖性的工具。
例如,给定一个Spring Boot fat jar,一个buildpack将为我们提供Java运行时。这使我们可以跳过 Dockerfile 并自动获得一个合理的docker 镜像。
Spring Boot 包括对 bulidpacks 的Maven和Gradle支持。例如,使用Maven构建时,我们将运行以下命令:
./mvnw spring-boot:build-image
我们观察下一些相关的输出,看看发生了什么:
[INFO] Building jar: target/demo-0.0.1-SNAPSHOT.jar
...
[INFO] Building image 'docker.io/library/demo:0.0.1-SNAPSHOT'
...
[INFO] > Pulling builder image 'gcr.io/paketo-buildpacks/builder:base-platform-api-0.3' 100%
...
[INFO] [creator] ===> DETECTING
[INFO] [creator] 5 of 15 buildpacks participating
[INFO] [creator] paketo-buildpacks/bellsoft-liberica 2.8.1
[INFO] [creator] paketo-buildpacks/executable-jar 1.2.8
[INFO] [creator] paketo-buildpacks/apache-tomcat 1.3.1
[INFO] [creator] paketo-buildpacks/dist-zip 1.3.6
[INFO] [creator] paketo-buildpacks/spring-boot 1.9.1
...
[INFO] Successfully built image 'docker.io/library/demo:0.0.1-SNAPSHOT'
[INFO] Total time: 44.796 s
第一行显示我们构建了标准的 fat jar,与其他典型的maven包一样。
下一行开始Docker映像构建。然后,看到这个 bulid 拉取了 packeto 构建器。
packeto 是基于云原生 bulidpacks 的实现。它负责分析我们的项目并确定所需的框架和库。在我们的例子中,它确定我们有一个Spring Boot项目并添加所需的构建包。
最后,我们看到生成的Docker映像和总构建时间。注意,在第一次构建时,花了相当多的时间下载构建包并创建不同的层。
buildpacks 的一大特点是Docker映像是多层的。因此,如果我们只更改应用程序代码,后续构建将更快:
...
[INFO] [creator] Reusing layer 'paketo-buildpacks/executable-jar:class-path'
[INFO] [creator] Reusing layer 'paketo-buildpacks/spring-boot:web-application-type'
...
[INFO] Successfully built image 'docker.io/library/demo:0.0.1-SNAPSHOT'
...
[INFO] Total time: 10.591 s
3. 层级jar包
在某些情况下,我们可能不喜欢使用 bulidpacks ——也许我们的基础架构已经绑定到另一个工具上,或者我们已经有了我们想要重新使用的自定义 Dockerfiles 。
基于这些原因,Spring Boot 还支持使用分层jars 构建Docker映像。为了了解它的工作原理,让我们看看一个典型的Spring Boot fat jar 布局:
org/
springframework/
boot/
loader/
...
BOOT-INF/
classes/
...
lib/
...
fat jar 由3个主要区域组成:
- 启动Spring应用程序所需的引导类
- 应用程序代码
- 第三方库
使用分层jar,结构看起来很相似,但是我们得到了一个新的 layers.idx 将 fat jar 中的每个目录映射到一个层的文件:
- "dependencies":
- "BOOT-INF/lib/"
- "spring-boot-loader":
- "org/"
- "snapshot-dependencies":
- "application":
- "BOOT-INF/classes/"
- "BOOT-INF/classpath.idx"
- "BOOT-INF/layers.idx"
- "META-INF/"
Out-of-the-box, Spring Boot provides four layers:
开箱即用,Spring Boot 提供4层:
- dependencies: 来自第三方的依赖
- snapshot-dependencies: 来自第三方的 snapshot 依赖
- resources: 静态资源
- application: 应用程序代码和资源(resources)
我们的目标是将应用程序代码和第三方库放置到层中,以反映它们更改的频率。
例如,应用程序代码可能是更改最频繁的代码,因此它有自己的层。此外,每一层都可以独立演化,只有当一层发生变化时,才会为它重建 Docker 镜像。
现在我们了解了分层 jar 结构,接下来看看如何利用它来制作 Docker 映像。
3.1.创建分层 jar
首先,我们必须建立一个项目来创建一个分层的jar。对于Maven,则需要在POM的 Spring Boot plugin 部分添加一个新的配置:
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<layers>
<enabled>true</enabled>
</layers>
</configuration>
</plugin>
有了这个配置,Maven package 命令(包括它的其他依赖命令)将使用前面提到的四个默认层生成一个新的分层jar。
3.2. 查看和提取分层
下一步,我们需要从 jar 中提取层,这样Docker镜像才能拥有正确的层。
要检查分层jar的任何层,可以运行以下命令:
java -Djarmode=layertools -jar demo-0.0.1.jar list
然后提取它们,运行命令:
java -Djarmode=layertools -jar demo-0.0.1.jar extract
3.3. 创建Docker映像
将这些层合并到 Docker 映像中的最简单方法是使用 Dockerfile :
FROM adoptopenjdk:11-jre-hotspot as builder
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} application.jar
RUN java -Djarmode=layertools -jar application.jar extract
FROM adoptopenjdk:11-jre-hotspot
COPY --from=builder dependencies/ ./
COPY --from=builder snapshot-dependencies/ ./
COPY --from=builder spring-boot-loader/ ./
COPY --from=builder application/ ./
ENTRYPOINT ["java", "org.springframework.boot.loader.JarLauncher"]
这个 Dockerfile 从fat jar中提取层,然后将每个层复制到Docker映像中。
每个COPY指令最终都会在Docker映像中生成一个新层。
如果我们构建这个Dockerfile,我们可以看到分层jar中的每个层都作为自己的层添加到Docker镜像中:
...
Step 6/10 : COPY --from=builder dependencies/ ./
---> 2c631b8f9993
Step 7/10 : COPY --from=builder snapshot-dependencies/ ./
---> 26e8ceb86b7d
Step 8/10 : COPY --from=builder spring-boot-loader/ ./
---> 6dd9eaddad7f
Step 9/10 : COPY --from=builder application/ ./
---> dc80cc00a655
...
4.总结
在本文中,我们学习了使用 Spring Boot 构建 Docker 映像的各种方法。
使用 buildpacks,我们可以获得合适的Docker镜像,而无需模板或自定义配置。
或者,再多花点功夫,我们就可以使用分层 jar 来获得一个更加定制的Docker镜像。
如果你觉得文章还不错,记得关注公众号: 锅外的大佬
刘一手的博客
Spring Boot 创建 Docker 镜像的更多相关文章
- 使用Spring Boot创建docker image
目录 简介 传统做法和它的缺点 使用Buildpacks Layered Jars 自定义Layer 简介 在很久很久以前,我们是怎么创建Spring Boot的docker image呢?最最通用的 ...
- maven 打包 spring boot 生成docker 镜像
1.所使用材料 ,spring boot 项目 基于maven ,maven 工具, docker工具 ps:为啥使用 docker 公司微服务需要启动太多,有两个优点吧! 1.方便管理,2.减少服务 ...
- CentOS7 下 配置Docker远程访问 与 windows下使用maven构筑Spring Boot 的 Docker镜像到远程服务端
1.设置Docker服务端,以支持远程访问: 修改docker服务端配置文件,命令: vim /usr/lib/systemd/system/docker.service 修改后: [Unit] De ...
- Spring Boot 和 Docker 实现微服务部署
Spring boot 开发轻巧的微服务提供了便利,Docker 的发展又极大的方便了微服务的部署.这篇文章介绍一下如果借助 maven 来快速的生成微服务的镜像以及快速启动服务. 其实将 Sprin ...
- Spring Boot与Docker部署
开启Docker远程访问 首先需要开启docker远程访问功能,以便可以进行远程操作. CentOS 6 修改/etc/default/docker文件,重启后生效(service docker re ...
- Spring Boot with Docker
翻译自:https://spring.io/guides/gs/spring-boot-docker/ Spring Boot with Docker 这篇教程带你一步步构建一个Docker镜像用来运 ...
- spring boot——结合docker
spring boot——结合docker 前言 Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的 liunx机器上,也可以实现虚 ...
- spring cloud教程之使用spring boot创建一个应用
<7天学会spring cloud>第一天,熟悉spring boot,并使用spring boot创建一个应用. Spring Boot是Spring团队推出的新框架,它所使用的核心技术 ...
- 使用Maven插件构建Spring Boot应用程序镜像
使用Maven插件构建Spring Boot应用程序的Docker镜像. 环境准备 1.Linux系统 2.安装JDK,Maven 3.安装Docker 应用实践 1.在应用程序根目录下添加Docke ...
随机推荐
- Java 集合看这一篇就够了
大家好,这里是<齐姐聊数据结构>系列之大集合. 话不多说,直接上图: Java 集合,也称作容器,主要是由两大接口 (Interface) 派生出来的: Collection 和 Map ...
- Springboot+Redis(发布订阅模式)跨多服务器实战
一:redis中发布订阅功能(http://www.redis.cn/commands.html#pubsub) PSUBSCRIBE pattern [pattern -]:订阅一个或者多个符合pa ...
- Flask之WTF
Flask-WTF是什么? 是一个关于表单的扩展库,可以自动生成表单的HTML代码和验证提交的表单数据,并且提供跨站请求伪造(Cross-Site Request Forgery)保护的功能,使用非常 ...
- c# 常用帮助类
C#常用帮助类 因为小土现在还是处于小白阶段,所以自己的知识技术还达不到要求,但是小土在网上找到一个大神的,等以后小土技术有了一定提升以后,在走自己的路,啥也不说了上货. 地址 :https://gi ...
- MySQL安装版本介绍
MySQL安装 源码安装 二进制格式的程序包 程序包管理器安装 yum dnf MySQL在CentOS上的情况 6上只有mySQL 7上只有mariadb 8上既有mysql也有mariadb Ce ...
- CentOS7使用firewalld管理防火墙
firewalld的基本使用 #启动 systemctl start firewalld #关闭 systemctl stop firewalld #查看状态 systemctl status fir ...
- CF1430 E. String Reversal(div 2)
题目链接:http://codeforces.com/contest/1430/problem/E 题意:有一串长度为n(n<=2*10^5)由小写字母组成的字符串,求通过相邻交换得到其反转串( ...
- MFiX-DEM中的并行碰撞搜索
基于MFiX-19.2.2 DEM并行程序中的颗粒循环 在DEM并行程序中,每个进程只循环该进程包含的颗粒,并且每个进程还有一层ghost cell,用来存放另一个进程发送过来的颗粒信息. 下面添加一 ...
- vue组件使用name属性来生成递归组件
先来个简单的数据 1 lists = [{ 2 id: 1, 3 title: '第一层', 4 children: [{ 5 id: 3, 6 title: '第二层', 7 children: [ ...
- java数据结构-08队列
一.什么是队列 队列是一种特殊的线性表,只能在头尾两端进行操作,特点是先进先出:就像排队买票一样,先来的先买 二.接口设计 三.代码实现 可以使用动态数组.链表等实现:这里两种实现栈与双向链表 1. ...