【问题标题】:How do you remotely update Java applications?如何远程更新 Java 应用程序?
【发布时间】:2010-09-12 15:06:34
【问题描述】:

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

该过程必须是无人值守且万无一失的(即,我们无法承受由于过早的互联网中断而破坏应用程序的代价)。

过去,我们使用各种外部脚本和实用程序来处理类似的任务,但由于它们有自己的依赖关系,因此结果更难维护且可移植性较差。在制作新东西之前,我想从社区获得一些意见。

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

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

【问题讨论】:

  • @Nzall 对于在这个问题上提出的更新赏金,使用自定义 JRE 不会在一定程度上解决它吗?和this Q&A 可能相关
  • @Naman 我不确定这将如何工作。我们希望将它从我们的服务器机器远程安装到客户端机器(带有库和自定义代码的 Karaf 核心),并且我们希望推送它,而无需人们手动复制安装程序并运行安装脚本。我们有一些客户需要更新 3 打代理,因此我们希望使升级更加顺畅。
  • 2020 年分布式部署管理和执行滚动发布是 Kubernetes 和其他容器编排器解决的两个大问题。可能太重了,无法将您的服务器改装为仅用于一个应用程序的 k8s 集群。您不能使用从 CI 服务器作业运行的 ssh 脚本完成自动重新部署吗?

标签: java release-management


【解决方案1】:

我推荐 Capistrano 用于多服务器部署。虽然它是为部署 Rails 应用程序而构建的,但我已经看到它成功地用于部署 Java 应用程序。

链接: Capistrano 2.0 Not Just for Rails

【讨论】:

    【解决方案2】:

    当 JVM 在其上运行时,无法修改 Jars,这会导致错误。我已经尝试过类似的任务,我想出的最好的方法是复制更新的 Jar 并将启动脚本转换为查看该 Jar。一旦你有了更新的 Jar,启动它并等待旧 Jar 在发出信号后结束。不幸的是,这意味着在几秒钟内丢失 GUI 等,但是在 java 中序列化大多数结构很容易,并且当前的 GUI 可以在实际关闭之前转移到更新的应用程序(尽管有些东西可能无法序列化!)。

    【讨论】:

      【解决方案3】:

      您没有指定服务器应用程序的类型 - 我将假设您没有运行 Web 应用程序(因为部署 WAR 已经完成了您所说的,并且您很少需要 Web 应用程序来做拉取类型更新。如果您谈论的是 Web 应用程序,以下讨论仍然适用 - 您只需为 WAR 文件而不是单个文件实现更新检查和 ping-pong。

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

      对此的一些一般想法(我们在同一个存储桶中有多个应用程序,并且正在考虑自动更新机制):

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

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

      3. 即使可以覆盖 JAR,您也可能需要考虑为您的 lib 路径使用 ping-pong 方法(如果您尚未将应用启动器配置为自动读取lib 文件夹并将它们自动添加到类路径中,那么这就是您真正想要做的事情)。以下是乒乓球的工作原理:

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

      1. 如上所述的乒乓球允许回滚。如果你设计得当,你可以有一个你的应用程序来测试它,然后永远不要改变它来检查它是否应该回滚给定的版本。这样,如果您搞砸并部署了破坏应用程序的东西,您可以使版本无效。这部分应用程序只需从坏的 lib-* 文件夹中删除 version.properties 文件,然后重新启动。保持这部分简单很重要,因为它是您的故障保险。

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

      【讨论】:

      • 是的,您的假设是正确的——我们没有使用 WAR 部署。该应用程序是服务器,但不适用于 Web 应用程序。感谢您提供有关 JNLP 的信息。我现在正在研究它...
      • 2020 更新:适用于遇到此答案的任何人。 Java Web Start (jnlp) 现已弃用。 oracle.com/java/technologies/javase/v9-deprecated-features.html
      【解决方案4】:

      我相信如果您使用基于 OSGi 的应用服务器,例如 SpringSource dm Server,您可以热部署 JAR 文件。我自己从未使用过它,但了解 Spring 产品组合的总体质量,我相信它值得一看。

      【讨论】:

        【解决方案5】:

        使更新成为原子更新非常困难,尤其是当您需要更新任何数据库时。

        但是,如果您不这样做,您可以首先确保您的应用程序可以从相对路径运行。也就是说,您可以将您的应用程序放在某个目录中,并且所有重要文件都相对于该位置找到,因此您的实际安装位​​置并不重要。

        接下来,复制您的安装。现在,您有了“运行”版本和“新”版本。

        使用您喜欢的任何技术(FTP、rsync、纸带,任何您喜欢的技术)更新“新”版本。

        验证您的安装(校验和、快速单元测试,无论您需要什么——如果您愿意,甚至可以在测试端口上启动它)。

        当您对新安装感到满意时,关闭原始运行实例,重命名原始目录(mv application application_old),重命名 NEW 目录(mv application_new application),然后重新启动它。

        您的停机时间缩短为服务器关闭和启动时间(因为重命名为“免费”)。

        如果您偶然发现一个严重错误,您的原始版本仍然存在。停止新服务器,将其重命名,重新启动旧服务器。回退速度非常快。

        另一个好处是您的服务基础架构是静态的(例如您的 rc 脚本、cron 作业等),因为它们指向“应用程序”目录,并且不会改变。

        也可以使用软链接而不是重命名目录来完成。无论哪种方式都可以。

        但该技术很简单,如果您的应用程序配合使用,则几乎是万无一失的。

        现在,如果您有数据库更改,那将是完全不同的令人讨厌的问题。理想情况下,如果您可以使您的数据库更改“向后兼容”,那么希望旧应用程序版本可以在新架构上运行,但这并不总是可能的。

        【讨论】:

          【解决方案6】:

          您绝对应该看看 OSGi,它是为这些情况(尤其是嵌入式产品)而创建的,并且被大量公司使用。您可以在应用程序运行时更新 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

          【讨论】:

            【解决方案7】:

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

            推荐!

            【讨论】:

              【解决方案8】:

              最新版本的 Java Web Start 允许在本地缓​​存中注入应用程序而无需实际调用程序,并且可以将其标记为“离线”。由于缓存是用来调用程序的,它只会在下次运行时更新。为此,您很可能需要名称中带有版本号的 jar(例如 our-library-2009-06-01.jar)。

              【讨论】:

                【解决方案9】:

                我会使用 ansible 将 jar 分发到多个服务器并运行更新脚本。

                为了让用户察觉不到更新,应用程序必须停止旧实例并快速启动一个新实例,其中 Java 应用程序不是很好。

                有很多方法可以在不停机的情况下进行更新,但它们都不是免费的。选择解决方案时,您应该考虑您可以接受的停机时间以及实现它的成本。

                我看到两个选项:

                1. 滚动更新,这意味着每次更新时都会使用新版本启动一个实例,然后验证它是否正常工作,如果是,则终止旧版本。此解决方案将需要某种代理(例如 haproxy),它将流量引导到有效实例。
                2. 优化应用启动时间。

                正如 Will Hartung 提到的,您还必须考虑外部依赖项,例如您可能还想更新的数据库架构。 为了获得无缝更新,您可能还希望对数据库进行滚动更新。它要求您不要在应用程序的任何两个连续版本之间引入任何重大更改。例如。当您要删除列时,在第一个版本中,您可以删除应用程序中对该列的所有引用,而在下一个版本中,您可以删除数据库中的列。

                【讨论】:

                • 我们不介意应用程序是否有临时停机时间。这些是用于远程启动脚本以用于 devops 目的的代理(如在软件代理中)(自动测试、部署其他软件)。预计它们不会全时运行,因为它们仅在客户需要它们时使用,我们的客户希望只需每几个月更新一次。
                • 在这种情况下,Ansible 似乎是一个合理的选择。其工作原理的总体思路是编写一个描述所需系统状态的 playbook,然后 ansible-playbook 命令将清单中的所有主机带到该状态。 Ansible 的要求很少,至少在 Unix 系统上运行时是这样。 Ansible 必须仅安装在启动更新过程的主机上,另一端只需要 ssh 和 Python。但是,某些插件可能有自己的要求。我们使用 Ansible 来管理不同类型的应用程序并对此感到满意。
                • 我们可以将 Ansible 作为商业产品的一部分提供吗?我们可以使用它来部署到 Windows 环境吗?
                • 可以使用 Ansible 部署到 Windows 环境,但是,我没有这样做的经验。在这里,您将获得一些关于它与部署到 Unix 系统有何不同的信息:docs.ansible.com/ansible/latest/user_guide/windows_usage.html
                • 关于许可。这取决于部署的工作方式。如果您将 ansible 用作命令行工具来部署到客户的环境,那绝对没问题。 Ansible 不必安装在客户的设备上。但是,如果您想向您的客户提供某种工具,那么他们就可以在他们的环境中进行部署。此类工具必须在 GPL 许可下开源。
                猜你喜欢
                • 2011-10-19
                • 2016-10-20
                • 2021-09-25
                • 2015-11-17
                • 1970-01-01
                • 2023-03-28
                • 1970-01-01
                • 2010-11-01
                • 2011-11-26
                相关资源
                最近更新 更多