spring-cloud-kubernetes

Spring Cloud Kubernetes and ConfigMaps across multiple namespaces


Upon reading the Spring Cloud Kubernetes documentation, it appears as though Spring Cloud Kubernetes supports the loading of properties from ConfigMaps across multiple namespaces. Following is the example from the documentation:

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

However, according to the Kubernetes documentation, "the Pod and the ConfigMap must be in the same namespace."

So, does Spring Cloud Kubernetes support the loading of properties from ConfigMaps across multiple namespaces and, if so, what is the proper configuration for allowing this to be done?

I have no problem loading properties from ConfigMaps in the same namespace as my Spring Boot pod, but when I try to load a property sourced from a ConfigMap in a different namespace (in this case, common), I encounter the following exception:

2021-07-22 21:28:06.658  WARN 1 --- [           main] .KubernetesClientConfigMapPropertySource : Unable to get ConfigMap common in namespace common
io.kubernetes.client.openapi.ApiException: Forbidden
    at io.kubernetes.client.openapi.ApiClient.handleResponse(ApiClient.java:993) ~[client-java-api-11.0.2.jar!/:na]
    at io.kubernetes.client.openapi.ApiClient.execute(ApiClient.java:905) ~[client-java-api-11.0.2.jar!/:na]
    at io.kubernetes.client.openapi.apis.CoreV1Api.listNamespacedConfigMapWithHttpInfo(CoreV1Api.java:28375) ~[client-java-api-11.0.2.jar!/:na]
    at io.kubernetes.client.openapi.apis.CoreV1Api.listNamespacedConfigMap(CoreV1Api.java:28263) ~[client-java-api-11.0.2.jar!/:na]
    at org.springframework.cloud.kubernetes.client.config.KubernetesClientConfigMapPropertySource.getData(KubernetesClientConfigMapPropertySource.java:56) ~[spring-cloud-kubernetes-client-config-2.0.3.jar!/:2.0.3]
    at org.springframework.cloud.kubernetes.client.config.KubernetesClientConfigMapPropertySource.<init>(KubernetesClientConfigMapPropertySource.java:41) ~[spring-cloud-kubernetes-client-config-2.0.3.jar!/:2.0.3]
    at org.springframework.cloud.kubernetes.client.config.KubernetesClientConfigMapPropertySourceLocator.getMapPropertySource(KubernetesClientConfigMapPropertySourceLocator.java:62) ~[spring-cloud-kubernetes-client-config-2.0.3.jar!/:2.0.3]
    at org.springframework.cloud.kubernetes.commons.config.ConfigMapPropertySourceLocator.getMapPropertySourceForSingleConfigMap(ConfigMapPropertySourceLocator.java:81) ~[spring-cloud-kubernetes-commons-2.0.3.jar!/:2.0.3]
    at org.springframework.cloud.kubernetes.commons.config.ConfigMapPropertySourceLocator.lambda$locate$0(ConfigMapPropertySourceLocator.java:67) ~[spring-cloud-kubernetes-commons-2.0.3.jar!/:2.0.3]
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1541) ~[na:na]
    at org.springframework.cloud.kubernetes.commons.config.ConfigMapPropertySourceLocator.locate(ConfigMapPropertySourceLocator.java:67) ~[spring-cloud-kubernetes-commons-2.0.3.jar!/:2.0.3]
    at org.springframework.cloud.bootstrap.config.PropertySourceLocator.locateCollection(PropertySourceLocator.java:51) ~[spring-cloud-context-3.0.3.jar!/:3.0.3]
    at org.springframework.cloud.bootstrap.config.PropertySourceLocator.locateCollection(PropertySourceLocator.java:47) ~[spring-cloud-context-3.0.3.jar!/:3.0.3]
    at org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration.initialize(PropertySourceBootstrapConfiguration.java:95) ~[spring-cloud-context-3.0.3.jar!/:3.0.3]
    at org.springframework.boot.SpringApplication.applyInitializers(SpringApplication.java:639) ~[spring-boot-2.4.9.jar!/:2.4.9]
    at org.springframework.boot.SpringApplication.prepareContext(SpringApplication.java:402) ~[spring-boot-2.4.9.jar!/:2.4.9]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:338) ~[spring-boot-2.4.9.jar!/:2.4.9]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1329) ~[spring-boot-2.4.9.jar!/:2.4.9]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1318) ~[spring-boot-2.4.9.jar!/:2.4.9]
    at com.example.echo.Bootstrapper.main(Bootstrapper.java:23) ~[classes!/:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
    at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
    at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:49) ~[service.jar:na]
    at org.springframework.boot.loader.Launcher.launch(Launcher.java:108) ~[service.jar:na]
    at org.springframework.boot.loader.Launcher.launch(Launcher.java:58) ~[service.jar:na]
    at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:88) ~[service.jar:na]

In summary, I am trying to determine how, in the Kubernetes world, to have a setup similar to what Spring Cloud Config provides me currently in our Docker Swarm environment where we are using a stack consisting of Spring Cloud Eureka, Spring Cloud Config, and Spring Cloud Gateway. For example, today I have the common properties that pertain to all my microservices available as application.yml and application-profile.yml files in my Git repo. My thinking is that these properties would be made available in Kubernetes as ConfigMaps in a common namespace, whereas my xyz-microservice.yml that today is loaded by Spring Cloud Config and combined with the application.yml and application-profile.yml properties would be in its own namespace.


Solution

  • Spring Cloud Kubernetes requires access to the Kubernetes API in order to be able to retrieve a list of addresses for pods running for a single service.

    Did you try the Service accounts section? - https://cloud.spring.io/spring-cloud-kubernetes/reference/html/#service-account

    Basically you need to create a RoleBinding for every namespace the ServiceAccount should have access to.