使用 ConfigMap PropertySource

Kubernetes 提供了一個名為 ConfigMap 的資源,用於將傳遞給應用程式的引數外部化,這些引數可以採用鍵值對或嵌入式 application.propertiesapplication.yaml 檔案形式。 Spring Cloud Kubernetes Config 專案在應用程式啟動期間提供 Kubernetes ConfigMap 例項,並在檢測到被觀察的 ConfigMap 例項發生更改時觸發 Bean 或 Spring 上下文的熱過載。

以下所有內容主要參考使用 ConfigMaps 的示例進行解釋,但 Secrets 也同樣適用,即:每個功能都支援 ConfigMaps 和 Secrets。

預設行為是基於 Kubernetes ConfigMap 建立一個 Fabric8ConfigMapPropertySource(或 KubernetesClientConfigMapPropertySource),該 ConfigMapmetadata.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,則名為 c1ConfigMap 將在應用程式執行的名稱空間中查詢。 請參閱名稱空間解析以更好地瞭解應用程式的名稱空間如何解析。

找到的任何匹配的 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.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 格式的 config map:

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 不同,這可能會導致讀取多個 config map。 和往常一樣,Secrets 也支援相同的功能。

您還可以根據讀取 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 中最後出現的屬性會覆蓋所有前面的值。

另一種選擇是為每個配置檔案建立一個不同的 config map,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 時啟用特定配置檔案的一種選擇是透過環境變數啟動 Spring Boot 應用程式,您可以在 PodSpec 的容器規範中定義該環境變數。Deployment 資原始檔如下所示:

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"

您可能會遇到多個具有相同屬性名稱的 config map 的情況。例如:

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 中放置這些 config map 的順序,您可能會得到意想不到的結果(最後一個 config map 獲勝)。 例如:

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。 這允許您為所有源設定一個“預設”策略,同時只覆蓋少數幾個源。

如果使用 config map 名稱不是一個選擇,您可以指定一個不同的策略,稱為:explicitPrefix。 由於這是一個您選擇的顯式字首,因此只能應用於 sources 級別。 同時,它的優先順序高於 useNameAsPrefix。 假設我們有第三個 config map 包含這些條目:

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

配置 configmaps 字首的方式同樣適用於 secrets;無論是基於名稱的 secrets 還是基於標籤的 secrets。 例如:

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

生成屬性源時,處理規則與 config maps 相同。 唯一的區別是,潛在地,按標籤查詢 secrets 意味著我們可能找到不止一個源。 在這種情況下,字首(如果透過 useNameAsPrefix 指定)將是為這些特定標籤找到的所有 secrets 的名稱。

還需要記住一點,我們支援按設定 prefix,而不是按 secret 設定。 最簡單的解釋方法是透過示例:

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

假設一個匹配此類標籤的查詢將返回兩個 secrets:secret-asecret-b。 這兩個 secrets 都具有相同的屬性名稱:color=sea-bluecolor=ocean-blue。 無法確定哪個 color 將最終成為屬性源的一部分,但其字首將是 secret-a.secret-b(自然排序後拼接的 secret 名稱)。

如果您需要更細粒度的結果,新增更多標籤來唯一標識 secret 將是一個選項。

預設情況下,除了讀取 sources 配置中指定的 config map 外,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;順序如前所述。 每個活動配置檔案都會生成一個這樣的配置檔案感知 config map。

儘管您的應用程式不應受此類 config map 的影響,但如果需要,可以將其停用。

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

請注意,就像之前一樣,您可以在兩個級別指定此屬性:應用於所有 config maps 或應用於單個 config maps;後者的優先順序更高。

您應該檢查安全配置部分。 要從 Pod 內部訪問 config maps,您需要具有正確的 Kubernetes 服務帳戶、角色和角色繫結。

使用 ConfigMap 例項的另一種選擇是,透過執行 Spring Cloud Kubernetes 應用程式並讓 Spring Cloud Kubernetes 從檔案系統讀取它們的方式,將它們掛載到 Pod 中。

此功能已被棄用,並將在未來的版本中移除(請改用 spring.config.import)。 此行為由 spring.cloud.kubernetes.config.paths 屬性控制。 您可以使用它作為之前描述機制的補充或替代。 spring.cloud.kubernetes.config.paths 需要一個包含每個屬性檔案完整路徑的 List,因為目錄不會被遞迴解析。 例如:
spring:
  cloud:
    kubernetes:
      config:
        paths:
          - /tmp/application.properties
          - /var/application.yaml
如果您使用 spring.cloud.kubernetes.config.pathsspring.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-retryspring-boot-starter-aop 新增到您的類路徑。 您可以透過設定 spring.cloud.kubernetes.config.retry.* 屬性來配置重試屬性,例如最大嘗試次數、退避選項(如初始間隔、乘數、最大間隔)。

如果您由於某種原因已將 spring-retryspring-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

退避的最大間隔。

spring.cloud.kubernetes.config.retry.multiplier

Double

1.1

下一個間隔的乘數。