概述
- Config Server
- 使用共享配置属性
- 使用应用与特定应用程序的配置属性
- 保证配置属性安全
- 动态更新配置属性
Config Server
1. 认识
1.1 引入bootstrap.yml
bootstrap.yml(bootstrap.properties)(和application.yml同目录下)用来在程序引导时执行,应用于更加早期配置信息读取,如可以使用来配置application.yml中使用到参数等bootstrap.yml配置有更高优先级,默认情况下属性不会被本地配置覆盖,优先于application.yml加载当使用
Spring Cloud Config Server的时候,应该在bootstrap.yml里面指定spring.application.name和spring.cloud.config.server.git.uri和一些加密/解密的信息
1.2 概述
Spring Cloud Config Server作为一个配置属性的集中数据源,也是一个微服务,可以为同一应用程序中的其他服务提供配置数据
通过Config Server集中管理配置数据,其他服务将不需要单独配置
配置数据存储在外部(如GitHub丶GitLab丶Microsoft’s Team Foundation Server 丶Gogs等),通过Git方式从外部加载到Config Server进行分配
如果有安全性需求,保存在外部的配置数据的value可以进行加密后存储到Git仓库或者使用
HashiCorp Vault替代Git仓库存储配置数据
2.使用Config Server
2.1 引入
方式一: 新建project时直接通过
spring Initializr引入(推荐)方式二: 在pom.xml中引入
<properties> <java.version>11</java.version> <spring-cloud.version>Hoxton.SR7</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-server</artifactId> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
2.2 启用
在启动类上添加注解
@EnableConfigServer应用启动时才会运行Config Server@EnableConfigServer @SpringBootApplication public class ConfigApplication { public static void main(String[] args) { SpringApplication.run(ConfigApplication.class, args); } }
2.3 配置远程仓库地址(从中获取配置数据)
在
Config Server的resources目录下创建bootstrap.yml文件并配置spring: cloud: config: server: git: uri: https://github.com/BrysonBS/learning #远程仓库地址(即打开仓库后地址栏地址) search-paths: configuration #查询仓库内路径(文件夹),可以使用通配符如more* default-label: cloud # 仓库分支 username: xxx #用户名 password: xxx #密码 application: name: config-server #定义应用名称 server: port: 8888 #服务启动端口
2.4 启动Config Server进行测试
启动
Config Server,然后通过浏览器地址栏输入http://localhost:8888/config-server/default/cloud查看结果(默认会从该仓库相应目录中查找application.yml或application.properties文件){ "name": "config-server", "profiles": ["default"], "label": "cloud", "version": "399d4f1c1a80c50de32c23d60117b2f665e76fc1", "state": null, "propertySources": [{ "name": "https://github.com/BrysonBS/learning/configuration/application.yml", "source": { "spring.application.name": "eureka-registry", "server.port": 8761, "eureka.instance.hostname": "localhost", "eureka.client.service-url.defaultZone": "http://${eureka.instance.hostname}:${server.port}/eureka" } }] }地址详解
http://localhost:8888/config-server/default/cloud http://localhost:8888 Config Server地址和端口号 config-server 应用名称(spring.application.name) default Active Spring profile(没有定义默认为default) cloud Git label/branch(可选,默认为master)
3. 使用共享配置属性
Config Server已经通过Git方式获取到配置数据,但是其他服务启动时还没有从Config Server中获取配置数据并应用,因此需要通过对其他服务配置,使其启动时从Config Server获取配置属性并应用
3.1 引入spring-cloud-starter-config
Maven依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency>引入此依赖之后该Spring boot应用作为配置服务器的客户端, 启动时会就会从
Config Server获取配置属性默认情况下接收到的所有属性都会应用到应用环境中去,同时这些属性也会进行缓存,此后即使
Config Server关闭了,配置属性仍然可用其他应用默认情况下从
http://localhost:8888地址的配置服务器获取配置属性,可以通过配置更改Config Server的地址#http://localhost:8888/application/default/cloud 请求地址 spring: cloud: config: uri: http://localhost:8888 #定义从那个配置服务器(Config Server)获取配置属性 # 默认值为http://localhost:8888 profile: default #默认值请求的profile name: application #默认值应用名称 #label: #git仓库branch名
3.2 Config Server 和 Service Registry的顺序问题
模式一: 一般情况下,
Eureka Service Registry启动时从Config Server获取配置属性模式二: 也可以选择让
Config Server作为微服务自己注册到Eureka Sercice Registry中,然后其他微服务发现Config Server,但是要通过配置设置Config Server为Discovery client,默认注册名为configserverspring.cloud.config.discovery.enabled = true
4. 使用应用与特定应用程序的配置属性
4.1 认识
默认情况下从远程git仓库中的
application.yml或者application.properties文件中获取的配置属性是应用共享的配置属性对于应用与特定应用的配置属性则根据
application name或者profile属性确定,重新查看2.4中测试地址http://localhost:8888/config-server/default/cloud,其中config-server为application name,defalut为激活的profile,即测试时改变这些参数即可获取不同应用的配置属性,启动时也是根据此地址发送请求获取配置数据,可以通过spring.cloud.config.name丶spring.cloud.config.profile丶spring.cloud.config.label设置路径上的应用名,profile,git仓库branch的值如果想通过
application name获取,对于特定应用的配置属性,存储在git仓库时文件名要以application name命名,如taco.yml对于profile属性设置则和以前相同
对于和共享属性
application.yml中有冲突的属性,会以特定配置属性值为准
4.2 通过application name属性使用特定配置属性
首先在需要从
Config Server获取配置的应用中添加spring.cloud.config.name或spring.application.name配置属性,设置请求时的应用名称远程git仓库中存储的文件名要以
spring.application.name的值命名作为Config Server客户端的应用(如
spring.cloud.config.name = taco)启动时就会从Config Server获取共享配置属性(application.yml)和对应应用名称的的配置属性(taco.yml)对于有冲突的配置属性以特定配置(
taco.yml)属性为准*实例: *
本地应用(需要从
Config Server获取配置数据):bootstrap.ymlspring: cloud: config: uri: http://localhost:8888 #定义从那个配置服务器(Config Server)获取配置属性 # 默认值为http://localhost:8888 name: eureka-registry #应用名称对应远程仓库文件名远程Git仓库下有一个
erueka-registry.yml文件相对应
4.3 通过profile属性使用特定配置属性
首先在需要从
Config Server获取配置的应用中添加spring.cloud.config.profile配置属性指定要使用的profile,如果要根据应用名则需要额外配置spring.cloud.config.name远程git仓库中存储的文件名要加上
-加spring.cloud.config.profile的值作为后缀,如属性为
spring.cloud.config.name = taco和spring.cloud.config.profile = production则对应的查找的远程git仓库文件名为taco-production.yml*实例: *
本地应用(需要从
Config Server获取配置数据):bootstrap.ymlspring: cloud: config: uri: http://localhost:8888 #定义从那个配置服务器(Config Server)获取配置属性 # 默认值为http://localhost:8888 profile: production name: taco远程Git仓库下有一个
taco-production.yml文件相对应
也可以在配置文件中使用
---形式创建多个profiles,一下实例本地应用(需要从
Config Server获取配置数据):bootstrap.ymlspring: cloud: config: uri: http://localhost:8888 #定义从那个配置服务器(Config Server)获取配置属性 # 默认值为http://localhost:8888 profile: registry name: profiles远程Git仓库下有一个
profiles.yml文件相对应,内容中需要定义profiles值spring: profiles: registry application: name: registry-profiles server: port: 8766 eureka: instance: hostname: localhost client: service-url: defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka --- spring: profiles: consumer application: name: consumer server: port: 8763
5. 保证配置属性安全
5.1 概述
配置信息可能包含敏感信息,对于放入远程Git仓库需要安全保证,
Config Server提供两种方式来保证其安全性
- 将配置属性的值加密后存入远程Git仓库
- 使用
HashiCorp's Vault替代远程Git仓库存储配置数据
Config Server提供了两个端点来加密或者解密数据/encrypt与/decrypt如
http://localhost:8888/encrypt,可以直接使用命令使用
curl http://localhost:8888/encrypt -d 8765 #加密8765
curl http://localhost:8888/decrypt -d 0c4e97a951510731b47ab0bc81f62ceab517f28bec007e36c6a1654773c1d4a1 #解密
5.2 将配置属性的值加密后存入远程Git仓库
5.2.1 对称加密
在
resources目录下创建bootstrap.yml文件(与application.yml同级目录)encrypt: key: 123456 #对称加密密钥远程Git仓库配置文件中采用加密数据,格式为
'{cipher}加密数据',标识为密文server: port: '{cipher}0c4e97a951510731b47ab0bc81f62ceab517f28bec007e36c6a1654773c1d4a1' #8765使用123456密钥加密后
5.2.2 非对称加密(如RSA)
生成密钥文件
tacokey.jks: 此处密钥库口令tacokey,密钥口令均为123456PS C:\Users\NBL\Downloads> keytool -genkeypair -alias tacokey -keyalg RSA -keystore tacokey.jks 输入密钥库口令: 再次输入新口令: 您的名字与姓氏是什么? [Unknown]: cn 您的组织单位名称是什么? [Unknown]: cn 您的组织名称是什么? [Unknown]: cn 您所在的城市或区域名称是什么? [Unknown]: cn 您所在的省/市/自治区名称是什么? [Unknown]: cn 该单位的双字母国家/地区代码是什么? [Unknown]: cn CN=cn, OU=cn, O=cn, L=cn, ST=cn, C=cn是否正确? [否]: y 输入的密钥口令 (如果和密钥库口令相同, 按回车): 密钥口令太短 - 至少必须为 6 个字符 输入 的密钥口令 (如果和密钥库口令相同, 按回车): 再次输入新口令: Warning: JKS 密钥库使用专用格式。建议使用 "keytool -importkeystore -srckeystore tacokey.jks -destkeystore tacokey.jks -deststoretype pkcs12" 迁移到行业标准格式 PKCS12。 将生成的密钥文件
tacokey.jks放入Config Server的resources文件夹下并在bootstrap.yml中进行配置encrypt: key-store: alias: tacokey location: classpath:/tacokey.jks password: tacokey #密钥库口令 secret: 123456 #密钥口令远程Git仓库中使用加密后的值存储
profiles.yml,格式为:'{chipher}加密数据'spring: profiles: registry-RSA application: name: registry-profiles-RSA server: port: '{cipher}AQA2nYXUQe6c9TDSrAsJzFQKBEMykrp7w14KA5WZAKbFzaf+wHz3SKFwlyNHi2481bvscJ/ZOjR6IfWwOllDDeVf7WyzuvPEE+Mo4intWOqNkVXeGeB4qesgL+fJLO3VfU9mBV3pmn/yon0YfEd04NmjmgPUyFClxwIrCNeo/U41RvmPLXQ5mbZ8DYR2ao75zzlgOXCBgy0EYLBsqSb4M2/shcaCTe7rvvqq8sqEcefvQhBsgDrR3ujUyu4m+4LO1teFxZrsa0kTXrG2pl65FTN09EFtMOHJI0Ws3NN42l8Uv1Oa7r2OwqA0hOZKS6TJMObcFs8Rrpym9vuLuZpIab+kHbWrqcnsTQ2CRleu6OVMnUfXyhIbDAcEGMqaZ1wtrDs=' #8765使用RSA加密后
5.2.3 spring.cloud.config.server.encrypt.enabled属性
在配置加密完成之后通过
http://localhost:8888/profiles/registry-RSA可以看到其中包含以下内容,加密值会被Config Server自动解密为8765{ "name": "profiles", "profiles": ["registry-RSA"], "label": null, "version": "86714bd2bf15a2f64f276ce490e1a374c8a25d24", "state": null, "propertySources": [{ "name": "https://github.com/BrysonBS/learning/configuration/profiles.yml (document #1)", "source": { "spring.profiles": "registry-RSA", "spring.application.name": "registry-profiles-RSA", "eureka.instance.hostname": "localhost", "eureka.client.service-url.defaultZone": "http://${eureka.instance.hostname}:${server.port}/eureka", "server.port": "8765" ########被自动解密 } }] }如果想要关闭
Config Server解密的方式,而是获取加密内容,然后由客户机自己解密(即其他从Config Server获取配置数据的的应用需要自己配置解密信息),则设置spring.cloud.config.server.encrypt.enabled = false(默认为true,必须在bootstrap.yml中配置),然后再通过http://localhost:8888/profiles/registry-RSA查看{ "name": "profiles", "profiles": ["registry-RSA"], "label": null, "version": "86714bd2bf15a2f64f276ce490e1a374c8a25d24", "state": null, "propertySources": [{ "name": "https://github.com/BrysonBS/learning/configuration/profiles.yml (document #1)", "source": { "spring.profiles": "registry-RSA", "spring.application.name": "registry-profiles-RSA", "server.port": "{cipher}AQA2nYXUQe6c9TDSrAsJzFQKBEMykrp7w14KA5WZAKbFzaf+wHz3SKFwlyNHi2481bvscJ/ZOjR6IfWwOllDDeVf7WyzuvPEE+Mo4intWOqNkVXeGeB4qesgL+fJLO3VfU9mBV3pmn/yon0YfEd04NmjmgPUyFClxwIrCNeo/U41RvmPLXQ5mbZ8DYR2ao75zzlgOXCBgy0EYLBsqSb4M2/shcaCTe7rvvqq8sqEcefvQhBsgDrR3ujUyu4m+4LO1teFxZrsa0kTXrG2pl65FTN09EFtMOHJI0Ws3NN42l8Uv1Oa7r2OwqA0hOZKS6TJMObcFs8Rrpym9vuLuZpIab+kHbWrqcnsTQ2CRleu6OVMnUfXyhIbDAcEGMqaZ1wtrDs=", ######还是加密数据 "eureka.instance.hostname": "localhost", "eureka.client.service-url.defaultZone": "http://${eureka.instance.hostname}:${server.port}/eureka" } }] }
5.3 使用HashiCorp's Vault替代Git仓库存储配置数据
Vault安装在本地,并且存储在Vault中
secret中的配置属性k,v(v会加密),保证配置数据的安全性,通常Git存储库和Vault同时使用,敏感数据保存在Vault中即可
5.3.1 安装Vault By HashiCorp(以windows为例)
- 下载并解压到指定目录如
D:/vault仅一个文件vault.exe - 配置
path环境变量增加值为如D:/vault,以便在任何路径使用shell窗口都能不通过路径直接运行
5.3.2 启动Vault并添加配置数据
运行
power shell启动并设置根令牌(root token是一个管理令牌,可以创建其他令牌(权限不同))vault server -dev -dev-root-token-id=roottoken #根令牌为roottoken可以通过浏览器
http://127.0.0.1:8200/进入UI界面输入根令牌后进去管理界面在secret/下添加文件及配置或者新打开一个
power shell通过命令添加#准备 $env:VAULT_ADDR="http://127.0.0.1:8200" #查看vault状态 vault status #新版本的Vault需要以下设置之后才能添加数据(为了与Config Server 兼容重新创建secret后端) vault secrets disable secret vault secrets enable -path=secret kv #添加配置数据到secret(后端秘密存储库路径)中: 在profiles中添加server.port=8767,相当于profiles.yml中添加 vault write secret/application server.port=8767 #查看写入的k/v数据 vault read secret/application #注意: 每次添加会替换掉路径下的所有k/v的值,因此需要一起添加才行 vault write secret/application server.port=8767 k1=v1对于
profile形式的配置数据添加,profiles.yml内部有profile: registry-Vault#相当于profiles.yml里面有profile: registry-Vault vault write secret/profiles,registry-Vault server.port=8767
5.3.3 Config Server配置
application.yml中配置启用Vault作为存储库spring: profiles: active: #同时使用vault(仅存储敏感信息),git(存储其他信息)存储库 - vault - git #如果想仅使用vault,去掉此处即可bootstrap.yml中配置Vault服务器信息spring: cloud: config: server: git: uri: https://github.com/BrysonBS/learning #远程仓库地址(即打开仓库后地址栏地址) search-paths: configuration #查询仓库内路径(文件夹) default-label: cloud # 仓库分支 username: XXX #用户名 password: XXX #密码 order: 2 #优先级 vault: #服务器地址:(默认)https://localhost:8200 host: localhost port: 8200 #scheme: https 启用https时(须配置ssl)使用 order: 1 #优先级: 先于git(值2)使用
5.3.4 其他应用配置(需要从Config Server获取配置数据的应用)
bootstrap.yml中配置Vault的令牌,令牌会在请求头header中携带X-Config-Token:roottokenspring: cloud: config: token: roottoken配置请求
Config Server地址bootstrap.ymlspring: cloud: config: uri: http://localhost:8888 #定义从那个配置服务器(Config Server)获取配置属性 # 默认值为http://localhost:8888 profile: registry-Vault name: profiles #请求: http://localhost:8888/profiles/registry-Vault #请求头中会携带令牌信息: X-Config-Token:roottoken
6. 动态更新配置属性
6.1 概述
Spring Cloud Config Server提供了两种方式可以实现在应用运行中动态更新配置
- Manual(手动): **
Config Server客户端(需要从Config Server获取配置属性的应用)有一个POST请求端点actuator/refresh,该应用直接发送此请求即可获取最新的配置属性并实现动态更新,需要额外引入Spring boot Actuator**提供支持- *Automatic(自动): * 提交配置到
Git仓库时触发,Config Server会自动通知(通过Message服务广播)所有使用此配置服务器的客户端(Config Server Client),需要引入额外的Spring Cloud项目Spring Cloud Bus for communicating提供支持
6.2 手动更新配置
6.2.1 引入Spring Boot Actuator
Actuator提供了运行时监察和运行时状态操作当一个应用设置从
Config Server获取配置属性时,Spring会自动配置一个特殊的Actuator端点,用于刷新配置属性,为了使用此端点,需要引入Actuator依赖并配置引入
ActuatorMaven依赖<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>暴露
Actuator的请求端点management: endpoints: web: exposure: include: refresh,health,info
6.2.2 更新配置
Config Server客户端远程Git仓库配置数据profiles.ymlspring: profiles: taco application: name: taco datasource: platform: h2 url: jdbc:h2:mem:test;DB_CLOSE_ON_EXIT=FALSE schema: - classpath:data/schema.sql data: - classpath:data/data.sql initialization-mode: always jpa: show-sql: true hibernate: ddl-auto: update server: port: 8762 #需要额外添加的配置暴露Actuator端点 management: endpoints: web: exposure: include: refresh,health,info greeting: message: Hello World!Config Server客户端(需要从Config Server获取配置属性的应用)引入Actuator依赖之后,只需要发送POST请求actuator/refresh即可动态更新配置改写
greeting.message的值然后发送POST请求更新则应用就会动态更新此属性值,例如:http://localhost:8762/actuator/refresh
6.3 自动更新配置
6.3.1 概述
流程图如下
在
Git仓库配置一个webhook,作用是当使用git push时触发执行Webhook(即向Webhook中配置的地址发送请求(Config Server的/monitor端点))当触发
Webhook请求发送给Config Server的/monitor端点之后,由Config Server对信息进行处理(默认情况下提供了几种对常见远程存储库(Git等)的处理实现)Config Server使用/monitor端点接收Webhook的请求,若要开启此端点则需要添加monitor依赖处理完成的信息通过
Message广播给所有订阅的配置客户端(Config Server Client)动态更新客户端配置Config Server Client需要添加与Message实现相对应的Spring Cloud Bus依赖项来实现自动更新
6.3.2 实现步骤
Git仓库配置
Webhooker- 进入Git仓库选择
Settings>Webhooks PayLoad URL为Config Server地址+端口+/monitor,如http://host.docker.internal:8888/monitorContent Type选择application/json,因为Config Server的端点/monitor不支持application/x-www-formurlencoded类型Secret选项会在Webhooker发生的请求头中添加标识X-Hub-Signature(GitHub),Config Server的/monitor端点不支持,因此留空即可
- 进入Git仓库选择
Config Server配置启用
/monitor端点需要引入monitor依赖<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-monitor</artifactId> </dependency>使用
Spring Cloud Stream: 可以在/monitor端点实现将message发布到订阅的Config Server Client若使用的是
RabbitMQ则引入<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-stream-rabbit</artifactId> </dependency>若使用的是
Kafka则引入<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-stream-kafka</artifactId> </dependency>同时要配置使用的
RabbitMQ或者Kafka#配置RabbitMQ spring: rabbitmq: host: rabbit.tacocloud.com port: 5672 username: tacocloud password: s3cr3t#配置Kafka spring: kafka: bootstrap-servers: - kafka.tacocloud.com:9092 - kafka.tacocloud.com:9093 - kafka.tacocloud.com:9094
创建
NOTIFICATION EXTRACTOR对于不同
Git服务实现,Webhook发送的POST请求有不同的格式,在/monitor端点通过组件处理webhook请求,可以根据不同请求格式尝试判断出请求来自那种Git服务,并将数据解析后通过message发布到订阅的Config Server Clients,对于常见的Git服务如GitHub丶GitLab丶Bitbucket等可以实现自动识别和解析,无需增加其他操作对于不能识别的
Git服务需要添加NOTIFICATION EXTRACTOR来解析@Component @Order(Ordered.LOWEST_PRECEDENCE - 300) public class GogsPropertyPathNotificationExtractor implements PropertyPathNotificationExtractor { @Override public PropertyPathNotification extract(MultiValueMap<String, String> headers,Map<String, Object> request){ if ("push".equals(headers.getFirst("X-Gogs-Event"))) { if (request.get("commits") instanceof Collection) { Set<String> paths = new HashSet<>(); @SuppressWarnings("unchecked") Collection<Map<String, Object>> commits = (Collection<Map<String, Object>>) request.get("commits"); for (Map<String, Object> commit : commits) { addAllPaths(paths, commit, "added"); addAllPaths(paths, commit, "removed"); addAllPaths(paths, commit, "modified"); } if (!paths.isEmpty()) { return new PropertyPathNotification(paths.toArray(new String[0])); } } } return null; } private void addAllPaths(Set<String> paths,Map<String, Object> commit,String name) { @SuppressWarnings("unchecked") Collection<String> files = (Collection<String>) commit.get(name); if (files != null) { paths.addAll(files); } } }
Config Server Clients配置引入对应的
Spring Cloud Bus,订阅了Config Server的客户端(Config Server Client)接收到message时即可以自动更新配置若使用AMQP: 如
RabbitMQ<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-bus-amqp</artifactId> </dependency>若使用
Kafka<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-bus-kafka</artifactId> </dependency>