Springboot 是这样提高创建 docker 容器的效率的

发布于:2023-01-04 ⋅ 阅读:(530) ⋅ 点赞:(0)

前言

小伙伴们好呀,今天来和大家聊聊这个  Springboot 在为创建高效容器方面中做的一个改动 。

当然,写这篇文章也不是因为实际项目真的需要我去研究这东西,而是我在上篇文章

《为什么SpringBoot可以直接运行 jar 包?》 中留了坑🕳 ,还有错误得纠正🐖 (原文也稍微改了下,但是只能改20字🙃)

这里应该改为

用 jarmode 的 extract 参数会自动将 jar 包中的文件按 layers.idx 中的分层提取出来。

改进原因

There’s always a certain amount of overhead when running a fat jar without unpacking it, and in a containerized environment this can be noticeable.

The other issue is that putting your application’s code and all its dependencies in one layer in the Docker image is sub-optimal.

上面这两句摘自 Springboot2.4.13 官方文档,但是这个改动是 2.3 就有了的(看了下 2.3 的,也还是这几句话🐷)

https://docs.spring.io/spring-boot/docs/2.4.13/reference/html/spring-boot-features.html#boot-features-container-images

大意就是说,

  1. 在容器中,如果没有解压就直接运行一个 jar 包,它带来的开销是明显的

  2. 将 应用代码和包依赖都混在同一层,也是可以被优化的地方。

w(゚Д゚)w ,感觉说的很有道理 哈哈哈

默默三连认同

但是这里有个疑问,那之前 2.3 之前是怎么解决这个问题的呢?还是说现在才解决的呢?🤔

求证

怀着严谨求证的态度,我打开了 Springboot2.2.9 的版本 👇

https://docs.spring.io/spring-boot/docs/2.2.9.RELEASE/reference/htmlsingle/#containers-deployment

可以看到这里列举了不高效的写法,以及推荐写法

这里我将 Springboot2.2.9 版本中 创建高效容器 的 Dockerfile 抄下来

FROM openjdk:8-jdk-alpine AS builder
WORKDIR target/dependency
ARG APPJAR=target/*.jar
COPY ${APPJAR} app.jar
RUN jar -xf ./app.jar

FROM openjdk:8-jre-alpine
VOLUME /tmp
ARG DEPENDENCY=target/dependency
COPY --from=builder ${DEPENDENCY}/BOOT-INF/lib /app/lib
COPY --from=builder ${DEPENDENCY}/META-INF /app/META-INF
COPY --from=builder ${DEPENDENCY}/BOOT-INF/classes /app
ENTRYPOINT ["java","-cp","app:app/lib/*","com.example.MyApplication"]

发现在 Springboot2.2.9 ,是通过解压 jar 包,并 copy  /BOOT-INF/classes , /BOOT-INF/lib 以及 /META-INF 这三个文件夹来创建高效容器的 🐖 。

再次回忆下这几个文件夹 👇

当然,根据我们上文的结论,可以知道这里拷贝这个 META-INF 文件夹也没啥意义。毕竟最后已经指定 Main Class 去运行了。

这里我也去掉并做了个简单验证,依然部署成功,并且可以正常访问(当然,官方那样做可能也有其他我没考虑到的因素,就不多赘述啦🐖)

验证

接着,我们来看看 Springboot2.4.13 版本 的 👇

https://docs.spring.io/spring-boot/docs/2.4.13/reference/html/spring-boot-features.html#boot-features-container-images

FROM adoptopenjdk:11-jre-hotspot as builder
WORKDIR application
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} application.jar
RUN java -Djarmode=layertools -jar application.jar extract

FROM adoptopenjdk:11-jre-hotspot
WORKDIR application
COPY --from=builder application/dependencies/ ./
COPY --from=builder application/spring-boot-loader/ ./
COPY --from=builder application/snapshot-dependencies/ ./
COPY --from=builder application/application/ ./
ENTRYPOINT ["java", "org.springframework.boot.loader.JarLauncher"]

这里就是我们上文中截取的代码了,当时也有强调说这个东西的作用 👇

所以再次回顾下这个官方的这段话,再次细品这段代码

COPY --from=builder application/dependencies/ ./
COPY --from=builder application/spring-boot-loader/ ./
COPY --from=builder application/snapshot-dependencies/ ./
COPY --from=builder application/application/ ./

可以发现,为了优化容器的创建,这里的 copy 顺序也是极为用心的。

先后顺序是

  1. 依赖包

  2. Springboot  loader 源码

  3. 依赖包中的快照

  4. 应用代码,配置文件等

因为按照 docker 容器的创建原理,底层没改变的话,是可以直接使用缓存的。

这就意味着,我们创建容器时,很多都可以用到缓存,直到到达 应用代码和配置文件这一层 ,这就极大的提高了容器的创建速度。

细心的小伙伴可以从上面 “验证” 这张图中发现有 Using cache 的字眼😝

小结

那么,到了这里,你有没有感受到前面提到的改进原因呢?

  1. 在容器中,如果没有解压就直接运行一个 jar 包,它带来的开销是明显的
    感受:Springboot 2.2.9 和 Springboot 2.4.13 都提供 高效的 dockerfile 方式

  2. 将 应用代码和包依赖都混在同一层,也是可以被优化的地方。
    感受:Springboot 2.2.9 和 Springboot 2.4.13 都有改动,但是 2.4 中分的更细,而且基本上,2.4 的 dockerfile 在任何地方都可以直接用,不用做过多修改,很方便。

所以结论就是在 2.2.9 中就有了优化了,但是 2.3 之后又弄了个 layers.idx 来进一步优化,规范这个分层,提供了一个更简便的方式!

所以最后就变得更好使了😂


下面进入项目的搭建教学~

这个小项目主要是用来获取各种 验证码 的(中英文动态都有),是好久以前发现的,而且这个小 demo,还有隐藏的 API 等着小伙伴去发现。😄

感兴趣的小伙伴可以简单了解下这个 captcha 的生成,还是很有意思的😝

IDEA 部署 docker 容器

# 修改Docker服务文件,需要先切换到root用户 
vim /lib/systemd/system/docker.service

ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock -H tcp://0.0.0.0:2375

# 然后重启 docker

IDEA 中建一个 docker

创建 Dockerfile 容器。

最后运行即可~

可以在这里看到容器运行的 log。

项目地址 

https://github.com/Java4ye/springboot-demo-4ye

最后

本分就分享到这里了。

  1. 了解了 Springboot 在创建高效容器上的细节—— layers.idx。通过 jarmode 的 extract 参数去提取分好的层,提供高效的同时还方便开发人员去使用。

  2. 同时了解到 IDEA 怎么部署项目到远程容器。

  3. 又解决一个技术问题 嘻嘻嘻😝


最近面试的小伙伴很多,对此我整理了一份Java面试题手册:基础知识、JavaOOP、Java集合/泛型面试题、
Java异常面试题、Java中的IO与NIO面试题、Java反射、Java序列化、Java注解、多线程&并发、JVM、Mysql、Redis、
Memcached、MongoDB、Spring、SpringBoot、SpringCloud、RabbitMQ、Dubbo、MyBatis、ZooKeeper、数据结构、算法、
Elasticsearch、Kafka、微服务、Linux等等。可以分享给大家学习。【持续更新中】领取方式【999】就可以领取资料了

 


网站公告

今日签到

点亮在社区的每一天
去签到