Best practices on Spring Cloud Kubernetes bootstrap configuration
Tags: spring, java, codequality, kubernetes
Published: 2021-05-30 13:22:30

In this article I want to show the best way to configure any Spring Cloud bootstrap properties in your application, making it the a most flexible and future-proof way.

Jump to the code [here](#how-to-k8s-config)!

### Microservices stack

Following or switching into more microservice architecture, most modern Spring boot applications start using one day something from [Spring Cloud](https://github.com/spring-cloud) stack additionally due to its features and compatibility with Spring Boot stack it already has, i.e. you take the whole _ecosystem_.

_I describe an example in [this article](https://dev.to/art_ptushkin/clean-code-in-spring-ecosystem-n4p) of how neat you it is possible to integrate Spring and Spring Boot by using starters._

Along side with Spring Cloud stack, most applications start using Kubernetes as microservices orchestration service, and for this we have the growing [Spring Cloud Kubernetes](https://github.com/spring-cloud/spring-cloud-kubernetes) project.

On purpose to externalise application's properties there are two most commons ways to implement this:

1. k8s configmap
2. Spring Cloud Config server

Considering [pros and cons](https://stackoverflow.com/questions/59139965/spring-cloud-config-server-vs-configmaps-for-cloud-kubernetes) both of these are worth to use and none is really _better_ than another.

Definitely, stack and complexity grows with this and following the _hello world_ approaches online developers rather implement configuration in a way that still brings problems and/or kept unclear for other developers.

One of the crucial and the most important things is enabling/disabling of configuration, as it brings the most influence to your runtime, activating or disabling beans. This is partially confirmed by Stackoverflow questions, querying:
* _spring configuration doesn't start_: 973
* _spring configuration disable_: 2748
* and all the other questions on bean duplication / missing problems

In order to make the best out of your configuration, I'd like to show some best practices on an example with Spring Cloud Kubernetes config.

Before jumping to the example it's better to know the basis about the Spring application properties order of precedence:

* application properties  https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.external-config
* bootstrap properties https://cloud.spring.io/spring-cloud-commons/multi/multi__spring_cloud_context_application_context_services.html

### <a name="how-to-k8s-config"></a>How to configure bootstrap properties

_What are the requirements to the best configuration?_

1. It always starts in each k8s environment
2. It doesn't start in tests
3. It start's for a local application run easily when you need it

_What do we know?_

1. `bootstrap` properties start before `application` one
2. application in k8s cluster container fetchs data from k8s configmap only if it's enabled beforehand
3. application must know _where_ to take the properties from before the load
4. `spring.cloud.kubernetes.enabled` enables all the sub-configurations
5. `spring.cloud.kubernetes.config.enabled` enables fetching configmap
6. `kubernetes` profile exists out of box and will be activated always [when the application runs as a pod inside Kubernetes](https://docs.spring.io/spring-cloud-kubernetes/docs/current/reference/html/index.html#kubernetes-profile-autoconfiguration)

Then we need:
* `bootstrap.yaml` in resources directory containing:
```yaml
spring:
  cloud:
    kubernetes:
      config:
        enabled: false
      enabled: false
```
* `bootstrap-kubernetes.yaml` containing:
```yaml
spring:
  cloud:
    kubernetes:
      config:
        enabled: true
      enabled: true
```
* `application.yaml` or `application-kubernetes.yaml` containing:
```yaml
spring:
  cloud:
    kubernetes:
      config:
        name: your-configmap-name
        namespace: ${KUBERNETES_NAMESPACE}
```
_Env variable `KUBERNETES_NAMESPACE` can't be determined on bootstrap configuration loading as the k8s configmap won't be fetched yet. Hence, we put it into `application` properties._
* k8s `service.yaml` containing:
```yaml
spec:
  containers:
    env:
    - name: "KUBERNETES_NAMESPACE"
      valueFrom:
      fieldRef:
        fieldPath: "metadata.namespace"
```
* [OPTIONAL] To make it more transparent to developers and let everyone know that `kubernetes` profile will be enabled I'd advice to put this profile explicitly, `service.yaml`. Another argument here, that if you already use `SPRING_PROFILES_ACTIVE` than it definitely will be clearer if you put _additionally_ the `kubernetes` profile, defining all the profiles list at the same place:
```yaml
spec:
  containers:
    env:
    - name: SPRING_PROFILES_ACTIVE
      value: "your-profile,kubernetes"
```

### Conclusions

With this configuration you can easily enable k8s by activating `kubernetes` profile and it will disabled on any other run.

### Hints

* Staring common `@SpringBootApplication` main class with `kubernetes` profile we aware that
  1. You might need env variable `KUBERNETES_NAMESPACE`
  2. It uses k8s context and namespace from the one that is used in your configuration (kubectl) now