Spring Cloud(十二):分布式配置管理 Config 之 配置详解

本篇是前面两篇 Config Git 和 JDBC 实现的文章的延续,主要对 Spring Cloud Config 服务器端和客户端的相关配置进行详细的描述。

具体包括引导启动、服务存储库、健康检测、安全认证、集成服务发现、请求失败重试等配置详解。

引导启动配置

EnvironmentRepository 为 Environment 对象提供服务,对配置数据的存储进行管理。EnvironmentRepository 的默认实例使用 Git 仓库存储配置数据。

此 Environment 是 Spring Environment 的一个浅拷贝(主要功能包括 propertySources )。Environment 资源由三个变量进行参数化:

  • {application}:映射到客户端的 spring.config.name,若不存则默认 spring.application.name 。
  • {profile}:映射到客户端的 spring.profiles.active。
  • {label}:服务端的特性,指配置文件的版本化(Git 标签)。

Config Client 必须有一个 bootstrap.yml 或者 bootstrap.properties 文件,这是引导启动的配置文件,优先级最高。

bootstrap.properties

1
2
spring.application.name=foo
spring.profiles.active=dev,mysql

服务存储库配置

Git 存储库设置

  1. 服务名

    1
    spring.application.name=config-repo
  2. 配置 Git 仓库

    1
    spring.cloud.config.server.git.uri=https://github.com/spring-cloud-samples/config-repo
  3. Git URI 使用占位符,支持 {profile}

    1
    spring.cloud.config.server.git.uri=https://github.com/spring-cloud-samples/${spring.application.name}
  4. 禁用配置服务器对 Git服务器的SSL证书的验证

    1
    spring.cloud.config.server.git.skip-ssl-validation=true
  5. 设置 HTTP 连接超时时长, 单位:秒

    1
    spring.cloud.config.server.git.timeout=10
  6. Spring Cloud Config 还可以通过对 application 和 profile 进行模式匹配来支持更复杂的需求。
    模式的格式是带有通配符(*) 的 {application}/{profile} ,可以配置多个,以逗号分隔,如下示例:

    1
    2
    3
    4
    5
    6
    spring.cloud.config.server.git.uri=https://github.com/spring-cloud-samples/config-repo
    spring.cloud.config.server.git.repos.simple=https://github.com/simple/config-repo
    spring.cloud.config.server.git.repos.special.pattern=special*/dev*,*special*/dev*
    spring.cloud.config.server.git.repos.special.uri=https://github.com/special/config-repo
    spring.cloud.config.server.git.repos.local.pattern=local*
    spring.cloud.config.server.git.repos.local.uri=file:/home/configsvc/config-repo

    如果 {application}/{profile} 与任何模式(pattern)都不匹配,则使用由 spring.cloud.config.server.git.uri 定义的默认 URI。上面示例,simple存储库的匹配模式是 simple/*(只匹配 spring.application.name=simple 的应用)。
    local存储库匹配所有 application names 的值以 local 为前缀开头的应用名(若没有配置文件匹配模式,则自动在后缀添加 /* )。

    注意:当只设置了 URI 属性时,才能使用 simple 示例的方式。

  7. pattern 属性数组绑定多个模式。
    repopattern 属性实际上是个数组,所以可以使用 YAML 数组(或 properties 中的 [0]、[1] 等后缀) 绑定多个模式。如果应用需要用到多个配置文件,可这样使用:

    1
    2
    3
    4
    5
    6
    7
    8
    # 使用数组匹配多个配置文件
    spring.cloud.config.server.git.uri=https://github.com/spring-cloud-samples/config-repo
    spring.cloud.config.server.git.repos.development.pattern[0]=*/development
    spring.cloud.config.server.git.repos.development.pattern[1]=*/staging
    spring.cloud.config.server.git.repos.development.uri=https://github.com/development/config-repo
    spring.cloud.config.server.git.repos.staging.pattern[0]=*/qa
    spring.cloud.config.server.git.repos.staging.pattern[1]=*/production
    spring.cloud.config.server.git.repos.staging.uri=https://github.com/staging/config-repo
  8. 从子目录中获取配置文件
    每个存储库可以把配置文件存放在子目录中,使用 searchPaths 来指定搜索路径。

    1
    spring.cloud.config.server.git.search-paths=foo,bar*

    上面示例,服务器会从存储库的根目录,子目录 foo/ 和目录名以 bar 开头的子目录。

  9. 设置第一次请求是否 clone 配置库
    默认情况下,服务器会在第一次请求配置时克隆远程存储库,可以通过 clone-on-start 来设置是否克隆。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    # 指定首次请求是否克隆存储库
    spring.cloud.config.server.git.uri=https://git/common/config-repo.git
    spring.cloud.config.server.git.repos.team-a.pattern=team-a-*
    spring.cloud.config.server.git.repos.team-a.clone-on-start=true
    spring.cloud.config.server.git.repos.team-a.uri=http://git/team-a/config-repo.git
    spring.cloud.config.server.git.repos.team-b.pattern=team-b-*
    spring.cloud.config.server.git.repos.team-b.clone-on-start=false
    spring.cloud.config.server.git.repos.team-b.uri=http://git/team-b/config-repo.git
    spring.cloud.config.server.git.repos.team-c.pattern=team-c-*
    spring.cloud.config.server.git.repos.team-c.uri=http://git/team-a/config-repo.git

    上面示例,在首次访问 team-a. 存储库时会被克隆,其它的存储库不会克隆。

    注意:在配置服务器启动时设置要克隆的存储库有助于快速识别错误的配置源(例如无效的存储库URI)。如果未启用 CloneOnStart,若配置源错误或无效仍能启动成功,并且在请求该配置,不会检测到错误。

Git 安全认证

要在远程存储库上使用 HTTP Basic 身份验证,需单独添加 username 和 password 属性(不在URL中),如下示例:

1
2
3
4
# Git 认证
spring.cloud.config.server.git.uri=https://github.com/spring-cloud-samples/config-repo
spring.cloud.config.server.git.username=trolley
spring.cloud.config.server.git.password=strongPassword

Git 也支持 SSH 连接并提供了自动配置,详细请参考官网。

强制更新本地脏库

如果本地副本变脏(例如,通过操作系统进程更改文件夹内容),Spring Cloud Config Server 将克隆远程 Git 存储库,而不是将远程存储库更新本地副本。可以使用 force pull 属性,强制配置服务器在本地副本变脏时从远程存储库中强制拉出并更新到本地。

1
2
# foce-pull 默认 false
spring.cloud.config.server.git.force-pull=true

如果配置了多个存储库,可给每个存储库添加 force-pull 属性。

1
2
3
4
5
spring.cloud.config.server.git.uri=https://git/common/config-repo.git
spring.cloud.config.server.git.force-pull=true
spring.cloud.config.server.git.repos.team-a.pattern=team-a-*
spring.cloud.config.server.git.repos.team-a.uri=http://git/team-a/config-repo.git
spring.cloud.config.server.git.repos.team-a.force-pull=true

删除未跟踪的库分支

Config Server 在检出分支到本地(例如,通过标签获取属性)后就拥有远程 Git 存储库的克隆,并将永远保留此分支,或直到下一次服务器重启(这将创建新的本地 repo)。

为保持本地存储库分支干净,可以设置到 deleteUntrackedBranches属性。让 Spring Cloud Config Server 强制从本地存储库中删除未跟踪的分支。

1
2
# 默认值 false
spring.cloud.config.server.git.delete-untracked-branches=true

存储库刷新频率

可使用 spring.cloud.config.server.git.refreshRate 来控制配置服务器从 Git 后端获取更新配置数据的频率,单位:秒,默认值:0,这意味着每次请求配置服务器时,配置服务器都将从 git repo 获取更新的配置。

1
spring.cloud.config.server.git.refresh-rate=5

设置本地存储目录

使用基于 VCS 的后端(git,svn),文件将被签出或克隆到本地文件系统。 默认情况下,它们放在系统临时目录中,前缀为 config-repo-

例如,在 Linux 上,它可能是 /tmp/config-repo-<randomid>。 某些操作系统会定期清理临时目录,这可能会导致意外发生,例如缺少属性。

要避免此问题,需要设置 spring.cloud.config.server.git.basedir 或 spring.cloud.config.server.svn.basedir 设置为不驻留在系统临时结构中的目录来更改 Config Server 使用的目录。

1
spring.cloud.config.server.git.basedir=/configserver/repo

JDBC 存储库配置

SpringCloud Config Server 支持 JDBC(关系数据库)作为配置属性的存储。

以通过将 spring-jdbc 添加到类路径并使用 jdbc profile 文件或通过添加一个类型为 JdbcEnvironmentRepository 的 Bean 来启用此功能。

1
2
# 使用 jdbc 存储库
spring.profiles.active=jdbc

如果在类路径上包含正确的依赖项,那么Spring Boot 将配置数据源。

数据库需要有一个名为 PROPERTIES 的表,包含 APPLICATION,PROFILE、 LABEL 的字段(与 Environment 中属性对应),包含与 Properties 样式中键和值对应的 KEYVALUE 字段。

所有字段都是 Java 中的 String 类型,数据库中可以设置为 VARCHAR 类型。JDBC 存储的属性值与 Spring Boot 的 {application}-{profile} .properties 属性文件效果一致,包括所有加密和解密,这些加密解密作为后处理步预应用(即不直接在存储库中实现)。

服务相关配置

服务器自身配置文件

Config Server 自身默认使用 native profile 文件,它不使用 git,而是从从本地类路径或文件系统加载配置文件(要指向spring.cloud.config.server.native.searchlocations 的任何静态 URL)。

显式配置 spring.profiles.active=native

1
2
# 注意,Windows 文件系统路径分隔需多加一个 /
spring.cloud.config.server.native.searchLocations=file:///${user.home}/config-repo)

searchLocations 的默认值与本地的 Spring Boot 相同,是 classpath:/,classpath:/config, file:./,file:./config。这不会将 application.properties 从服务器暴露给所有客户端,因为服务器中存在的任何属性源在被发送到客户端之前都会被删除。

searchLocations 属性可以使用占位符,如 *{application},{profile}, {label}*,可以使用 spring.cloud.config.server.native.addLabelLocations=false 禁用 {label}

与所有应用共享配置

使用基与文件(Git、SVN)管理的存储库,在所有客户端应用之间共享文件名为 application*模式(如:application.properties、application.yml、application-*.properties)的资源。

可以使用这些文件来配置全局默认值,也可由应用指定的配置文件来覆盖这些默认值。

配置服务属性覆盖

Config Server 具有覆盖(overrides)功能,可以为所有应用程序提供配置属性。

被重写的属性不能被标准的 Spring Boot 勾子更改。要声明覆盖,将键值对映射到 spring.cloud.config.server.overrides,如下示例:

1
2
# 配置属性覆盖
spring.cloud.config.server.overrides.foo=bar

上面的示例,作为配置客户端的所有应用都读取 foo=bar,与其自身配置无关,即执行了覆盖。

可以通过在远程存储库中设置 spring.cloud.config.overrideNone=true (默认为 false) ,将客户端中所有覆盖的优先级更改为更像默认值,让应用程序在环境变量或系统属性中提供自己的值。

配置服务健康检测

Config Server 自带了一个运行状态指示器,用于检查配置的 EnvironmentRepository 是否正常工作。默认情况下,它会向 EnvironmentRepository 向应用名为 app,默认 profile,和 EnvironmentRepository 实现提供的默认 lable 发送请求。

也可以配置为检查多个应用程序以及自定义配置文件和自定义标签,也可禁用健康指示器,如下示例:

1
2
3
4
5
6
7
# 健康指标器
spring.cloud.config.server.health.repositories.myservice.label=mylabel
spring.cloud.config.server.health.repositories.myservice-dev.name=myservice
spring.cloud.config.server.health.repositories.myservice-dev.profiles=development

# 禁用健康检测
spring.cloud.config.server.health.enabled=false

配置服务安全认证

Spring Security 和 Spring Boot 为多种安全认证方式提供了支持。

若使用默认的 Spring Boot 配置的 HTTP Basic 安全认证,引入 Spring Security 依赖包(如:spring-boot-starter-security)。默认的用户名是 user,密码是随机产生,建议配置账号密码,并对其进行加密:

1
2
3
# Spring Security 账号密码
spring.security.user.name=admin
spring.security.user.password=strong_password

加密解密和密钥

要使用加密和解密功能,需要在 JVM 中安装完整全功能的 JCE(默认情况下不包括它)。可以从Oracle下载 Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files 并依说明安装(实际是将 JRE lib/security 目录中的两个策略文件替换为下载的)。

如果远程属性源包含加密内容(值以 {cipher} 开头),则在通过 HTTP 发送到客户端应用之前对其进行解密。此优点是属性值不会以明文存储在文件中。

如果值无法解密,则从属性原中删除该值,并添加一个额外的具有相同键的属性,但前缀为 invalid 且值为 not applicable(通常为 <n/a>)。这是为了防止密文被用作密码并意外泄漏。

  1. 加密

    如果为客户端应用设置远程存储库,它可能包含类似以下内容的 application.yml:
    application.yml

    1
    2
    spring.datasource.username=dbuser
    spring.datasource.password={cipher}FKSAJDFGYOS8F7GLHAKERGFHLSAJ

    加密的值不能使用引号括起来,否则无法正确解密,如上所示。

    服务器还公开 /encrypt/decrypt 端点(假设这些端点是安且并且只能授权代理访问)

    数据库需要有一个名为PROPERTIES的表,其中包含名为APPLICATION,PROFILE和LABEL的列(具有通常的环境含义),以及属性样式中键和值对的KEY和VALUE。

    如果编辑远程配置文件,则可以使用配置服务器通过 POST 请求 /encrypt 端点来加密值,如下例所示:

    1
    2
    $ curl localhost:8888/encrypt -d mysecret
    682bc583f4641835fa2db009355293665d2647dade3375c0ee201de2a49f7bda

    如果加密的值中包含需要进行 URL编码 的字符,执行 curl 需要使用--data urlencode 选项,以确保被正确编码。

    获取加密值并添加密码前缀 {cipher},然后将其放入到 yaml 或 properties 文件,然后保存更新远程存储区。

    注意:确保不要在加密值中包含任何 curl 命令统计信息,否则执行 curl 可能导致异外情况。

  2. 解密
    解密是通过 curl 向 /decrypt 请求(前提是服务器配置了对称密钥或完整密钥对),如下例所示:

    1
    2
    $ curl localhost:8888/decrypt -d 682bc583f4641835fa2db009355293665d2647dade3375c0ee201de2a49f7bda
    mysecret

    /encrypt/decrypt 端点也接受 /*/{name}/{profiles} 形式的路径,当客户端调用主环境资源时,这些路径可用于控制每个 application (name) 和每个 profile 的基础上的加密。要以这种粒度方式控制加密,还必须提供TextEncryptorLocator 类型的 Bean,它为每个名称和配置文件创建不同的加密程序。默认情况下提供的加密不会这样做(所有加密都使用相同的密钥)。

    Spring 命令行客户端也可用用于加密解密,前提是安装了 Spring Cloud CLI 扩展:

    1
    2
    3
    4
    $ spring encrypt mysecret --key foo
    682bc583f4641835fa2db009355293665d2647dade3375c0ee201de2a49f7bda
    $ spring decrypt --key foo 682bc583f4641835fa2db009355293665d2647dade3375c0ee201de2a49f7bda
    mysecret

    使用加密密钥的方式,如 RSA 公钥加密,使用 –key 选项,后面跟 @ 和文件路径:

    1
    2
    $ spring encrypt mysecret --key @${HOME}/.ssh/id_rsa.pub
    AQAjPgt3eFZQXwt8tsHAVv/QHiY5sI2dRcR+...
  3. 密钥管理

    配置服务器可以使用对称(共享)密钥或非对称密钥(RSA密钥对)。在安全性方面,非对称更优,但使用对称密钥通常更方便,因为它是在 bootstrap.properties 中配置的单个属性值。

    要配置对称密钥,需要将 encrypt.key 设置为密钥字符串(或使用 ENCRYPT_KEY 环境变量使其不在纯文本配置文件中)。

    要配置非对称密钥,需使用密钥库(例如,由 JDK 附带的 keytool 工具创建)。keystore 属性是 encrypt.keystore.*中的 * 部分。

    • encrypt.keyStore.location:资源位置
    • encrypt.keyStore.password:解锁密钥的密码。
    • encrypt.keyStore.alias:标识使用的密钥。

    加密是用公钥完成的,使用私钥解密。

  4. 创建测试密钥

    创建一个测试密钥,如下命令:

    1
    2
    3
    $ keytool -genkeypair -alias mytestkey -keyalg RSA \
    -dname "CN=Web Server,OU=Unit,O=Organization,L=City,S=State,C=US" \
    -keypass changeme -keystore server.jks -storepass letmein

    server.jks 文件放在类路径,然后在 bootstrap.yml 文件中为配置服务器创建如下设置:

    1
    2
    3
    4
    5
    6
    encrypt:
    keyStore:
    location: classpath:/server.jks
    password: letmein
    alias: mytestkey
    secret: changeme

    若希望客户端代替服务器对密码内容进行解密,可以在 bootstrap.[yml|properties] 配置文件中设置 spring.cloud.config.server.encrypt.enabled=false 来关闭传输解密的属性。

配置客户端详解

Spring Boot 应用可以立即利用 Spring Config Server(或其他外部属性源)。 它还提供了一些与环境变化事件相关的其他有用功能。

配置第一引导启动

Spring Boot 应用引入了 spring-cloud-config-client 依赖,默认情况下,当配置客户端启动时,会通过 spring.cloud.config.uri 属性绑定到配置服务器并获取远程属性源来初始化 Spring Environment。

所有 Config Client 应用都需要一个 bootstrap.yml(或环境变量),通过 spring.cloud.config.uri 属性设置配置服务器地址。

服务发现引导启动

若客户端应用集成了 Spring Cloud Netflix Eureka Service Discovery 或 Spring Cloud Consul,使用了 DiscoveryClient 实现,则可以将 Config Server 注册到 Discovery Service 中。但是,默认的 Config First模式下,客户端无法利用注册(服务注册失效)。

如果希望使用 DiscoveryClient 来定位配置服务器,可以设置 spring.cloud.config.discovery.enabled=true(默认 false) 来定位。配置客户端都需要 bootstrap.yml (或环境变量)

例如,使用 Spring Cloud Netflix Eureka,需要添加 eureka.client.serviceUrl.defaultZone 属性来配置 Eureka Server 地址。默认的 service ID 是 configserver,但可以通过设置 spring.cloud.config.discovery.serviceId 来修改。在 Config Server 端,通过常规方式提供服务,例如设置 spring.application.name 。

Discovery client 都支持某些元数据映射(例如,为 eureka 提供了 eureka.instance.metadata.map)。Config Server 的一些其他属性可能需要在其服务注册元数据中进行配置,以便客户端能正确连接,如果使用 HTTP Basic 保护配置服务器,则可以将凭据配置 userpassword,如果 Config Server 具有上下文路径,则可以设置 configPath 元数据。

以下是作为 Eureka Client 的 Config Server 的 yaml 配置文件:

bootstrap.yml

1
2
3
4
5
6
eureka:
instance:
metadata-map:
user: admin
password: 123456
configPath: /config

Config Client 快速失败

在某些情况下,如果服务无法连接到配置服务器,可能希望它无法启动。如果这是必要的,需要设置 bootstrap 配置属性spring.cloud.config.fail-fast=true 以使客户机在异常情况下停止。

Config Client 重试

如果希望客户端应用在启动时连接 Config Server 失败后能够重试,需要设置 spring.cloud.config.fail-fast=true,并引入 spring-retryspring-boot-starter-aop 依赖包。

默认会重试 6 次,第一次重试间隔为 1000 毫秒,后续重试的时间间隔是上次重试时间的 1.1 倍。

重试配置的属性参数可以通过 spring.cloud.config.retry.* 来设置。

bootstrap.properties

1
2
3
4
5
6
7
8
# 最多重试资数
spring.cloud.config.retry.max-attempts=6
# 初始重试间隔时长
spring.cloud.config.retry.initial-interval=1000
# 重试最大间隔时长
spring.cloud.config.retry.max-interval=2000
# 下一间隔的系数(乘以上次重试间隔时长)
spring.cloud.config.retry.multiplier=1.1

若要完全控制重试行为,需要添加一个 RetryOperationsInterceptor 类型 的 Bean,通过 RetryInterceptorBuilder 创建。

定位远程配置资源

Config Server 提供来自 /{name}/{profile}/{label} 的属性源,默认值如下:

  • name = ${spring.application.name}
  • profile = ${spring.profiles.active} (actually Environment.getActiveProfiles())
  • label = master

备注:设置属性 ${spring.application.name} 时,不要在应用名称前面加上保留字 application- 以防止无法正确的解析属性源。

还可以通过设置spring.cloud.config.*来覆盖默认属性(* 可以是 name、profile 或 label)。label 对于回滚以前版本的配置非常有用,

该标签可用于回滚到以前版本的配置。使用默认 Config Server 实现(Git 存储库),label 可以是 git 标签、分支名、或 提交 ID(commit ID)。label 也可以配置多个,以逗号分隔,客户端会常试每个一值,直到成功为止。

1
spring.cloud.config.label=myfeature,develop

多个配置服务Urls

若 Config Server 部署了多个实例来保证高可用。Config Client 可以通过设置 spring.cloud.config.uri 指定多个 URL,以逗号分隔。如果使用 Discovery 第一引导模式,需要将所有实例注册注注册中心中,如 Eureka Server。

注意:只有在 Config Server 未运行或发生连接超时,高可用才能起作用。如果 Config Server 返回 500(服务器内部错误),或返回 401(认证凭据错误或其他原则),则 Config Client 不会尝试从其他 URL 获取属性。此类错误表示用户问题,而不是可用性问题。

如果在 Config Server 服务器上使用 HTTP Basic 安全认证,只有在 spring.cloud.config.uri 属性的每个 URL 中嵌入凭据(账号/密码),才能支持每个 Config Server 的身份验证。

请求读取超时

若要设置请求读取超时,可使用如下设置

1
2
# 单位:毫秒,默认值:3分5秒
spring.cloud.config.request-read-timeout=10000

请求安全认证

如果在服务器上使用 HTTP Basic 安全认证,则客户端需要知道密码(如果不是默认密码,则需要知道用户名)。

Cofnig Client 可以通过 spring.cloud.config.uri 属性指定服务器的用户名称密码,如以下示例所示:

bootstrap.yml:在 URL 中嵌入账号密码

1
2
3
4
spring:
cloud:
config:
uri: https://user:secret@myconfig.mycompany.com

bootstrap.yml:单独设置账号密码

1
2
3
4
5
6
spring:
cloud:
config:
uri: https://myconfig.mycompany.com
username: user
password: secret

客户端健康检测

Config Client 提供了 Spring Boot Health Indicator,尝试从 Config Server 加载配置。

可以通过设置 health.config.enabled=false 来禁用健康检则。 基于性能的考虑,会缓存检测的响应数据,默认缓存时长为5分钟,要更改该值,请设置 health.config.time-to-live 属性(以毫秒为单位)。

自定义 RestTemplate

在某些情况下,可能需要自定义从客户端向配置服务器发出的请求。通常,这样做涉及传递特殊的 Authorization 头信息来验证对服务器的请求。这需要提供自定义 RestTemplate。

  1. 使用 PropertySourceLocator 的实现创建一个新的 Bean。如下示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @Configuration
    public class CustomConfigServiceBootstrapConfiguration {
    @Bean
    public ConfigServicePropertySourceLocator configServicePropertySourceLocator() {
    ConfigClientProperties clientProperties = configClientProperties();
    ConfigServicePropertySourceLocator configServicePropertySourceLocator = new ConfigServicePropertySourceLocator(clientProperties);
    configServicePropertySourceLocator.setRestTemplate(customRestTemplate(clientProperties));
    return configServicePropertySourceLocator;
    }
    }
  2. resources/META-INF 目录,创建一个 spring.factories 文件,并且指定自定义配置,示例如下:

    1
    2
    org.springframework.cloud.bootstrap.BootstrapConfiguration=\
    com.my.config.client.CustomConfigServiceBootstrapConfiguration

Spring Cloud(十二):分布式配置管理 Config 之 配置详解

http://blog.gxitsky.com/2019/04/15/SpringCloud-12-config-settings-detail/

作者

光星

发布于

2019-04-15

更新于

2022-06-17

许可协议

评论