Using a ConfigMap PropertySource

Kubernetes 提供的名为 ConfigMap 的资源可以将以键值对或嵌入式 application.propertiesapplication.yaml 文件形式传递给应用程序的参数对外化。 Spring Cloud Kubernetes Config 项目使 Kubernetes ConfigMap 实例在应用程序启动时可用,并在观察到的 ConfigMap 实例发生更改时触发 Bean 或 Spring 上下文的热重新加载。

Kubernetes provides a resource named ConfigMap to externalize the parameters to pass to your application in the form of key-value pairs or embedded application.properties or application.yaml files. The Spring Cloud Kubernetes Config project makes Kubernetes ConfigMap instances available during application startup and triggers hot reloading of beans or Spring context when changes are detected on observed ConfigMap instances.

以下所有内容主要参考使用 ConfigMap 的示例来解释,但秘密的情况也是一样的,即:每个功能都同时支持两者。

Everything that follows is explained mainly referring to examples using ConfigMaps, but the same stands for Secrets, i.e.: every feature is supported for both.

默认行为是基于 Kubernetes ConfigMap 创建一个 Fabric8ConfigMapPropertySource(或 KubernetesClientConfigMapPropertySource),其中 metadata.name 为:

The default behavior is to create a Fabric8ConfigMapPropertySource (or a KubernetesClientConfigMapPropertySource) based on a Kubernetes ConfigMap that has metadata.name of either:

  • value of spring.cloud.kubernetes.config.name

  • value of your Spring application (as defined by spring.application.name property)

  • the String literal "application"

然而,可以使用多个 ConfigMap 实例进行更高级的配置。spring.cloud.kubernetes.config.sources 列表使这成为可能。例如,你可以定义以下 ConfigMap 实例:

However, more advanced configuration is possible where you can use multiple ConfigMap instances. The spring.cloud.kubernetes.config.sources list makes this possible. For example, you could define the following ConfigMap instances:

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,将在应用程序运行的命名空间中查找名为 c1ConfigMap。请参阅 Namespace resolution 更好地了解如何解析应用程序的命名空间。

In the preceding example, if spring.cloud.kubernetes.config.namespace had not been set, the ConfigMap named c1 would be looked up in the namespace that the application runs. See Namespace resolution to get a better understanding of how the namespace of the application is resolved.

找到的任何匹配的 ConfigMap 都将按如下方式处理:

Any matching ConfigMap that is found is processed as follows:

  • Apply individual configuration properties.

  • Apply as yaml (or properties) the content of any property that is named by the value of spring.application.name (if it’s not present, by application.yaml/properties)

  • Apply as a properties file the content of the above name + each active profile.

一个示例应该会更有意义。让我们假设 spring.application.name=my-app,并且我们有一个名为 k8s 的活动配置文件。对于如下配置:

An example should make a lot more sense. Let’s suppose that spring.application.name=my-app and that we have a single active profile called k8s. For a configuration as below:

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

这是我们最终加载的内容:

This is what we will end-up loading:

  • my-app.yaml treated as a file

  • my-app-k8s.yaml treated as a file

  • my-app-dev.yaml ignored, since dev is not an active profile

  • not-my-app.yaml ignored, since it does not match spring.application.name

  • someProp: someValue plain property

加载属性的顺序如下:

The order of loading properties is a as follows:

  • first load all properties from my-app.yaml

  • then all from profile-based sources: my-app-k8s.yaml

  • then all plain properties someProp: someValue

这意味着基于配置文件的资源优先于非基于配置文件的资源(就像在纯粹的 Spring 应用程序中一样);且普通属性优先于基于配置文件的资源和非基于配置文件的资源。以下是一个示例:

This means that profile based sources take precedence over non-profile based sources (just like in a vanilla Spring app); and plain properties take precedence over both profile and non-profile based sources. Here is an example:

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=valueDkey2=valueB

After processing such a ConfigMap, this is what you will get in the properties: key1=valueD, key2=valueB.

上述流程的唯一例外是当 ConfigMap 包含表示文件是 YAML 或属性文件的*单个*键时。在这种情况下,键的名称不必是 application.yamlapplication.properties(可以是任何名称),并且正确处理属性的值。此功能促进了使用以下内容创建 ConfigMap 的用例:

The single exception to the aforementioned flow is when the ConfigMap contains a single key that indicates the file is a YAML or properties file. In that case, the name of the key does NOT have to be application.yaml or application.properties (it can be anything) and the value of the property is treated correctly. This features facilitates the use case where the ConfigMap was created by using something like the following:

kubectl create configmap game-config --from-file=/path/to/app-config.yaml

假设我们有一个名为 demo 的 Spring Boot 应用程序,该应用程序使用以下属性来读取其线程池配置。

Assume that we have a Spring Boot application named demo that uses the following properties to read its thread pool configuration.

  • pool.size.core

  • pool.size.maximum

可以将其外置到 yaml 格式的配置文件映射中,如下所示:

This can be externalized to config map in yaml format as follows:

kind: ConfigMap
apiVersion: v1
metadata:
  name: demo
data:
  pool.size.core: 1
  pool.size.max: 16

对于大多数情况,单独的属性都能正常运行。但是,有时,嵌入式 yaml 更方便。在这种情况下,我们使用名为 application.yaml 的单个属性来嵌入我们的 yaml,如下所示:

Individual properties work fine for most cases. However, sometimes, embedded yaml is more convenient. In this case, we use a single property named application.yaml to embed our yaml, as follows:

kind: ConfigMap
apiVersion: v1
metadata:
  name: demo
data:
  application.yaml: |-
    pool:
      size:
        core: 1
        max:16

以下示例也适用:

The following example also works:

kind: ConfigMap
apiVersion: v1
metadata:
  name: demo
data:
  custom-name.yaml: |-
    pool:
      size:
        core: 1
        max:16

您还可以定义基于标签进行搜索,例如:

You can also define the search to happen based on labels, for example:

spring:
  application:
    name: labeled-configmap-with-prefix
  cloud:
    kubernetes:
      config:
        enableApi: true
        useNameAsPrefix: true
        namespace: spring-k8s
        sources:
          - labels:
              letter: a

这将在具有标签 {letter : a} 的名称空间 spring-k8s 中搜索每个配置文件映射。此处需要注意的重要事项是,与按名称读取配置文件映射不同,这可能导致读取多个配置文件映射。与往常一样,秘密也支持相同的功能。

This will search for every configmap in namespace spring-k8s that has labels {letter : a}. The important thing to notice here is that unlike reading a configmap by name, this can result in multiple config maps read. As usual, the same feature is supported for secrets.

您还可以根据在读取 ConfigMap 时合并在一起的活动简档来配置 Spring Boot 应用程序。您可以为不同的简档提供不同的属性值,方法是使用指定特定于简档的值的 application.propertiesapplication.yaml 属性,每个属性位于其自己的文档中(由 --- 序列表示),如下所示:

You can also configure Spring Boot applications differently depending on active profiles that are merged together when the ConfigMap is read. You can provide different property values for different profiles by using an application.properties or application.yaml property, specifying profile-specific values, each in their own document (indicated by the --- sequence), as follows:

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 应用程序中的配置如下:

In the preceding case, the configuration loaded into your Spring Application with the development profile is as follows:

  greeting:
    message: Say Hello to the Developers
  farewell:
    message: Say Goodbye to the Developers

但是,如果 production 简档处于活动状态,则配置将变为:

However, if the production profile is active, the configuration becomes:

  greeting:
    message: Say Hello to the Ops
  farewell:
    message: Say Goodbye

如果两个简档都处于活动状态,则 ConfigMap 中最后出现的属性将覆盖任何前置值。

If both profiles are active, the property that appears last within the ConfigMap overwrites any preceding values.

另一种选择是针对每个简档创建一个不同的配置文件映射,Spring Boot 将基于活动简档自动获取该映射。

Another option is to create a different config map per profile and spring boot will automatically fetch it based on active profiles

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 documentation。在部署至 Kubernetes 时激活特定的配置文件的一个选项是使用您可以在容器规范中的 PodSpec 中定义的环境变量来启动您的 Spring Boot 应用程序。部署资源文件,如下所示:

To tell Spring Boot which profile should be enabled see the Spring Boot documentation. One option for activating a specific profile when deploying to Kubernetes is to launch your Spring Boot application with an environment variable that you can define in the PodSpec at the container specification. Deployment resource file, as follows:

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"

您可能会遇到具有相同属性名称的多个配置文件映射的情况。例如:

You could run into a situation where there are multiple configs maps that have the same property names. For example:

kind: ConfigMap
apiVersion: v1
metadata:
  name: config-map-one
data:
  application.yml: |-
    greeting:
      message: Say Hello from one

and

kind: ConfigMap
apiVersion: v1
metadata:
  name: config-map-two
data:
  application.yml: |-
    greeting:
      message: Say Hello from two

根据您在 bootstrap.yaml|properties 中放置这些映射的顺序,您可能会得到一个意外的结果(最后一个配置文件映射获胜)。例如:

Depending on the order in which you place these in bootstrap.yaml|properties, you might end up with an un-expected result (the last config map wins). For example:

spring:
  application:
    name: cloud-k8s-app
  cloud:
    kubernetes:
      config:
        namespace: default-namespace
        sources:
         - name: config-map-two
         - name: config-map-one

将导致属性 greetings.messageSay Hello from one

will result in property greetings.message being Say Hello from one.

有一种方法可以通过指定 useNameAsPrefix 来更改此默认配置。例如:

There is a way to change this default configuration by specifying useNameAsPrefix. For example:

spring:
  application:
    name: with-prefix
  cloud:
    kubernetes:
      config:
        useNameAsPrefix: true
        namespace: default-namespace
        sources:
          - name: config-map-one
            useNameAsPrefix: false
          - name: config-map-two

此类配置将生成两个属性:

Such a configuration will result in two properties being generated:

  • greetings.message equal to Say Hello from one.

  • config-map-two.greetings.message equal to Say Hello from two

请注意,spring.cloud.kubernetes.config.useNameAsPrefix 的优先级_低于_ spring.cloud.kubernetes.config.sources.useNameAsPrefix。这允许您为所有源设置“默认”策略,同时允许仅覆盖少数源。

Notice that spring.cloud.kubernetes.config.useNameAsPrefix has a lower priority than spring.cloud.kubernetes.config.sources.useNameAsPrefix. This allows you to set a "default" strategy for all sources, at the same time allowing to override only a few.

如果不使用 ConfigMap 名称作为选项,您可以指定称为 : explicitPrefix 的不同策略。因为这是一个您选择的_显式_前缀,所以只能将其提供给 sources 级别。同时其优先级高于 useNameAsPrefix。让我们假设我们有包含以下条目的第三个 ConfigMap:

If using the config map name is not an option, you can specify a different strategy, called : explicitPrefix. Since this is an explicit prefix that you select, it can only be supplied to the sources level. At the same time it has a higher priority than useNameAsPrefix. Let’s suppose we have a third config map with these entries:

kind: ConfigMap
apiVersion: v1
metadata:
  name: config-map-three
data:
  application.yml: |-
    greeting:
      message: Say Hello from three

如下配置:

A configuration like the one below:

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

将生成三个属性:

will result in three properties being generated:

  • greetings.message equal to Say Hello from one.

  • two.greetings.message equal to Say Hello from two.

  • config-map-three.greetings.message equal to Say Hello from three.

您可以针对 ConfigMap 配置前缀的方式与针对 Secret 的相同,针对基于名称和基于标签的 Secret 均可。例如:

The same way you configure a prefix for configmaps, you can do it for secrets also; both for secrets that are based on name and the ones based on labels. For example:

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

在生成属性源时应用相同的处理规则,如同 ConfigMap 所用。唯一的区别是,通过标签查找 Secret 可能意味着会找到多个源。在这种情况下,前缀(如果通过 useNameAsPrefix 指定)将是要查找的特定标签的所有 Secret 的名称。

The same processing rules apply when generating property source as for config maps. The only difference is that potentially, looking up secrets by labels can mean that we find more than one source. In such a case, prefix (if specified via useNameAsPrefix) will be the names of all secrets found for those particular labels.

还要记住的一点是,我们支持的是每个_源_的 prefix,而不是每个 Secret 的 prefix。最简单的解释方法是通过示例:

One more thing to bear in mind is that we support prefix per source, not per secret. The easiest way to explain this is via an example:

spring:
  application:
    name: prefix-based-secrets
  cloud:
    kubernetes:
      secrets:
        enableApi: true
        useNameAsPrefix: true
        namespace: spring-k8s
        sources:
          - labels:
              color: blue
            useNameAsPrefix: true

假设匹配此类标签的查询将提供两个 Secret 作为结果:secret-asecret-b。这两个 Secret 具有相同的属性名称:color=sea-bluecolor=ocean-blue。未定义哪个`color` 将最终成为属性源的一部分,但前缀将是 secret-a.secret-b(按 Secret 名称自然排序后连接)。

Suppose that a query matching such a label will provide two secrets as a result: secret-a and secret-b. Both of these secrets have the same property name: color=sea-blue and color=ocean-blue. It is undefined which color will end-up as part of property sources, but the prefix for it will be secret-a.secret-b (concatenated sorted naturally, names of the secrets).

如果您需要更精细的结果,添加更多标签以唯一识别 Secret 将是一种选择。

If you need more fine-grained results, adding more labels to identify the secret uniquely would be an option.

默认情况下,除了读取在 sources 配置中指定的 ConfigMap 之外,Spring 还将尝试从“受配置文件感知”的源读取所有属性。最简单的解释方法是通过示例。假设您的应用程序启用了名为“dev”的配置文件,并且您有如下配置:

By default, besides reading the config map that is specified in the sources configuration, Spring will also try to read all properties from "profile aware" sources. The easiest way to explain this is via an example. Let’s suppose your application enables a profile called "dev" and you have a configuration like the one below:

spring:
  application:
    name: spring-k8s
  cloud:
    kubernetes:
      config:
        namespace: default-namespace
        sources:
          - name: config-map-one

除了读取 config-map-one 之外,Spring 还将尝试读取 config-map-one-dev;按照此特定顺序读取。每个活动配置文件都会生成此类配置文件感知 ConfigMap。

Besides reading the config-map-one, Spring will also try to read config-map-one-dev; in this particular order. Each active profile generates such a profile aware config map.

尽管您的应用程序不应该受此类 ConfigMap 的影响,但可以在需要时禁用它:

Though your application should not be impacted by such a config map, it can be disabled if needed:

spring:
  application:
    name: spring-k8s
  cloud:
    kubernetes:
      config:
        includeProfileSpecificSources: false
        namespace: default-namespace
        sources:
          - name: config-map-one
            includeProfileSpecificSources: false

请注意,与之前一样,您可以指定此属性的两个级别:所有 ConfigMap 或单个 ConfigMap;后者具有更高的优先级。

Notice that just like before, there are two levels where you can specify this property: for all config maps or for individual ones; the latter having a higher priority.

你应该检查一下安全配置部分。要从 pod 内访问 ConfigMap,你需要具有正确的 Kubernetes 服务账户、角色和角色绑定。

You should check the security configuration section. To access config maps from inside a pod you need to have the correct Kubernetes service accounts, roles and role bindings.

使用 ConfigMap 实例的另一种选择是通过运行 Spring Cloud Kubernetes 应用程序并将 Spring Cloud Kubernetes 从文件系统读取它们来将它们装载到 Pod 中。

Another option for using ConfigMap instances is to mount them into the Pod by running the Spring Cloud Kubernetes application and having Spring Cloud Kubernetes read them from the file system.

此功能已弃用,并将在未来版本中移除(请改用`spring.config.import`)。此行为受`spring.cloud.kubernetes.config.paths`属性的控制。你可以在此外机制之外或改用该机制使用它。`spring.cloud.kubernetes.config.paths`需要每个属性文件的完整路径列表,因为不递归解析目录。例如:

This feature is deprecated and will be removed in a future release (Use spring.config.import instead). This behavior is controlled by the spring.cloud.kubernetes.config.paths property. You can use it in addition to or instead of the mechanism described earlier. spring.cloud.kubernetes.config.paths expects a List of full paths to each property file, because directories are not being recursively parsed. For example:

spring:
  cloud:
    kubernetes:
      config:
        paths:
          - /tmp/application.properties
          - /var/application.yaml

如果你使用`spring.cloud.kubernetes.config.paths`或`spring.cloud.kubernetes.secrets.path`,则自动重新加载功能将不工作。你将需要向`/actuator/refresh`端点发送一个`POST`请求或重新启动/重新部署应用程序。

If you use spring.cloud.kubernetes.config.paths or spring.cloud.kubernetes.secrets.path the automatic reload functionality will not work. You will need to make a POST request to the /actuator/refresh endpoint or restart/redeploy the application.

在某些情况下,您的应用程序可能无法使用 Kubernetes API 加载一些 ConfigMap。如果您希望应用程序在这种情况发生时终止启动过程,您可以设置`spring.cloud.kubernetes.config.fail-fast=true` 以使应用程序启动因异常而失败。

In some cases, your application may be unable to load some of your ConfigMaps using the Kubernetes API. If you want your application to fail the start-up process in such cases, you can set spring.cloud.kubernetes.config.fail-fast=true to make the application start-up fail with an Exception.

您还可以让您的应用程序在失败时重试加载 ConfigMap 属性源。首先,您需要设置 spring.cloud.kubernetes.config.fail-fast=true。然后,您需要将 spring-retry`和 `spring-boot-starter-aop 添加到类路径。您可以配置重试属性,如最大尝试次数,初始间隔、乘数、最大间隔等回退选项,方法是设置`spring.cloud.kubernetes.config.retry.*` 属性。

You can also make your application retry loading ConfigMap property sources on a failure. First, you need to set spring.cloud.kubernetes.config.fail-fast=true. Then you need to add spring-retry and spring-boot-starter-aop to your classpath. You can configure retry properties such as the maximum number of attempts, backoff options like initial interval, multiplier, max interval by setting the spring.cloud.kubernetes.config.retry.* properties.

如果你出于某种原因在类路径中已经有了`spring-retry`和`spring-boot-starter-aop`,并想要启用快速失败,但不想启用重试;你可以通过设置`spring.cloud.kubernetes.config.retry.enabled=false`禁用`ConfigMap``PropertySources`的重试。

If you already have spring-retry and spring-boot-starter-aop on the classpath for some reason and want to enable fail-fast, but do not want retry to be enabled; you can disable retry for ConfigMap PropertySources by setting spring.cloud.kubernetes.config.retry.enabled=false.

Table 1. Properties:
Name Type Default Description

spring.cloud.kubernetes.config.enabled

Boolean

true

Enable ConfigMaps PropertySource

spring.cloud.kubernetes.config.name

String

${spring.application.name}

Sets the name of ConfigMap to look up

spring.cloud.kubernetes.config.namespace

String

Client namespace

Sets the Kubernetes namespace where to lookup

spring.cloud.kubernetes.config.paths

List

null

Sets the paths where ConfigMap instances are mounted

spring.cloud.kubernetes.config.enableApi

Boolean

true

Enable or disable consuming ConfigMap instances through APIs

spring.cloud.kubernetes.config.fail-fast

Boolean

false

Enable or disable failing the application start-up when an error occurred while loading a ConfigMap

spring.cloud.kubernetes.config.retry.enabled

Boolean

true

Enable or disable config retry.

spring.cloud.kubernetes.config.retry.initial-interval

Long

1000

Initial retry interval in milliseconds.

spring.cloud.kubernetes.config.retry.max-attempts

Integer

6

Maximum number of attempts.

spring.cloud.kubernetes.config.retry.max-interval

Long

2000

Maximum interval for backoff.

spring.cloud.kubernetes.config.retry.multiplier

Double

1.1

Multiplier for next interval.