137. Kubernetes PropertySource 实现

配置 Spring Boot 应用程序的最常见方法是创建一个包含键-值对的application.properties|yamlapplication-profile.properties|yaml文件,这些键-值对为应用程序或 Spring Boot 启动器提供自定义值。用户可以通过指定系统属性或环境变量来覆盖这些属性。

137.1 ConfigMap PropertySource

Kubernetes 提供了一个名为ConfigMap的资源,用于以键值对或嵌入式application.properties|yaml文件的形式外部化要传递给您的应用程序的参数。 Spring Cloud Kubernetes 配置项目使 Kubernetes 的 ConfigMap 在应用程序引导期间可用,并且在观察到的 ConfigMap 上检测到更改时触发 Bean 或 Spring 上下文的热重载。

默认行为是基于 Kubernetes ConfigMap创建一个ConfigMapPropertySource,该_3 具有您的 Spring 应用程序名称(由其spring.application.name属性定义)的metadata.name或在bootstrap.properties文件中的以下键spring.cloud.kubernetes.config.name下定义的自定义名称。

但是,在可以使用多个 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 will lookup a ConfigMap named c1 in namespace default-namespace
         - name: c1
         # Spring Cloud Kubernetes will lookup a ConfigMap named default-name in whatever namespace n2
         - namespace: n2
         # Spring Cloud Kubernetes will lookup a ConfigMap named c3 in namespace n3
         - namespace: n3
           name: c3

在上面的示例中,尚未设置spring.cloud.kubernetes.config.namespace,然后将在应用程序运行的名称空间中查找名为c1的 ConfigMap。

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

  • 应用各个配置属性。

  • 将名为application.yaml的任何属性的内容用作yaml

  • 将名为application.properties的任何属性的内容用作属性文件

上述流程的唯一 exception 是ConfigMap包含表示文件是 YAML 或属性文件的 键。在这种情况下,键的名称不必为application.yamlapplication.properties(可以是任何值),并且该属性的值将被正确处理。此功能有助于使用以下方式创建ConfigMap的用例:

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

Example:

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

  • pool.size.core

  • pool.size.maximum

可以将其外部化为yaml格式的配置 Map:

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

在大多数情况下,单个属性都可以正常工作,但有时嵌入yaml更为方便。在这种情况下,我们将使用一个名为application.yaml的属性来嵌入yaml

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

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

还可以根据Active配置文件对 Spring Boot 应用程序进行不同的配置,这些配置文件在读取 ConfigMap 时将合并在一起。可以使用application.properties|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配置文件处于Active状态,则配置为:

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

如果两个配置文件都处于Active状态,则配置 Map 中最后出现的属性将覆盖先前的值。

为了告诉 Spring Boot 应该在引导程序中启用哪个profile,可以使用环境变量将系统属性传递给 Java 命令,以使用 OpenShift DeploymentConfig或 Kubernetes ReplicationConfig资源文件定义一个 env 变量来启动 Spring Boot 应用程序,如下所示:

apiVersion: v1
kind: DeploymentConfig
spec:
  replicas: 1
  ...
    spec:
      containers:
      - env:
        - name: JAVA_APP_DIR
          value: /deployments
        - name: JAVA_OPTIONS
          value: -Dspring.profiles.active=developer

注意: -检查“安全配置”部分,要从 Pod 内部访问配置 Map,您需要具有正确的 Kubernetes 服务帐户,角色和角色绑定。

使用 ConfigMaps 的另一个选项是将它们安装到运行 Spring Cloud Kubernetes 应用程序的 Pod 中,并让 Spring Cloud Kubernetes 从文件系统中读取它们。此行为由spring.cloud.kubernetes.config.paths属性控制,可以作为上述机制的补充或替代。可以使用,分隔符在spring.cloud.kubernetes.config.paths中指定多个(精确)文件路径

表 137.1. 属性:

NameTypeDefaultDescription
spring.cloud.kubernetes.config.enabledBooleantrue启用机密 PropertySource
spring.cloud.kubernetes.config.nameString${spring.application.name}将 ConfigMap 的名称设置为 lookup
spring.cloud.kubernetes.config.namespaceStringClient namespace设置 Kubernetes 命名空间在哪里查找
spring.cloud.kubernetes.config.pathsListnull设置 ConfigMap 的安装路径

137.2 机密 PropertySource

Kubernetes 具有Secrets的概念,用于存储敏感数据,例如密码,OAuth 令牌等。此项目提供与Secrets的集成,以使 Spring Boot 应用程序可以访问机密。可以使用spring.cloud.kubernetes.secrets.enabled属性显式启用/禁用此功能。

启用SecretsPropertySource后,将从以下来源查找Secrets的 Kubernetes:

  • 从 Secret 坐骑递归阅读

  • 以应用程序命名(由spring.application.name定义)

  • 匹配一些标签

请注意,出于安全原因,默认情况下,未启用通过 API 消费机密(以上第 2 点和第 3 点)**,建议容器通过已安装的卷共享机密。如果启用通过 API 使用机密,则建议通过授权策略,例如 RBAC限制对机密的访问。

如果找到了机密,则其数据可供应用程序使用。

Example:

假设我们有一个名为demo的 spring boot 应用程序,它使用属性读取其数据库配置。我们可以使用以下命令创建 Kubernetes 机密:

oc create secret generic db-secret --from-literal=username=user --from-literal=password=p455w0rd

这将创建以下 Secret(使用oc get secrets db-secret -o yaml显示):

apiVersion: v1
data:
  password: cDQ1NXcwcmQ=
  username: dXNlcg==
kind: Secret
metadata:
  creationTimestamp: 2017-07-04T09:15:57Z
  name: db-secret
  namespace: default
  resourceVersion: "357496"
  selfLink: /api/v1/namespaces/default/secrets/db-secret
  uid: 63c89263-6099-11e7-b3da-76d6186905a8
type: Opaque

请注意,数据包含 create 命令提供的 Literals 的 Base64 编码版本。

然后,您的应用程序可以使用此 Secret,例如通过将 Secret 的值导出为环境变量来使用:

apiVersion: v1
kind: Deployment
metadata:
  name: ${project.artifactId}
spec:
   template:
     spec:
       containers:
         - env:
            - name: DB_USERNAME
              valueFrom:
                 secretKeyRef:
                   name: db-secret
                   key: username
            - name: DB_PASSWORD
              valueFrom:
                 secretKeyRef:
                   name: db-secret
                   key: password

您可以通过多种方式选择要使用的 Secret:

  • 通过列出 SecretMap 的目录:-Dspring.cloud.kubernetes.secrets.paths =/etc/secrets/db-secret,etc/secrets/postgresql``
If you have all the secrets mapped to a common root, you can set them like:

-Dspring.cloud.kubernetes.secrets.paths=/etc/secrets

  • 通过设置一个命名的 Secret:``````

  • 通过定义标签列表:-Dspring.cloud.kubernetes.secrets.labels.broker = activemq -Dspring.cloud.kubernetes.secrets.labels.db = postgresql

表 137.2. 属性:

NameTypeDefaultDescription
spring.cloud.kubernetes.secrets.enabledBooleantrue启用机密 PropertySource
spring.cloud.kubernetes.secrets.nameString${spring.application.name}将 Secret 名称设置为查找
spring.cloud.kubernetes.secrets.namespaceStringClient namespace设置 Kubernetes 命名空间在哪里查找
spring.cloud.kubernetes.secrets.labelsMapnull设置用于查找机密的标签
spring.cloud.kubernetes.secrets.pathsListnull设置安装机密的路径(示例 1)

注意: -属性spring.cloud.kubernetes.secrets.labels的行为与Map-based binding定义的行为相同。 -属性spring.cloud.kubernetes.secrets.paths的行为如Collection-based binding所定义。 -出于安全原因,可能会限制通过 API 访问机密,首选方式是将机密安装到 POD。

使用机密的应用示例(尽管尚未更新为使用新的spring-cloud-kubernetes项目):spring-boot-camel-config

137.3 PropertySource 重新加载

某些应用程序可能需要检测外部属性源上的更改并更新其内部状态以反映新配置。当相关的ConfigMapSecret发生更改时,Spring Cloud Kubernetes 的重新加载功能能够触发应用程序重新加载。

此功能默认情况下处于禁用状态,可以使用配置属性spring.cloud.kubernetes.reload.enabled=true启用(例如,在 application.properties 文件中)。

支持以下级别的重载(属性spring.cloud.kubernetes.reload.strategy):- refresh(默认) :仅重载带有@ConfigurationProperties@RefreshScopeComments 的配置 bean。此重载级别利用了 Spring Cloud Context 的刷新功能。 - restart_context :整个 Spring * ApplicationContext *正常重启。使用新配置重新创建 Bean。 - shutdown :关闭 Spring * ApplicationContext *以激活容器的重启。使用此级别时,请确保所有非守护进程线程的生命周期都已绑定到 ApplicationContext,并且已将复制控制器或副本集配置为重新启动 Pod。

Example:

假设使用默认设置( refresh 模式)启用了重载功能,那么当配置 Map 更改时,以下 Bean 将被刷新:

@Configuration
@ConfigurationProperties(prefix = "bean")
public class MyConfig {

    private String message = "a message that can be changed live";

    // getter and setters

}

观察更改有效发生的一种方法是创建另一个 bean,该 bean 定期打印消息。

@Component
public class MyBean {

    @Autowired
    private MyConfig config;

    @Scheduled(fixedDelay = 5000)
    public void hello() {
        System.out.println("The message is: " + config.getMessage());
    }
}

可以使用ConfigMap更改应用程序打印的消息,如下所示:

apiVersion: v1
kind: ConfigMap
metadata:
  name: reload-example
data:
  application.properties: |-
    bean.message=Hello World!

与 pod 关联的ConfigMap中名为bean.message的属性的任何更改都将反映在输出中。更一般地说,与属性相关联的更改将以@ConfigurationProperties注解的prefix字段定义的值为前缀,并会在应用程序中反映出来。上面说明了+116+与广告连播相关联

完整示例可在spring-cloud-kubernetes-reload-example中找到。

重新加载功能支持两种操作模式:-事件(默认) :使用 Kubernetes API(Web 套接字)监视配置 Map 或机密的更改.任何事件都会对配置进行重新检查,并在发生更改的情况下重新加载.服务帐户上的view角色是必需的,以便侦听配置 Map 更改.Secret 需要更高级别的角色(例如edit)(默认情况下不监视 Secret).- 轮询 :从配置 Map 和 Secret 中定期重新创建配置,以查看其是否已更改.可以使用属性spring.cloud.kubernetes.reload.period来配置轮询时间,默认为 15 秒**。它需要与受监视的属性源相同的角色。例如,这意味着对文件安装的机密源使用轮询不需要特定的特权。

表 137.3. 属性:

NameTypeDefaultDescription
spring.cloud.kubernetes.reload.enabledBooleanfalse启用监视属性源和配置重载
spring.cloud.kubernetes.reload.monitoring-config-mapsBooleantrue允许监视配置 Map 中的更改
spring.cloud.kubernetes.reload.monitoring-secretsBooleanfalse允许监视机密更改
spring.cloud.kubernetes.reload.strategyEnumrefresh触发重新加载时使用的策略( refreshrestart_contextshutdown )
spring.cloud.kubernetes.reload.modeEnumevent指定如何侦听属性源中的更改( eventpolling )

注意 :- spring.cloud.kubernetes.reload. 下的属性不应在配置 Map 或机密中使用:在运行时更改此类属性可能会导致意外结果; -使用“刷新”级别时,删除属性或整个配置 Map 不会恢复 Bean 的原始状态。