使用 ConfigMap
PropertySource
ConfigMap
PropertySource
Kubernetes 提供了一个名为 ConfigMap 的资源,用于将传递给应用程序的参数外部化,这些参数可以以键值对的形式或嵌入的 application.properties
或 application.yaml
文件的形式存在。Spring Cloud Kubernetes Config 项目使得 Kubernetes 的 ConfigMap
实例在应用程序启动时可用,并在检测到观察的 ConfigMap
实例发生更改时触发 bean 或 Spring 上下文的热重载。
以下内容主要参考使用 ConfigMaps 的示例进行解释,但同样适用于 Secrets,即:所有功能都对两者都支持。
默认行为是基于 Kubernetes ConfigMap
创建一个 Fabric8ConfigMapPropertySource
(或 KubernetesClientConfigMapPropertySource
),该 ConfigMap
的 metadata.name
为以下之一:
-
spring.cloud.kubernetes.config.name
的值 -
你的 Spring 应用程序的值(由
spring.application.name
属性定义) -
字符串字面量
"application"
然而,更高级的配置是可能的,您可以使用多个 ConfigMap
实例。spring.cloud.kubernetes.config.sources
列表使得这一点成为可能。例如,您可以定义以下 ConfigMap
实例:
spring:
application:
name: cloud-k8s-app
cloud:
kubernetes:
config:
name: default-name
namespace: default-namespace
sources:
# Spring Cloud Kubernetes looks up a ConfigMap named c1 in namespace default-namespace
- name: c1
# Spring Cloud Kubernetes looks up a ConfigMap named default-name in whatever namespace n2
- namespace: n2
# Spring Cloud Kubernetes looks up a ConfigMap named c3 in namespace n3
- namespace: n3
name: c3
在前面的示例中,如果未设置 spring.cloud.kubernetes.config.namespace
,则会在应用程序运行的命名空间中查找名为 c1
的 ConfigMap
。请参阅 命名空间解析 以更好地理解如何解析应用程序的命名空间。
找到的任何匹配的 ConfigMap
将按以下方式处理:
-
应用单独的配置属性。
-
将任何以
spring.application.name
的值为名称的属性内容作为yaml
(或properties
)应用(如果不存在,则使用application.yaml/properties
)。 -
将上述名称加上每个激活的配置文件的内容作为属性文件应用。
一个例子会让事情更加清晰。假设 spring.application.name=my-app
,并且我们有一个名为 k8s
的活跃配置文件。对于如下配置:
kind: ConfigMap
apiVersion: v1
metadata:
name: my-app
data:
my-app.yaml: |-
...
my-app-k8s.yaml: |-
..
my-app-dev.yaml: |-
..
not-my-app.yaml: |-
..
someProp: someValue
这是我们最终将加载的内容:
-
my-app.yaml
被视为文件 -
my-app-k8s.yaml
被视为文件 -
my-app-dev.yaml
被忽略,因为dev
不是活动的配置文件 -
not-my-app.yaml
被忽略,因为它不匹配spring.application.name
-
someProp: someValue
普通属性
加载属性的顺序如下:
-
首先从
my-app.yaml
加载所有属性 -
然后从基于配置文件的源加载所有属性:
my-app-k8s.yaml
-
最后加载所有普通属性
someProp: someValue
这意味着基于配置文件的源优先于非基于配置文件的源(就像在普通的 Spring 应用程序中一样);而普通属性优先于基于配置文件和非基于配置文件的源。以下是一个示例:
kind: ConfigMap
apiVersion: v1
metadata:
name: my-app
data:
my-app-k8s.yaml: |-
key1=valueA
key2=valueB
my-app.yaml: |-
key1=valueC
key2=valueA
key1: valueD
在处理这样的 ConfigMap 之后,你将在属性中得到以下内容:key1=valueD
、key2=valueB
。
上述流程的唯一例外是当 ConfigMap
包含一个指示文件为 YAML 或 properties 文件的单一键时。在这种情况下,键的名称不必是 application.yaml
或 application.properties
(它可以是任何名称),并且属性的值会被正确处理。此功能促进了 ConfigMap
通过类似以下方式创建的用例:
kubectl create configmap game-config --from-file=/path/to/app-config.yaml
假设我们有一个名为 demo
的 Spring Boot 应用程序,它使用以下属性来读取其线程池配置。
-
pool.size.core
:核心池大小 -
pool.size.maximum
:最大池大小
这可以外部化为 yaml
格式的配置映射,如下所示:
kind: ConfigMap
apiVersion: v1
metadata:
name: demo
data:
pool.size.core: 1
pool.size.max: 16
在大多数情况下,单独的属性都能很好地工作。然而,有时嵌入的 yaml
更方便。在这种情况下,我们使用一个名为 application.yaml
的属性来嵌入我们的 yaml
,如下所示:
kind: ConfigMap
apiVersion: v1
metadata:
name: demo
data:
application.yaml: |-
pool:
size:
core: 1
max:16
以下示例同样有效:
kind: ConfigMap
apiVersion: v1
metadata:
name: demo
data:
custom-name.yaml: |-
pool:
size:
core: 1
max:16
你也可以基于标签定义搜索,例如:
spring:
application:
name: labeled-configmap-with-prefix
cloud:
kubernetes:
config:
enableApi: true
useNameAsPrefix: true
namespace: spring-k8s
sources:
- labels:
letter: a
这将会在命名空间 spring-k8s
中搜索所有带有标签 {letter : a}
的 ConfigMap。需要注意的是,与通过名称读取 ConfigMap 不同,这种方式可能会导致读取多个 ConfigMap。通常,Secrets 也支持相同的功能。
你也可以根据激活的配置文件来以不同的方式配置 Spring Boot 应用程序,这些配置文件在读取 ConfigMap
时会合并在一起。你可以使用 application.properties
或 application.yaml
属性文件为不同的配置文件提供不同的属性值,通过在每个文档中指定特定于配置文件的属性值(用 ---
序列表示),如下所示:
kind: ConfigMap
apiVersion: v1
metadata:
name: demo
data:
application.yml: |-
greeting:
message: Say Hello to the World
farewell:
message: Say Goodbye
---
spring:
profiles: development
greeting:
message: Say Hello to the Developers
farewell:
message: Say Goodbye to the Developers
---
spring:
profiles: production
greeting:
message: Say Hello to the Ops
在前面的案例中,使用 development
配置文件加载到你的 Spring 应用程序中的配置如下:
greeting:
message: Say Hello to the Developers
farewell:
message: Say Goodbye to the Developers
然而,如果 production
配置文件处于激活状态,配置将变为:
greeting:
message: Say Hello to the Ops
farewell:
message: Say Goodbye
如果两个配置文件都处于活动状态,ConfigMap
中最后出现的属性将覆盖之前的任何值。
另一个选项是为每个配置文件创建不同的配置映射,Spring Boot 将根据激活的配置文件自动获取它。
kind: ConfigMap
apiVersion: v1
metadata:
name: demo
data:
application.yml: |-
greeting:
message: Say Hello to the World
farewell:
message: Say Goodbye
kind: ConfigMap
apiVersion: v1
metadata:
name: demo-development
data:
application.yml: |-
spring:
profiles: development
greeting:
message: Say Hello to the Developers
farewell:
message: Say Goodbye to the Developers
kind: ConfigMap
apiVersion: v1
metadata:
name: demo-production
data:
application.yml: |-
spring:
profiles: production
greeting:
message: Say Hello to the Ops
farewell:
message: Say Goodbye
要告诉 Spring Boot 应该启用哪个 profile
,请参阅 Spring Boot 文档。在部署到 Kubernetes 时,激活特定 profile 的一种方法是使用环境变量启动 Spring Boot 应用程序,你可以在容器规范的 PodSpec 中定义该环境变量。部署资源文件如下:
apiVersion: apps/v1
kind: Deployment
metadata:
name: deployment-name
labels:
app: deployment-name
spec:
replicas: 1
selector:
matchLabels:
app: deployment-name
template:
metadata:
labels:
app: deployment-name
spec:
containers:
- name: container-name
image: your-image
env:
- name: SPRING_PROFILES_ACTIVE
value: "development"
你可能会遇到这样一种情况:有多个配置映射具有相同的属性名称。例如:
kind: ConfigMap
apiVersion: v1
metadata:
name: config-map-one
data:
application.yml: |-
greeting:
message: Say Hello from one
和
kind: ConfigMap
apiVersion: v1
metadata:
name: config-map-two
data:
application.yml: |-
greeting:
message: Say Hello from two
根据你在 bootstrap.yaml|properties
中放置这些内容的顺序,你可能会得到一个意外的结果(最后一个配置映射会覆盖前面的配置)。例如:
spring:
application:
name: cloud-k8s-app
cloud:
kubernetes:
config:
namespace: default-namespace
sources:
- name: config-map-two
- name: config-map-one
将导致属性 greetings.message
的值为 Say Hello from one
。
可以通过指定 useNameAsPrefix
来更改此默认配置。例如:
spring:
application:
name: with-prefix
cloud:
kubernetes:
config:
useNameAsPrefix: true
namespace: default-namespace
sources:
- name: config-map-one
useNameAsPrefix: false
- name: config-map-two
这样的配置将会生成两个属性:
-
greetings.message
等于Say Hello from one
。 -
config-map-two.greetings.message
等于Say Hello from two
请注意,spring.cloud.kubernetes.config.useNameAsPrefix
的优先级 低于 spring.cloud.kubernetes.config.sources.useNameAsPrefix
。这允许你为所有配置源设置一个“默认”策略,同时允许仅覆盖其中的一部分。
如果无法使用配置映射名称,你可以指定一个不同的策略,称为:explicitPrefix
。由于这是一个你选择的显式前缀,它只能提供给 sources
层级。同时,它的优先级高于 useNameAsPrefix
。假设我们有第三个配置映射,其中包含以下条目:
kind: ConfigMap
apiVersion: v1
metadata:
name: config-map-three
data:
application.yml: |-
greeting:
message: Say Hello from three
如下所示的配置:
spring:
application:
name: with-prefix
cloud:
kubernetes:
config:
useNameAsPrefix: true
namespace: default-namespace
sources:
- name: config-map-one
useNameAsPrefix: false
- name: config-map-two
explicitPrefix: two
- name: config-map-three
将生成三个属性:
-
greetings.message
等于来自一的问候
。 -
two.greetings.message
等于来自二的问候
。 -
config-map-three.greetings.message
等于来自三的问候
。
与配置 ConfigMap 前缀的方式相同,你也可以为 Secret 进行配置;无论是基于名称的 Secret 还是基于标签的 Secret。例如:
spring:
application:
name: prefix-based-secrets
cloud:
kubernetes:
secrets:
enableApi: true
useNameAsPrefix: true
namespace: spring-k8s
sources:
- labels:
letter: a
useNameAsPrefix: false
- labels:
letter: b
explicitPrefix: two
- labels:
letter: c
- labels:
letter: d
useNameAsPrefix: true
- name: my-secret
在生成属性源时,应用的规则与配置映射相同。唯一的区别在于,通过标签查找密钥可能意味着我们会找到多个源。在这种情况下,前缀(如果通过 useNameAsPrefix
指定)将是针对这些特定标签找到的所有密钥的名称。
还有一点需要注意的是,我们支持每个来源的 prefix
,而不是每个密钥。通过一个例子来解释这一点是最简单的:
spring:
application:
name: prefix-based-secrets
cloud:
kubernetes:
secrets:
enableApi: true
useNameAsPrefix: true
namespace: spring-k8s
sources:
- labels:
color: blue
useNameAsPrefix: true
假设一个查询匹配这样的标签将提供两个秘密作为结果:secret-a
和 secret-b
。这两个秘密具有相同的属性名称:color=sea-blue
和 color=ocean-blue
。最终哪个 color
会成为属性源的一部分是未定义的,但其前缀将是 secret-a.secret-b
(秘密名称按自然顺序连接)。
如果你需要更细粒度的结果,添加更多标签来唯一标识密钥将是一个选择。
默认情况下,除了读取在 sources
配置中指定的配置映射外,Spring 还会尝试从“配置文件感知”的来源读取所有属性。最简单的解释方式是通过一个示例。假设你的应用程序启用了一个名为 "dev" 的配置文件,并且你有如下配置:
spring:
application:
name: spring-k8s
cloud:
kubernetes:
config:
namespace: default-namespace
sources:
- name: config-map-one
除了读取 config-map-one
,Spring 还会尝试读取 config-map-one-dev
;按照这个特定的顺序。每个激活的配置文件都会生成一个这样与配置文件相关的配置映射。
虽然您的应用程序不应受到此类配置映射的影响,但如有需要,可以禁用它:
spring:
application:
name: spring-k8s
cloud:
kubernetes:
config:
includeProfileSpecificSources: false
namespace: default-namespace
sources:
- name: config-map-one
includeProfileSpecificSources: false
注意,就像之前一样,你可以在两个层级上指定这个属性:针对所有配置映射或针对单个配置映射;后者的优先级更高。
你应该检查安全配置部分。要从 Pod 内部访问配置映射,你需要有正确的 Kubernetes 服务账户、角色和角色绑定。
使用 ConfigMap
实例的另一种选择是通过运行 Spring Cloud Kubernetes 应用程序将它们挂载到 Pod 中,并让 Spring Cloud Kubernetes 从文件系统中读取它们。
此功能已弃用,并将在未来的版本中移除(请使用 spring.config.import
替代)。此行为由 spring.cloud.kubernetes.config.paths
属性控制。你可以将其与前面描述的机制结合使用,或者替代它。spring.cloud.kubernetes.config.paths
期望每个属性文件的完整路径列表,因为目录不会递归解析。例如:
spring:
cloud:
kubernetes:
config:
paths:
- /tmp/application.properties
- /var/application.yaml
如果你使用了 spring.cloud.kubernetes.config.paths
或 spring.cloud.kubernetes.secrets.path
,自动重新加载功能将不会生效。你需要向 /actuator/refresh
端点发送一个 POST
请求,或者重启/重新部署应用程序。
在某些情况下,您的应用程序可能无法使用 Kubernetes API 加载某些 ConfigMaps
。如果您希望在这种情况下使应用程序的启动过程失败,可以设置 spring.cloud.kubernetes.config.fail-fast=true
,以使应用程序启动失败并抛出异常。
你也可以让应用程序在加载 ConfigMap
属性源失败时进行重试。首先,你需要设置 spring.cloud.kubernetes.config.fail-fast=true
。然后你需要在类路径中添加 spring-retry
和 spring-boot-starter-aop
。你可以通过设置 spring.cloud.kubernetes.config.retry.*
属性来配置重试属性,例如最大尝试次数、退避选项(如初始间隔、乘数、最大间隔)等。
如果由于某些原因,您已经在 classpath 中包含了 spring-retry
和 spring-boot-starter-aop
,并且希望启用快速失败(fail-fast),但不希望启用重试功能;您可以通过设置 spring.cloud.kubernetes.config.retry.enabled=false
来禁用 ConfigMap
PropertySources
的重试功能。
表 1. 属性:
名称 | 类型 | 默认值 | 描述 |
---|---|---|---|
spring.cloud.kubernetes.config.enabled | Boolean | true | 启用 ConfigMaps PropertySource |
spring.cloud.kubernetes.config.name | String | ${spring.application.name} | 设置要查找的 ConfigMap 的名称 |
spring.cloud.kubernetes.config.namespace | String | 客户端命名空间 | 设置要查找的 Kubernetes 命名空间 |
spring.cloud.kubernetes.config.paths | List | null | 设置 ConfigMap 实例挂载的路径 |
spring.cloud.kubernetes.config.enableApi | Boolean | true | 启用或禁用通过 API 使用 ConfigMap 实例 |
spring.cloud.kubernetes.config.fail-fast | Boolean | false | 启用或禁用加载 ConfigMap 时发生错误时应用程序启动失败 |
spring.cloud.kubernetes.config.retry.enabled | Boolean | true | 启用或禁用配置重试。 |
spring.cloud.kubernetes.config.retry.initial-interval | Long | 1000 | 初始重试间隔,单位为毫秒。 |
spring.cloud.kubernetes.config.retry.max-attempts | Integer | 6 | 最大重试次数。 |
spring.cloud.kubernetes.config.retry.max-interval | Long | 2000 | 最大回退间隔。 |