Docker优化Springboot应用程序

Docker优化Springboot应用程序_第1张图片
图片.png

Docker功能强大,使用简单。Docker允许开发人员为他们创建的软件创建可移植的、自包含的图像。这些图像可以可靠且可重复地部署。你可以很容易地从Docker中获得很多价值,但是要最大限度地从Docker中获得价值,有一些重要的概念需要理解。在进行持续集成和持续交付时,如何构建Docker形象具有可测量的影响。在本文中,我将重点讨论在进行迭代开发和部署时,如何采用更有效的方法为Springboot应用程序构建Docker映像。标准方法有一些缺点,所以我们来看看它们是什么,以及如何做得更好。

Docker关键概念

有四个关键的Docker概念:图像、层、DockerFile和Docker缓存。简单地说,dockerfile描述了如何构建docker映像。图像由若干层组成。dockerfile从一个基础图像开始,并添加了额外的层。当新内容添加到图像中时,将生成一个新层。构建的每个层都被缓存,以便在后续构建中重用。当Docker构建运行时,它将重新使用缓存中的任何现有层。这减少了每次构建所需的总时间和空间。任何已更改或以前未创建的内容都将根据需要进行创建。

Docker优化Springboot应用程序_第2张图片
图片.png

层内容物

这就是层的重要性所在。Docker缓存中的现有层只能在该层内容不变的情况下使用。在Docker构建之间更改的层越多,Docker重建图像所需的工作就越多。层顺序也很重要。只有当所有父层都不变时,才能重用层。最好稍后放置更频繁更改的层,这样对它们的更改影响的子层更少。

层次的顺序和内容很重要。将应用程序打包为Docker映像时,最简单的方法是将整个应用程序推放到一个层中。但是,如果该应用程序包含许多静态库依赖项,那么即使更改了最小数量的代码,也需要重新构建整个层。这最终在Docker缓存中浪费了大量的构建时间和空间。

层影响部署

部署Docker映像时,层也很重要。在部署Docker映像之前,它们被推送到远程Docker存储库。此存储库充当所有部署映像的源,通常包含同一映像的许多版本。Docker非常高效,每层只存储一次。然而,对于经常部署并且有不断重建的大层的图像,这种效率无法发挥作用。大的层,即使其中的更改很小,也必须单独存储在存储库中,并在网络中进行推送。这会对部署时间产生负面影响,因为需要为未更改的片段移动和存储重复位。

Docker中的Springboot应用程序

使用uber-jar方法的Springboot应用程序本身就是独立的部署单元。此模型非常适合在虚拟机或构建包上部署,因为该应用程序提供了它所需的一切。然而,这是Docker部署的一个缺点:Docker已经提供了打包依赖项的方法。将整个Springboot的jar放到Docker映像中是很常见的。但是,这会导致Docker映像的应用层中有太多不变的位。

Docker优化Springboot应用程序_第3张图片
图片.png

在Spring社区中有一个关于在运行Spring引导应用程序时减少部署大小和时间的讨论,特别是在Docker中。[1][2][3][4]在我看来,这最终是简单与效率的折衷。为SpringBoot应用程序构建Docker映像的最常见方法是我将称之为“单层”方法。这在技术上是不正确的,因为实际上dockerfile创建了不止一个层,但这对于讨论来说已经足够好了。

单层方法

让我们来看一下单层方法。单层方法快速、直接、易于理解和使用。Docker的Spring Boot Guide(Spring引导指南)设计了单层DockerFile以构建Docker映像:

FROM openjdk:8-jdk-alpine
VOLUME /tmp
ARG JAR_FILE
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]

最终的结果是一个运行正常的Docker映像,它的运行方式与您期望的Springboot应用程序的运行方式完全相同。但是,由于它基于整个应用程序JAR,所以会遇到分层效率问题。随着应用程序源代码的更改,整个Springboot的jar将被重建。下次构建Docker映像时,将重新构建整个应用程序层,包括所有未更改的库依赖项。

让我们来看一个具体的例子,在本例中是Spring宠物诊所。

更深入地了解单层方法

单层方法构建一个Docker映像,其中Springboot的jar作为一个开放的JDK基映像之上的Docker层:

$ docker images
REPOSITORY                    TAG         IMAGE ID            CREATED             SIZE
springio/spring-petclinic     latest      94b0366d5ba2        16 seconds ago      140MB

生成的Docker映像为140 MB。可以使用docker history命令检查层。您可以看到SpringBoot应用程序jar,它被复制到了大小为38.3MB的映像中。

$ docker history springio/spring-petclinic
IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
94b0366d5ba2        52 seconds ago      /bin/sh -c #(nop)  ENTRYPOINT ["java" "-Djav…   0B
213dff56a4bd        53 seconds ago      /bin/sh -c #(nop) COPY file:d3551559c2aa35af…   38.3MB
bc453a32748e        6 minutes ago       /bin/sh -c #(nop)  ARG JAR_FILE                 0B
7fe0bb0d8026        6 minutes ago       /bin/sh -c #(nop)  VOLUME [/tmp]                0B
cc2179b8f042        8 days ago          /bin/sh -c set -x  && apk add --no-cache   o…   97.4MB
           8 days ago          /bin/sh -c #(nop)  ENV JAVA_ALPINE_VERSION=8…   0B
           8 days ago          /bin/sh -c #(nop)  ENV JAVA_VERSION=8u151       0B
           8 days ago          /bin/sh -c #(nop)  ENV PATH=/usr/local/sbin:…   0B
           8 days ago          /bin/sh -c #(nop)  ENV JAVA_HOME=/usr/lib/jv…   0B
           8 days ago          /bin/sh -c {   echo '#!/bin/sh';   echo 'set…   87B
           8 days ago          /bin/sh -c #(nop)  ENV LANG=C.UTF-8             0B
           5 months ago        /bin/sh -c #(nop)  CMD ["/bin/sh"]              0B
           5 months ago        /bin/sh -c #(nop) ADD file:093f0723fa46f6cdb…   4.15MB

下次构建Docker映像时,将重新创建整个38MB层,因为JAR文件已重新打包。

在本例中,应用程序的大小相对较小(仅基于SpringBootStarterWeb和其他依赖项,如SpringActuator)。在现实世界中,这些大小通常要大一些,因为它们不仅包括Spring引导库,还包括其他第三方库。根据我的经验,实际的Spring引导应用程序的大小可以在50 MB到250 MB之间,如果不是更大的话。

更仔细地观察应用程序,只有372 KB的应用程序JAR是应用程序代码。其余38MB是库依赖项。这意味着只有0.1%的层实际上在变化。其余99.9%不变。

层生命周期

这说明了分层的基本考虑:内容的生命周期。一个层的内容应该具有相同的生命周期。SpringBoot应用程序的内容有两个不同的生命周期:不经常更改的库依赖项和经常更改的应用程序类。

每次由于应用程序代码更改而重新构建层时,也包括未更改的二进制文件。在快速的应用程序开发环境中,应用程序代码不断地更改和重新部署,这种额外的成本可能会变得非常昂贵。

想象一下一个应用程序团队正在迭代宠物诊所。团队每天更改和重新部署应用程序10次。这10个新层的成本将是383MB,每天。使用更多现实世界的大小,这可以高达2.5GB或每天更多。这最终是对构建时间、部署时间和Docker存储库空间的极大浪费。

这种快速、增量的开发和交付是权衡变得重要的时候。继续使用简单的单层方法,或者采用更有效的替代方法。

拥抱Docker,走向双层

在这种简单和高效的权衡中,我认为正确的选择是“双层”方法。(可能有更多的层,但太多的层可能有害,并且违背Docker的最佳实践)。在双层方法中,我们构造Docker映像,使SpringBoot应用程序的库依赖项存在于应用程序代码下面的一个层中。这样,层就可以遵循内容的不同生命周期。通过将不经常更改的库依赖项下推到一个单独的层中,并且只保留顶层中的应用程序类,迭代重建和重新部署将更快。

Docker优化Springboot应用程序_第4张图片
图片.png

双层方法加速了迭代开发构建和最小化部署时间。结果因应用程序而异,但平均而言,这会将应用程序部署大小减少90%,相应地减少部署周期时间。

在本系列的下一篇文章中,为SpringBoot应用程序创建双层Docker映像,我将介绍如何使用OpenLiberty项目中的一个新工具为SpringBoot应用程序构建双层Docker映像。

Docker优化Springboot应用程序_第5张图片
图片.png

你可能感兴趣的:(Docker优化Springboot应用程序)