Leggere dati da ConfigMap Kubernetes con Spring

L

Kubernetes è ormai sempre più diffuso per fare il deploy di applicazioni, soprattuto in architetture a microservizi.
D’altro canto, per quanto riguarda applicazioni Java scritte utilizzando Spring Boot, l’architettura Netflix è stata ormai praticamente abbandonata a favore di soluzioni che sfruttano direttamente quelle che sono le potenzialità di Kubernetes per fare ad esempio service discovery, gestione di configurazioni, ecc.

Spring sta seguendo questa nuova tendenza con un progetto dedicato: “Spring Cloud Kubernetes”.
In questo articolo vedremo come utilizzarlo per leggere configurazioni direttamente da ConfigMap, soluzione che, come già detto, ci permette di sfruttare appieno le funzionalità di Kubernetes e di poter gestire a caldo cambi di valori.

Dipendenze

Iniziamo col mostrare le dipendenze Maven necessarie:

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Hoxton.SR8</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-kubernetes-dependencies</artifactId>
            <version>1.1.6.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<dependencies>
    <!-- needed for spring kubernetes config reloads -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>

    <!-- spring cloud kubernetes config server -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-kubernetes-config</artifactId>
    </dependency>
</dependencies>

Configurazione

L’inizializzazione dei valori viene fatta da Spring in fase di bootstrap, pertanto è necessario definire la configurazione per accedere al/ai ConfigMap nel file bootstrap.yaml.
Vediamo la configurazione generica:

spring:
  application:
    name: cloud-k8s-app
  cloud:
    kubernetes:
      config: # not needed if spring.application.name is equal to configmap metadata name
        name: default-name # ConfigMap metadata name
        namespace: default-namespace
        sources: # configure this to load from more than one ConfigMap
         # 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

Tutta la parte spring.cloud.kubernetes non è necessaria se il valore di spring.application.name è uguale al metadata name del ConfigMap.
La parte spring.cloud.kubernetes.config.sources invece è necessaria se si ha bisogno di collegarsi a più ConfigMap, altrimenti è sufficiente mettere il valore del metadata name in spring.cloud.kubernetes.config.name.

Esempio

A questo punto vediamo un esempio in cui andremo a leggere valori da due ConfigMap distinti di cui mostriamo di seguito la definizione:

apiVersion: v1
kind: ConfigMap
metadata:
  name: k8s-demo-configmap
data:
  application.properties: |-
    greeting.message=Hello from
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: k8s-demo-custom-configmap
data:
  custom-conf.properties: |-
    custom.name=K8s

e successivamente il file bootstrap.yaml con i riferimenti ai ConfigMap da cui leggere valori:

spring:
  application:
    name: k8s-demo
  cloud:
    kubernetes:
      reload:
        enabled: true
        mode: event
        strategy: refresh
      config:
        namespace: default
        sources:
          - name: k8s-demo-configmap
          - name: k8s-demo-custom-configmap

Di seguito invece le due classi che mappano tramite l’annotazione @ConfigurationProperties i due ConfigMap:

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

@Configuration
@ConfigurationProperties(prefix = "greeting")
public class GreetingConfig {
    private String message = "Hi from";
}
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

@Configuration
@ConfigurationProperties(prefix = "custom")
public class CustomConfig {
    private String name = "Spring";
}

A questo punto definiamo un semplice controller in cui andiamo ad utilizzare le due classi che gestiscono le configurazioni per leggere i valori presi dalle due ConfigMap.

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class K8sDemoController {

    @Autowired
    private GreetingConfig config;

    @Autowired
    private CustomConfig customConfig;


    @GetMapping(value = "/hello")
    public String hello() {
        return config.getMessage() + " " + customConfig.getName();
    }
}

Invocando il metodo GET /hello verrà mostrato la stringa “Hello from K8S”.

Con la configurazione definita nel file bootstrap.yaml, se andiamo a modificare un valore di un ConfigMap questo verrà automaticamente aggiornato da Spring e reso disponibile a runtime.

Oltre ad usare i PropertySources come fatto nell’esempio è possibile recuperare valori di un ConfigMap direttamente dal bean Environment di Spring. Spring mette tutti i valori letti da tutti i ConfigMap nella sua istanza di Enviroment a cui è possibile accedere facendone @Autowired nei vostri bean.

ATTENZIONE: Il meccanismo di reload non funziona se si leggono i valori del ConfigMap usando l’annotazione @Value, poiché questi vengono appunto iniettati al boot nell’istanza del bean.

Reload

Ci sono differenti modalità e strategie per gestire il reload dei valori da ConfigMap.
Sostanzialmente Spring offre 3 modalità di refresh (refresh, restart_context e shutdown) e due strategie (event o polling).
Per i dettagli a riguardo si rimanda alla documentazione ufficiale.

REPOsitory

Vi lascio il link ad un esempio di implementazione:
http://git@github.com:alessiofiore/k8s-demo.git

Riferimenti

https://docs.spring.io/spring-cloud-kubernetes/docs/current/reference/html/index.html

A proposito di me

Alessio Fiore

Grande appassionato di sviluppo software e linguaggi di programmazione ha una grande conoscenza nel mondo Java e framework come Spring e Hibernate.
Laureato magistrale in Ingegneria Informatica lavora come sviluppatore software in ambito Telco principalmente lato backend, ma coltivando sempre molto interesse su tecnologie frontend.
Ha una grande esperienza su system integration, architetture software e sistemi distribuiti.

Di Alessio Fiore

I nostri Partner

Gli articoli più letti

Articoli recenti

Commenti recenti