介绍
使用Spring Cloud Kubernetes部署Spring应用程序到k8s集群时,主要的目的是动态读取ConfigMap/Secret里面的配置。本文主要介绍如何开发Spring应用。
使用ConfigMap的数据
添加依赖
xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-kubernetes-fabric8-config</artifactId>
<version>3.0.4</version>
</dependency>
加载单一ConfigMap
Spring Cloud k8s Config默认会在应用所在的namespace搜寻名字为spring.application.name
属性值的ConfigMap,然后加载里面的属性到Spring Config中。
但是,前提是在Spring应用的配置文件(application.yml)中,指定了spring.config.import
属性:
yaml
spring:
application:
name: demo-resource-server
config:
import: "kubernetes:"
在没有bootstrap依赖,并且没有bootstrap.yml文件的情况下,如果没有上述属性,那么Spring Cloud k8s不会搜寻k8s来查找配置。
使用上述配置,只要在k8s集群中,与应用同一个namespace里面,有一个名字叫做demo-resource-server
的ConfigMap,那么里面的配置就会被加载到Spring的Config中。
比如:
yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: demo-resource-server
namespace: default
data:
application.properties: |-
demo.name=demo-app-from-k8s
因为这个ConfigMap的名字(demo-resource-server)与应用的spring.application.name
(demo-resource-server)相同,所以这个ConfigMap的内容(application.properties里面的内容)就会被加载到Spring的Config中。
加载多个ConfigMap
如果想从多个ConfigMap加载配置,甚至从不同的namespace加载配置,可以使用如下的配置(application.yml)
yaml
spring:
application:
name: demo-resource-server
config:
import: "kubernetes:"
cloud:
kubernetes:
config:
sources:
- name: demo-resource-server
- name: demo-app
这样,部署到k8s的应用,就会在应用所在在namespace查找名字为demo-resource-server和demo-app的两个ConfigMap,来加载配置了。
甚至,你可以利用ConfigMap的标签(labels)来查找符合条件的ConfigMap。如下:
yaml
spring:
application:
name: labeled-configmap-with-prefix
cloud:
kubernetes:
config:
sources:
- labels:
letter: a
这个配置,会导致Spring应用从当前namespace中查找所有具有label:({letter : a}
)的所有ConfigMap数据被加载成配置。
属性前缀
如果有多个ConfigMap被匹配到,那么对于重复的属性,到底哪个起作用,其实是未知的,不过大部分情况下,可以认为最后一个生效。为了区分不同的ConfigMap过来的属性,可以通过两个配置选项来给属性配置前缀。
-
use-name-as-prefix
-
默认
false
,如果为true
则给当前ConfigMap中的所有属性添加一个前缀,前缀的名字就是ConfigMap的名字 -
比如:
yamlspring: application: name: with-prefix cloud: kubernetes: config: namespace: default-namespace sources: - name: config-map-one use-name-as-prefix: false
那么假设在config-map-one这个ConfigMap中有一个属性:
demo.name
,那么在加载到Spring的时候,就变成了:config-map-one.demo.name
-
-
explicit-prefix
-
默认
false
,如果为true
则给当前ConfigMap显式地指定一个前缀 -
比如:
yamlspring: application: name: with-prefix cloud: kubernetes: config: useNameAsPrefix: true namespace: default-namespace sources: - name: config-map-one useNameAsPrefix: false - name: config-map-two explicit-prefix: two
那么假设在config-map-two这个ConfigMap中有一个属性:
demo.name
,那么加载到Spring的时候,就变成了:tow.demo.name
。
-
Profile
在Spring Cloud k8s寻找匹配的ConfigMap时,默认会同时考虑生效的profile。
比如,你有如下配置:
yaml
spring:
application:
name: demo-app
config:
import: "kubernetes:"
并且正在生效的profile是dev
,那么Spring Cloud k8s在查找匹配的ConfigMap时,会考虑:demo-app和demo-app-dev两个ConfigMap。
如果想要禁止这个功能,可以关闭属性:spring.cloud.kubernetes.config.include-profile-specific-sources
yaml
spring:
application:
name: demo-app
config:
import: "kubernetes:"
cloud:
kubernetes:
config:
include-profile-specific-sources: false
使用Secret的数据
默认行为
- 默认Spring应用程序只能通过共享卷(volume)来访问secret,这也是官方推荐的做法
- 默认
spring.cloud.kubernetes.secrets.enable-api=false
,意味着不能通过名字/labels来查找secret
通过Volume访问Secret数据
在部署(deployment)中,指定通过环境变量的方式来获取Secret数据
yaml
apiVersion: v1
kind: Deployment
metadata:
name: ${project.artifactId}
spec:
template:
spec:
containers:
- env:
- name: DB_USERNAME
valueFrom:
secretKeyRef:
name: db-secret
key: username
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-secret
key: password
或者,也可以通过-Dspring.cloud.kubernetes.secrets.paths=/etc/secrets/db-secret,etc/secrets/postgresql
这样的配置来指定secret挂在的目录
通过API访问Secret数据
这不是推荐的做法,但是这样却能够达到reload的效果。方式其实与ConfigMap一样:
yaml
spring:
application:
name: cloud-k8s-app
cloud:
kubernetes:
secrets:
name: default-name
namespace: default-namespace
sources:
# Spring Cloud Kubernetes looks up a Secret named s1 in namespace default-namespace
- name: s1
# Spring Cloud Kubernetes looks up a Secret named default-name in namespace n2
- namespace: n2
# Spring Cloud Kubernetes looks up a Secret named s3 in namespace n3
- namespace: n3
name: s3
自动刷新配置(reload)
当
spring.cloud.kubernetes.config.fail-fast=true
时,reload不起作用
若想启用reload,只要打开这个配置开关:spring.cloud.kubernetes.reload.enabled=true
。
默认,只会reload ConfigMap的更新,如果也想要reload Secret的更新,可以设置属性:spring.cloud.kubernetes.reload.monitoring-secrets=true
。
reload级别
-
refresh
(默认)- 只有那些标注了
@ConfigurationProperties
或者@RefreshScope
的bean被reload
- 只有那些标注了
-
restart_context
-
整个SpringContext会被优雅的重启,bean会根据新的配置被重建
-
为了达到这个目的,必须暴露restart actuator:
yamlmanagement: endpoint: restart: enabled: true endpoints: web: exposure: include: restart
-
-
shutdown
- ApplicationContext会被关闭,从而导致容器重启(container restart)
- 需要确保所有k8s的controller被配置成重启pod
reload的两种方式
-
Event
(默认)- 使用websocket推送configmap/secret的变更事件
-
Polling
- 定期主动查询configmap/secret,查看是否有变动,如果有则更新配置
结论
使用Spring Cloud Kubernetes开发的Spring应用,可以实现动态读取ConfigMap/Secret中的配置,适合配置需要动态调整的场景。
特别地,本文是基于3.0.4版本撰写,所以使用的是spring.config.import
的方式来启用k8s配置读取。对于老版本,可能大部分使用的是bootstrap,与本文的配置方式不一样。