본문 바로가기
IT/Spring Cloud

Spring Cloud 시리즈 4 - Service Discovery (feat. Eureka)

by 최고영회 2023. 5. 30.
728x90
반응형
SMALL

Service Discovery 란?

  • MSA 에서 서비스간 원격 호출 시 각 서비스의 IP/Port 등의 정보를 알아야 한다.
  • Cloud 환경에서 오토스케일등에 의해 동적 생성되거나 컨테이너 기반 배포로 인해 서비스의 IP 가 동적으로 변경된다.
  • Microservice 가 다른 서비스를 호출할 때 해당 서비스의 위치 정보를 알아낼 수 있는 기능이 필요하다.
  • 이것을 바로 서비스 디스커버리 라고 한다.

 

Client side discovery 

  • A Service 의 인스턴스들이 생성될 때 Service registry 에 등록한다.
  • Client 는 Service registry 에 Service A 의 주소를 물어보고 등록된 주소를 받아 호출한다. 
  • 비교적 간단하다. (장)
  • Client 가 사용 가능한 서비스 인서튼스에 대해 알수 있기 때문에 각 서비스별 로드밸런싱 방법을 선택할 수 있다. (장)
  • Client 와 Service registry 간 dependency (단)
  • Client (language, framework이 다를 수 있음)에서 service discovery 로직 (regster 나 discovery)를 구현해야 함 (단)
  • ex) Netflix-Eureka

 

Server side discovery 

  • A Service 의 인스턴스들이 생성될 때 Service registry 에 등록한다.
  • Client 는 Proxy (로드밸런서) 를 호출 한다. (API Gateway 에서 이 역할을 할 수 있다)
  • Proxy 가 Service registry 로 부터 A 서비스 위치를 확인하고 라우팅 한다. 
  • Discovery 기능이 client 로 부터 분리되어 있어 의존성이 낮다. (장)
  • Client (language, framework이 다를 수 있음)에서 service discovery 로직 (regster 나 discovery)를 구현 필요 없다 (장)
  • Kubernetes Cluster, AWS Elastic Load Balancer 등의 환경에서는 server side discovery 를 무료 제공한다. (장)
  • 고가용성 로드밸런서가 배포 환경에 제공되어야 한다. (단)
  • ex) AWS ELB(Elastic Load Banancer), Kubernetes Cluster

 

Service Registry 

  • Service Discovery 의 Key-Point 로 각 서비스 인스턴스의 network location 정보를 포함하는 DB 와 같다. 
  • 높은 가용성이 보장되어야 하며 최신 상태 정보를 유지해야 한다. 
  • 이를 위해 Service discovery 에 전문화된 솔루션인 Eureka, Consul, Zookepper, etcd 등을 이용하는 것이 좋다. 

 


 

Eureka

  • Microservice 들의 정보를 registry 에 등록하여 관리할 수 있도록 한다.
  • Microservice 인스턴스의 증설, 삭제 등을 동적으로 탐색하며 Ribbon 을 이용해 추가적인 로드밸런싱까지 사용할 수 있다.
  • 로드밸런싱과 장애 복구가 가능한 서비스 환경을 구성할때 client 에게 가용한 서비스 인스턴스의 network location 정보를 동적으로 제공할 수 있다.

 

Eureka 구성

  • Eureka Server: Service Registry 서버
    • 등록 정보는 service id 와 url 을 포함한다.
    • microservice 가 시작되면 Eurka server 에 접근해서 service id, url 등의 정보를 자동으로 등록하고 30초 모다 heartbeat ping 을 날린다. 
  • Eureka Client: eurka 에 다른 서비스 위치를 조회하는 서비스
  • Eureka Service: Eureka 에 등록을 요청한 서비스 
  • Eureka Instance: Eureka 에 등록된 조회 가능한 Eureka Service

 

 


Eureka Server

Service registry 역할을 하는 eureka server 를 개발해 보자.

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

Spring initialize project 구성 시 eureka server 를 check 하거나 dependency 에 직접 추가

 

server:
  port: 8881
  
spring:
  application:
    name: yhkim-eureka-server

# 자기 자신을 자동 등록 하지 않도록 false 설정
eureka:
  client:
    register-with-eureka: false
    fetch-registry: false
  server:
    renewal-percent-threshold: 0.85

.yaml  에 필요한 정보를 설정한다. 

 

 

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

@EnableEurekaServer 를 추가해주면 끝

 

 

run 해서 설정한 port 로 접속해 보면 정상적으로 구동된것을 볼 수 있다. 

 

Eureka Client

service registry 에 등록한 client app 에 eureka 를 적용한다고 생각하면 된다.

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

필요한 dependency 를 추가해 주고 

 

spring:
  application:
    name: yhkim-eureka-client-a
server:
  port: 0
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8881/eureka/
  instance:
    instance-id: ${spring.application.name}:${vcap.application.instance_id:${spring.application.instance_id:${random.value}}}

.yaml 에 필요한 정보를 설정해 준다.

application.name 은 eurka server 에서 식별되는 값으로 해당 서비스의 이름을 적어주면 된다.

service-url 의 defaultZone 에 위에서 설정한 eureka server url 을 적어주고

server.port 는 이 서비스의 instance 를 여러개 띄울때 random 하게 띄우기 위해 0 으로 설정했다.

여러개의 instance 가 하나의 application 으로 instance id 만 unique 하게 갖도록 하기 위해 instance-id 를 위와 같이 설정해 주었다.

 

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

@EnableEurekaServer 처럼 @EnableEurekaClient 만 적용하면 쉽게 적용할 수 있다. 

 

 

@RestController
public class TestController {

    @Value("${eureka.instance.instance-id}")
    String instanceId;

    @GetMapping("/a/hello")
    public ResponseEntity<String> testC() {
        return ResponseEntity.ok("Hello Younghoi Kim, it's eureka - " + instanceId);
    }
}

test 를 위해 controller 를 하나 만들어 보자.

 

 

Run 해보면

eureka server 에 registering service... 하고 정상적으로 (204  No-Content) 등록된 것을 볼 수 있다. 

 

3개의 instance 를 run 하면 Eureka server 에서 3개의 instance 가 모두 정상 등록된 것을 확인할 수 있다. 

 

이제 위 서비스(yhkim-eureka-client-a)를 호출하는 다른 Micoreservice (yhkim-eureka-client-b)를 추가해 보자. 

yhkim-eureka-client-a 와 동일하게 dependency 추가하고 @EnableEurekaClient 하고 아래와 같이 controller 를 만들어서 a 서비스를 호출해 보자. 

@RestController @RequiredArgsConstructor @Slf4j
public class TestController {
    final RestTemplate restTemplate;
    final DiscoveryClient discoveryClient;

    @GetMapping("/b/hello")
    public ResponseEntity<String> hello() {
        String baseUrl = "";
        List<ServiceInstance> list = discoveryClient.getInstances("YHKIM-EUREKA-CLIENT-A");
        for (ServiceInstance instance : list) {
            log.info("{}", instance.toString());
        }  
        baseUrl = list.get(0).getUri().toString();
        return ResponseEntity.ok(restTemplate.getForObject(baseUrl + "/a/hello", String.class));
    }
}

eureka 에 등록되어 있는 name 으로 a 서비스를 찾아서 그중 첫번째 instance 로 호출하도록 해 두었다.

a 서비스의 network location 정보를 알 수 없는 상태에서 오토스케일링, 배포 등의 작업이 진행되더라도 b 서비스는 eureka server 를 통해 a 서비스의 위치 정보를 받아 호출이 가능해 졌다.

 

소스 코드를 보면 (baseUrl = list.get(0).getUri());

instance 가 여러개가 있는데 이 중 첫번째 instance 를 가져와 호출하도록 한 상태다. 

 

각 instance 의 상태를 고려하고나 random 하게 또는 로드밸런싱의 기본적인 알고리즘인 라운드 로빈 방식등을 이용해서 

로드밸런싱 하려면 어떻게 해야 할까? 

이건 다음 시간에 Ribbon, Spring Cloud Loadbalancer 를 이용해 확인해 본다. 

728x90
반응형
LIST