본문 바로가기
프로그래밍/Spring Cloud

[MSA] Spring Cloud Eureka - 실습편

by 사바라다 2020. 2. 10.

안녕하세요. 오늘은 저번시간에 이어서 Spring Cloud Eureka에 대해서 알아보도록 하겠습니다. 저번시간에는 Eureka의 이론적인 부분에 집중했다면 이번시간에는 실제로 적용해보는 시간을 가지도록 하겠습니다. Client는 Zuul을 사용할 것입니다. Zuul에 대해서 잘 모르시는 분들은 Zuul에 대한 포스팅을 참고해주시기 바랍니다.

프로젝트 구성요소

  • Service( Eureka-Client ) : 실제 로직이 실행되는 서비스
  • Eureka-Server : 서비스들의 정보를 관리하는 Eureka Server
  • Zuul (Eureka-Client) : 실제 서비스로 Routing하는 Edge 서비스

아키텍처

eureka 서비스 로직

  1. MSA를 구성하는 서비스들은 본인의 정보(IP, Port, AppName, instanceID)들을 Eureka Server에 전달
  2. 해당 정보를 사용하고자 하는 Service는 Eureka Server에 정보 요청
  3. 해당 요청 정보를 이용하여 통신

Eureka Server

가장먼저 eureka server를 생성하는 방법을 알아보도록 하겠습니다. eureka server는 서비스들의 정보를 가지고 있는 서버입니다.

가장먼저 기본 Spring Boot 프로젝트를 생성한 후 아래의 dependency를 추가합니다. 아래의 Dependency를 추가하면 Eureka Server를 사용할 수 있게 됩니다.

implementation("org.springframework.cloud:spring-cloud-starter-netflix-eureka-server:2.1.2.RELEASE")

그리고 Java파일을 만들도록 하겠습니다. @EnableEurekaServer Annotation을 달면 바로 Eureka Server로 사용할 수 있게 됩니다.

@EnableEurekaServer
@SpringBootApplication
public class EurekaServiceApplication {

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

아래는 Eureka의 application.yml 파일입니다. register-with-eureka는 eureka의 registry에 등록할지 여부이고, fetch-registry는 registry에 있는 정보를 가져올지 여부입니다. eureka-server는 사용하지 않으므로 false 로 기입해줍니다. 그리고 eureka의 service-url은 보인 server의 url을 적어줍니다.

eureka:
  instance:
    hostname: localhost
  client:
    register-with-eureka: false
    fetch-registry: false
    service-url:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

이렇게 어렵지않게 eureka-server를 생성해보았습니다. 이제 실제 로직이 실행되는 Service를 한번 만들어보겠습니다.

Service

service는 아래와 같은 의존성을 가짐으로써 eureka-client를 구현할 수 있습니다.

implementation("org.springframework.cloud:spring-cloud-starter-netflix-eureka-client:2.1.2.RELEASE")

아래는 실행하는 main과 test를 위해서 /ping을 호출 할 수 있도록 controller를 만들도록 하겠습니다.

@SpringBootApplication
public class OrderApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderApplication.class, args);
    }
}
@RestController
public class HealthCheckController {
    @GetMapping("/ping")
    public ResponseEntity<String> healthCheck() {
        log.info("###health check");
        return ResponseEntity.ok("pong");
    }
}

그리고 service는 아래와 같이 설정파일을 변경해 줍니다.

server: 
  port: 8080

spring: 
  application: 
    name: service-1

eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:8761/eureka/

이렇게 까지 하고 서버를 실행하면 Eureka-server에 Eureka-client인 service가 정보를 지속적으로 전달하게 됩니다. 2개의 서버를 띄운 후 http://localhost:8761에 접속 해보면 Instances currently registered with Eureka에 아래와 같이 1개의 Instance가 등록되어 출력되는 것을 확인할 수 있습니다.

eureka-home

Zuul

마지막으로 Eureka-Server에 등록되어 있는 정보를 가져와 실제 통신에 사용하는 Zuul에 대해서 실습해 보겠습니다. Zuul이 아닌 일반 Client를 사용하고자 하시는 분들은 Ribbon 또는 feign를 사용하시면 됩니다.

implementation "org.springframework.cloud:spring-cloud-starter-netflix-eureka-client:2.1.2.RELEASE"

Service와 동일하게 eureka-client를 의존합니다.

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

@EnableDiscoveryClient를 사용하면 eureka-client에서 fetch한 정보를 통신에 사용할 수 있게 됩니다.

@EnableZuulProxy는 End-Point를 이용한 라우팅을 허용합니다. 이렇게 하면 Eureka-Server의 appId로 Routing이 이루어질 Service를 정할 수 있습니다.

실제로 라우팅되는 Service 설정은 yaml 파일로 진행합니다.


server: 
  port: 8100

zuul:
  routes:
    service-1:
      path: /order/**
      serviceId: service-1

eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:8761/eureka/

yaml 파일은 위와 같이 설정해줍니다. service-1은 우리가 통신하고자 하는 service의 appName입니다. zuul.routes.service-1.path는 zuul의 서비스의 end-point에 /order/** 으로 접근 시 service-1의 서비스로 라우팅 된다는 의미입니다. 그러면 위의 설정을 이용하여 테스트를 진행해보도록 하겠습니다.

통합 테스트

예상되는 시나리오는 http://localhost:8100/order/ping으로 호출하면 zuul에서 service-1의 서비스인 http://localhost:8080/ping를 호출하여 응답을 가져올 것이라는 것입니다.

3개의 서버를 다 띄운 후 테스트해보도록 하겠습니다.

실제 결과는 위와 같이 우리가 예상한 대로 나옵니다. 그렇다면 Zuul에서 Debug 모드로 한번 Log를 출력해보도록 하겠습니다.

2020-02-10 12:03:19.009 [473fa7af3cefeaa0/473fa7af3cefeaa0] DEBUG - [nio-8100-exec-1] c.n.l.DynamicServerListLoadBalancer     [241] : List of Servers for service-1 obtained from Discovery client: [192.168.4.91:8081] 
2020-02-10 12:03:19.020 [473fa7af3cefeaa0/473fa7af3cefeaa0] DEBUG - [nio-8100-exec-1] c.n.l.DynamicServerListLoadBalancer     [246] : Filtered List of Servers for service-1 obtained from Discovery client: [192.168.4.91:8081] 
2020-02-10 12:03:19.021 [473fa7af3cefeaa0/473fa7af3cefeaa0] DEBUG - [nio-8100-exec-1] c.n.l.DynamicServerListLoadBalancer     [179] : Setting server list for zones: {defaultzone=[192.168.4.91:8081]} 

호출 시 local cache에 담겨있는 값중 기 입력했던 serviceId가 있는지 체크하며 있다면 해당 service의 ip와 port를 가져오는 것을 알 수 있었습니다.

2020-02-10 12:03:19.169 [473fa7af3cefeaa0/6ec4819ec96888b2] DEBUG - [RibbonCommand-1] c.n.loadbalancer.LoadBalancerContext    [492] : service-1 using LB returned Server: 192.168.4.91:8081 for request /ping 

그리고 가져온 IP, Port 정보를 이용하여 위와 같이 라우팅으로 실제 서비스를 호출합니다.

마무리

오늘은 이렇게 Eureka를 실제로 Spring 프로젝트에서 사용하는 방법에 대해서 알아보았습니다.

감사합니다.

참조

https://cloud.spring.io/spring-cloud-netflix/reference/html/

댓글