令人惊讶的是,部署一个软件在计算机上运行的过程并不简单。应用程序是一种复杂的生物,在部署时可能会发现自己处于陌生的环境中,在该环境中它与不同的硬件、不同的基础架构软件和其他相邻程序进行交互。确保应用程序的生存和发展是其代码和部署过程的责任。两者之间的平衡通常取决于用于构建程序的语言、运行时和工具,因此,不同的部署工具可能适用于不同的技术堆栈。
虽然 JVM 应用程序对它们的环境的要求非常低——只需一个 JVM 和一个内核——但令人惊讶的是,到目前为止,还不存在用于 JVM 应用程序的良好通用部署工具/机制。 Fat JAR 并不总是有效,即使有效,它们也可能需要特定于平台的脚本。最近有些人使用 Docker 来部署 Java 应用程序,但 Docker 不适合这项任务:它的主要目的之一是提供通用的应用程序可移植性——这是 JVM 应用程序已经具备的——因此它需要下载、部署和管理一个各种完整的操作系统映像和存储库。作为与运行时无关的工具,Docker 也未能利用 JVM 的优势。
今天,经过一年的成熟,我们很高兴地宣布发布 Capsule 1.0 ,这是一个简单、健壮和灵活的 JVM 应用程序部署工具。 Capsule 迎合了 JVM 应用程序的独特优势和要求,我们相信它是部署 JVM 应用程序的最简单 和 最强大的方式,无论是桌面应用程序、微服务还是复杂的 Web 应用程序。 Capsule 不仅适用于 Java 应用程序,而且适用于所有 JVM 语言,从 Jruby、Jython 和 Groovy,到 Kotlin、Clojure 和 Scala,再到 Frege 和 OCaml-Java。如果您正在为 JVM 编写程序,则应该尝试一下 Capsule。
考虑 capsule 的一种方式是将它看做是类固醇的胖 JAR(它也允许本地库并且从不干扰您的依赖项)和一个声明式启动脚本合二为一;另一个是将其视为构建工具的部署时对应物。正如构建工具管理您的构建一样,Capsule 管理从构建到应用程序启动的一切。
我们在设计 Capsule 时牢记以下原则:
- 启动必须是确定性的但又是灵活的,可能是安全的并且有选择地受到限制。 启动胶囊不需要启动脚本。胶囊查找请求的 JVM 版本,设置类路径和必要的代理并设置 JVM 标志。胶囊还可以在启动时创建自己的容器——以限制资源使用,或使用众所周知的端口而不受其他程序的干扰——并且由于 JVM 应用程序可以在非特权容器中运行,因此这些容器是安全的。或者,可以通过 JVM 自己的安全机制来提供安全性。此外,capsules 只需要一个内核和一个 JVM——甚至不需要 shell——因此它们可以在像 OSv 这样的 JVM 微内核上运行。所有这些功能都可以通过 caplets 完全可编程和组合,caplets 是自定义胶囊行为的组件。
- 不要在现有工具和新标准发明新工具和新标准时发明新工具和新标准。 Capsule 是用 Java 编写的,可以用 Java 进行扩展。它尊重 JVM 生态系统,不重新发明轮子,并使用现有的工具和标准。胶囊被打包在一个可执行的 JAR 中,并将所有元数据存储为简单的 JAR 清单属性;如果需要,它会从 Maven 存储库中全部或部分下载,并使用流行的 JVM 构建工具(如 Maven、Gradle 或 Leiningen)构建。 Capsule 本身是一个简单的 Maven 依赖项,所有构建工具插件也是如此。无需安装新工具。
Caplets 胶囊魔法
Capsule 在保持简单的同时提供所有这些功能的方式是通过 caplets ,它们是自定义胶囊行为的模块。 Caplet 可以嵌入到胶囊中,或单独打包并在命令行中使用以包装和修改现有胶囊的行为。
Capsule 的第一个 caplet 是 Maven caplet ,它允许您在清单属性中声明应用程序的部分或全部依赖项,而不是将它们嵌入到胶囊 JAR 中。虽然这对于许多应用程序来说可能不是必需的,但这里有两个使用示例,它们展示了 Capsule 的潜力。
第一个示例 是一个简单的 Hello World servlet。构建时,它会创建一个可以部署到任何 servlet 容器的标准 WAR 文件。仔细检查会发现 WAR 有点特别。其内容是:
247 META-INF/MANIFEST.MF
1124 WEB-INF/classes/co/paralleluniverse/examples/HelloWorldServlet.class
653 WEB-INF/web.xml
161596 Capsule.class
1467463 capsule-maven-1.0.jar
如您所见,WAR 包含
Capsule
类,这意味着它是一个胶囊,以及一个嵌入式 JAR,即 Maven caplet 的
capsule-maven-1.0.jar
。 JAR 清单看起来是这样的:
247 META-INF/MANIFEST.MF
1124 WEB-INF/classes/co/paralleluniverse/examples/HelloWorldServlet.class
653 WEB-INF/web.xml
161596 Capsule.class
1467463 capsule-maven-1.0.jar
如果不是将 WAR 部署到 servlet 容器,而是直接执行它,
java -jar build/libs/capsule-runnable-war.war
(或者,如果
./capsule-runnable-war.war
“真的executable”——查看用户文档的说明),它会自动下载 Jetty,并使用它来启动 servlet。 Jetty 工件将被缓存并在使用它的其他 caplets 之间共享。
另一个示例
使用 JavaScript 和在 JVM 上实现 Node.js 的 Avatar 项目。胶囊 JAR 仅包含 JavaScript 源代码、
Capsule
类和 Maven caplet:
247 META-INF/MANIFEST.MF
1124 WEB-INF/classes/co/paralleluniverse/examples/HelloWorldServlet.class
653 WEB-INF/web.xml
161596 Capsule.class
1467463 capsule-maven-1.0.jar
当 capsule 启动时,Avatar 运行时——包括特定于本地操作系统的本地库——将从 Maven 存储库下载,在本地缓存,并与其他 Avatar capsule 共享。
其他 caplets 包括一个 守护进程 caplet ,它将胶囊作为 Unix 或 Windows 守护进程启动,一个 安全 caplet ,它在 Java 沙箱(由指定的安全策略定义)中启动胶囊,一个 桌面 caplet ,它将包含 GUI 的胶囊将应用程序转换为适用于 Windows、Mac 或 Linux 的本机(包括图标!)可执行文件, 容器 caplet ,它在容器内运行一个胶囊等等。
胶囊的轻型容器
容器 是沙箱应用程序以及简化部署和整合服务器的有效方式,因此无论软件堆栈如何,它们都是有用的开发操作和安全工具。然而,由于 JVM 应用程序对环境要求极低(即内核和 JVM)并且通常是可移植的,因此使用像 Docker 这样的容器解决方案是浪费时间、空间和便利性。另一方面, shield caplet 为胶囊创建了一个轻量级容器,而不需要创建大图像。
例如,我们可以通过简单的桥接网络在容器中轻松运行 quasar-stocks Web 应用程序:
247 META-INF/MANIFEST.MF
1124 WEB-INF/classes/co/paralleluniverse/examples/HelloWorldServlet.class
653 WEB-INF/web.xml
161596 Capsule.class
1467463 capsule-maven-1.0.jar
然后我们可以轻松地检索运行应用程序的容器的 IP 地址:
247 META-INF/MANIFEST.MF
1124 WEB-INF/classes/co/paralleluniverse/examples/HelloWorldServlet.class
653 WEB-INF/web.xml
161596 Capsule.class
1467463 capsule-maven-1.0.jar
当一切都按预期工作时,在最终部署服务器(可能 作为守护进程 )上启动相同的命令并配置端口转发以公开服务将使我们的 Web 应用程序在强大的安全沙箱中运行几乎为零.
现在怎么办?
前往 capsule.io 并开始发射胶囊!
- 它们需要阴影来避免碰撞,但通常这还不够,而且它们不支持本机库。
- 他们可能需要操作系统外壳执行不可移植的启动脚本,以选择正确的 JRE 版本,设置类路径、代理和 JVM 参数。
- 当然,所有支持 JVM 的平台,以及可能包含的所需脚本和本机工件。
- 除了可能需要更长的启动时间,例如依赖项重新下载。
- shield caplet 使用 LXC 将胶囊放入容器中。