5

I was playing with Kubernetes in Minikube. I could able to deploy spring boot sample application into Kubernetes.

I am exploring Kubernetes configMap. I could successfully run a spring boot application with a spring cloud starter and picking the property keys from config map. Till here I am successful.

The issue I am facing currently is configmap reload.

Here is my config map :

ConfigMap.yaml

 apiVersion: v1
kind: ConfigMap
metadata:
  name: minikube-sample
  namespace: default
data:
  app.data.name: name
  application.yml: |-
    app:
      data:
        test: test

bootstrap.yaml

management:
    endpoint:
        health:
            enabled: true
        info:
            enabled: true
        restart:
            enabled: true
spring:
    application:
        name: minikube-sample
    cloud:
        kubernetes:
            config:
                enabled: true
                name: ${spring.application.name}
                namespace: default
            reload:
                enabled: true

HomeController:

package com.minikube.sample.rest.controller;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.minikube.sample.properties.PropertiesConfig;
import lombok.Getter;
import lombok.Setter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Lookup;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author Gorantla, Eresh
 * @created 06-12-2018
 */
@RestController
@RequestMapping("/home")
public class HomeResource {

    @Autowired
    PropertiesConfig config;

    @GetMapping("/data")
    public ResponseEntity<ResponseData> getData() {
        ResponseData responseData = new ResponseData();
        responseData.setId(1);
        responseData.setName(config.getName());
        responseData.setPlace("Hyderabad");
        responseData.setValue(config.getTest());
        return new ResponseEntity<>(responseData, HttpStatus.OK);
    }

    @Getter
    @Setter
    public class ResponseData {
        private String name;
        private Integer id;
        private String place;
        private String value;
    }
}

deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: minikube-sample
  namespace: default
spec:
  selector:
      matchLabels:
        app: minikube-sample

  replicas: 1
  template:
    metadata:
      labels:
        app: minikube-sample
    spec:
      containers:
        - name: minikube-sample
          image: minikube-sample:latest
          imagePullPolicy: Never
          ports:
            - containerPort: 8080
          env:
            - name: env.namespace
              value: default
          volumeMounts:
            - name: config
              mountPath: /config
      volumes:
        - name: config
          configMap:
            name: minikube-sample

I used @ConfigurationProperties to reload properties.

Dependencies

 <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-kubernetes</artifactId>
            <version>1.1.0.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-kubernetes-config</artifactId>
            <version>1.1.0.RELEASE</version>
        </dependency>

What I did ? I have gone through spring cloud documentation. "The view role on the service account is required in order to listen for config map changes." Then I created cluster view role through below command

C:\Users\eresh.gorantla\apps\minikube-sample\src\main\fabric8 (master -> origin)
λ kubectl create clusterrolebinding minikube-sample --clusterrole=view --serviceaccount=default:minikube --namespace=default
clusterrolebinding.rbac.authorization.k8s.io/minikube-sample created

But when I update the configmap in kubernetes, the properties are not reloaded on the fly. I suspect something wrong in clusterrolebinding. Please provide your thoughts. Any help is appreciated.

Eresh
  • 135
  • 1
  • 3
  • 9

3 Answers3

2

The Deployment doesn't have serviceAccountName configured so it uses the default service account. The command in the question, however - kubectl create clusterrolebinding ... --serviceaccount=default:minikube... - is for an account named minikube in the default namespace.

Moreover, creating clusterrolebinding may be "too much" when rolebinding for the namespace would work.

With the Deployment being for the default namespace (metadata.namespace: default), this should create a proper rolebinding to grant read-only permission to the default account:

kubectl create rolebinding default-sa-view \
  --clusterrole=view \
  --serviceaccount=default:default \
  --namespace=default

For reference, see Using RBAC Authorization.

gears
  • 690
  • 3
  • 6
2

To access ConfigMaps and get Refresh events:

  1. see annotations on config properties class @Configuration(proxyBeanMethods = false) see also @RefreshScope on config properties class.

    @Configuration(proxyBeanMethods = false)
    @ConfigurationProperties(prefix = "bean")
    @RefreshScope
    public class ClientConfig {
    
     private String message = "Default Message from java code - to be overwritten from config";
    
     public String getMessage() {...
     public void setMessage(String message) {...
    }
    

2 Add permissions to access ConfigMaps

kubectl create -f perm.yaml -n <NAMESPACE>

where perm.yaml is:

  kind: Role
  apiVersion: rbac.authorization.k8s.io/v1
  metadata:
    namespace: yldbg
    name: namespace-reader
  rules:
    - apiGroups: ["", "extensions", "apps"]
      resources: ["configmaps", "pods", "services", "endpoints", "secrets"]
      verbs: ["get", "list", "watch"]
  ---
  kind: RoleBinding
  apiVersion: rbac.authorization.k8s.io/v1
  metadata:
    name: namespace-reader-binding
    namespace: yldbg
  subjects:
  - kind: ServiceAccount
    name: default
    apiGroup: ""
  roleRef:
    kind: Role
    name: namespace-reader
    apiGroup: ""
  • After creating permissions, deploy pods and services.

  • when modifying the config map, you will see a refresh event in pod logs

    EventBasedConfigurationChangeDetector - Detected change in config maps
    EventBasedConfigurationChangeDetector - Reloading using strategy: REFRESH
    PropertySourceBootstrapConfiguration - Located property source: [BootstrapPropertySource {name='bootstrapProperties-configmap.client-svc.myns'}]
    SpringApplication - The following profiles are active: kubernetes
    

by yl

ylev
  • 2,313
  • 1
  • 23
  • 16
  • For me setting the permission to access ConfigMaps (to the default namespace in my case), as explained in this answer, did the trick! – marcor92 May 08 '21 at 09:17
1

Thanks gears for your answer. rolebinding is enough with role view in the namespace for the configmap to be available in container.

I solved the problem with updating dependencies. The Spring boot version with 2.1.8.Release and version of spring-cloud-kubernetes 1.1.0.Release didn't work out for me. I suspect to many dependencies added. I cleaned up pom file and that worked well.

Pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.minikube.sample</groupId>
    <artifactId>kubernetes-configmap-reload</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>
    <name>minikube-sample</name>
    <description>Demo project for Spring Cloud Kubernetes</description>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-kubernetes-config</artifactId>
            <version>1.1.0.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.4</version>
            <scope>provided</scope>
        </dependency>


        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>


</project>

You can find repository link here -- https://github.com/ereshzealous/kubernetes-configmap-reload

Thanks Eresh

GreenGiant
  • 4,930
  • 1
  • 46
  • 76
Eresh
  • 135
  • 1
  • 3
  • 9