如何远程更新Java应用程序?

28

我们有一个Java服务器应用程序,它在许多计算机上运行,并且都连接到互联网,其中一些位于防火墙后面。我们需要从中央站点远程更新JAR文件和启动脚本,而不会对应用程序本身造成明显的中断。

这个过程必须是无人值守的和完全可靠的(即我们不能因为时机不当的网络中断而破坏应用程序)。

在过去,我们使用了各种外部脚本和实用程序来处理类似的任务,但是由于它们有自己的依赖关系,结果更难维护并且不太可移植。在制作新东西之前,我想向社区寻求一些意见。

是否已经有人找到了好的解决方案?有什么想法或建议吗?

仅澄清一下:这个应用程序是一个服务器,但不是用于Web应用程序(这里没有Web应用程序容器或WAR文件)。它只是一个自治的Java程序。


@Nzall 对于此问题提出的更新赏金,使用自定义JRE在某种程度上是否可以解决它?这个问答可能相关。 - Naman
1
@Naman,我不确定这将如何运作。我们想要从我们的服务器机器远程安装到客户端机器(具有库和自定义代码的Karaf核心),并且我们希望在没有人手动复制安装程序和运行安装脚本的情况下推送此操作。我们有一些需要更新的拥有3打代理的客户,因此我们希望使升级更加顺畅。 - Nzall
在2020年,分布式部署管理和执行滚动发布是Kubernetes和其他容器编排器解决的两个重大问题。然而,仅为了一个应用程序而将服务器改装为k8s集群可能过于笨重。您是否可以通过从CI服务器作业运行的ssh脚本来实现自动重新部署呢? - Richard Woods
9个回答

10

您没有指定服务器应用程序的类型 - 我假设您没有运行 Web 应用程序(因为部署 WAR 文件已经可以实现您要讨论的内容,而且很少需要 Web 应用程序来执行拉取式更新。如果您正在讨论 Web 应用程序,则以下讨论仍然适用 - 您只需将更新检查和 ping-pong 实现到 WAR 文件中,而不是单个文件)。

您可能需要查看 jnlp - WebStart 就是基于此技术的(这是一种客户端应用程序部署技术),但我非常确定它也可以针对服务器类型的应用程序执行更新。无论如何,jnlp 做得很好,提供了可用于下载所需版本的所需 JAR 的描述符...

关于此的一些普遍想法(我们在同一个桶中有几个应用程序,并考虑自动更新机制):

  1. 考虑拥有一个 bootstrap.jar 文件,该文件能够读取 jnlp 文件并在启动应用程序之前下载所需/更新的 jar 文件。

  2. JAR 文件即使在应用程序运行时也可以更新(至少在 Windows 上,这是最有可能锁定运行文件的操作系统)。如果您使用自定义类装入器或者有一堆可能随时加载或卸载的 JAR,则可能会遇到问题,但如果您创建了防止此类问题的机制,则覆盖 JAR 然后重新启动应用程序应该足以实现更新。

  3. 即使可以覆盖 JAR,您可能还想考虑在 lib 路径上使用 ping-pong 方法(如果您尚未配置应用程序启动器以自动读取 lib 文件夹中的所有 jar 文件并将它们自动添加到类路径中,则确实需要这样做)。以下是 ping-pong 的操作方式:

应用程序启动并查看lib-ping\version.properties和lib-pong\version.properties文件,确定哪一个版本较新。假设lib-ping的版本更高。启动器在启动期间搜索lib-ping*.jar文件,并将这些文件添加到类路径中。当您进行更新时,将jar文件下载到lib-pong中(或者从lib-ping复制jar文件,如果您想节省带宽且JAR实际上没有更改,尽管这很少值得一试!)。一旦您将所有的JAR文件复制到lib-pong中,最后要做的就是创建version.properties文件(这样可以检测到部分lib文件夹的中断更新并进行清除)。最后,重新启动应用程序,引导程序会选择使用lib-pong作为所需的类路径。

  1. 如上所述的ping-pong方法允许回滚。如果您适当地设计它,就可以有一个应用程序的一部分,您可以对其进行大量测试,然后永远不更改该部分,以检查是否应回滚给定版本。这样,如果您部署了破坏应用程序的东西,可以使该版本无效。该应用程序部分只需要从错误的lib-*文件夹中删除version.properties文件,然后重新启动即可。保持此部分非常简单很重要,因为这是您的故障安全。

  2. 您可以拥有多个文件夹(而不是ping/pong,只需使用lib-yyyymmdd并清除除最新的5个以外的所有文件),这允许更高级(但更复杂!)的JAR回滚。


是的,你的假设是正确的——我们没有使用WAR部署。该应用程序是一个服务器,但不是用于Web应用程序。感谢关于JNLP的信息。我现在正在研究它... - David Crow
2
2020年更新:对于任何遇到这个答案的人来说,Java Web Start(jnlp)现已被弃用。 https://www.oracle.com/java/technologies/javase/v9-deprecated-features.html - Philip Attisano

6
你应该一定要看看OSGi,它是专门为这些情况(尤其是嵌入式产品)而创建的,并被许多公司使用。您可以在应用程序运行时更新“bundles” jar文件,添加和删除它们。我自己没有使用过它,所以我不知道开源框架/服务器的质量如何,但以下是一些有用的链接,可以帮助您入门:

http://www.osgi.org/Main/HomePage
http://www.aqute.biz/Code/Bnd
http://blog.springsource.com/2008/02/18/creating-osgi-bundles/
http://blog.springsource.com/
http://www.knopflerfish.org/
http://felix.apache.org/site/index.html


4
我建议使用Capistrano进行多服务器部署。虽然它是为了部署Rails应用程序而构建的,但我已经看到它成功地用于部署Java应用程序。
链接: Capistrano 2.0不仅适用于Rails

4
很难使更新成为原子操作,特别是如果需要进行任何数据库更新。
但是,如果不需要进行数据库更新,您可以首先确保应用程序可以从相对路径运行。也就是说,您可以将应用程序放在某个目录中,并且所有重要文件都相对于该位置找到,因此您的实际安装位置并不重要。
接下来,复制您的安装。现在,您有“运行”版本和“新”版本。
使用您喜欢的任何技术(FTP、rsync、纸带等)更新“新”版本。
验证您的安装(校验和、快速单元测试,您需要的任何内容 - 甚至在测试端口上启动它,如果您喜欢的话)。
当您满意新的安装时,关闭原始运行实例,重命名原始目录(mv application application_old),重命名NEW目录(mv application_new application),然后重新启动它。
您的停机时间仅减少到服务器关闭和启动时间(因为重命名是“免费”的)。
如果偶然检测到关键错误,则仍然存在原始版本。停止新服务器,将其重命名回来,重新启动旧服务器。非常快速的回退。
另一个好处是,您的服务基础设施是静态的(如rc脚本、cron作业等),因为它们指向“应用程序”目录,而且不会改变。
也可以使用符号链接而不是重命名目录。无论哪种方式都可以。
但是,如果您有数据库更改,则完全是不同的麻烦问题。理想情况下,如果您可以使数据库更改“向后兼容”,那么希望旧应用程序版本可以在新模式上运行,但这并非总是可能的。

3

在JVM运行的同时无法修改Jar文件,否则会导致错误。我尝试过类似的任务,最好的方法是复制更新后的Jar文件并转换启动脚本以查看该Jar文件。一旦您拥有了更新的Jar文件,请启动它并等待旧的Jar文件在给出终止信号后结束。不幸的是,这意味着GUI等将会丢失一秒钟,但大多数Java结构可以轻松序列化,并且当前的GUI可以在实际关闭之前转移到更新的应用程序中(某些内容可能无法序列化!)。


3
我会使用ansible将jars分发到多个服务器并运行更新脚本。
为了使用户无感知地进行更新,应用程序必须非常快速地停止旧实例并启动新实例,在这方面Java应用程序表现不佳。
有一些方法可以在更新期间实现零停机,但没有其中一个是免费的。选择解决方案时,您应考虑可接受的停机时间和可以接受的成本。
我看到两个选择:
1.滚动更新,这意味着每次更新时,您都会使用新版本启动一个实例,然后验证它是否正常工作,如果是,则终止旧版本。此解决方案将需要某种代理(例如haproxy),它将将流量指向有效实例。
2.优化应用程序启动时间。
正如Will Hartung所提到的,您还必须考虑外部依赖项,例如可能希望更新的数据库模式。 为了实现无缝更新,您可能还想在数据库上进行滚动更新。这将要求您在任何连续两个应用程序发布之间不引入任何破坏性更改。例如,当您要删除一列时,在第一个发布中,您会删除应用程序中对该列的所有引用,并在下一个发布中允许删除数据库中的该列。

我们不介意应用程序有暂时的停机时间。这些代理(即软件代理)用于远程启动脚本以进行devops目的(自动测试、部署其他软件)。它们不需要全天候运行,因为它们只在客户需要时使用,而我们的客户希望每隔几个月才更新它们。 - Nzall
在这种情况下,Ansible似乎是一个合理的选择。它的工作原理是编写一个playbook来描述所需的系统状态,ansible-playbook命令将清单中的所有主机带到该状态。至少在Unix系统上运行时,Ansible的要求非常少。只需要在启动更新过程的主机上安装Ansible,在另一端只需要ssh和Python即可。但是某些插件可能有自己的要求。我们使用Ansible来管理不同类型的应用程序,并对其感到满意。 - Cezary Butler
我们可以将Ansible作为商业产品的一部分进行发布吗?并且我们可以使用它来部署到Windows环境吗? - Nzall
可以使用Ansible部署到Windows环境,但我没有这方面的经验。在这里,您将了解如何与部署到Unix系统有所不同:https://docs.ansible.com/ansible/latest/user_guide/windows_usage.html - Cezary Butler
关于许可证。这取决于部署的方式。如果您使用ansible作为命令行工具来部署到客户环境,那么完全没有问题。Ansible不必安装在客户设备上。但是,如果您想向客户提供某种工具,以便他们能够在自己的环境中进行部署。这些工具必须在GPL许可下开源。 - Cezary Butler

2

如果您使用基于OSGi的应用服务器,例如SpringSource dm Server,我相信您可以热部署JAR文件。虽然我自己从未使用过它,但是了解到Spring产品系列的一般质量,我相信这值得一试。


0
Java Web Start的最新版本允许将应用程序注入本地缓存,而不实际调用程序,并且可以标记为“离线”。由于缓存是用于调用程序的,因此它仅在下一次运行时更新。为了使其正常工作,您很可能需要在名称中使用版本号的jar文件(例如,our-library-2009-06-01.jar)。

0

我们使用 Eclipse,它是 OSGi 的更新系统,我们的经验非常好。

推荐!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接