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
https://italiancoders.it/leggere-dati-da-configmap-kubernetes-con-spring/