【问题标题】:Updating build dependencies in lots of microservices easily轻松更新大量微服务中的构建依赖项
【发布时间】:2020-06-13 09:39:26
【问题描述】:

在微服务中使用共享库是一种不好的做法。但是每个微服务都依赖于框架和库:Spring Boot、Kafka Java Client 等。

如果一个系统由 30 个基于 Spring Boot 的微服务组成,并使用 Gradle 或 Maven 作为构建工具并以 Docker 镜像的形式分发,那么有没有一种简单的方法可以在所有微服务中更新 Spring Boot 版本?一个一个地更新所有的微服务需要付出很多努力。而且系统拥有的微服务越多,就越需要付出更多的努力。

并且更新的原因可能是某些依赖项中的安全漏洞。 例如,Spring Boot 2.2.0.RELEASE 依赖于具有漏洞CVE-2020-1938 的 Tomcat 9.0.27(仅作为示例)。该漏洞已在 Tomcat 9.0.31 中修复,因此更新到 Spring Boot 2.2.4.RELEASE 即可解决此问题。

一种解决方法是将build.gradle 中的Spring Boot 版本声明为2.2.+。但是仍然有人必须重建所有微服务(例如,在 Jenkins 上)。

此外,通过这种方法,您可以放松对版本管理的控制并将其委托给 Gradle(始终尽可能使用最新版本)。为了仍然可以控制依赖关系,可以在 gradle.properties 中声明版本并由 Jenkins 以某种方式挂载到工作区中:

spring.version=2.2.4.RELEASE

如果漏洞很严重,则没有时间等待带有修复的新 Spring Boot 版本,并且必须在 build.gradle 中明确更新 Tomcat。甚至替换为 Undertow 也需要更改 build.gradle

使用独立的 Tomcat 而不是嵌入式也没有多大帮助,因为微服务是作为 Docker 映像而不是作为部署到 servlet 容器的 WAR 文件分发的。

在 Java EE 中,安全更新理论上非常简单。您更新应用程序服务器(例如 WildFly)或应用安全补丁,如果应用程序仅使用 Java EE API,则无需进一步操作。微服务是否有类似的简单依赖更新概念(至少在理论上)?

【问题讨论】:

  • 虽然不规范,但这是一个非常有趣的问题。
  • 这就是许多公司转向 monorepo 的原因。

标签: java spring-boot architecture microservices software-design


【解决方案1】:

附带说明,微服务通过使组件更加自主和可维护来带来价值,但这也有成本:集成和维护它们并不便宜。所以微服务的粒度也应该在这方面考虑。

如果一个系统由 30 个基于 Spring Boot 的微服务和 使用 Gradle 或 Maven 作为构建工具并作为 Docker 分发 图片,有没有一种简单的方法来更新 Spring Boot 版本 微服务?一个一个地更新所有的微服务需要很多时间 努力。一个系统拥有的微服务越多,付出的努力就越多 必填。

使用您引用的 Jenkins 等 CI 工具,在每个微服务应用程序中进行相关的自动化测试,更新其 pom/gradle 文件的 Spring 版本,甚至将它们部署在具有自动化 IT 基础设施工具的集成环境中并不难Ansible.
一般来说,如果项目属于多模块项目(maven 版本插件和 gradle 版本插件这样做),则可以使用 maven/gradle 插件更新所有项目的依赖版本。
如果项目不属于多模块项目,则还有其他选择,但您应该添加手动 SCM 检索步骤。
整个任务可以使用 shell 脚本完成,或者使用 jenkins 作业更好地使用 as custom job parameter 使用 Spring Boot 版本并且您可以按需执行。使用 Jenkins 会更好,因为您可以编写一个管道作业,在需要时集成 SCM 检索、docker 代理、构建执行和 shell 执行。总体而言,您将执行步骤的所有细节(成功或失败)保存在团队使用的工具中。

理论上,Java EE 的安全更新非常简单。你更新一个 应用服务器(例如 WildFly)或应用安全补丁,如果 应用程序仅使用 Java EE API,无需进一步操作

根据我最近的记忆,在所有环境中更新 Java EE 服务器从来都不是一件容易的事(不像更新文件中的版本那么简单)。
官方更新 Weblogic 和 Websphere 的程序非常非常挑剔,更新也需要很长时间,因为这些服务器怎么说...臃肿,而且回滚到以前的版本也并不总是那么容易。

【讨论】:

    【解决方案2】:

    感谢davidxxxanswer 我想出了我认为干净且简单的解决方案。

    一般的想法是简单地自动化手动更新库版本的过程,而不是使用解决方法(例如,动态版本 1.+ 使构建不确定,在 Jenkins 上安装集中管理的 gradle.properties 等)。

    1. 使用 BOM(物料清单),这是一种特殊的 POM,它捆绑了多个依赖项并提供它们的版本。

      dependencies {
          implementation platform("org.springframework.boot:spring-boot-dependencies:${springBootVersion}")
          implementation platform("org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}")
      
          implementation 'org.springframework.boot:spring-boot-starter-web' //version is taken from BOM
          implementation 'org.springframework.cloud:spring-cloud-context' //version is taken from BOM
          //...
      }
      
    2. 使用answer 中所述的特定依赖项创建自定义 BOM

      dependencies {
          //...
          implementation platform("com.example:my-dependencies:${myBomVersion}")
          //...
      }
      
    3. 这种方式在build.gradle 中只有 BOM 依赖项将与版本一起声明。所有其他依赖项将使用 BOM 提供的版本。在gradle.properties中声明BOM版本

      springBootVersion=2.2.4.RELEASE
      springCloudVersion=Hoxton.SR1
      myBomVersion=1.0.0
      
    4. 要更新依赖项,您只需更新 gradle.properties 中的 BOM 版本。可以使用 sed 自动执行此任务

      sed -i'.original' -E 's|(springBootVersion)=(.+)|\1=2.2.6.RELEASE|g' gradle.properties
      
    5. 可以创建用于更新版本的 Bash 脚本,例如./update-version.sh springBootVersion 2.2.6.RELEASE 并调用了参数化的 Jenkins 作业。存储库、版本变量名称和新版本值可以作为参数提供。

    所描述的方法稍微简化了更新库的过程,同时具有确定性构建(您始终知道使用了哪些版本的库)。

    【讨论】:

      猜你喜欢
      • 2017-09-08
      • 2013-12-20
      • 1970-01-01
      • 2014-11-25
      • 2016-11-24
      • 2021-12-17
      • 2015-09-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多