Springboot应用镜像构建
容器 ——云原生的基石
一个SpringBoot应用的上云其实很简单,我们只需要将打出来的jar包放入一个带有jdk环境的Docker基础镜像中运行起来就行。但是如果我们深入思考真的是这样吗?简单的复制jar包在容器中运行总是会比运行解压之后的文件多一些的开销,而在容器化环境中,这种开销是很明显的。另一个问题是,把应用程序的代码和它的所有依赖放在Docker镜像的一个层中并不是优雅的做法,由于重新编译代码的频率一定比我们升级所使用的依赖的频率要高很多,所以通常情况下在DockerFile中把事情分开一些比较好。 如果把jar文件放在应用类之前的那一层,Docker通常只需要改变最底层,就可以从其缓存中获取其他文件(充分利用unionFS)。
基于上述前提,我们想要做一个高效的Springboot DockerFile只需要两步:
- 分层的jar包
- 编写Dockerfiles
分层的jar包
为了更容易创建优化的Docker镜像,SpringBoot支持在jar中添加一个层索引文件,它提供了一个层的列表以及jar中应包含的部分,索引中的层列表是根据层应该被添加到Docker/OCI镜像中的顺序排列的:
- dependencies (用于常规发布的依赖)
- spring-boot-loader (针对 org/springframework/boot/loader 下的所有内容)
- snapshot-dependencies (用于snapshot的依赖)
- application (用于application 类和资源)
这种分层设计是为了根据代码在应用程序构建之间变化的可能性来分离代码,dependencies代码在不同的构建之间变化的可能性较小,所以它被放在自己的层中,以允许工具从缓存中重新使用这些层。application在不同的构建之间更有可能发生变化,所以它被隔离在一个单独的层中。
编写Dockerfiles
虽然在Dockerfile中只需几行就可以将SpringBoot的jar转换成docker镜像,但我们将使用分层功能可以创建一个优化的docker镜像。当我们创建一个包含分层文件的jar时,spring-boot-jarmode-layertools.jar将被作为依赖添加到我们的应用jar中(解压之后能看到)。有了classpath上的这个jar,我们可以在一个特殊的模式下启动应用程序,这个模式允许bootstrap代码运行与我们的应用程序完全不同的东西,例如提取层里的东西。
1 |
|
1 |
|
1 |
|
这是一个多阶段的docker文件,构建者阶段提取了以后需要的目录,每个 COPY 命令都与jarmode提取的层有关。
当然,不使用jarmode也可以编写Dockerfile, 我们可以使用unzip和mv的一些组合来把东西移到正确的层,但jarmode简化了这一点。