Spring Cloud Consul Archives - Piotr's TechBlog https://piotrminkowski.com/tag/spring-cloud-consul/ Java, Spring, Kotlin, microservices, Kubernetes, containers Tue, 20 Oct 2020 13:54:36 +0000 en-US hourly 1 https://wordpress.org/?v=6.9.1 https://i0.wp.com/piotrminkowski.com/wp-content/uploads/2020/08/cropped-me-2-tr-x-1.png?fit=32%2C32&ssl=1 Spring Cloud Consul Archives - Piotr's TechBlog https://piotrminkowski.com/tag/spring-cloud-consul/ 32 32 181738725 A New Era Of Spring Cloud https://piotrminkowski.com/2020/05/01/a-new-era-of-spring-cloud/ https://piotrminkowski.com/2020/05/01/a-new-era-of-spring-cloud/#comments Fri, 01 May 2020 10:14:25 +0000 http://piotrminkowski.com/?p=7973 Almost 1.5 years ago Spring Team announced the decision of moving most of Spring Cloud Netflix components into maintenance mode. It means that new features have no longer been added to these modules beginning from Greenwich Release Train. Currently, they are starting work on Ilford Release Train, which is removing such popular projects like Ribbon, […]

The post A New Era Of Spring Cloud appeared first on Piotr's TechBlog.

]]>
Almost 1.5 years ago Spring Team announced the decision of moving most of Spring Cloud Netflix components into maintenance mode. It means that new features have no longer been added to these modules beginning from Greenwich Release Train. Currently, they are starting work on Ilford Release Train, which is removing such popular projects like Ribbon, Hystrix, or Zuul from Spring Cloud. The only module that will still be used is a Netflix discovery server — Eureka.
This change is significant for Spring Cloud since from beginning it was recognized by its integration with Netflix components. Moreover, Spring Cloud Netflix is still the most popular Spring Cloud project on GitHub (~4k stars).
Simultaneously with announcing a decision about moving Netflix components into maintenance mode, Spring Team has started working on the replacements. And so, Ribbon will be replaced by Spring Cloud Load Balancer, Hystrix by Spring Cloud Circuit Breaker built on top of Resilience4J library. Spring Cloud Gateway which a competitive solution Zuul is already very popular projects, and since Ilford release would be the only option for API gateway.
The main goal of this article is to guide you through building microservices architecture with new Spring Cloud components without deprecated Netflix projects. This article is a continuation of my first article written about one year ago about future of Spring Cloud: The Future Of Spring Cloud Microservices After Netflix Era. The source code of sample applications is available on GitHub in the repository: https://github.com/piomin/course-spring-microservices.git.

Spring Cloud video course

You may also find a video guide, where I’m showing all the features described here with more details. It is available on my YouTube channel and consists of four parts:

Part 1 – Introduction to Spring Boot (~35 minutes)
Part 2 – Distributed Configuration and Service Discovery (~36 minutes)
Part 3 – Inter-service communication (~34 minutes)
Part 4 – API Gateway (~22 minutes)

Architecture

The diagram visible below illustrates the architecture of our sample system. Here we have the characteristic elements for microservices like API gateway, discovery server, and configuration server. In the next sections of this article, I’ll show how to use Spring Cloud components that provide an implementation of those patterns. Currently, the main component for adding the API gateway to your system is Spring Cloud Gateway.
Spring Cloud provides integrations to several solutions that may be used as a discovery server: Netflix Eureka, HashiCorp Consul, Alibaba Nacos, or Apache ZooKeeper. The most common choice is between the first two of them. While Spring Cloud Netflix Eureka is dedicated just for discovery, Spring Cloud Consul may realize both discovery feature basing on Consul Services, and distributed configuration feature basing on Consul Key/Value engine.

In turn, Spring Cloud Config is responsible just for providing a mechanism for configuration management. However, it may also be integrated with third-party tools like Vault from HashiCorp.
We will figure out how to integrate our applications with discovery and configuration servers on the example of two simple Spring Boot applications callme-service and caller-service. The application caller-service is also calling endpoints exposed by the callme-service. We will enable such mechanisms on the caller-service like client-side load balancer with new Spring Cloud Load Balancer, and circuit breaker with new Spring Cloud Circuit Breaker built on top of Resilience4J.

a-new-era-of-spring-cloud

Service discovery

Switching between discovery servers on the client-side is very easy with Spring Cloud due to the DiscoveryClient abstraction. This switch comes down to the replacement of a single dependency in Maven pom.xml. So if you are using Eureka you should add the following starter in your microservice.

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

On the other hand, if you are using Consul you should add the following starter in your microservice.

<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>

The situation is a little bit more complicated if you are defining some non-default configuration settings for a discovery client. In that case, you need to use properties specific just for Eureka, or just for Consul. For example, if you are running more than one instance of a single application on the same host with dynamic HTTP server port feature enabled (option server.port=0), you have to set a unique id of every instance. Here’s the property used for Eureka’s client.

eureka:
  instance:
    instanceId: ${spring.cloud.client.hostname}:${spring.application.name}:${random.value}

For the Consul client the same configuration looks as shown below.

spring:
  cloud:
    consul:
      discovery:
        instanceId: ${spring.cloud.client.hostname}:${spring.application.name}:${random.value}

You can easily configure and run Eureka discovery in your microservices architecture using Spring Cloud Netflix Eureka Server module. You just need to create the Spring Boot application that includes that module.

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

We also need to enable Eureka for the application by annotating the main class with @EnableEurekaServer.

@SpringBootApplication
@EnableEurekaServer
class DiscoveryServerApplication

fun main(args: Array<String>) {
    runApplication<DiscoveryServerApplication>(*args)
}

The most convenient way to run Consul on the local machine is by using its Docker image. We can Consul on Docker container in development mode by executing the following command.

$ docker run -d --name=consul -e CONSUL_BIND_INTERFACE=eth0 -p 8500:8500 consul:1.7.2

Distributed configuration with Spring cloud

The next important element in our architecture is a configuration server. The most popular solution in Spring Cloud that provides mechanisms for distributed configuration is Spring Cloud Config. Spring Cloud Config provides server-side and client-side support for externalized configuration in a distributed system.
With the config server, you have a central place to manage external properties for applications across all environments. Some other solutions may be used in microservices-based architecture as a configuration server: Consul, ZooKeeper, or Alibaba Nacos. However, all these solutions are not strictly dedicated to distributed configuration, they can act as a discovery server as well.
Spring Cloud Config may integrate with different tools for storing data. The default implementation of the server storage backend uses git, but we can use some other tools like HashiCorp Vault for managing secrets and protecting sensitive data or just a simple file system. Such different backends may be together in the single config server application. We just need to activate the appropriate profiles in application properties with spring.profiles.active. We may override some default, for example, change the address of a Vault server or set an authentication token.

spring:
  application:
    name: config-server
  profiles:
    active: native,vault
  cloud:
    config:
      server:
        native:
          searchLocations: classpath:/config-repo
        vault:
          host: 192.168.99.100
          authentication: TOKEN
          token: spring-microservices-course

The same as for Consul we should use Docker to run an instance of Vault in development mode. We can set a static root token for authentication using the environment variable VAULT_DEV_ROOT_TOKEN_ID.

$ docker run -d --name vault --cap-add=IPC_LOCK -e 'VAULT_DEV_ROOT_TOKEN_ID=spring-microservices-course' -p 8200:8200 vault:1.4.0

When using Spring Cloud Config together with the discovery we may choose between two available approaches called Config First Bootstrap and Discovery First Bootstrap. In Discovery First Bootstrap a config server is registering itself in discovery service. Thanks to that each microservice can localize a config server basing on its registration id.
Since a configuration is injected in the bootstrap phase we need to use bootstrap.yml for setting properties on the client-side. To enable “discovering” for the config server on the client side we should set the property spring.cloud.config.discovery.enabled to true. We should also override registered service id of config server if it is different from auto-configured configserver (in our case it is config-server). Of course, we can also use Consul as a configuration properties source.

spring:
  application:
    name: callme-service
  cloud:
    config:
      discovery:
        enabled: true
        serviceId: config-server
    consul:
      host: 192.168.99.100
      config:
        format: YAML

Inter-service communication

Currently, there are three Spring components for inter-service communication over HTTP that have integration with service discovery: synchronous RestTemplate, reactive WebClient and declarative REST client OpenFeign. The RestTemplate component is available within the Spring Web module, WebClient within Spring WebFlux. To include Spring Cloud OpenFeign we need to include a dedicated starter.


<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

To use RestTemplate or WebClient for communication with discovery support, we need to register the beans and annotate them with @LoadBalanced. It is also worth setting the proper timeouts for such communication, especially if you are not using a circuit breaker.

@SpringBootApplication
@EnableFeignClients
class InterCallerServiceApplication {

    @Bean
    @LoadBalanced
    fun template(): RestTemplate = RestTemplateBuilder()
        .setReadTimeout(Duration.ofMillis(100))
        .setConnectTimeout(Duration.ofMillis(100))
        .build()

    @Bean
    @LoadBalanced
    fun clientBuilder(): WebClient.Builder {
        val tcpClient: TcpClient = TcpClient.create()
            .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 100)
            .doOnConnected { conn ->
                conn.addHandlerLast(ReadTimeoutHandler(100, TimeUnit.MILLISECONDS))
            }
        val connector = ReactorClientHttpConnector(HttpClient.from(tcpClient))
        return WebClient.builder().clientConnector(connector)
    }
    
}

Although Ribbon has been moved to maintenance mode almost 1.5 years ago it is still used as a default client-side load balancer in the newest stable version of Spring Cloud. Since Spring Cloud Load Balancer is included in common dependencies it is available in your application. Therefore, the only thing you need to do is to disable Ribbon in the configuration using spring.cloud.loadbalancer.ribbon.enabled property.
Currently, we don’t have many options for load balancer customization. One of them is the ability to configure client cache settings. By default, each client is caching the list of target services and refreshing them every 30 seconds. Such an interval may be too long in your situation.
We can easily change it in the configuration as shown below and set it to for example 1 second. If your load balancer is integrated with Eureka discovery you also need to decrease an interval of the fetching registry, which is by default 30 seconds. After those, both changes your client can refresh the list of currently running services almost immediately.

spring:
  cloud:
    loadbalancer:
      cache:
        ttl: 1s
      ribbon:
        enabled: false
eureka:
  client:
    registryFetchIntervalSeconds: 1

Circuit breaker

The circuit breaker is a popular design pattern used in a microservices architecture. It is designed to detect failures and encapsulates the logic of preventing a failure from constantly recurring. Spring Cloud provides an abstraction for using different circuit breaker implementations. Currently, we may use Netflix Hystrix, Sentinel, Spring Retry, and Resilience4J. To enable Spring Cloud Circuit Breaker based on Resilience4J we need to include the following dependency.

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
</dependency>

Here’s the code responsible for registering Customizer bean, that configures a circuit breaker behavior.

@Bean
fun defaultCustomizer(): Customizer<Resilience4JCircuitBreakerFactory> {
  return Customizer { factory: Resilience4JCircuitBreakerFactory ->
    factory.configureDefault { id: String? ->
      Resilience4JConfigBuilder(id)
        .timeLimiterConfig(TimeLimiterConfig.custom()
          .timeoutDuration(Duration.ofMillis(500))
          .build())
        .circuitBreakerConfig(CircuitBreakerConfig.custom()
          .slidingWindowSize(10)
          .failureRateThreshold(33.3F)
          .slowCallRateThreshold(33.3F)
        .build())
      .build()
    }
  }
}

The settings of the circuit breaker have been visualized in the picture below. The sliding window size sets the number of requests which are used for calculating the error rate. If we have more than 3 errors in the window of size 10 a circuit is open.

circuit-breaker

In the last step, we need to create a circuit breaker instance using Resilience4JCircuitBreakerFactory bean and enable it for the HTTP client as shown below.

@RestController
@RequestMapping("/caller")
class CallerController(private val template: RestTemplate, private val factory: Resilience4JCircuitBreakerFactory) {

  private var id: Int = 0

  @PostMapping("/random-send/{message}")
  fun randomSend(@PathVariable message: String): CallmeResponse? {
    val request = CallmeRequest(++id, message)
    val circuit = factory.create("random-circuit")
    return circuit.run { template.postForObject("http://inter-callme-service/callme/random-call",
      request, CallmeResponse::class.java) }
  }
    
}

Spring Cloud API gateway

The last missing element in our microservices architecture is an API Gateway. Spring Cloud Gateway is the project that helps us in implementing such a component. Currently, it is the second most popular Spring Cloud project just after Spring Cloud Netflix. It has around 2k stars on GitHub. It is built on top of the Spring WebFlux and Reactor project. It works reactively and requires Netty as a runtime framework.
The main goal of API gateway is to hide the complexity of the microservices system from an external client by providing an effective way of routing to APIs, but it can also solve some problems around security or resiliency. The main component used for configuring Spring Cloud Gateway is a route.
It is defined by an ID, a destination URI, a collection of predicates, and a collection of filters. A route is matched if the aggregate predicate is true. With filters, you can modify requests and responses before or after sending the downstream request.
With a predefined set of gateway filters, we may enable such mechanisms like path rewriting, rate limiting, discovery client, circuit breaking, fallback, or routing metrics. To enable all these features on the gateway we first need to include the following dependencies.

<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
   <groupId>org.jetbrains.kotlin</groupId>
   <artifactId>kotlin-reflect</artifactId>
</dependency>
<dependency>
   <groupId>org.jetbrains.kotlin</groupId>
   <artifactId>kotlin-stdlib</artifactId>
</dependency>
<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-circuitbreaker-reactor-resilience4j</artifactId>
</dependency>

To enable all the previously listed features we don’t have to implement much code. Almost everything is configurable in application properties.

spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true 
          lowerCaseServiceId: true
      routes:
        - id: inter-callme-service
          uri: lb://inter-callme-service
          predicates:
            - Path=/api/callme/**
          filters:
            - RewritePath=/api(?/?.*), $\{path}
            - name: RequestRateLimiter
              args:
                redis-rate-limiter.replenishRate: 20
                redis-rate-limiter.burstCapacity: 40
            - name: CircuitBreaker
              args:
                name: sampleSlowCircuitBreaker
                fallbackUri: forward:/fallback/test
        - id: inter-caller-service
          uri: lb://inter-caller-service
          predicates:
            - Path=/api/caller/**
          filters:
            - StripPrefix=1
            - name: RequestRateLimiter
              args:
                redis-rate-limiter.replenishRate: 20
                redis-rate-limiter.burstCapacity: 40
    loadbalancer:
      ribbon:
        enabled: false
  redis:
    host: 192.168.99.100

management:
  endpoints.web.exposure.include: '*'
  endpoint:
    health:
      show-details: always

Some settings still need to be configured in the code. It is a configuration of the circuit breaker, that is based on Resilience4J project, where we need to register the bean Customizer. We also have to define a key for rate-limiting responsible setting a strategy of choosing requests for counting the limits.

@SpringBootApplication
class ApiGatewayApplication {

  @Bean
  fun keyResolver(): KeyResolver = KeyResolver { _ -> Mono.just("1") }

  @Bean
  fun defaultCustomizer(): Customizer<ReactiveResilience4JCircuitBreakerFactory> {
    return Customizer { factory: ReactiveResilience4JCircuitBreakerFactory ->
      factory.configureDefault { id: String? ->
        Resilience4JConfigBuilder(id)
          .timeLimiterConfig(TimeLimiterConfig.custom()
            .timeoutDuration(Duration.ofMillis(500))
            .build())
          .circuitBreakerConfig(CircuitBreakerConfig.custom()
            .slidingWindowSize(10)
            .failureRateThreshold(33.3F)
            .slowCallRateThreshold(33.3F)
            .build())
          .build()
      }
    }
  }

}

fun main(args: Array<String>) {
   runApplication<ApiGatewayApplication>(*args)
}

Conclusion

In this article, you may take a quick introduction to using the latest Spring Cloud components for building microservices architecture. For more details, you may refer to my video course published on YouTube.

The post A New Era Of Spring Cloud appeared first on Piotr's TechBlog.

]]>
https://piotrminkowski.com/2020/05/01/a-new-era-of-spring-cloud/feed/ 4 7973
Microservices with Spring Boot, Spring Cloud Gateway and Consul Cluster https://piotrminkowski.com/2019/11/06/microservices-with-spring-boot-spring-cloud-gateway-and-consul-cluster/ https://piotrminkowski.com/2019/11/06/microservices-with-spring-boot-spring-cloud-gateway-and-consul-cluster/#comments Wed, 06 Nov 2019 09:34:04 +0000 https://piotrminkowski.wordpress.com/?p=7431 The Spring Cloud Consul project provides integration for Consul and Spring Boot applications through auto-configuration. By using the well-known Spring Framework annotation style, we may enable and configure common patterns within microservice-based environments. These patterns include service discovery using Consul agent, distributed configuration using Consul key/value store, distributed events with Spring Cloud Bus, and Consul […]

The post Microservices with Spring Boot, Spring Cloud Gateway and Consul Cluster appeared first on Piotr's TechBlog.

]]>
The Spring Cloud Consul project provides integration for Consul and Spring Boot applications through auto-configuration. By using the well-known Spring Framework annotation style, we may enable and configure common patterns within microservice-based environments. These patterns include service discovery using Consul agent, distributed configuration using Consul key/value store, distributed events with Spring Cloud Bus, and Consul Events. The project also supports a client-side load balancer based on Netflix’s Ribbon and an API gateway based on Spring Cloud Gateway.
In this article I will cover the following topics:

  • Integrating Spring Boot application with Consul discovery
  • Integrating Spring Cloud Gateway with Consul discovery
  • Using Consul KV for distributing configuration across Spring Boot applications
  • Running Consul in a clustered mode
  • Defining virtual zones for microservices and gateway

Microservices Architecture with Spring Cloud Consul cluster

Let’s proceed to the example system built with Spring Cloud Consul cluster support. It consists of four independent microservices. Some of them may call endpoints exposed by the others. The application source code is available on GitHub here: https://github.com/piomin/sample-spring-cloud-consul.git.
In the current example, we will try to develop a simple order system where customers may buy products. If a customer decides to confirm a selected list of products to buy, the POST request is sent to the order-service. It is processed by the Order prepare(@RequestBody Order order) method inside REST controller. This method is responsible for order preparation. First, it calculates the final price, considering the price of each product from the list, customer order history, and their category in the system by calling the proper API method from the customer-service. Then, it verifies if the customer’s account balance is enough to execute the order by calling the account-service, and finally, it returns the calculated price. If the customer confirms the action, the PUT /{id} method is called. The request is processed by the method Order accept(@PathVariable Long id) inside REST controller. It changes the order status and withdraws money from the customer’s account. The system architecture is broken down into the individual microservices hidden behind API gateway as shown here:

spring-cloud-consul-arch

The description created above should give you a big picture of our example system. However, business logic plays a supporting role, technically we have four Spring Boot applications using Consul discovery and KV store communicating with each other through REST APIs. The whole system is hidden for the external client behind the API gateway built on top of the Spring Cloud Gateway. Let’s proceed to the implementation.

1. Building Spring Cloud Consul Microservices

Let’s begin from dependencies. We use the currently newest stable version of Spring Boot – 2.2.0.RELEASE together with Spring Cloud Release Train Hoxton.RC1. The minimal set of required dependencies is to have Spring Web, Actuator (optionally), and Spring Cloud Consul (discovery + config).

<parent>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-parent</artifactId>
   <version>2.2.0.RELEASE</version>
</parent>

<dependencyManagement>
   <dependencies>
      <dependency>
         <groupId>org.springframework.cloud</groupId>
         <artifactId>spring-cloud-dependencies</artifactId>
         <version>Hoxton.RC1</version>
         <type>pom</type>
         <scope>import</scope>
      </dependency>
   </dependencies>
</dependencyManagement>

<dependencies>
   <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-consul-all</artifactId>
   </dependency>
   <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
   </dependency>
   <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-actuator</artifactId>
   </dependency>
</dependencies>

When running the application we will use dynamic listen port number generation feature by setting property server.port to 0. Because we will run more than instance of every service we also need to override default value of spring.cloud.consul.discovery.instance-id which is based on port number that is not applicable when it is set to 0. Here’s our application.yml file for account-service.

spring:  
  cloud:
    consul:
      discovery:
        instance-id: "${spring.cloud.client.hostname}:${spring.application.name}:${random.int[1,999999]}"

server:
  port: 0

The configuration is deployed on Consul, which means we are only having bootstrap.yml file on classpath. If you have both Spring Cloud Consul Discovery and Config dependencies distributed configuration is enabled by default. You only have to override the address of the Consul server if required.

spring:  
  application:
    name: account-service
  cloud:
    consul:
      host: 192.168.99.100
      port: 8500

In the current version of Spring Cloud we don’t have to enable anything, so just need to declare the main class:

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

2. Running Consul Cluster using Docker

In this section how to set up the local environment similar to the production mode. Therefore, we would like to have a scalable, production-grade service discovery infrastructure, consisting of some nodes working together inside the cluster. Consul provides support for clustering based on a gossip protocol used for communication between members and a Raft consensus protocol for a leadership election. I wouldn’t like to go into the details of that process, but some basics about Consul architecture should be clarified.
The first important element is the Consul agent. An agent is a long-running daemon on every member of the Consul cluster. It may be run in either client or server mode. All agents are responsible for running checks and keeping services registered, in different nodes and in sync, globally. Our main goal in this section is to set up and configure the Consul cluster using its Docker image. First, we will start the container, which acts as a leader of the cluster. There is only one difference in the currently used Docker command than for the standalone Consul server. We have set the environment variable CONSUL_BIND_INTERFACE=eth0 in order to change the network address of the cluster agent from 127.0.0.1 to the one available for the other member containers. My Consul server is now running at the internal address 172.17.0.2. To check out what your address is (it should be the same) you may run the command docker logs consul. The appropriate information is logged just after the container startup. Here’s the command that starts the first Consul node:


$ docker run -d --name consul-1 -p 8500:8500 -e CONSUL_BIND_INTERFACE=eth0 consul

Knowledge of that address is very important since now we have to pass it to every member container startup command as a cluster join parameter. We also bind it to all
interfaces by setting 0.0.0.0 as a client address. Now, we may easily expose the client agent API outside the container using the -p parameter:


$ docker run -d --name consul-2 -e CONSUL_BIND_INTERFACE=eth0 -p 8501:8500 consul agent -dev -join=172.17.0.2
$ docker run -d --name consul-3 -e CONSUL_BIND_INTERFACE=eth0 -p 8502:8500 consul agent -dev -join=172.17.0.2

After running two containers with Consul agent, you may check out the full list of cluster members by executing the following command on the leader’s container:

spring-cloud-consul-logs

We can always get the same information using the Consul Web Console.

spring-cloud-consul-ui

We may easily change the default Consul node address for the Spring Boot application by changing configuration properties. Spring Cloud Consul cluster support allows you to define only a single host address and port number of Consul agent. It is worth noting that in normal production mode with multiple machines you would install only a Consul agent on every machine, which is connected with a cluster of Consul servers.

spring:
  application:
    name: customer-service
  cloud:
    consul:
      host: 192.168.99.100
      port: 8501

3. Inter-service Communication

An inter-service communication is performed using OpenFeign declarative REST client. We can also include Spring Cloud Sleuth dependency for propagating correlationId between subsequent calls.

<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>

The OpenFeign client is auto-integrated with service discovery. To use it we need to declare an interface with required methods for communication. The interface has to be annotated with @FeignClient that points to the service using its discovery name.

@FeignClient(name = "account-service")
public interface AccountClient {

   @GetMapping("/customer/{customerId}")
   List<Account> findByCustomer(@PathVariable("customerId") Long customerId);
   
}

Finally, OpenFeign client needs to be enabled for the whole application.


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

4. Enable Zone Affinity Mechanism

When using Spring Cloud Discovery we may take advantage of zones affinity mechanism. If your microservices has been deployed to multiple zones, you may prefer that those services communicate with other services within the same zone before trying to access them in another zone. The same rule applies to API gateway that prefers communication with microservices within the same zone as gateway.
For testing purposes we run two instances of every microservice distributed across two zones: zone1 and zone2. The same with gateway-service. The current architecture of our system looks as shown below.
microservices-consul-2.png
The whole mechanism is enabled through the configuration. We need to set the default zone name for our microservice using property spring.cloud.consul.discovery.instanceZone. I defined two profiles for each application that may be set during startup with --spring.profiles.active command-line argument.

---
spring:
  profiles: zone1
  cloud:
    consul:
      discovery:
        instanceZone: zone1

---
spring:
  profiles: zone2
  cloud:
    consul:
      discovery:
        instanceZone: zone2

Spring Cloud provides a zone affinity mechanism based on Consul tags. If you set spring.cloud.consul.discovery.instanceZone property, Spring Cloud Consul tags a registered instance of service with zone metadata. The name of that tag may be overridden with spring.cloud.consul.discovery.defaultZoneMetadataName property. Assuming you have run two instances of each microservice divided into two zones using the command, for example java -jar --spring.profiles.active=zone1 target/order-service-1.1.jar, you should see the following list of registered services on your Consul instance.

microservices-consul-3

Here’s a more detailed view is the Nodes section that prints all tags and a listen port number for every instance of microservice.

microservices-consul-5

We can also display all running instances of a single service. In the following picture you can see instances of account-service.

microservices-consul-4

5. Building API Gateway with Spring Cloud

Since now, we have succesfully run all the microservices in two instances distributed across two different zones. Because they are all listening on dynamically generated ports we need an API gateway which is exposed on a static port to an external client. Here’s the list of dependencies used for building gateway-service:

<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-consul-all</artifactId>
</dependency>
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

Because we would like to run a gateway on a static port the configuration of Maven profiles is slightly larger than for microservices. We also don’t need to register gateway in Consul discovery, because it is not accessed internally. We will run two instances of gateway, first available under port 8080 in zone1, and second available under port 9080 in zone2. Because it is not registered in Consul discovery we have to manually set a value for zone tag.

---
spring:
  profiles: zone1
  cloud:
    consul:
      discovery:
        instanceZone: zone1
        register: false
        registerHealthCheck: false
        tags: zone=zone1
server:  
  port: ${PORT:8080}

---
spring:
  profiles: zone2
  cloud:
    consul:
      discovery:
        instanceZone: zone2
        register: false
        registerHealthCheck: false
        tags: zone=zone2
server:  
  port: ${PORT:9080}

To enable integration with Consul discovery we need to set property spring.cloud.gateway.discovery.locator.enabled to true. In order to expose service under custom path we should define Path predicate and RewritePath filter for each service. In that case account-service is available under address http://localhost:8080/account/, customer-service under http://localhost:8080/customer/ etc.

spring:
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true
      routes:
        - id: account-service
          uri: lb://account-service
          predicates:
            - Path=/account/**
          filters:
            - RewritePath=/account/(?<path>.*), /$\{path}
        - id: customer-service
          uri: lb://customer-service
          predicates:
            - Path=/customer/**
          filters:
            - RewritePath=/customer/(?<path>.*), /$\{path}
        - id: order-service
          uri: lb://order-service
          predicates:
            - Path=/order/**
          filters:
            - RewritePath=/order/(?<path>.*), /$\{path}
        - id: product-service
          uri: lb://product-service
          predicates:
            - Path=/product/**
          filters:
            - RewritePath=/product/(?<path>.*), /$\{path}

Now you can be sure that each request incoming to gateway-service started in zone1 would be forwarded to in the first place to microservice also started in zone1. And the same for zone2.

6. Distributed Configuration

Consul Config is automatically enabled for the application just after including dependency spring-cloud-starter-consul-config. Of course it is included together with spring-cloud-starter-consul-all also. Configuration is stored in the /config folder by default. We can create the configuration per all applications or just for a single application in a dedicated folder. Assuming we have four microservices and API gateway deployed in two zones we would have to define ten configuration folders. We have different options for storing application properties, but I chose YAML format. YAML must be set in the appropriate data key in consul. So the Consul folders structure for all our sample applications looks as shown below.


config/account-service,zone1/data
config/account-service,zone2/data
config/customer-service,zone1/data
config/customer-service,zone2/data
config/order-service,zone1/data
config/order-service,zone2/data
config/product-service,zone1/data
config/product-service,zone2/data
config/gateway-service,zone1/data
config/gateway-service,zone2/data

Here’s the typical configuration for one our sample microservice running in zone1 zone.

spring:  
  cloud:
    consul:
      discovery:
        instanceId: "${spring.cloud.client.hostname}:${spring.application.name}:${random.int[1,999999]}"
        instanceZone: zone1      
server.port: 0

And the same configuration created on Consul for account-service with active zone1 profile.

microservices-consul-1

In case we use Consul Config for our application the only file that should be available on classpath is bootstrap.yml. Except overriding Consul IP address or port if required we have to set the format of configuration properties to YAML. Here’s bootstrap.yml file for account-service.

spring:  
  application:
    name: account-service
  cloud:
    consul:
      host: 192.168.99.100
      port: 8500
        config:
          format: YAML

Summary

In this article I show you how to run microservices using Spring Consul Discovery and Config in the local environment similar to production. The applications use an affinity mechanism for inter-service communication and integrate with a cluster of Consul nodes. The configuration may be stored on the classpath or externalized to be stored on Consul with division into different active profiles. I think that Consul is a future of Spring Cloud microservices in the post-Netflix era, so it is definitely worth to know Spring Cloud Consul cluster support better. Before starting with Spring Cloud Consul cluster it worth knowing the basics: Quick Guide to Microservices with Spring Boot 2.0 and Spring Cloud.

The post Microservices with Spring Boot, Spring Cloud Gateway and Consul Cluster appeared first on Piotr's TechBlog.

]]>
https://piotrminkowski.com/2019/11/06/microservices-with-spring-boot-spring-cloud-gateway-and-consul-cluster/feed/ 19 7431
Deploying Spring Cloud Microservices on Hashicorp’s Nomad https://piotrminkowski.com/2018/04/17/deploying-spring-cloud-microservices-on-hashicorps-nomad/ https://piotrminkowski.com/2018/04/17/deploying-spring-cloud-microservices-on-hashicorps-nomad/#respond Tue, 17 Apr 2018 11:33:20 +0000 https://piotrminkowski.wordpress.com/?p=6417 Nomad is a little less popular HashiCorp’s cloud product than Consul, Terraform, or Vault. It is also not as popular as competitive software like Kubernetes and Docker Swarm. However, it has its advantages. While Kubernetes is specifically focused on Docker, Nomad is a more general purpose. It supports containerized Docker applications as well as simple […]

The post Deploying Spring Cloud Microservices on Hashicorp’s Nomad appeared first on Piotr's TechBlog.

]]>
Nomad is a little less popular HashiCorp’s cloud product than Consul, Terraform, or Vault. It is also not as popular as competitive software like Kubernetes and Docker Swarm. However, it has its advantages. While Kubernetes is specifically focused on Docker, Nomad is a more general purpose. It supports containerized Docker applications as well as simple applications delivered as an executable JAR files. Besides that, Nomad is architecturally much simpler. It is a single binary, both for clients and servers, and does not require any services for coordination or storage.

In this article I’m going to show you how to install, configure and use Nomad in order to run on it some microservices created in Spring Boot and Spring Cloud frameworks. Let’s move on.

Step 1. Installing and running Nomad

HashiCorp’s Nomad can be easily started on Windows. You just have to download it from the following site https://www.nomadproject.io/downloads.html, and then add nomad.exe file to your PATH. Now you are able to run Nomad commands from your command-line. Let’s begin from starting Nomad agent. For simplicity, we will run it in development mode (-dev). With this option it is acting both as a client and a server. Here’s command that starts the Nomad agent on my local machine.

$ nomad agent -dev -network-interface="WiFi" -consul-address=192.168.99.100:8500
 

Sometimes you could be required to pass selected network interface as a parameter. We also need to integrate agent node with Consul discovery for the purpose of inter-service communication discussed in the next part of this article. The most suitable way to run Consul on your local machine is through a Docker container. Here’s the command that launches single node Consul discovery server and exposes it on port 8500. If you run Docker on Windows it is probably available under address 192.168.99.100.

$ docker run -d --name consul -p 8500:8500 consul
 

Step 2. Creating job

Nomad is a tool for managing a cluster of machines and running applications on them. To run the application there we should first create job. Job is the primary configuration unit that users interact with when using Nomad. Job is a specification of tasks that should be ran by Nomad. The job consists of multiple groups, and each group may have multiple tasks.

There are some properties that has to be provided, for example datacenters. You should also set type parameter that indicates scheduler type. I set type service, which is designed for scheduling long lived services that should never go down, like an application exposing HTTP API.

Let’s take a look on Nomad’s job descriptor file. The most important elements of that configuration has been marked by the sequence numbers:

  1. Property count specifies the number of the task groups that should be running under this group. In practice it scales up number of instances of the service started by the task. Here, it has been set to 2.
  2. Property driver specifies the driver that should be used by Nomad clients to run the task. The driver name corresponds to a technology used for running the application. For example we can set docker, rkt for containerization solutions or java for executing Java applications packaged into a Java JAR file. Here, the property has been set to java.
  3. After settings the driver we should provide some configuration for this driver in the job spec. There are some options available for java driver. But I decided to set the absolute path to the downloaded JAR and some JVM options related to the memory limits.
  4. We may set some requirements for the task including memory, network, CPU, and more. Our task requires max 300 MB of RAM, and enables dynamic port allocation for the port labeled “http”.
  5. Now, it is required to point out very important thing. When the task is started, it is passed an additional environment variable named NOMAD_HOST_PORT_http which indicates the host port that the HTTP service is bound to. The suffix http relates to the label set for the port.
  6. Property service inside task specifies integrations with Consul for service discovery. Now, Nomad automatically registers a task with the provided name when a task is started and de-registers it when the task dies. As you probably remember, the port number is generated automatically by Nomad. However, I passed the label http to force Nomad to register in Consul with automatically generated port.
job "caller-service" {
  datacenters = ["dc1"]
  type = "service"
  group "caller" {
    count = 2 # (1)
    task "api" {
      driver = "java" # (2)
      config { # (3)
        jar_path    = "C:\\Users\\minkowp\\git\\sample-nomad-java-services\\caller-service\\target\\caller-service-1.0.0-SNAPSHOT.jar"
        jvm_options = ["-Xmx256m", "-Xms128m"]
      }
      resources { # (4)
        cpu    = 500
        memory = 300
          network {
          port "http" {} # (5)
          }
      }
      service { # (6)
        name = "caller-service"
        port = "http"
      }
    }
    restart {
      attempts = 1
    }
  }
}
 

Once we saved the content visible above as job.nomad file, we may apply it to the Nomad node by executing the following command.


nomad job run job.nomad
 

Step 3. Building sample microservices

Source code of sample applications is available on GitHub in my repository sample-nomad-java-services. There are two simple microservices callme-service and caller-service. I have already use that sample for in the previous articles for showing inter-service communication mechanism. Microservice callme-service does nothing more than exposing endpoint GET /callme/ping that displays service’s name and version.

@RestController
@RequestMapping("/callme")
public class CallmeController {

private static final Logger LOGGER = LoggerFactory.getLogger(CallmeController.class);

   @Autowired
   BuildProperties buildProperties;

   @GetMapping("/ping")
   public String ping() {
      LOGGER.info("Ping: name={}, version={}", buildProperties.getName(), buildProperties.getVersion());
      return buildProperties.getName() + ":" + buildProperties.getVersion();
   }

}
 

Implementation of caller-service endpoint is a little bit more complicated. First, we have to connect our service with Consul in order to fetch a list of registered instances of callme-service. Because we use Spring Boot for creating sample microservices, the most suitable way to enable Consul client is through the Spring Cloud Consul library.

<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
 

We should override auto-configured connection settings in application.yml. In addition to host and property we have also set spring.cloud.consul.discovery.register property to false. We don’t want discovery client to register application in Consul after startup, because it has been already performed by Nomad.

spring:
  application:
    name: caller-service
  cloud:
    consul:
      host: 192.168.99.100
      port: 8500
    discovery:
      register: false
 

Then we should enable Spring Cloud discovery client and RestTemplate load balancer in the main class of application.

@SpringBootApplication
@EnableDiscoveryClient
public class CallerApplication {

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

   @Bean
   @LoadBalanced
   RestTemplate restTemplate() {
      return new RestTemplate();
   }

}
 

Finally, we can implement method GET /caller/ping that call endpoint exposed by callme-service.

@RestController
@RequestMapping("/caller")
public class CallerController {

   private static final Logger LOGGER = LoggerFactory.getLogger(CallerController.class);

   @Autowired
   BuildProperties buildProperties;
   @Autowired
   RestTemplate restTemplate;

   @GetMapping("/ping")
   public String ping() {
      LOGGER.info("Ping: name={}, version={}", buildProperties.getName(), buildProperties.getVersion());
      String response = restTemplate.getForObject("http://callme-service/callme/ping", String.class);
      LOGGER.info("Calling: response={}", response);
      return buildProperties.getName() + ":" + buildProperties.getVersion() + ". Calling... " + response;
   }

}
 

As you probably remember the port of application is automatically generated by Nomad during task execution. It passes an additional environment variable named NOMAD_HOST_PORT_http to the application. Now, this environment variable should be configured inside application.yml file as the value of server.port property.

server:
  port: ${NOMAD_HOST_PORT_http:8090}
 

The last step is to build the whole project sample-nomad-java-services with mvn clean install command.

Step 4. Using Nomad web console

During two previous steps, we have created, build, and deployed our sample applications on Nomad. Now, we should verify the installation. We can do it using CLI or by visiting the web console provided by Nomad. The web console is available under address http://localhost:4646.

In the main site of the web console, we may see the summary of existing jobs. If everything goes fine field status is equal to RUNNING and bar Summary is green.

nomad-1

We can display the details of every job on the list. The next screen shows the history of the job, reserved resources, and the number of running instances (tasks).

nomad-2

If you would like to check out the details related to the single task, you should navigate to Task Group details.

nomad-3

We may also display the details related to the client node.

nomad-4

To display the details of allocation select the row in the table. You would be redirected to the following site. You may check out there an IP address of the application instance.

nomad-5

Step 5. Testing a sample system

Assuming you have succesfully deployed the applications on Nomad you should see the following services registered in Consul.

nomad-6

Now, if you call one of two available instances of caller-service, you should see the following response. The address of callme-service instance has been successfully fetched from Consul through Spring Cloud Consul Client.

nomad-7

The post Deploying Spring Cloud Microservices on Hashicorp’s Nomad appeared first on Piotr's TechBlog.

]]>
https://piotrminkowski.com/2018/04/17/deploying-spring-cloud-microservices-on-hashicorps-nomad/feed/ 0 6417