Mostrando entradas con la etiqueta netflix eureka. Mostrar todas las entradas
Mostrando entradas con la etiqueta netflix eureka. Mostrar todas las entradas

sábado, 27 de noviembre de 2021

Spring Boot Admin: Iniciación

Hoy vamos a ver una genial aplicación que nos permitirá monitorizar nuestras aplicaciones de Spring Boot, Spring Boot Admin a.k.a SBA.  Esta aplicación tiene múltiples cualidades pero para poder hacer uso de ellas, primero tenemos que configurarlo.  Y en este post nos centraremos en como configurarlo principalmente, primero con el método por defecto y luego haciendo uso de Eureka. Para ello, hemos utilizado las siguientes versiones:

  • Spring-boot: 2.5.7
  • Spring-admin: 2.5.4
  • Spring-cloud: 2020.0.3

Para empezar crearemos un servidor que será el encargado de recopilar la información y proporcionarnos una interfaz gráfica para acceder a los mismos. 

Vamos a empezar con una versión básica. Crearemos un servidor que se pueda registrar a si mismo. Para ello deberemos seguir los siguientes pasos:

  • Añadir la dependencia de spring spring-boot-starter-web. 
  • Añadir la dependencia de codecentric spring-boot-admin-starter-server que contendrá el motor del servidor.
  • Añadir la dependencia de codecentric spring-boot-admin-starter-client que nos permitirá conectar el propio servidor consigo mismo. 
  • Crear la clase de arranque y configuración con las anotaciones: @EnableAdminServer, @EnableAutoConfiguration, @SpringBootApplication.
  • Indicar a través de la propiedad spring.boot.admin.client.url en el fichero de configuración la ruta donde estará nuestra aplicación. Para el ejemplo: http://localhost:8080.
  • Indicar a través de la propiedad management.endpoints.web.exposure.include la habilitación de los endpoints de Actuator. 
Ya solo tenemos que arrancar la aplicación y podemos verla desplegada y auto descubierta. 


Con esto tendremos la configuración básica, pero tenemos que tener en cuenta que es una aplicación para la administración de otras aplicaciones. Y aunque en ciertos casos puede desplegarse dentro de una red interna donde no tendremos problemas de seguridad, también es posible que sea desplegado en entornos productivos y por tanto debe de tener algún tipo de seguridad, sobre todo si esta disponible desde Internet. Para ello añadiremos una configuración básica que nos permita incluir una pantalla de logín y un pequeño filtro de usuario. Y para llevarlo a cabo debemos realizar los siguientes pasos:
  • Añadimos la librería spring-boot-starter-security
  • En el fichero de configuración, añadimos las propiedades spring.security.user.name/passsword que configuren un usuario por defecto. 
  • En el fichero de configuración, añadimos las propiedades spring.boot.admin.client.username/password que permitan al cliente, el mismo, registrarse en el servidor
  • En el fichero de configuración, añadimos las propiedades spring.boot.admin.client.instance.metadata.user.name/password que permitan al servidor acceder a los endpoints securizados de métrica del cliente.
  • Además añadimos una clase java de configuración de seguridad que permita acceder sin restricciones a los endpoints info y health además de a la pantalla de login. Securizando el resto de URLs y endpoints de la aplicación. Configuración obtenida de la documentación oficial, aquí.
@Configuration(proxyBeanMethods = false)
public class SecuritySecureConfig extends WebSecurityConfigurerAdapter {
  private final AdminServerProperties adminServer;
  private final SecurityProperties security;

  public SecuritySecureConfig(AdminServerProperties adminServer, SecurityProperties security) {
    this.adminServer = adminServer;
    this.security = security;
  }
  @Override
  protected void configure(HttpSecurity http) throws Exception {
    SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
    successHandler.setTargetUrlParameter("redirectTo");
    successHandler.setDefaultTargetUrl(this.adminServer.path("/"));

    http.authorizeRequests(
        (authorizeRequests) -> authorizeRequests.antMatchers(this.adminServer.path("/assets/**")).permitAll() 
            .antMatchers(this.adminServer.path("/actuator/info")).permitAll()
            .antMatchers(this.adminServer.path("/actuator/health")).permitAll()
            .antMatchers(this.adminServer.path("/login")).permitAll().anyRequest().authenticated() 
    ).formLogin(
        (formLogin) -> formLogin.loginPage(this.adminServer.path("/login")).successHandler(successHandler).and() 
    ).logout((logout) -> logout.logoutUrl(this.adminServer.path("/logout"))).httpBasic(Customizer.withDefaults()) 
        .csrf((csrf) -> csrf.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) 
            .ignoringRequestMatchers(
                new AntPathRequestMatcher(this.adminServer.path("/instances"),
                    HttpMethod.POST.toString()), 
                new AntPathRequestMatcher(this.adminServer.path("/instances/*"),
                    HttpMethod.DELETE.toString()), 
                new AntPathRequestMatcher(this.adminServer.path("/actuator/**")) 
            ))
        .rememberMe((rememberMe) -> rememberMe.key(UUID.randomUUID().toString()).tokenValiditySeconds(1209600));
  }
  // Required to provide UserDetailsService for "remember functionality"
  @Override
  protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.inMemoryAuthentication().withUser(security.getUser().getName())
        .password("{noop}" + security.getUser().getPassword()).roles("USER");
  }
}

Ahora al intentar acceder a la pantalla anterior veremos el formulario de acceso, en el cual deberemos indicar el usuario y contraseña indicados en el fichero de configuración:


Una vez que tenemos el servidor desplegado y securizado. Vamos a proceder a configurar un cliente que se pueda conectar a dicho servidor. La configuración es igual a la que hemos visto anteriormente:
  • Añadir la librería spring-boot-admin-starter-client.
  • En el fichero de configuración, añadimos las propiedades spring.boot.admin.client.username/password que permitan al cliente registrarse en el servidor.
  • En el fichero de configuración, indicar a través de la propiedad spring.boot.admin.client.url, la ruta donde estará nuestra aplicación. 
  • En el fichero de configuración, indicar a través de la propiedad management.endpoints.web.exposure.include la habilitación de los endpoints de Actuator. 
Y una vez que hemos arrancado la nueva aplicación, desde la pestaña Aplicaciones del menú de administración del SBA, podremos ver cuales son los clientes conectados al mismo. Además, clickeando sobre cualquiera de los clientes, podremos ver información detallada sobre dicha aplicación. 



Pero como estamos en un mundo donde los micro servicios dominan el panorama actual, ahora vamos a ver una configuración un poco más actual. Habilitando la configuración a través de un Service Discovery como Eureka. Para ello, crearemos un servidor Eureka, puedes ver el código de ejemplo aquí. 

Una vez que tenemos arrancado el servidor de Eureka, procederemos a realizar la configuración necesaria en el servidor de SBA. Para ello seguimos los siguientes pasos:
  • Añadimos la dependencia spring-cloud-starter-netflix-eureka-client, que nos permitirá conectarnos con el servidor de Eureka. Por tanto ya no es necesaria que utilicemos la librería spring-boot-admin-starter-client, ni las propiedades con el sufijo spring.boot.admin en el fichero de configuración.
  • Añadimos la dependencia spring-boot-starter-actuator en caso de que este como dependencia pasiva de otra librería. 
  • En el fichero de configuración, indicamos a través de la URL por defecto del servidor en la propiedad eureka.client.serviceUrl.defaultZone.
  • En el fichero de configuración, habilitamos que Eureka conozca el estado de la aplicación a través del endpoint health de la aplicación con la propiedad eureka.client.healthcheck.enabled.
  • En el fichero de configuración, indicamos las credenciales de seguridad para acceder a los endpoints securizados a través de las propiedades eureka.instance.metadata-map.user.name/password. Este paso solo es necesario si los endpoints se encuentran securizados.
Si hemos seguido los pasos correctamente tanto en el servidor SBA y en los clientes, al acceder a Eureka podremos visualizarlos los dos:


Y si accedemos al servidor SBA también podremos visualizar ambas aplicaciones en el mismo. 

Como siempre, si tenemos los conceptos más o menos claros, montar una aplicación con Spring Boot es bastante sencillo. Si quieres acceso al código visita el siguiente enlace. 

sábado, 12 de diciembre de 2020

Apache Camel: Service Discovery con Spring Cloud Netflix

Ya hemos visto bastante de Apache Camel, y hoy veremos como realizar Service Discovery a través de la librería de Spring Cloud Netflix. Pero antes como siempre veremos varios conceptos para entender mejor luego el ejemplo.

Debemos ubicarnos en un entorno de creación y utilización de Microservicios. Y como, si creamos Microservicios y los desplegamos a través de contenedores en la nube, sus localizaciones de red (IPs y direcciones) pueden ser dinámicas. Y por tanto nos pueden llevar a que la configuración que tengamos sobre la ubicación de los mismos no sea válida siempre. 

De ahí surge la necesidad del Service Discovery, o lo que es lo mismo, detección automática de dispositivos y servicios ofrecidos en una misma red. De esta forma, siempre tendremos la configuración correcta sobre los Microservicios disponibles. 

Para llevarlo a cabo necesitamos de un Service Registry que será un servidor encargado de recoger la información sobre los Microservicios: su ubicación, estado, etc. Y los Microservicios actuarían como los clientes de este Service Registry. Hay diferentes implementaciones de Service Registry y nosotros utilizaremos Netflix Eureka. 

Para llevar a cabo el Service Discovery se pueden utilizar varios patrones, y nosotros vamos a elegir el Client‑Side Discovery Pattern. Donde será el  cliente el encargado de registrarse en el Service Registry. 

Una vez asentadas las bases, podemos empezar con nuestro ejemplo. El cual consta de 3 aplicaciones:

  • Un Service Registry realizado solo con Spring Boot
  • Un Service Client realizado con Spring Boot, el cual tendrá un recurso que nos devolverá valores. 
  • Un Service Client realizado con Apache Camel, el cual se conectará al Service Registry y al otro Service Client dinámicamente. 
Empecemos por el Service Registry. Será la parte más sencilla, básicamente un proyecto de Spring Boot con la dependencia spring-cloud-starter-netflix-eureka-server y la anotación @EnableEurekaServer.

@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
    public static void main(final String[] args) {
        SpringApplication.run(EurekaServerApplication.class, args);
    }
}

Eso sí, al ser una prueba de concepto en local y con un único Service Registry, tendremos que modificar el fichero de configuración de la aplicación para evitar errores de esta configuración y trazas excesivas. Y es debido a que el Service Registry está preparado para que funcione en un cluster y por tanto al iniciarse intenta buscar otras instancias y registrarse en las mismas. Por ello el fichero de configuración quedará de la siguiente forma:

server.port=8761

eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false

logging.level.com.netflix.eureka=OFF
logging.level.com.netflix.discovery=OFF

El siguiente paso será crear un cliente con Spring Boot que se conecte al Service Registry y que tenga un recurso denominado /books que devuelva información. Esta aplicación se denominará discovery-client.

Por último, procederemos a crear nuestro Microservicio con Apache Camel, el cual conectaremos al Service Registry y con el cual también invocaremos al Microservicio discovery-client. Para llevar a cabo todo esto nos basaremos en las siguientes librerías de Spring Boot y Spring Cloud. El resto serán las librerías habituales de Apache Camel. 

<dependency>
      <groupId>org.apache.camel.springboot</groupId>
      <artifactId>camel-spring-cloud-netflix-starter</artifactId>
</dependency>
<dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
      <version>2.2.4.RELEASE</version>
</dependency>

A nivel de configuración tendremos que indicar, cuál será el nombre de nuestra aplicación (necesario a la hora de registrarse en el servidor Eureka) y donde se encuentra el Service Registry. Recordemos que vamos a utilizar el patrón Client‑Side Discovery, por tanto debe ser el cliente el que haga saber al servidor que está disponible. 

spring.application.name=app-camel-eureka-client
eureka.client.serviceUrl.defaultZone = http://localhost:8761/eureka/

Si ya arrancamos nuestra aplicación y accedemos al servidor Eureka en la ruta http://localhost:8761. Podremos ver que los dos Microservicios se encuentran disponibles y aparte algo de información sobre el propio servidor. 


Con esto ya podemos comprobar que nuestra infraestructura funciona, y gracias al servidor ubicar en cada momento a nuestros Microservicios. Ahora vamos a ver como podemos invocarlos dinámicamente desde Apache Camel, y vamos a verlo de dos formas diferentes. 

Para realizar la primera invocación dinámica vamos a hacer uso del componente de Apache Camel Service Call. A través del cual podremos hacer invocaciones a servicios remotos que estén registrados en un Service Registry. Tendremos que indicar el nombre del servicio y su recurso en este caso discovery-client y books. 

@Override
public void configure() throws Exception {
    restConfiguration().component("servlet").bindingMode(RestBindingMode.json);

    rest("/serviceCall").get().produces(MediaType.APPLICATION_JSON_VALUE).route()
    .serviceCall("discovery-client/books?bridgeEndpoint=true")
    //Is it becouse http endpoint produces a Stream as the body and once the stream is read, it is no longer available.
    .convertBodyTo(String.class)
    .log("Body: ${body}").unmarshal().json();
}

De esta forma no tendremos que configurar la URL del Microservicio a invocar, solo debemos conocer cuál es su identificador en el Service Registry. 

También podremos realizar esta llamada a través de una instancia de DiscoveryClient, utilidad de Spring Boot. El cual dado un identificador de un Microservicio, nos indicará su URI y otros valores asociados a ella. 

public static final String URL_DISCOVER_BOOKS = "%s/books?bridgeEndpoint=true";

@Autowired
private DiscoveryClient dClient;

@Override
public void configure() throws Exception {
    restConfiguration().component("servlet").bindingMode(RestBindingMode.json);

    rest("/discoveryCall").get().produces(MediaType.APPLICATION_JSON_VALUE).route()
    .to(String.format(URL_DISCOVER_BOOKS, dClient.getInstances("discovery-client").get(0).getUri()))
//Is it becouse http endpoint produces a Stream as the body and once the stream is read, it is no longer available. .convertBodyTo(String.class) .log("Body: ${body}").unmarshal().json(); }

Como siempre podéis ver que con Apache Camel, Spring Boot y todos los starters podemos realizar grandes cosas y fácilmente. Si quereis podeis ver todo el código aquí.