Spring Cloud Config : Implémentation, Usage et Intégration avec Eureka

Spring Cloud Config est une solution dédiée aux architectures de microservices, offrant un support centralisé pour la gestion des configurations externes. Il permet de maintenir les paramètres de toutes les applications microservices, pour tous les environnements, au sein d'un référentiel unique. Ce système se compose de deux éléments principaux : le serveur de configuration (Config Server) et le client de configuration (Config Client).

Le serveur de configuration est une application microservice autonome qui interagit avec un système de gestion de versions (par défaut Git) pour stocker et distribuer les configurations. Il expose des interfaces permettant aux clients de récupérer leurs paramètres et, potentiellement, de gérer le chiffrement/déchiffrement des données. Le client de configuration, quant à lui, est intégré dans chaque microservice et est responsable de la récupération et du chargement de sa configuraton au démarrage, s'assurant ainsi que les applications utilisent les paramètres les plus récents et pertinents pour leur environnement.

Les objectifs principaux de Spring Cloud Config incluent :

  • La centralisation de la gestion des fichiers de configuration.
  • La facilitation de la gestion des configurations spécifiques à chaque environnement (développement, test, production, etc.).
  • La possibilité de mises à jour dynamiques des configurations sans nécessiter le redémarrage des services, notamment en combinaison avec Spring Cloud Bus.
  • L'exposition des informations de configuration via des interfaces REST.

Le Config Server interagit principalement avec des dépôts Git (ou SVN), permettant ainsi un versionnement complet et une gestion aisée des configurations.

Mise en œuvre basique du Config Server

La première étape consiste à configurer un serveur de configuration pour qu'il se connecte à un dépôt Git distant et en lise les configurations. Pour cet exemple, nous utiliserons un dépôt GitHub.

Commencez par créer un nouveau dépôt Git (par exemple, app-configurations) sur GitHub et clonez-le localement. Ensuite, placez-y un simple fichier application.yml puis poussez-le vers votre dépôt pour vérifier la communication.

Dans votre IDE, créez un nouveau projet Spring Boot, par exemple config-service-core.

Ajoutez la dépendance suivante à votre fichier 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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.example.app</groupId>
        <artifactId>mon-projet-cloud</artifactId>
        <version>1.0.0-SNAPSHOT</version>
    </parent>
    <artifactId>config-service-core</artifactId>

    <properties>
        <java.version>1.8</java.version>
    </properties>

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

        <!-- Inclut les fonctionnalités du serveur de configuration Spring Cloud -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-config-server</artifactId>
        </dependency>

        <!-- Ajouté pour les outils de développement, facultatif -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-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>

Dans la classe principale de votre application Spring Boot (par exemple, ConfigServiceCoreApplication), ajoutez l'annotation @EnableConfigServer :

package com.example.configservice;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;

/**
 * Point d'entrée de l'application Config Server.
 * Active le serveur de configuration Spring Cloud.
 */
@EnableConfigServer
@SpringBootApplication
public class ConfigServiceCoreApplication {

    public static void main(String[] args) {
        SpringApplication.run(ConfigServiceCoreApplication.class, args);
    }
}

Configurez ensuite le fichier application.yml de votre Config Server pour qu'il pointe vers votre dépôt Git :

server:
  port: 8888 # Port d'écoute du serveur de configuration

spring:
  application:
    name: config-management-service # Nom de l'application dans Eureka (si utilisée)
  cloud:
    config:
      server:
        git:
          uri: https://github.com/votre-utilisateur/app-configurations.git # URL de votre dépôt Git
          default-label: main # Branche par défaut si non spécifiée par le client
          timeout: 5 # Timeout pour les opérations Git en secondes
          clone-on-start: true # Cloner le dépôt au démarrage, utile pour les tests

Pour un accès facilité et des URL plus lisibles, vous pouvez ajouter une entrée à votre fichier hosts local (situé généralement à C:\Windows\System32\drivers\etc\hosts sous Windows ou /etc/hosts sous Linux/macOS) :

127.0.0.1 config.monreseau.local

Démarrez votre application Config Server. Vous devriez pouvoir accéder aux configurations via des URL spécifiques. Par exemple, pour l'application nommée config-management-service et le profil default, vous pouvez requêter : http://config.monreseau.local:8888/config-management-service/default/main (ou /config-management-service-default.yml).

Le service HTTP du Config Server propose des ressources selon les formats suivants :

  • /{application}/{profile}[/{label}]
  • /{application}-{profile}.yml
  • /{label}/{application}-{profile}.yml
  • /{application}-{profile}.properties
  • /{label}/{application}-{profile}.properties

Où :

  • application est le nom du service client.
  • profile est le profil de configuration (par exemple, dev, prod).
  • label est la branche Git (par exemple, main, develop).

Mise en œuvre basique du Config Client

Pour démontrer l'interacsion, nous allons créer un client qui se connectera au serveur de configuration pour récupérer ses paramètres depuis le dépôt Git distant.

Pour nos tests, nous allons déposer plusieurs fichiers de configuration dans le dépôt Git (app-configurations) :

application.yml (dans le dépôt Git) :

# Fichier de configuration général pour le dépôt Git
# Ce fichier définit un profil actif par défaut si aucun n'est spécifié.
spring:
  profiles:
    active: default

my-service-dev.yml (dans le dépôt Git) :

app:
  instance:
    port: 7001
    environment: development
  message: "Service configuré pour l'environnement de développement."

my-service-prod.yml (dans le dépôt Git) :

app:
  instance:
    port: 7002
    environment: production
  message: "Service configuré pour l'environnement de production."

Créez un nouveau projet Spring Boot pour le client, par exemple client-app-service. Modifiez le pom.xml pour inclure les dépendances suivantes :

<parent>
    <groupId>com.example.app</groupId>
    <artifactId>mon-projet-cloud</artifactId>
    <version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>client-app-service</artifactId>

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-config</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency<
    <!-- Autres dépendances usuelles -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <scope>runtime</scope>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

Le Config Client doit lire sa configuration avant le démarrage complet de l'application. Pour cela, la configuration du serveur de configuration doit être placée dans un fichier bootstrap.yml (ou bootstrap.properties) :

server:
  port: 9000 # Port d'écoute du client

spring:
  application:
    name: client-app-service # Nom du microservice client
  cloud:
    config:
      name: my-service # Le nom de base du fichier de configuration à récupérer (ex: 'my-service-dev.yml')
      uri: http://config.monreseau.local:8888 # Adresse du Config Server
      profile: prod # Le profil de configuration à activer (ex: 'prod' pour 'my-service-prod.yml')
      label: main # La branche Git à utiliser (par ex. 'main' ou 'develop')
      fail-fast: true # Échec rapide si le serveur de config n'est pas accessible au démarrage

Pour un accès local, ajoutez une entrée à votre fichier hosts pour le client :

127.0.0.1 client-app.dev

Créez un contrôleur pour exposer les propriétés chargées :

package com.example.clientapp.controller;

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

/**
 * Contrôleur simple pour récupérer une valeur de configuration depuis le serveur.
 * La valeur de 'app.instance.port' est injectée dynamiquement.
 */
@RestController
@RequestMapping("/api/config")
public class AppConfigController {

    // Injecte la valeur de la propriété 'app.instance.port' depuis la configuration distante
    @Value("${app.instance.port:0}") // 0 comme valeur par défaut si non trouvée
    private String currentApplicationPort;

    @Value("${app.instance.environment:unknown}")
    private String currentEnvironment;
    
    @Value("${app.message:No message}")
    private String applicationMessage;

    /**
     * Expose le port de l'application et l'environnement configuré.
     * @return Une chaîne de caractères indiquant le port, l'environnement et un message.
     */
    @GetMapping("/details")
    public String getConfigDetails() {
        return "L'application fonctionne sur le port : [" + currentApplicationPort + "] " +
               "dans l'environnement : [" + currentEnvironment + "]. Message : [" + applicationMessage + "]";
    }
}

Démarrez d'abord le Config Server (port 8888), puis le Config Client (port 9000). Accédez ensuite à http://client-app.dev:9000/api/config/details. Le client devrait afficher les informations récupérées du fichier my-service-prod.yml depuis le dépôt Git.

Vous pouvez également tester l'accès direct aux configurations via le Config Server :

  • http://config.monreseau.local:8888/my-service/dev/main pour le profil de développement.
  • http://config.monreseau.local:8888/my-service/prod/main pour le profil de production.

Intégration de Spring Cloud Config avec Eureka

L'intégration du Config Server avec Eureka permet à la fois au Config Server lui-même de s'enregistrer et d'être découvert, et aux clients de découvrir le Config Server via Eureka.

Configuration d'Eureka Server via Config

Créez un fichier de configuration dans votre dépôt Git, par exemple discovery-server-config.yml :

# Configuration générique pour le serveur Eureka
spring:
  profiles:
    active: dev # Profil par défaut actif

---
# Profil de développement pour Eureka
spring:
  config:
    activate:
      on-profile: dev
server:
  port: 2222 # Port pour l'instance Eureka en DEV

eureka:
  instance:
    hostname: localhost
  client:
    register-with-eureka: false # Ce serveur Eureka ne s'enregistre pas lui-même
    fetch-registry: false # Ne tente pas de récupérer le registre des autres serveurs Eureka
    service-url:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ # URL pour les clients Eureka
  server:
    enable-self-preservation: false # Désactiver la fonction de self-preservation pour les tests (pour le dev)

---
# Profil de production pour Eureka
spring:
  config:
    activate:
      on-profile: prod
server:
  port: 2223 # Port pour l'instance Eureka en PROD

eureka:
  instance:
    hostname: eureka-prod-host
  client:
    register-with-eureka: false
    fetch-registry: false
    service-url:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

Créez un nouveau projet Spring Boot pour votre serveur Eureka, par exemple eureka-config-center. Ajoutez les dépendances suivantes à son pom.xml :

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>

Créez un fichier bootstrap.yml pour qu'Eureka récupère sa propre configuration auprès du Config Server :

spring:
  application:
    name: eureka-discovery-service # Nom de l'application Eureka
  cloud:
    config:
      name: discovery-server-config # Nom du fichier de configuration dans Git (e.g., discovery-server-config-dev.yml)
      uri: http://config.monreseau.local:8888 # Adresse du Config Server
      profile: dev # Profil à charger (dev ou prod)
      label: main # Branche Git
      fail-fast: true

Dans la classe principale d'Eureka, activez le serveur Eureka :

package com.example.eureka;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@SpringBootApplication
@EnableEurekaServer
public class EurekaConfigCenterApplication {

    public static void main(String[] args) {
        SpringApplication.run(EurekaConfigCenterApplication.class, args);
    }
}

Démarrez d'abord le Config Server, puis l'application Eureka. Accédez à http://localhost:2222/ pour vérifier que le tableau de bord Eureka est fonctionnel et a bien chargé sa configuration distante.

Intégration d'un service fournisseur avec Config et Eureka

Nous allons maintenant configurer un service fournisseur pour qu'il récupère sa configuration via Spring Cloud Config et s'enregister auprès d'Eureka.

Créez un fichier de configuration pour le service fournisseur dans votre dépôt Git, par exemple data-service-config.yml :

spring:
  profiles:
    active: dev # Profil par défaut pour le service de données

---
# Configuration pour le profil 'dev' du service de données
spring:
  config:
    activate:
      on-profile: dev
  application:
    name: data-provider-service
  datasource:
    url: jdbc:mysql://localhost:3306/dev_db?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC
    username: devuser
    password: devpassword
    driver-class-name: com.mysql.cj.jdbc.Driver
  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true
server:
  port: 1007
eureka:
  instance:
    appname: data-provider-service
    instance-id: ${spring.application.name}:${server.port}
    prefer-ip-address: true
  client:
    service-url:
      defaultZone: http://localhost:2222/eureka/ # URL du serveur Eureka de dev

---
# Configuration pour le profil 'prod' du service de données
spring:
  config:
    activate:
      on-profile: prod
  application:
    name: data-provider-service
  datasource:
    url: jdbc:mysql://prod-db-host:3306/prod_db?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC
    username: produser
    password: prodpassword
    driver-class-name: com.mysql.cj.jdbc.Driver
  jpa:
    hibernate:
      ddl-auto: validate # Valider le schéma en production
    show-sql: false
server:
  port: 1008
eureka:
  instance:
    appname: data-provider-service
    instance-id: ${spring.application.name}:${server.port}
    prefer-ip-address: true
  client:
    service-url:
      defaultZone: http://localhost:2223/eureka/ # URL du serveur Eureka de prod

Créez un nouveau module Spring Boot, par exemple data-provider-service. Ajoutez les dépendances nécessaires dans son pom.xml, y compris celles pour Eureka Client et Config Client :

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<!-- Autres dépendances : web, jpa, mysql, druid, etc. -->

Créez un fichier bootstrap.yml pour ce service fournisseur :

spring:
  application:
    name: data-provider-service # Nom du service fournisseur
  cloud:
    config:
      name: data-service-config # Nom du fichier de configuration dans Git
      uri: http://config.monreseau.local:8888 # Adresse du Config Server
      profile: dev # Profil à charger (dev ou prod)
      label: main # Branche Git
      fail-fast: true

Après avoir démarré le Config Server et le serveur Eureka, démarrez votre data-provider-service. Il devrait récupérer sa configuration depuis le Config Server et s'enregistrer auprès d'Eureka. Vous pouvez vérifier son enregistrement sur le tableau de bord Eureka (http://localhost:2222/).

Chemins de recherche de configuration

Par défaut, Spring Cloud Config recherche les fichiers de configuration à la racine du dépôt Git. Cependant, dans les architectures complexes, il est souvent préférable d'organiser les configurations par sous-répertoires (par exemple, un répertoire par application ou par groupe de services). Pour permettre au Config Server de trouver ces fichiers organisés, il est nécessaire de spécifier des chemins de recherche via la propriété search-paths.

Modifiez le fichier application.yml de votre Config Server (config-service-core) :

server:
  port: 8888

spring:
  application:
    name: config-management-service
  cloud:
    config:
      server:
        git:
          uri: https://github.com/votre-utilisateur/app-configurations.git
          # Spécifie les répertoires où le Config Server doit rechercher les fichiers de configuration.
          # Chaque chemin est relatif à la racine du dépôt Git.
          search-paths: services/microservice-A, services/microservice-B
          default-label: main

Créez les répertoires services/microservice-A et services/microservice-B dans votre dépôt Git. Placez-y des fichiers de configuration, par exemple :

services/microservice-A/app-A-profiles.yml (dans Git) :

# Fichier de configuration pour microservice-A
spring:
  profiles:
    active: dev # Profil par défaut si non spécifié

---
# Configuration spécifique au profil 'dev' pour microservice-A
spring:
  config:
    activate:
      on-profile: dev
app:
  name: microservice-A
  environment: developpement
  message: "Bonjour depuis le service A (DEV)!"

---
# Configuration spécifique au profil 'test' pour microservice-A
spring:
  config:
    activate:
      on-profile: test
app:
  name: microservice-A
  environment: test
  message: "Salutations depuis le service A (TEST)!"

services/microservice-B/app-B-profiles.yml (dans Git) :

# Fichier de configuration pour microservice-B
spring:
  profiles:
    active: prod # Profil par défaut si non spécifié

---
# Configuration spécifique au profil 'prod' pour microservice-B
spring:
  config:
    activate:
      on-profile: prod
app:
  name: microservice-B
  environment: production
  message: "Bienvenue sur le service B (PROD)!"

Poussez ces modifications vers votre dépôt Git. Redémarrez votre Config Server (port 8888).

Vous pouvez maintenant accéder à ces configurations via des URL, en spécifiant le nom de l'application et le profil. Le Config Server utilisera les search-paths pour trouver les fichiers correspondants :

  • http://config.monreseau.local:8888/app-A-profiles/test/main
  • http://config.monreseau.local:8888/app-B-profiles/prod/main

Cela permet une organisation logique et une gestion plus structurée des fichiers de configuration pour un grand nombre de microservices.

Étiquettes: SpringCloudConfig Microservices ConfigurationManagement Git Eureka

Publié le 17 juillet à 01h26