轻松更新大量微服务中的构建依赖项

Updating build dependencies in lots of microservices easily

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

如果一个系统由 30 个微服务组成,基于 Spring 引导并使用 Gradle 或 Maven 作为构建工具并作为 Docker 图像分发,是否有在所有微服务中更新 Spring 引导版本的简单方法?一个一个地更新所有的微服务需要付出很多努力。而且一个系统的微服务越多,需要付出的努力就越多。

更新的原因可能是某些依赖项中存在安全漏洞。 例如,Spring Boot 2.2.0.RELEASE depends on Tomcat 9.0.27 that has vulnerability CVE-2020-1938 (just as an example). The vulnerability was fixed in Tomcat 9.0.31, so updating to Spring Boot 2.2.4.RELEASE 将解决问题。

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

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

spring.version=2.2.4.RELEASE

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

中进行改动

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

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

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

If a system consists of 30 microservices based on Spring Boot and using Gradle or Maven as a build tool and distributed as Docker images, is there an easy way to update Spring Boot version in all microservices? Updating all microservices one by one takes a lot of efforts. And the more microservices a system has, the more efforts is required.

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

In Java EE security updates in theory was super easy. You update an application server (e.g. WildFly) or apply a security patch and if application uses only Java EE API, no further action is required

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

多亏了 davidxxx's 我想出了我认为干净简单的解决方案。

总体思路是简单地自动执行手动更新库版本的过程,而不是使用变通方法(例如,动态版本 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)=(.+)|=2.2.6.RELEASE|g' gradle.properties
    
  5. 可以创建 Bash 脚本来更新版本,例如./update-version.sh springBootVersion 2.2.6.RELEASE 并调用了参数化的 Jenkins 作业。存储库、版本变量名称和新版本值可以作为参数提供。

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