Vamos a continuar la serie de microservicios añadiendo en este post un par de componentes fundamentales de infraestructura en arquitecturas de microservicios:
- Config Server (Spring Cloud Config): Nos permitirá centralizar y delegar en un servicio la provisión de la configuración de todos nuestros microservicios.
- Registry / Discovery Service (Eureka Netflix OSS): Este componente será una pieza central en la arquitectura en el que cada servicio se registrará y a su vez podrá consultar la dirección de los demás servicios para consumirlos.
El escenario a implementar será el siguiente:
Lo primero que haremos será crear nuestro servidor de configuración con Spring Cloud Config para que provea de la configuración a los demás componentes de forma remota y centralizada. Esta configuración serán archivos yml almacenados en un repositorio Git.
Recordemos que todo el código puede descargarse de nuestro repositorio en GitHub.
1 Config server
Implementación
Nuestro servidor de configuración será una aplicación Spring Boot a la que añadiremos la anotación @EnableConfigServer:
@EnableConfigServer
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
La dependencia que tenemos que añadir al pom es la siguiente:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
Algo a tener en cuenta es que hemos añadido en el dependencyManagement de nuestro pom lo siguiente:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Camden.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
Con ello conseguimos abstraernos de la gestion de versiones de las dependencias que necesitaremos de Spring Cloud, gracias al concepto BOM (Bill Of Materials) con Maven.
Configuración
Tendremos 2 ficheros de configuración:
- application.yml:
server:
port: 8888
spring:
application:
name: configserver
cloud:
config:
server:
git:
uri: https://github.com/atSistemas/cygnus-config
logging:
file: logs/${spring.application.name}.log
level:
com.netflix.discovery: 'OFF'
org.springframework.cloud: 'DEBUG'
com.atsistemas: 'DEBUG'
server.port: Indica el puerto donde arrancará el servidor.
spring.application.name: el nombre de la aplicación.
spring.cloud.config.server.git.uri: La ruta del repositorio de configuración.
Por defecto haremos que el nombre de los ficheros que vamos a servir concida con el nombre de los servicios respectivamente.
Si quisieramos mantaner estos ficheros en filesystem sería algo así:
spring:
cloud:
config:
server:
native:
searchLocations:${HOME}/cygnus-config
y tendríamos que tener también la siguiente property:
spring:
profiles:
active: native
- logback.xml para la configuración de logging:
<configuration>
<include resource="org/springframework/boot/logging/logback/base.xml" />
<logger name="org.springframework.web" level="DEBUG" />
<logger name="com.atsistemas" level="DEBUG" />
</configuration>
Para arrancar nuestro servidor de configuración: mvn clean spring-boot:run
2 Eureka Server
Implementación
Nuestro servidor de registry / discovery estará basado en Eureka de Netflix. Para ello crearemos una aplicación Spring Boot y anotaremos la clase principal con @EnableEurekaServer:
@SpringBootApplication
@EnableEurekaServer
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Configuración
- bootstrap.yml:
spring:
application:
name: eureka-server
cloud:
config:
failFast: true
uri: http://config-server:8888
Es importante incluir este fichero en todos los servicios, ya que ahora queremos que reciban su configuración de forma remota del config server. Para ello necesitamos en tiempo de arranque las siguientes propiedades:
spring.application.name define el nombre de la aplicación. Con este nombre se registrará en Eureka y por defecto será el nombre del fichero de configuración que el configserver va a leer para esa aplicación.
spring.cloud.config.failFast=true establecemos que el servicio falle en tiempo de arranque si por alguna razón no le es provista su configuración desde el configserver. Una práctica común es establecer esta property a false y añadir al servicio un fichero properties con los valores por defecto de su configuración. De esta manera podemos prevenir que el servicio no arranque si por ejemplo consideraramos este servicio crítico en nuestra infraestructura.
spring.cloud.config.uri define el endpoint del servicio de configuración.
En nuestro repositorio Git tendremos el fichero:
- eureka-server.yml:
server:
port: 1111 # HTTP port
logging:
file: logs/${spring.application.name}.log
level:
com.netflix.discovery: 'OFF'
org.springframework.cloud: 'DEBUG'
com.atsistemas: 'DEBUG'
la propery server.port define el puerto donde arrancará Eureka.
- application.yml:
Este fichero se empaqueta con la aplicación para proveer la configuración por defecto en caso de fallo al obtenerla través del configserver (para ello se necesita la property spring.cloud.config.failFast=false)
Algo a tener en cuenta respecto a las dos piezas clave en nuestra infraestructura como son el servidor de configuración y el servicio de discovery es que tenemos 2 estrategias a seguir:
- Config first: Se dispone del servidor de configuración arrancado para que todos los servicios lo primero que hagan es obtener su configuración de él incluido el endpoint de Eureka para registrarse a continuación.
- Discovery first: Teniendo Eureka arrancado se configura en el bootstrap de los servicios su endpoint, incluyendo el configserver. De esta manera los servicios en el arranque consultan a Eureka el endpoint del configserver para obtener su configuración. Esta estrategia tiene un recargo de una llamada de red adicional y como ventaja el que el/los configserver estén registrados en Eureka. Esto es interesante por ejemplo para monitorizar los configserver con Turbine, y para delegar en Ribbon el balanceo de peticiones al configserver. Más adelante hablaremos sobre esto.
En nuestro ejemplo hemos optado por la estrategia de config first. Si quisiéramos tener el configserver en alta disponibilidad delegaríamos el balanceo en otro componente out of the box. Podríamos utilizar por ejemplo Zuul, Nginx, un ESB, balanceador hardware…
Para arrancar nuestro servidor Eureka: mvn clean spring-boot:run
3 Sadr
Este microservicio implementado en el post anterior, obtendrá su configuración del config-server y se registrará en Eureka en tiempo de arranque.
En su fichero bootstrap.yml se definen las siguientes properties:
spring:
application:
name: sadr-service
cloud:
config:
failFast: false
uri: http://config-server:8888
Y en el repositorio de configuración tendremos el fichero:
- sadr-service.yml:
# HTTP Server
server:
port: 0 # HTTP port
# Discovery Server Access
eureka:
client:
serviceUrl:
defaultZone: http://discovery-server:1111/eureka/
server.port: Indica el puerto en el que el servicio arrancará. Podemos definir un puerto aleatorio con el valor 0, puesto que ahora quien quiera consumir el servicio no necesita saber su url, solamente consultar a Eureka.
eureka.client.serviceUrl.defaultZone: Indica el endpoint de Eureka.
Para arrancar nuestros servicios debemos hacerlo en el siguiente orden (mvn pring-boot:run
):
1º Config Server
2º Eureka
3º Sadr
Podemos comprobar que Sadr se ha registrado correctamente en Eureka en el dashboard:
http://localhost:1111/
Esto es todo por el momento. En el siguiente post veremos como consumir microservicios de forma balanceada con Ribbon a través de Eureka.
Si te gusta esta serie, ¡no te olvides de seguirnos en Twitter!