使用 ConfigMap PropertySource

Kubernetes 提供了一個名為 ConfigMap 的資源,用於將參數外部化,以金鑰值對或嵌入式 application.propertiesapplication.yaml 檔案的形式傳遞給您的應用程式。Spring Cloud Kubernetes Config 專案使 Kubernetes ConfigMap 實例在應用程式啟動期間可用,並在偵測到觀察到的 ConfigMap 實例發生變更時,觸發 Bean 或 Spring 內容的熱重新載入。

以下所有內容主要參考使用 ConfigMap 的範例進行解釋,但同樣適用於 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 的屬性的內容 (如果不存在,則為 application.yaml/properties) 套用為 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=valueDkey2=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 的容器規格中定義該環境變數。部署資源檔案,如下所示

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

根據您將這些 config map 放置在 bootstrap.yaml|properties 中的順序,您可能會得到非預期的結果 (最後一個 config map 勝出)。例如

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。這允許您為所有來源設定「預設」策略,同時僅允許覆寫少數來源。

如果使用 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

您可以使用與設定 configmap 前綴相同的方式,為 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 map 相同的處理規則。唯一的區別在於,潛在地,依標籤查找 secrets 可能意味著我們找到多個來源。在這種情況下,前綴 (如果透過 useNameAsPrefix 指定) 將是為這些特定標籤找到的所有 secrets 的名稱。

還有一點需要記住的是,我們支援每個來源而不是每個 secret 的 prefix。解釋這一點的最簡單方法是透過範例

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 (串連自然排序的 secrets 名稱)。

如果您需要更精細的結果,則可以選擇新增更多標籤以唯一識別 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 map 或對於個別 config map;後者具有更高的優先順序。

您應該查看安全性設定章節。若要從 Pod 內部存取 config map,您需要具有正確的 Kubernetes 服務帳戶、角色和角色繫結。

使用 ConfigMap 實例的另一個選項是將它們掛載到 Pod 中,方法是執行 Spring Cloud Kubernetes 應用程式,並讓 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.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,並且想要啟用快速失敗,但不希望啟用重試;您可以透過設定 spring.cloud.kubernetes.config.retry.enabled=false 來停用 ConfigMap PropertySources 的重試。
表 1. 屬性
名稱 類型 預設值 描述

spring.cloud.kubernetes.config.enabled

布林值

true

啟用 ConfigMaps PropertySource

spring.cloud.kubernetes.config.name

字串

${spring.application.name}

設定要查找的 ConfigMap 名稱

spring.cloud.kubernetes.config.namespace

字串

用戶端命名空間

設定要查找的 Kubernetes 命名空間

spring.cloud.kubernetes.config.paths

清單

null

設定掛載 ConfigMap 實例的路徑

spring.cloud.kubernetes.config.enableApi

布林值

true

啟用或停用透過 API 取用 ConfigMap 實例

spring.cloud.kubernetes.config.fail-fast

布林值

false

當載入 ConfigMap 時發生錯誤時,啟用或停用應用程式啟動失敗

spring.cloud.kubernetes.config.retry.enabled

布林值

true

啟用或停用組態重試。

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

長整數

1000

初始重試間隔,以毫秒為單位。

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

整數

6

最大嘗試次數。

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

長整數

2000

退避的最大間隔。

spring.cloud.kubernetes.config.retry.multiplier

雙精度浮點數

1.1

下一個間隔的乘數。