优化Docker的Spring|优化Docker的Spring Boot应用程序
Docker功能强大且易于使用。它允许开发人员为他们创建的软件创建可移植的,独立的图像。可以可靠且可重复地部署这些图像。您可以轻松地从Docker中检索值,但为了充分利用Docker,有一些对您来说很重要的概念。当您进行持续集成和交付时,如何构建Docker镜像会产生可衡量的影响。在本文中,我将重点介绍如何在进行迭代开发和部署时采用更有效的方法为Spring Boot应用程序构建Docker镜像。标准方法有一些缺点,所以在这里,我们看看它们是什么以及如何更好地做到这一点。
关键码头概念
Docker有四个关键概念:图像,图层,Dockerfile和Docker缓存。简而言之,Dockerfile描述了如何构建Docker镜像。图像由许多层组成。Dockerfile以基本映像开始并添加其他层。将新内容添加到图像时会生成新图层。构建的每个层都是缓存的,因此可以在后续构建中重复使用。当Docker构建运行时,它将重新使用它可以从缓存中的任何现有层。这减少了每次构建所需的总时间和空间。任何已更改或之前未构建的内容都将根据需要构建。
图层内容很重要
这就是层的重要性发挥作用的地方。仅当该层的内容未更改时,才能使用Docker缓存中的现有层。Docker构建之间更改的层越多,Docker重建映像所需的工作就越多。图层顺序也很重要。只有在所有父图层都保持不变的情况下才能重复使用图层。最好在以后放置更频繁更改的图层,以便对它们进行更改会影响更少的子图层。
层的顺序和内容很重要。将应用程序打包为Docker映像时,最简单的方法是将整个应用程序推送到单个层。但是,如果在更改最少量的代码时该应用程序包含许多静态库依赖项,则需要重建整个层。这最终会在Docker缓存中浪费大量的构建时间和空间。
图层影响部署
部署Docker镜像时,图层也很重要。在部署Docker映像之前,它们将被推送到远程Docker存储库。此存储库充当所有部署映像的源,并且通常包含同一映像的许多版本。Docker非常高效,只存储一层。但是,对于经常部署且具有不断重建的大图层的图像,此效率无法工作。大型图层,即使其内部的变化很小,也必须单独存储在存储库中并在网络中推送。这会对部署时间产生负面影响,因为需要为不变化的部分移动和存储重复位。
Docker中的Spring Boot应用程序
使用超级jar方法的Spring Boot应用程序是独立的部署单元。此模型非常适合在虚拟机或构建包上进行部署,因为应用程序可以随身携带所需的一切。但是,这是Docker部署的一个缺点:Docker已经提供了打包依赖关系的方法。将整个Spring Boot JAR放入Docker镜像是很常见的。但是,这会导致Docker镜像的应用程序层中有太多不变的位。
Spring社区正在讨论如何在运行Spring Boot应用程序时减少部署大小和时间,特别是在Docker中。[1] [2] [3] [4]在我看来,这最终是在简单性和效率之间进行权衡。为Spring Boot应用程序构建Docker镜像的最常用方法是我称之为“单层”方法。这在技术上并不正确,因为实际上Dockerfile创建了多个层,但它足以满足本次讨论的目的。
单层方法
我们来看看单层方法。单层方法快速,直接,易于理解和使用。Docker的Spring Boot指南列出了单层Dockerfile来构建Docker镜像:
来自 openjdk:8 - jdk - alpine
VOLUME/ tmp
ARGJAR_FILE
COPY$ { JAR_FILE } 应用。罐
ENTRYPOINT [ “java”,“ -Djava.security.egd = file:/ dev /./ urandom”,“ -jar”,“/ app.jar” ]
最终结果是一个正常运行的Docker镜像,其运行方式与您期望运行Spring Boot应用程序的方式完全相同。但是,它受到分层效率问题的困扰,因为它基于整个应用程序JAR。随着应用程序源的更改,整个Spring Boot JAR都会重建。下一次构建Docker镜像时,将重建整个应用程序层,包括所有未更改的库依赖项。
更深入地了解单层方法
单层方法使用Spring Boot JAR构建Docker镜像,因为Open JDK基础映像之上的Docker层如下所示:
$ docker 图片
存储 标签 图像 ID 创建 大小
springio / spring - petclinic 最新 94 b0366d5ba2 16 秒 前 140 MB
生成的Docker镜像为140 MB。您可以使用该docker history命令检查图层。您可以看到Spring Boot应用程序JAR已复制到大小为38.3 MB的映像中。
$ 搬运工 历史 springio / 春天- 的PetClinic
IMAGE 创建 CREATED BY SIZE COMMENT
94 b0366d5ba2 52 秒 前 / bin / sh - c #(nop) ENTRYPOINT [ “java” “ - Djav … 0B
213 dff56a4bd 53 秒 前 / bin / sh - c #(nop)COPY 文件:d3551559c2aa35af … 38.3 MB
bc453a32748e 6 分钟 前 / bin / sh - c #(nop) ARG JAR_FILE 0 B
7f e0bb0d8026 6 分钟 前 / bin / sh - c #(nop) VOLUME [ / tmp ] 0 B
cc2179b8f042 8 天 前 / bin / sh - c set - x && apk 添加 - 否- 缓存 o … 97.4 MB
< missing > 8 天 前 / bin / sh - c #(nop) ENV JAVA_ALPINE_VERSION = 8. … 0 B
< missing > 8 天 前 / bin / sh - c #(nop) ENV JAVA_VERSION = 8u 151 0 B
< missing > 8 天 前 / bin / sh - c #(nop) ENV PATH = / usr / local / sbin:… 0 B
< missing > 8 天 前 / bin / sh - c #(nop) ENV JAVA_HOME = / usr / lib / jv … 0 B
< missing > 8 天 前 / bin / sh - c { echo ‘#!/ bin / sh’ ; 回声 “设置… 87B
< 缺失> 8 天内进行付运 前 / 仓/ SH - ? #(NOP) ENV LANG = ?。UTF - 8 0 B.
< missing > 5 个月 前 / bin / sh - c #(nop) CMD [ “/ bin / sh” ] 0 B.
< missing > 5 个月 前 / bin / sh - c #(nop)ADD 文件:093f 0723f a46f6cdb … 4.15 MB
下次构建Docker镜像时,将重新创建整个38 MB层,因为JAR文件已重新打包。
在此示例中,应用程序大小相对较小,仅基于 spring-boot-starter-web 其他依赖项,例如 spring-actuator。在现实世界中,这些大小通常要大一些,因为它们不仅包括Spring Boot库,还包括其他第三方库。根据我的经验,实际的Spring Boot应用程序的大小范围可以从50 MB到250 MB(如果不是更大)。
仔细观察应用程序,应用程序代码中只有372 KB的应用程序JAR。剩余的38 MB是库依赖项。这意味着只有0.1%的层实际上在变化。其余99.9%未变。
层生命周期
这说明了分层的基本考虑:内容的生命周期。图层的内容应该具有相同的生命周期。Spring Boot应用程序的内容有两个不同的生命周期:不经常更改的库依赖项和频繁更改的应用程序类。
每次由于应用程序代码更改而重建层时,还包括不变的二进制文件。在应用程序代码不断变化和重新部署的快速应用程序开发环境中,这种附加成本会变得非常昂贵。
想象一下,一个应用团队在Pet Clinic上进行迭代。团队每天更改和重新部署应用程序10次。这10个新层的成本将为每天383 MB。使用更多真实尺寸,每天最多可达2.5 GB或更多。这最终会浪费构建时间,部署时间和Docker存储库空间。
这种快速,渐进的开发和交付是在权衡变得重要的时候。您必须继续使用简单的单层方法或采用更有效的替代方法。
拥抱Docker,Go Dual Layer
在这种简单和效率之间的权衡中,我觉得正确的选择是“双层”方法。(更多层可能,但太多层可能是有害的,并且违反了 Docker最佳实践)。在双层方法中,我们构建Docker镜像,以便Spring Boot应用程序的库依赖项存在于应用程序代码下面的层中。这样,层遵循内容的不同生命周期。通过将不经常更改的库依赖关系推送到单独的层并仅将应用程序类保留在顶层,迭代重建和重新部署将更快。
双层方法可加速迭代开发,并最大限度地缩短部署时间。结果因应用程序而异,但平均而言,这会将应用程序部署大小减少90%,同时相应减少部署周期时间。
【优化Docker的Spring|优化Docker的Spring Boot应用程序】在下一篇文章“ 为Spring Boot Apps创建双层Docker镜像”中,我将介绍如何使用Open Liberty项目中的新工具为Spring Boot应用程序构建双层Docker镜像。
推荐阅读
- 热闹中的孤独
- JAVA(抽象类与接口的区别&重载与重写&内存泄漏)
- 放屁有这三个特征的,请注意啦!这说明你的身体毒素太多
- 一个人的旅行,三亚
- 布丽吉特,人生绝对的赢家
- 慢慢的美丽
- 尽力
- 一个小故事,我的思考。
- 家乡的那条小河
- Docker应用:容器间通信与Mariadb数据库主从复制