5. Kubernetes PropertySource implementations

The most common approach to configuring your Spring Boot application is to create an application.properties or application.yaml or an application-profile.properties or application-profile.yaml file that contains key-value pairs that provide customization values to your application or Spring Boot starters. You can override these properties by specifying system properties or environment variables.spring-doc.cn

5.1. Using a ConfigMap PropertySource

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 bootstrapping and triggers hot reloading of beans or Spring context when changes are detected on observed ConfigMap instances.spring-doc.cn

The default behavior is to create a Fabric8ConfigMapPropertySource based on a Kubernetes ConfigMap that has a metadata.name value of either the name of your Spring application (as defined by its spring.application.name property) or a custom name defined within the bootstrap.properties file under the following key: spring.cloud.kubernetes.config.name.spring-doc.cn

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-doc.cn

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

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.spring-doc.cn

Any matching ConfigMap that is found is processed as follows:spring-doc.cn

  • Apply individual configuration properties.spring-doc.cn

  • Apply as yaml the content of any property named application.yaml.spring-doc.cn

  • Apply as a properties file the content of any property named application.properties.spring-doc.cn

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:spring-doc.cn

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

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

This can be externalized to config map in yaml format as follows:spring-doc.cn

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

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:spring-doc.cn

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

The following example also works:spring-doc.cn

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

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:spring-doc.cn

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

In the preceding case, the configuration loaded into your Spring Application with the development profile is as follows:spring-doc.cn

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

However, if the production profile is active, the configuration becomes:spring-doc.cn

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

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

Another option is to create a different config map per profile and spring boot will automatically fetch it based on active profilesspring-doc.cn

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

To tell Spring Boot which profile should be enabled at bootstrap, you can pass SPRING_PROFILES_ACTIVE environment variable. To do so, you can launch your Spring Boot application with an environment variable that you can define it in the PodSpec at the container specification. Deployment resource file, as follows:spring-doc.cn

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:spring-doc.cn

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

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-doc.cn

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

will result in property greetings.message being Say Hello from one.spring-doc.cn

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

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:spring-doc.cn

  • greetings.message equal to Say Hello from one.spring-doc.cn

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

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.spring-doc.cn

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:spring-doc.cn

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-doc.cn

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:spring-doc.cn

  • greetings.message equal to Say Hello from one.spring-doc.cn

  • two.greetings.message equal to Say Hello from two.spring-doc.cn

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

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.

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. 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. You can specify multiple (exact) file paths in spring.cloud.kubernetes.config.paths by using the , delimiter.spring-doc.cn

You have to provide the full exact path to each property file, because directories are not being recursively parsed.
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.
Table 1. Properties:
Name Type Default Description

spring.cloud.kubernetes.config.enabledspring-doc.cn

Booleanspring-doc.cn

truespring-doc.cn

Enable ConfigMaps PropertySourcespring-doc.cn

spring.cloud.kubernetes.config.namespring-doc.cn

Stringspring-doc.cn

${spring.application.name}spring-doc.cn

Sets the name of ConfigMap to look upspring-doc.cn

spring.cloud.kubernetes.config.namespacespring-doc.cn

Stringspring-doc.cn

Client namespacespring-doc.cn

Sets the Kubernetes namespace where to lookupspring-doc.cn

spring.cloud.kubernetes.config.pathsspring-doc.cn

Listspring-doc.cn

nullspring-doc.cn

Sets the paths where ConfigMap instances are mountedspring-doc.cn

spring.cloud.kubernetes.config.enableApispring-doc.cn

Booleanspring-doc.cn

truespring-doc.cn

Enable or disable consuming ConfigMap instances through APIsspring-doc.cn

5.2. Secrets PropertySource

Kubernetes has the notion of Secrets for storing sensitive data such as passwords, OAuth tokens, and so on. This project provides integration with Secrets to make secrets accessible by Spring Boot applications. You can explicitly enable or disable This feature by setting the spring.cloud.kubernetes.secrets.enabled property.spring-doc.cn

When enabled, the Fabric8SecretsPropertySource looks up Kubernetes for Secrets from the following sources:spring-doc.cn

  1. Reading recursively from secrets mountsspring-doc.cn

  2. Named after the application (as defined by spring.application.name)spring-doc.cn

  3. Matching some labelsspring-doc.cn

By default, consuming Secrets through the API (points 2 and 3 above) is not enabled for security reasons. The permission 'list' on secrets allows clients to inspect secrets values in the specified namespace. Further, we recommend that containers share secrets through mounted volumes.spring-doc.cn

If you enable consuming Secrets through the API, we recommend that you limit access to Secrets by using an authorization policy, such as RBAC. For more information about risks and best practices when consuming Secrets through the API refer to this doc.spring-doc.cn

If the secrets are found, their data is made available to the application.spring-doc.cn

Assume that we have a spring boot application named demo that uses properties to read its database configuration. We can create a Kubernetes secret by using the following command:spring-doc.cn

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

The preceding command would create the following secret (which you can see by using kubectl get secrets db-secret -o yaml):spring-doc.cn

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

Note that the data contains Base64-encoded versions of the literal provided by the create command.spring-doc.cn

Your application can then use this secret — for example, by exporting the secret’s value as environment variables:spring-doc.cn

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

You can select the Secrets to consume in a number of ways:spring-doc.cn

  1. By listing the directories where secrets are mapped:spring-doc.cn

    -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:spring-doc.cn

    -Dspring.cloud.kubernetes.secrets.paths=/etc/secrets
  2. By setting a named secret:spring-doc.cn

    -Dspring.cloud.kubernetes.secrets.name=db-secret
  3. By defining a list of labels:spring-doc.cn

    -Dspring.cloud.kubernetes.secrets.labels.broker=activemq
    -Dspring.cloud.kubernetes.secrets.labels.db=postgresql

As the case with ConfigMap, more advanced configuration is also possible where you can use multiple Secret instances. The spring.cloud.kubernetes.secrets.sources list makes this possible. For example, you could define the following Secret instances:spring-doc.cn

spring:
  application:
    name: cloud-k8s-app
  cloud:
    kubernetes:
      secrets:
        name: default-name
        namespace: default-namespace
        sources:
         # Spring Cloud Kubernetes looks up a Secret named s1 in namespace default-namespace
         - name: s1
         # Spring Cloud Kubernetes looks up a Secret named default-name in namespace n2
         - namespace: n2
         # Spring Cloud Kubernetes looks up a Secret named s3 in namespace n3
         - namespace: n3
           name: s3

In the preceding example, if spring.cloud.kubernetes.secrets.namespace had not been set, the Secret named s1 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.spring-doc.cn

Table 2. Properties:
Name Type Default Description

spring.cloud.kubernetes.secrets.enabledspring-doc.cn

Booleanspring-doc.cn

truespring-doc.cn

Enable Secrets PropertySourcespring-doc.cn

spring.cloud.kubernetes.secrets.namespring-doc.cn

Stringspring-doc.cn

${spring.application.name}spring-doc.cn

Sets the name of the secret to look upspring-doc.cn

spring.cloud.kubernetes.secrets.namespacespring-doc.cn

Stringspring-doc.cn

Client namespacespring-doc.cn

Sets the Kubernetes namespace where to look upspring-doc.cn

spring.cloud.kubernetes.secrets.labelsspring-doc.cn

Mapspring-doc.cn

nullspring-doc.cn

Sets the labels used to lookup secretsspring-doc.cn

spring.cloud.kubernetes.secrets.pathsspring-doc.cn

Listspring-doc.cn

nullspring-doc.cn

Sets the paths where secrets are mounted (example 1)spring-doc.cn

spring.cloud.kubernetes.secrets.enableApispring-doc.cn

Booleanspring-doc.cn

falsespring-doc.cn

Enables or disables consuming secrets through APIs (examples 2 and 3)spring-doc.cn

You can find an example of an application that uses secrets (though it has not been updated to use the new spring-cloud-kubernetes project) at spring-boot-camel-configspring-doc.cn

5.3. Namespace resolution

Finding an application namespace happens on a best-effort basis. There are some steps that we iterate in order to find it. The easiest and most common one, is to specify it in the proper configuration, for example:spring-doc.cn

spring:
  application:
    name: app
  cloud:
    kubernetes:
      secrets:
        name: secret
        namespace: default
        sources:
         # Spring Cloud Kubernetes looks up a Secret named 'a' in namespace 'default'
         - name: a
         # Spring Cloud Kubernetes looks up a Secret named 'secret' in namespace 'b'
         - namespace: b
         # Spring Cloud Kubernetes looks up a Secret named 'd' in namespace 'c'
         - namespace: c
           name: d

Remember that the same can be done for config maps. If such a namespace is not specified, it will be read (in this order):spring-doc.cn

  1. from property spring.cloud.kubernetes.client.namespacespring-doc.cn

  2. from a String residing in a file denoted by spring.cloud.kubernetes.client.serviceAccountNamespacePath propertyspring-doc.cn

  3. from a String residing in /var/run/secrets/kubernetes.io/serviceaccount/namespace file (kubernetes default namespace path)spring-doc.cn

  4. from a designated client method call (for example fabric8’s : KubernetesClient::getNamespace), if the client provides such a method.spring-doc.cn

Failure to find a namespace from the above steps will result in an Exception being raised.spring-doc.cn

5.4. PropertySource Reload

This functionality has been deprecated in the 2020.0 release. Please see the Spring Cloud Kubernetes Configuration Watcher controller for an alternative way to achieve the same functionality.

Some applications may need to detect changes on external property sources and update their internal status to reflect the new configuration. The reload feature of Spring Cloud Kubernetes is able to trigger an application reload when a related ConfigMap or Secret changes.spring-doc.cn

By default, this feature is disabled. You can enable it by using the spring.cloud.kubernetes.reload.enabled=true configuration property (for example, in the application.properties file).spring-doc.cn

The following levels of reload are supported (by setting the spring.cloud.kubernetes.reload.strategy property):spring-doc.cn

  • refresh (default): Only configuration beans annotated with @ConfigurationProperties or @RefreshScope are reloaded. This reload level leverages the refresh feature of Spring Cloud Context.spring-doc.cn

  • restart_context: the whole Spring ApplicationContext is gracefully restarted. Beans are recreated with the new configuration. In order for the restart context functionality to work properly you must enable and expose the restart actuator endpointspring-doc.cn

management:
  endpoint:
    restart:
      enabled: true
  endpoints:
    web:
      exposure:
        include: restart
  • shutdown: the Spring ApplicationContext is shut down to activate a restart of the container. When you use this level, make sure that the lifecycle of all non-daemon threads is bound to the ApplicationContext and that a replication controller or replica set is configured to restart the pod.spring-doc.cn

Assuming that the reload feature is enabled with default settings (refresh mode), the following bean is refreshed when the config map changes:spring-doc.cn

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

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

    // getter and setters

}

To see that changes effectively happen, you can create another bean that prints the message periodically, as followsspring-doc.cn

@Component
public class MyBean {

    @Autowired
    private MyConfig config;

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

You can change the message printed by the application by using a ConfigMap, as follows:spring-doc.cn

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

Any change to the property named bean.message in the ConfigMap associated with the pod is reflected in the output. More generally speaking, changes associated to properties prefixed with the value defined by the prefix field of the @ConfigurationProperties annotation are detected and reflected in the application. Associating a ConfigMap with a pod is explained earlier in this chapter.spring-doc.cn

The full example is available in spring-cloud-kubernetes-reload-example.spring-doc.cn

The reload feature supports two operating modes: * Event (default): Watches for changes in config maps or secrets by using the Kubernetes API (web socket). Any event produces a re-check on the configuration and, in case of changes, a reload. The view role on the service account is required in order to listen for config map changes. A higher level role (such as edit) is required for secrets (by default, secrets are not monitored). * Polling: Periodically re-creates the configuration from config maps and secrets to see if it has changed. You can configure the polling period by using the spring.cloud.kubernetes.reload.period property and defaults to 15 seconds. It requires the same role as the monitored property source. This means, for example, that using polling on file-mounted secret sources does not require particular privileges.spring-doc.cn

Table 3. Properties:
Name Type Default Description

spring.cloud.kubernetes.reload.enabledspring-doc.cn

Booleanspring-doc.cn

falsespring-doc.cn

Enables monitoring of property sources and configuration reloadspring-doc.cn

spring.cloud.kubernetes.reload.monitoring-config-mapsspring-doc.cn

Booleanspring-doc.cn

truespring-doc.cn

Allow monitoring changes in config mapsspring-doc.cn

spring.cloud.kubernetes.reload.monitoring-secretsspring-doc.cn

Booleanspring-doc.cn

falsespring-doc.cn

Allow monitoring changes in secretsspring-doc.cn

spring.cloud.kubernetes.reload.strategyspring-doc.cn

Enumspring-doc.cn

refreshspring-doc.cn

The strategy to use when firing a reload (refresh, restart_context, or shutdown)spring-doc.cn

spring.cloud.kubernetes.reload.modespring-doc.cn

Enumspring-doc.cn

eventspring-doc.cn

Specifies how to listen for changes in property sources (event or polling)spring-doc.cn

spring.cloud.kubernetes.reload.periodspring-doc.cn

Durationspring-doc.cn

15sspring-doc.cn

The period for verifying changes when using the polling strategyspring-doc.cn

Notes: * You should not use properties under spring.cloud.kubernetes.reload in config maps or secrets. Changing such properties at runtime may lead to unexpected results. * Deleting a property or the whole config map does not restore the original state of the beans when you use the refresh level.spring-doc.cn