Spring Cloud Config简介
配置文件是我们再熟悉不过的了,尤其是 Spring Boot 项目,除了引入相应的 maven 包之外,剩下的工作就是完善配置文件了,例如 mysql、redis 、security 相关的配置。除了项目运行的基础配置之外,还有一些配置是与我们业务有关系的,比如说七牛存储、短信相关、邮件相关,或者一些业务上的开关。
对于一些简单的项目来说,我们一般都是直接把相关配置放在单独的配置文件中,以 properties 或者 yml 的格式出现,更省事儿的方式是直接放到 application.properties 或 application.yml 中。但是这样的方式有个明显的问题,那就是,当修改了配置之后,必须重启服务,否则配置无法生效。
Spring Cloud Config 应运而生。
使用Spring Cloud Config可以实现以下的功能
- 集中管理配置
- 不同环境,不同配置
- 运行时期动态调整。并且在修改配置时不会停止微服务
- 配置修改后自动更新
Spring Cloud Config 为分布式系统外部化配置提供了服务器端和客户端的支持,它包括Config Server 和 Config Client 两部分。默认使用 Git 存储配置内容(也可以使用Subversion、本地文件系统或 Vault 存储配置)。Config Client 会在微服务启动时,请求 Config Server以获取所需要的配置属性,并且缓存在本地以提高性能。
简单的逻辑视图如下

Spring Cloud Config项目结构
首先参照上篇文章将Eureka Server部署到kubernetes中,然后就能开始部署融入了Eureka的Spring Cloud Config搭建之路了。

首先我们需要在github上面创建一个仓库用于存放配置文件
关于仓库的组织结构可以参看我的另外一篇文章
Spring Cloud Config 管理 Git仓库的两种方式
此项目中我采用的是上文中的第一种组织方式,即多个项目共用一个git仓库,如图所示,每个目录对应一个项目。

然后我们在Idea中创建一个Spring Cloud Config项目。
Spring Cloud Config需要配置核心maven依赖如下
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- spring cloud config 服务端包 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<!-- eureka client 端包 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
Spring Cloud Config中也不需要写任何的业务代码,只需要在启动类上面加上对应的注解即可。
@SpringBootApplication 标识这是一个启动类,并且把该启动类注入到容器中
@EnableConfigServer 标识该项目作为Spring Cloud中的配置中心
@EnableEurekaClient 标识该项目作为Spring Cloud中d的Eureka Client

同时,我们也需要创造两个配置文件bootstrap.yaml和application.yaml
bootstrap.yaml和application.yaml的区别可以查看这篇文章。
Spring Cloud中bootstrap.yaml和application.yaml的区别
bootstrap.yaml
spring:
application:
name: ConfigServer
cloud:
config:
server:
git:
#配置Git仓库地址以及账号密码
uri: https://github.com/zjy5755202/spring-cloud-config.git
username: github账号
password: github密码
#配置了文件搜索的位置
search-paths: /{application}
default-label: master
application.yaml
server:
port: 8762
#配置eureka相关信息
eureka:
client:
fetch-registry: true
register-with-eureka: true
service-url:
#此处的url填些的是上一篇文章中Eureka部署后在集群中的service IP:port/eureka
defaultZone: http://10.152.183.24:8761/eureka
instance:
prefer-ip-address: true
Spring Cloud Config 部署到 k8s
首先需要在在项目中创建一个DockerFile文件

在idea中连接上远程机的docker

然后将此镜像推送到机器上面

此时在我们的机器上面运行docker images即可查看到镜像已经推送到机器上面, 然后使用docker tag命令重命名镜像
docker tag config:latest 1181370590/config:v0.0.1
使用docker push命令将此docker镜像推送到dockerHub中
docker push 1181370590/config:v0.0.1
然后创建Config.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: config
labels:
app: config
spec:
replicas: 1
selector:
matchLabels:
app: config
template:
metadata:
labels:
app: config
spec:
containers:
- name: config
image: 1181370590/config:v0.0.1
ports:
- containerPort: 8761
---
apiVersion: v1
kind: Service
metadata:
name: config-server
spec:
ports:
- port: 8762
protocol: TCP
targetPort: 8762
type: NodePort
selector:
app: config
使用命令部署
kubectl create -f Config.yaml
使用命令查看pod和service是否部署成功
kubectl get pods
kubectl get services
即可看到Eureka部署到了k8s集群当中

成功注册到Eureka中

能够查看仓库中的配置文件。

Spring Cloud Config 融入 Spring Cloud Bus
Spring Cloud Config 在项目启动时加载配置内容这一机制,导致了它存在一个缺陷,修改配置文件内容后,不会自动刷新。例如我们上面的项目,当服务已经启动的时候,去修改 github 上的配置文件内容,这时候,再次刷新页面,对不起,还是旧的配置内容,新内容不会主动刷新过来。
但是,总不能每次修改了配置后重启服务吧。如果是那样的话,还是不要用它了为好,直接用本地配置文件岂不是更快。
如果只有一个 client 端的话,那我们用 webhook的方法来手动刷新 ,设置手动刷新都不算太费事,但是如果端比较多的话呢,一个一个去手动刷新未免有点复杂。这样的话,我们可以借助 Spring Cloud Bus 的广播功能,让 client 端都订阅配置更新事件,当配置更新时,触发其中一个端的更新事件,Spring Cloud Bus 就把此事件广播到其他订阅端,以此来达到批量更新。
在此项目中我使用的是Spring Cloud Config + Spring Cloud Bus(其中选用了RabbitMQ)来实现自动更新配置中心的功能。
逻辑视图如下

项目融入Spring Cloud Bus
融入Spring Cloud Bus,首先需要做的就是在Spring Config Server端导入Spring Cloud Bus的maven依赖
<!-- springcloud-bus依赖实现配置自动更新,rabbitmq -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
然后在Server端的application.yaml中添加部分配置信息
server:
port: 8762
#配置bus相关消息
cloud:
bus:
enabled: true
trace:
enabled: true
#配置rabbitmq相关信息
rabbitmq:
host: rabbmq的地址,文章后部分会提到
port: 5672
username: guest
password: guest
#配置eureka相关信息
eureka:
client:
fetch-registry: true
register-with-eureka: true
service-url:
defaultZone: http://10.152.183.24:8761/eureka
instance:
prefer-ip-address: true
status-page-url-path: actuator/info
health-check-url-path: actuator/health
#暴露所有接口,暴露/bus/refresh接口
management:
endpoints:
web:
exposure:
#暴露/bus/refresh接口
include: refresh
endpoint:
health:
show-details: always
至此,Config Server端的改造基本完成。
接下来我们还需要在Config Client端做部分配置才能真正实现Bus自动刷新。
在Config Client中,需要在maven中导入如下依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
然后到application.yaml中添加如下配置
management.endpoints.web.exposure.include=refresh
最后在Controller上面加上@RefreshScope即可
部署融合后的项目到kubernetes
首先需要在k8s部署rabbitmq来提供消息总线的功能。
创建Rabbitmq.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
fabric8.io/iconUrl: https://raw.githubusercontent.com/docker-library/docs/81187b7b50f5af5bdb64d75882f4d9c782ad52c3/rabbitmq/logo.png
labels:
app: rabbitmq
provider: rabbit
version: 3.6.11-management
group: com.rabbit
name: rabbitmq
spec:
replicas: 1
selector:
matchLabels:
app: rabbitmq
provider: rabbit
group: com.rabbit
template:
metadata:
annotations:
fabric8.io/iconUrl: https://raw.githubusercontent.com/docker-library/docs/81187b7b50f5af5bdb64d75882f4d9c782ad52c3/rabbitmq/logo.png
labels:
app: rabbitmq
provider: rabbit
version: 3.6.11-management
group: com.rabbit
spec:
containers:
- env:
- name: RABBITMQ_DEFAULT_USER
value: "guest"
- name: RABBITMQ_DEFAULT_PASS
value: "guest"
image: rabbitmq:3.6.11-management
imagePullPolicy: IfNotPresent
name: rabbitmq
ports:
- containerPort: 15672
name: manager
- containerPort: 5672
name: broker
---
apiVersion: v1
kind: List
items:
- apiVersion: v1
kind: Service
metadata:
annotations:
api.service.kubernetes.io/path: /
fabric8.io/iconUrl: https://raw.githubusercontent.com/docker-library/docs/81187b7b50f5af5bdb64d75882f4d9c782ad52c3/rabbitmq/logo.png
labels:
expose: "true"
app: rabbitmq
provider: rabbit
version: 3.6.11-management
group: com.rabbit
name: mqadmin
spec:
ports:
- name: http
port: 15672
protocol: TCP
targetPort: 15672
selector:
app: rabbitmq
provider: rabbit
group: com.rabbit
- apiVersion: v1
kind: Service
metadata:
annotations:
api.service.kubernetes.io/path: /
fabric8.io/iconUrl: https://raw.githubusercontent.com/docker-library/docs/81187b7b50f5af5bdb64d75882f4d9c782ad52c3/rabbitmq/logo.png
labels:
expose: "true"
app: rabbitmq
provider: rabbit
version: 3.6.11-management
group: com.rabbit
name: rabbitmq
spec:
ports:
- name: http
port: 5672
protocol: TCP
targetPort: 5672
selector:
app: rabbitmq
provider: rabbit
group: com.rabbit
运行命令部署rabbitmq
kubectl create -f Rabbitmq.yaml
然后我们需要将spring cloud config中的rabbitmq的地址更改为rabbitmq在k8s中的service Ip.
修改好后就和之前一样,打包镜像到主机,重命名,上传到dockerhub即可。
kubectl delete -f Config.yaml
docker tag config-bus:latest 1181370590/config-bus:v0.0.1
docker push 1181370590/config-bus:v0.0.1

创建Config-bus.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: config
labels:
app: config
spec:
replicas: 1
selector:
matchLabels:
app: config-bus
template:
metadata:
labels:
app: config-bus
spec:
containers:
- name: config-bus
image: 1181370590/config-bus:v0.0.1
ports:
- containerPort: 8761
---
apiVersion: v1
kind: Service
metadata:
name: config-server
spec:
ports:
- port: 8762
protocol: TCP
targetPort: 8762
type: NodePort
selector:
app: config-bus
使用命令部署config-bus
kubectl create -f Config-bus.yaml
部署成功啦

参考文章
Spring Cloud Config 实现配置中心,看这一篇就够了
docker+k8s+springcloud微服务集群部署实例
SpringCloud系列教程 | 第八篇:Spring Cloud Bus 消息总线