微服务架构升级一:配置中心

在Spring Cloud微服务开发的体系下,主推的配置中心是Spring Cloud Config。

Spring Cloud Config支持使用文件系统、Git等等来保存application.yml(或application.properties)中的参数,根据spring.profile.active参数值(default/dev/test/prod)的不同来区分开发、测试、生产环境。

一开始,我们也是用Spring Cloud Config + 文件系统,具体的方案是:

在测试环境、生产环境中,分别部署一个ConfigServer,application.yml以文件的形式保存在CephFS中,通过Kubernetes PV挂载到Pod中供ConfigServer访问。spring.profile.active参数的值都是default

此外,本地开发环境和测试环境共用一个ConfigServer,因此,测试环境额外有XXX-dev.yml等文件。

上述方案有如下的缺点:

  1. 存在多个副本。

    一处变动需要修改多个地方,可能造成开发、测试、生产环境的application.yml不一致。

  2. 无法在线编辑。

    如果要修改application.yml,首先得在本地编辑好,然后上传到测试、生产环境。

  3. 没有用上spring.profile.active参数;

  4. 占用系统资源。

    部署了多个ConfigServer。

  5. 动态刷新复杂。

    如果启用Spring Cloud Config的动态刷新服务,还要再部署一个RabbitMQ或者Kafka。

当然,测试、生产环境可以通过共用一个ConfigServer来克服缺点1和缺点3。但是,缺点2、缺点4和缺点5是靠Spring Cloud Config自己解决不了的。

为了克服上述5个缺点,同时,考虑到运行环境是Kubenetes,决定用Spring Cloud Kubernetes + Kubenetes ConfigMap替换Spring Cloud Config。

ConfigMap是Kubernetes内置的一种API对象,无需安装部署,被用来保存Key-Value,底层的技术是etcd。通过Lens、Kuboard等等图形化管理工具可以在线编辑ConfigMap的属性和值。Spring Cloud Kubernetes提供Reload机制来跟踪ConfigMap属性值的变化。

配置Spring Boot

classpath下新增bootstrap.yml文件:

spring:
  application:
    name: XXX

  cloud:
    kubernetes:
      config:
        namespace: default
        sources:
          - name: application
          - name: ${spring.application.name}

spring.cloud.kubernetes.config.namespring.cloud.kubernetes.config.namespace设置该应用配置参数默认的名称和命名空间,名称和ConfigMap的metadata.name属性一一对应。

在Spring Cloud Config中,默认的、共用的参数都被保存在XXX[-default].yml(XXX是applicationspring.application.name)中。举个例子,如果spring.profile.active的值是dev,就会依次读取application.ymlapplication-dev.ymlXXX.ymlXXX-dev.yml文件。如果出现同名的参数,就会后面的覆盖前面的。

在Spring Cloud Kubernetes中,可以通过spring.cloud.kubernetes.config.sources参数来配置多个ConfigMap实现类似的效果。

部署ConfigMap

对于默认、开发、测试、生产环境,就有XXX.ymlXXX-dev.ymlXXX-test.ymlXXX-prod.yml等4套参数,可以都保存在同一个ConfigMap中,使用spring.profiles: default/dev/test/prod区分,

kind: ConfigMap
apiVersion: v1
metadata:
  name: XXX
data:
  application.yml: |-
    msg: "Hello, World"
    ---
    spring:
      profiles: dev
    msg: "你好,世界"
    ...

也可以分别保存在不同的ConfigMap中,

kind: ConfigMap
apiVersion: v1
metadata:
  name: XXX
data:
  XXX.yml: |-
    msg: "Hello, World"
kind: ConfigMap
apiVersion: v1
metadata:
  name: XXX-dev
data:
  XXX-dev.yml: |-
    spring:
      profiles: dev
    msg: "你好,世界"

……

推荐后面这种方式!可以先把参数写到一个文件中,然后使用如下脚本创建ConfigMap:

kubectl create configmap XXX --from-file=~/config/XXX.yaml

为了让应用能在运行时确定使用哪套参数,需要在Kubernetes Deployment文件中声明环境变量SPRING_PROFILES_ACTIVE

动态刷新

设置spring.cloud.kubernetes.reload.enabled=true即可启用动态刷新服务:

spring:
  cloud:
    kubernetes:
      reload:
        enabled: true
        strategy: restart_context
        mode: polling
        period: 15000

spring.cloud.kubernetes.reload.strategy有3种策略:

  • refresh:只替换应用上下文中被@ConfigurationProperties@RefreshScope注解标注的Bean;

  • restart_context:重启应用;

    如果使用restart_context策略,就要配上spring-boot-starter-actuator,并按照如下方式配置:

    management:
      endpoint:
        restart:
          enabled: true
      endpoints:
        web:
          exposure:
            include: restart
    
  • shutdown:关闭应用;

spring.cloud.kubernetes.reload.mode有2种模式:

  • event:事件;
  • polling:定时作业;
写于 2023年04月26日