Using a ConfigMap PropertySource

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

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

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

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

  • Spring 应用程序的值(由 spring.application.name 属性定义)

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

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

  • Apply individual configuration properties.

  • yaml(或 properties)的形式应用由 spring.application.name(如果不存在,则由 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 ignored,因为 devnot 活跃配置文件

  • not-my-app.yaml ignored,因为它不匹配 spring.application.name

  • someProp: someValue plain property

加载属性的顺序如下:

  • 首先,从 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=valueDkey2=valueB

上述流程的唯一例外是当 ConfigMap 包含表示文件是 YAML 或属性文件的*单个*键时。在这种情况下,键的名称不必是 application.yamlapplication.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

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

您还可以根据在读取 ConfigMap 时合并在一起的活动简档来配置 Spring Boot 应用程序。您可以为不同的简档提供不同的属性值,方法是使用指定特定于简档的值的 application.propertiesapplication.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 documentation。在部署至 Kubernetes 时激活特定的配置文件的一个选项是使用您可以在容器规范中的 PodSpec 中定义的环境变量来启动您的 Spring Boot 应用程序。部署资源文件,如下所示:

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.messageSay 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。这允许您为所有源设置“默认”策略,同时允许仅覆盖少数源。

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

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 等于 Say Hello from one

  • two.greetings.message 等于 Say Hello from two

  • config-map-three.greetings.message 等于 Say Hello from three

您可以针对 ConfigMap 配置前缀的方式与针对 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

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

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

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 名称自然排序后连接)。

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

默认情况下,除了读取在 sources 配置中指定的 ConfigMap 之外,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;按照此特定顺序读取。每个活动配置文件都会生成此类配置文件感知 ConfigMap。

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

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

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

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

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

此功能已弃用,并将在未来版本中移除(请改用`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 加载一些 ConfigMap。如果您希望应用程序在这种情况发生时终止启动过程,您可以设置`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.*` 属性。

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

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

设置 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

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.