让容器使用 OpenJDK 和现有容器的库

Jor*_*ens 19 java docker

我正在用我的论文做一些实验,涉及容器发生的冷启动问题。我的测试应用程序是一个基于 openjdk 映像构建的 Spring Boot 应用程序。我想尝试解决冷启动问题的第一件事是:

准备一个容器,容器中是 openjdk 和 springboot 应用程序使用的库。我启动我的另一个容器,使用现有容器的 ipc 和网络命名空间,然后能够使用 openjdk 和这个容器的库来运行 jar 文件。

我不确定如何实现这一目标?我可以通过使用卷来实现这一点,还是应该寻找一种完全不同的方法?

另一方面,如果我想要 x 个容器运行,我将确保有 x 个预先存在的容器在运行。这是为了确保每个容器都有自己特定的 librarycontainer 可以使用。这样可以吗?

简而言之,我可以通过使用通过 ipc/net 连接的第二个容器来加速 spring boot 应用程序的任何方式;会对我的问题有所帮助。

Mar*_*nik 5

Spring boot 是一个纯粹的“运行时”框架。

如果我的问题是正确的,请描述以下情况:

因此,假设您有一个装有 JDK 和一些 jar 的容器 A。仅此一点并不意味着您有一个正在运行的进程。所以它更像是一个准备好重用文件的卷(或者可能是一个关于 docker 镜像的层)。

此外,您还有另一个容器 B,其中包含应该以某种方式启动的 Spring Boot 应用程序(可能使用容器 A 中的开放 jdk 或其专用 JDK)。

现在究竟想“加速”什么?映像的大小(例如,较小的映像意味着在 CI/CD 管道中部署速度更快)?spring boot 应用程序启动时间(从生成 JVM 到 Spring boot 应用程序启动并运行之间的时间间隔)?或者,您可能正在尝试在运行时加载更少的类?

解决出现的问题的技术是不同的。但总而言之,我认为您可能需要查看 Graal VM 集成,它可能会创建本机映像并加快启动时间。这个东西很新,我自己还没有尝试过。我相信它正在进行的工作和春天将努力推动这一进程(这只是我的猜测,所以请持保留态度)。

不管怎样,你可能有兴趣阅读这篇文章

但是,我怀疑它与您所描述的研究有关。

更新 1

根据您的评论 - 让我提供一些可能有帮助的其他信息。此更新包含来自“现实生活”工作经验的信息,我将其发布是因为它可能有助于在您的论文中找到方向。

所以,我们首先有一个 spring boot 应用程序。

默认情况下它是一个 JAR 并且它的 Pivotal 建议还有一个 WAR 选项(正如 Josh Long,他们的开发者倡导者说:“让 JAR 不是 WAR”)

这个 spring boot 应用程序通常包含一些 web 服务器 - 默认情况下用于传统 Spring Web MVC 应用程序的 Tomcat,但您可以将其切换到 Jetty 或 undertow。如果您正在运行“反应式应用程序”(自 spring boot 2 起支持 Spring WebFlux),您的默认选择是 Netty。

一方面要注意,并非所有 Spring Boot 驱动的应用程序都必须包含某种嵌入式 Web 服务器,但我将把这个微妙的点放在一边,因为您似乎以 Web 服务器为目标(您提到了 tomcat,一种更快的服务能力)请求等,因此我的假设)。

好的,现在让我们试着分析一下当你启动一个 Spring Boot 应用程序 JAR 时会发生什么。

首先 JVM 本身启动 - 进程启动,堆被分配,内部类被加载等等。这可能需要一些时间(大约一秒甚至稍长,具体取决于服务器、参数、磁盘速度等)。 该线程解决了 JVM 启动速度是否真的很慢的问题,我可能无法再添加更多内容。

好的,现在是时候加载 tomcat 内部类了。这在现代服务器上再次需要几秒钟。Netty 似乎更快,但您可以尝试下载 tomcat 的 stanalone 发行版并在您的机器上启动它,或者创建一个没有 spring boot 但使用嵌入式 Tomcat 的示例应用程序,看看我在说什么。

到目前为止一切顺利,不是我们的应用程序。正如我在开头所说的,spring boot 是纯粹的运行时框架。所以必须先加载spring/spring boot本身的类,然后才是应用本身的类。如果应用程序使用一些库 - 它们也会被加载,有时甚至会在应用程序启动期间执行自定义代码:Hibernate 可能会检查架构和/或扫描数据库架构定义,甚至更新底层架构,Flyway/Liquidbase 可以执行架构迁移等等,Swagger 可能会扫描控制器并生成文档,等等。

现在,“现实生活”中的这个过程甚至可能需要一分钟甚至更长时间,但这不是因为 Spring Boot 本身,而是来自应用程序中创建的 bean,这些 bean 在“constructor”/“post-construct”中有一些代码- 在 Spring Boot 应用程序上下文初始化期间发生的事情。另一个旁注,我不会真正深入了解 spring boot 应用程序启动过程的内部,spring boot 是一个非常强大的框架,它在幕后发生了很多事情,我假设您已经以一种方式使用过 spring boot或其他 - 如果没有,请随时提出具体问题 - 我/我的同事将尝试解决。

如果你去start.spring.io可以创建一个示例演示应用程序 - 它会加载得非常快。所以这一切都取决于您的应用程序 bean。

有鉴于此,究竟应该优化什么?

您在评论中提到可能有一个 tomcat 与一些 JAR 一起运行,因此它们不会在 Spring Boot 应用程序启动时加载。

好吧,就像我们的同事提到的那样,这确实更像是一个“传统的”Web servlet 容器/应用程序服务器模型,我们业内人士“使用了很长时间”(大约 20 年左右)。

这种部署确实具有“始终启动并运行”的 JVM 进程,该进程“始终”准备好接受 WAR 文件 - 应用程序的包存档。一旦它检测到扔进某个文件夹的 WAR - 它将通过创建分层类加载器并加载应用程序 JAR/类来“部署”应用程序。在您的上下文中有趣的是,可以在多个战争之间“共享”库,以便只加载一次。例如,如果您的 tomcat 主机,比如说,3 个应用程序(读取 3 个 WAR)并且都在使用 oracle 数据库驱动程序,您可以将此驱动程序的 jar 放到一些共享libs文件夹,它只会被类加载器加载一次,它是每个“WAR”创建的类加载器的“父”。这个类加载器层次结构至关重要,但我相信它超出了问题的范围。

我曾经使用过这两种模型(使用嵌入式服务器驱动的弹簧启动,没有使用嵌入式 Jetty 服务器的弹簧启动的应用程序和“老派”tomcat/jboss 部署)。

根据我的经验,随着时间的推移,我们的许多同事都同意这一点,出于多种原因,spring boot 应用程序在操作上更加方便(同样,这些原因超出了 IMO 问题的范围,如果您需要对此了解更多),这就是为什么它的当前“趋势”和“传统”部署仍在行业中的原因或许多非纯技术原因(历史上,系统在维护模式下“定义为”,您已经有一个部署基础设施,一个“知道”如何部署的“系统管理员”团队,你能说出它的名字,但最重要的是没有纯粹的技术)。

现在有了所有这些信息,您可能会更好地理解为什么我建议看一下 Graal VM,它允许通过本机映像更快地启动应用程序。

还有一点可能相关。如果您正在选择允许快速启动的技术,那么您可能会使用 Amazon Lambda 或如今其他云提供商提供的替代方案。

该模型允许“计算”能力(CPU)的几乎无限可扩展性,并且在引擎盖下,一旦检测到容器实际上什么都不做,它们就会“启动”容器并立即“杀死”它们。对于这种应用,spring boot simple 不太适合,但基本上 Java 也是如此,再次,因为 JVM 进程启动相对较慢,所以一旦他们像这样启动容器,直到时间太长它开始运作。

您可以在此处阅读有关 Spring 生态系统在该领域必须提供的内容,但它与您的问题并不真正相关(我正在尝试提供方向)。

当您需要一个可能需要一些时间才能启动的应用程序时,Spring Boot 会大放异彩,但一旦启动,它就可以非常快地完成工作。是的,如果应用程序没有被实际工作“占用”,则可以停止应用程序(我们使用术语向外扩展/缩减),这种方法也是一种新方法(约 3-4 年)并且在以下情况下效果最佳“托管”部署环境,如 kubernetes、亚马逊 ECS 等。