Eureka

Durante los pasados meses, Fon, mi empresa, ha organizado una experiencia de comunidades tecnológicas, que consistía en asignarnos un par de horas semanales para trabajar en un proyecto en grupo para investigar sobre tecnologías nuevas. En la comunidad a la que pertenecía, la comunidad de Backend decidimos hacer una prueba de concepto con los productos del stack de Netflix. El siguiente artículo intenta explicar lo que aprendí de uno de ellos, Eureka.

charla

Eureka

¿Que soluciona?

Eureka es un servicio basado en REST que se utiliza para el descubrimiento y registro automático de servicios, lo que facilita las tareas de escalabilidad, balanceo de carga y tolerancia fallos en una arquitectura de microservicios. Netflix lo utiliza en AWS cloud, que es donde están desplegados sus microservicios. Eureka tiene dos componentes cliente y servidor.

¿Como funciona?

Explicado a grandes rasgos, tendríamos un servidor Eureka en nuestra arquitectura de microservicios y cada microservicio sería cliente de este servidor. Al arrancar cualquiera de los microservicios, se comunicaría vía REST con el servidor diciéndole donde está (IP y puerto), quién es (definido en la propiedad spring.application.name del fichero aplication.yml), donde esta la url donde consultar su estado interno (endpoint health de actuator por defecto), donde está su página de home, etc. A partir de ese momento cada microservicio se comunica con Eureka indicando que sigue vivo, con lo que la documentación de Eureka llama «heartbeats» que se traduciría como latidos. Por defecto los heartbeats se envían cada 30 segundos. Si el servidor Eureka no recibe está comunicación durante un tiempo configurable por parte de una instancia de microservicio (90 segundos por defecto), esta se elimina de la lista de instancias disponibles. Los microservicios utilizan la lista con información del resto de microservicios para comunicarse entre ellos, a través del balanceador de carga software llamado Ribbon, el cual está también basado en la tecnología desarrollada por Netflix. Tanto servidor como clientes tienen una caché con la información del resto de servicios disponibles. Un microservicio no se muestra como disponible hasta que su información está en las caches de servidor y clientes. Es posible definir información adicional para ser compartida entre los microservicios añadiéndola mediante configuración. Eureka como servidor incluye un panel de monitorización con información útil como por ejemplo la lista de servicios registrados en el servidor, cuales son los servidores réplica sí los hay, etc.

Modos de configurar un servidor Eureka

Un servidor Eureka se puede configurar para existir solo en nuestra arquitectura (standalone mode) o para funcionar con otras instancias de Eureka (peer awareness). En este segundo modo, las distintos servidores Eureka se registran entre ellos replicando su información, con lo que consigue mayor disponibilidad y resistencia a fallos. En el ejemplo que mostraré en la siguiente sección, se explica como configurar dos instancias de Eureka en modo peer awareness. Actualmente, Peer awareness es la manera de configurar Eureka más común en proyectos en producción. Cuando un servidor comienza a recibir información sobre los microservicios, esta información es replicada en el resto de servidores de los que tiene conocimiento. Si esta coordinación falla, la información es enviada en el siguiente heartbeat. Los servidores Eureka utilizan el mismo mecanismo para hablar entre ellos que cuando hay una comunicación Eureka cliente-servidor. Cada microservicio incluye en su fichero de properties la lista de servidores Eureka con los que se puede comunicar y se conectan al primer servidor de la lista por defecto.

Modo self-preservation

Eureka puede entrar en modo self-preservation para evitar eliminar instancias registradas cuando se experimentan fallos de red pero los microservicios gozan de buena salud. Eureka entra en este modo, si los heartbeats durante los últimos 15 minutos (valor por defecto), son menos que los heartbeats esperados, valor que se definen en un límite configurable. La fórmula sería para calcular este límite es:

  • Número de instancias * Número de heatbeats esperados por minuto * porcentaje esperado

Si por ejemplo de servicios que tenemos es 4, y se esperan 2 heatbeats por minuto (1 cada 30 segundos) y el porcentaje esperado es 0.85 tendríamos: 4 * 2 * 0.85 = 7 heatbeats por minuto.

Para definir estos valores entran en juego las siguientes propiedades de la configuración de Eureka:

  • eureka.instance.leaseRenewalIntervalInSeconds: define cada cuanto se envía los heartbeats al servidor Eureka
  • eureka.server.renewalPercentThreshold: defíne el límite de instancias que tienen que renovar su información por debajo del cual se entra en modo self preservation.
  • eureka.server.renewalThresholdUpdateIntervalMs: cada cuanto tiempo se calcula si Eureka tiene que entrar en self-preservation mode.

Ejemplo

Lo más sencillo para entender como funciona Eureka es ver un ejemplo. Podéis encontrar el código del ejemplo en github. En el ejemplo vamos a crear dos proyectos SpringBoot que serán servidores Eureka en modo peer awareness y un cliente Eureka. Este es un ejemplo pensado para ser ejecutado en una máquina, para desplegar Eureka en AWS se necesita configuración extra que podéis encontrar aquí. Para crear los proyectos recomiendo usar spring initializr que es una web que nos permite generar proyectos SpringBoot definiendo previsamente sus dependencias en maven o gradle:

springinitializr

Para el primero de los servidores necesitamos crear un proyecto SpringBoot con la dependencia del starter de Eureka server. Después necesitamos añadir la anotación @EnableEurekaServer en el método main de la clase Application.

package com.fon.community;

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

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

Y añadir la siguiente configuración en el fichero de propiedades application.yml:

server:
  port: 8761

spring:
  application:
    name: eureka-peer

eureka:
  instance:
    hostname: peer1
  client:
    serviceUrl:
      defaultZone: http://user:password@peer2:8762/eureka/

security:
  basic:
    enabled: true
  user:
    name: user
    password: password

Está configuración contiene entre otras cosas, donde se va encontrar la réplica del servidor (el otro servidor que veremos a continuación), definida mediante la propiedad eureka.client.serviceUrl.defaultZone. Adicionalmente vamos a securizar el servidor, esto se hace definiendo como true la propiedad security.basic.enable y definiendo las propiedades username y password de security.basic. Para crear el otro servidor tenemos que hacer exactamente lo mismo pero definiendo otro puerto, hostname y como servidor replica indicar el primer servidor que hemos creado.

server:
  port: 8762

spring:
  application:
    name: eureka-peer

eureka:
  instance:
    hostname: peer2
  client:
    serviceUrl:
      defaultZone: http://user:password@peer1:8761/eureka/

security:
  basic:
    enabled: true
  user:
    name: user
    password: password

Para que el ejemplo funcione hay que añadir estas líneas al fichero /etc/hosts

127.0.0.1       peer1
127.0.0.1       peer2

Por último debemos crear otro proyecto que contendrá un cliente, que tendrá una sencilla API REST. Cuando lo arranquemos se registrará en ambos servidores Eureka. Requiere incluir la dependencia con el starter de Eureka. La clase Application requiere la anotación @EnableDiscoveryClient. También hemos añadido un sencillo endpoint en la ruta «/hello»:

package com.fon.serverclient;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@EnableDiscoveryClient
@SpringBootApplication
public class Application {

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

   @RestController
   class HelloController {
      @RequestMapping("/hello")
      public String serviceInstancesByApplicationName() {
         return "HelloWorld!";
      }
   }
}

Y añadir la siguiente configuración en el fichero de propiedades application.yml:

server:
  port: 0

spring:
  application:
    name: server-client

eureka:
  client:
    serviceUrl:
      defaultZone: http://user:password@peer1:8761/eureka/, http://user:password@peer2:8761/eureka/

Por último, arrancando los 3 proyectos y accediendo al panel de Eureka de los servidores (http://localhost:8761 y http://localhost:8762) podremos ver que los cada servidor tiene como replica al otro servidor y además el cliente está registrado. Para acceder a los paneles son requeridas las credenciales que hemos definido en la configuración. Esta información no se mostrará de manera inmediata, hay que esperar a que los distintos elementos se registren y descubran entre si.

EurekaServer1

EurekaServer2

Buenas prácticas

Peer awareness

Es recomendable utilizar Eureka en modo peer awareness en lugar de standalone en entornos de alta disponibilidad. Funcionando en este modo los clientes se comunican con el primer servidor incluido en la lista de servidores Eureka de su configuración. Si este servidor deja de estar disponible pasan a comunicarse con el siguiente en la lista y si más tarde se recupera, vuelven al primero. Adicionalmente cada cliente contiene una caché con la información acerca del resto de instancias que continua disponible, si hay fallos de comunicación con el servidor Eureka. Ambos mecanismos hacen que Eureka sea muy robusto cuando funciona utilizando varias replicas.

Health check

Por defecto, Eureka usa los heartbeats para saber si el cliente está activo, pero no conoce su estado interno. Eureka se puede configurar para enviar el estado de la aplicación determinado por la librería actuator. Para hacer esto hay que aplicar la siguiente configuración:

eureka:
  client:
    healthcheck:
      enabled: true

Comunicación entre servicios

Es recomendable utilizar Feign para comunicaciones entre microservicios. Feign es otra librería de Netflix que nos permite declarar clientes REST de forma declarativa.

Entender como configurar Eureka

Eureka es altamente configurable y y es conveniente entender como funciona en detalle, si se va utilizar en un proyecto en producción. Para ampliar información sobre cómo configurar Eureka, he encontrado muy útil el siguiente artículo donde el autor explica en detalle las principales propiedades de configuración de Eureka.

Deja un comentario