En arquitecturas distribuidas, con múltiples microservicios desplegados en distintos servidores, realizar cambios de configuración en cada una de ellos puede tornarse una tarea compleja y demandante.

Spring Cloud Config es una herramienta que permite exteriorizar y centralizar la configuración de los microservicios en un solo lugar, lo que facilita enormemente la administración de la configuración del sistema.

Por default, Spring Cloud Config utiliza Git como repositorio para centralizar la configuración de cada uno de los servicios. Esto nos permite aprovechar algunas sus ventajas:

  • Control de cambios
  • Utilizar labels para hacer referencia a versiones específicas

Setup del proyecto y sus dependencias

Código fuente: https://github.com/ignacioerro/spring-cloud-config-sample

  • config-server: Spring Cloud Config Server
  • consumer-service y producer-service: Spring Cloud Config Clients
Arquitectura de spring-config

Arquitectura de spring-config

NOTA: Para generar los proyectos voy a utilizar Spring Initializer

Config server

1. En primer lugar vamos a construir el config-server, cuyo único propósito es obtener las configuraciones desde un repositorio remoto Git y servirlas a los demás servicios.
config-server

config-server

En este caso, la única dependencia que vamos a agregar es spring-cloud-config-server:
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-config-server</artifactId>
</dependency>
2. El siguiente paso es anotar ConfigServerApplicationcon la anotación @EnableConfigServer:
@EnableConfigServer
@SpringBootApplication
public class ConfigServerApplication {
  public static void main(String

[] args) { SpringApplication.run(ConfigServerApplication.class, args); } }

3. Por último, solo queda indicarle al config-server de dónde debe obtener las configuraciones. Para ésto, voy a utilizar un repositorio Git disponible en GitHub.
config-server/src/main/resources/application.yml
server: 
  port: ${PORT:8888}
spring:
  application:
    name: config-server 
  cloud:
    config:
      server:
        git:
          uri: https://github.com/ignacioerro/spring-cloud-configuration

El config-server expone un conjunto de recursos para que los servicios puedan acceder a sus configuraciones:

  • /{application}/{profile}[/{label}]
  • /{application}-{profile}.yml
  • /{label}/{application}-{profile}.yml
Donde application es inyectado utilizando el nombre configurado en la propiedad spring.application.name, profile es el perfil activo, label es una etiqueta de Git opcional.
En este caso, tenemos 2 servicios que necesitan consumir sus propiedades desde el config-server, por lo tanto vamos a tener 3 archivos de propiedades en el respositorio:
  • application.yml: propiedades comunes a todos los servicios que componen el sistema.
global-message: Configuration server is alive!
  • consumer-service.yml: propiedades exclusivas para el consumer-service.
server:
    port: ${PORT:8889}
message: Consumer configuration server is alive!
  • producer-service.yml: propiedades exclusivas para el producer-service.
server:
    port: ${PORT:8890}
message: Producer configuration server is alive!

Config clients: microservices

1. A continuación vamos a construir los 2 servicios similares que van a consumir sus propiedades desde el config-server, consumer-service y producer-service:
consumer-service

consumer-service

producer-service

producer-service

Como vemos, en ambos casos las únicas dependencias que agregamos son spring-cloud-starter-config y spring-boot-starter-web:
<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>

2. Algo muy importante que debemos tener en cuenta, es que debemos renombrar el archivo de propiedades application.[properties|yml] por bootstrap.[properties|yml] en las 2 aplicaciones que acabamos de crear. Y en cada uno agregamos el nombre de la aplicación, el cual es necesario para mapear la aplicación con el archivo de configuración correspondiente.

consumer-service/src/main/resources/bootstrap.yml

spring:
  application:
    name: consumer-service

producer-service/src/main/resources/bootstrap.yml

spring:
  application:
    name: producer-service
3. A continuación, vamos a crear un servicio rest en ambas aplicaciones para demostrar cómo cada uno accede a sus propiedades específicas y globales.
  • message: es una propiedad específica, configurada en cada servicio.
  • global-message: es una propiedad global del sistema, que puede ser accedida por ambos servicios.

consumer-service/src/main/java/com/example/consumerservice/ConsumerServiceApplication.java

@RestController
class Consumer {
  @Value("${message}")
  private String message;
  
  @Value("${global-message}")
  private String globalMessage;
  
  @RequestMapping(method = RequestMethod.GET)
  public Map<String, String> message() {
    Map<String, String> response = new HashMap<>();
    
    response.put("message", message);
    response.put("global-message", globalMessage);
    
    return response;
  }
}

producer-service/src/main/java/com/example/producerservice/ProducerServiceApplication.java

@RestController
class Producer {
  @Value("${message}")
  private String message;
  
  @Value("${global-message}")
  private String globalMessage;
  
  @RequestMapping(method = RequestMethod.GET)
  public Map<String, String> message() {
    Map<String, String> response = new HashMap<>();
    
    response.put("message", message);
    response.put("global-message", globalMessage);
    
    return response;
  }
}

Ejecución de los servicios

Es importante tener en cuenta que para levantar los servicios es necesario hacerlo en el orden correcto. En este caso necesitamos levantar primero el config-server, de esta manera la configuración está disponible antes de proceder a levantar el resto de los servicios.
Ejecución de servicios

Ejecución de servicios

Una vez que los servicios están levantados, podemos observar que fueron capaces de obtener su configuración desde el config-server ya que cada uno se levantó en el puerto especificado: 8889 el consumer-service y 8890 el producer-service.
Si accedemos al config-server en el puerto 8888, podemos ver las propiedades específicas y globales que están configuradas para cada aplicación:

Configuración de consumer-service: http://localhost:8888/consumer-service/master

{
    "name": "consumer-service",
    "profiles": ["master"],
    "label": null,
    "version": null,
    "state": null,
    "propertySources": [{
        "name": "https://github.com/ignacioerro/spring-cloud-configuration/consumer-service.yml",
        "source": {
            "server.port": "${PORT:8889}",
            "message": "Consumer configuration server is alive!"
        }
    }, {
        "name": "https://github.com/ignacioerro/spring-cloud-configuration/application.yml",
        "source": {
            "global-message": "Configuration server is alive!"
        }
    }]
}

Configuración de producer-service: http://localhost:8888/producer-service/master

{
    "name": "producer-service",
    "profiles": ["master"],
    "label": null,
    "version": null,
    "state": null,
    "propertySources": [{
        "name": "https://github.com/ignacioerro/spring-cloud-configuration/producer-service.yml",
        "source": {
            "server.port": "${PORT:8890}",
            "message": "Producer configuration server is alive!"
        }
    }, {
        "name": "https://github.com/ignacioerro/spring-cloud-configuration/application.yml",
        "source": {
            "global-message": "Configuration server is alive!"
        }
    }]
}
Luego, podemos acceder al servicio que creamos en cada una de las aplicaciones y verificar cómo responden con un mensaje específico de cada app (message), y un mensaje global a todo el sistema (global-message):

Aplicación consumer-service: http://localhost:8889

{
    "global-message": "Configuration server is alive!",
    "message": "Consumer configuration server is alive!"
}

Aplicación producer-service: http://localhost:8890

{
    "global-message": "Configuration server is alive!",
    "message": "Producer configuration server is alive!"
}

Conclusión

Spring Cloud Config es una herramienta muy útil que nos permite extraer y centralizar la configuración de un sistema distribuido en un único repositorio, lo que facilita la gestión y administración de la configuración de un sistema que puede contar con múltiples servicios desplegados en distintos servidores. Además nos permite aprovechar las ventajas de tener toda la configuración bajo un sistema de control de versiones.

Referencia