
Service Discovery란
MSA환경에서 동적으로 변경되는 서비스들의 위치(IP, 포트)를 자동으로 찾고 연결하는 기술을 의미
기존 모놀리식 아키텍처에서는 모든 기능이 하나의 애플리케이션에 포함되어 있기때문에 내부에서 직접 호출함으로써 Service Discovery와 같은 역할이 필요하지 않았음
MSA는 서비스가 여러개로 분리되어있기때문에 서비스 간의 네트워크 통신이 필요하게 됨
만약 Service Discovery가 없는 MSA라면 서비스가 변경될 때마다 수동으로 IP를 변경해야하고 이는 운영이 매우 복잡해지며 장애 발생 가능성이 증가하게 된다.
Discovery Service 없이 MSA를 사용할 경우
예를 들어서 기존 MSA서비스에서 UserService와 ProductService만 운영해왔었는데 OrderService를 추가한다고 가정해보자면,
OrderService 애플리케이션을 추가하기위해서는 새로운 IP와 PORT가 할당될텐데 OrderService하나만을 수동으로 등록한다면 큰 어려움은 없겠지만 MSA의 특성상 수십 수백개의 서비스들이 추가될텐데 이를 수동으로 관리하기에는 각각의 MSA서비스들간의 통신을 위해서라도 모든 서비스에 OrderService의 IP와 PORT를 등록해주어야하는 큰 어려움이 있음.
Discovery Server를 사용하면 새로 추가되는 서비스를 IP와 PORT번호로 식별하는 것이 아니라 해당 서비스의 고유 이름을 부여함으로써 IP와 PORT대신 이름으로 서비스를 식별할 수 있음.

위 그림은 이해를 돕기위한 이미지입니다.
Discovery Server가 클라이언트의 요청을 받아 Proxy역할을 하는 듯 보이지만, 실제로는 요청을 서비스에 직접 보내는 역할을 수행하지 않고 Eureka Server에 등록된 MSA 서비스의 정보를 반환하는 역할만 수행합니다.
Disovery Server와 API Gateway
일반적으로 MSA를 구현한다고 할 때 Discovery Server와 API Gateway는 항상 함께 사용되는 햄버거와 콜라와도 같다.
그렇다면 API Gateway를 사용하는 이유는 무엇이고 API Gateway없이 Discovery Server만으로 MSA를 구현하기위해서는 클라이언트의 API 요청이 API Gateway를 거치지 않고 호출하고자하는 서비스에 직접 요청을 하게되고 이는 서비스들이 공통적으로 필요로하는 인증처리, 로깅과 같은 공통로직을 모든 서비스들에 구현을 해주어야하고 이는 코드의 중복으로 이어지며 비효율적이라고 할 수 있다.
Discovery Server만으로 MSA를 구현한다고 했을때는 아래의 코드와 같이 구현함으로써 Discovery Server에 등록된 MSA서비스 API를 사용할 수 있다.
OnlyDiscoveryService.application.yml
spring:
application:
name: only-discovery-service
eureka:
instance:
instance-id: ${spring.application.name}:${spring.application.instance_id:${random.value}}
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
- name: Discovery Server의 등록될 이름
- instance-id: 서비스로부터 생성될 인스턴스의 이름 MSA의 서비스들은 많은 인스턴스들을 생성하기때문에
random.value
를 추가 - serviceUrl-defaultZone: 서비스를 등록하고자하는 Eureka 서버의 경로
@Component
public class RestTemplateConfig {
@Bean
@LoadBalanced public RestTemplate restTemplate() {
return new RestTemplate();
}
}
@LoadBalanced
어노테이션을 RestTemplate
에 붙혀줌으로써 RestTemplate Bean은 Discovery Server에 등록되어있는 서비스에 요청을 보내게된다.
@RequestMapping
@RestController
public class TestController {
private RestTemplate restTemplate;
@Autowired
public TestController(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
@GetMapping("/test")
public String get() {
return restTemplate.getForObject("http://user-service/health_check", String.class);
}
}
이제 OnlyDiscoveryService에 /test요청을 보내면 Discovery Server에 등록된 user-service의 /health_check
를 호출하여 응답을 받을 수 있다.
요청경로를 자세히 살펴보면 도메인이 Discovery Server에 등록된 user-serivce임을 확인할 수 있다.
하지만 이렇게 API Gateway를 사용하지 않을 경우 위에서 설명했듯이 MSA 서비스들의 공통로직을 처리해주어야한다는 불편함이 있기때문에 API Gateway와 함께 사용하는 것이 일반적이다.
동작방식
클라이언트 사이드 디스커버리
- 클라이언트(서비스 호출자)가 직접 서비스 레지스트리에서 서비스의 위치를 조회한 후 요청을 보냄
- 로드 밸런싱도 클라이언트가 직접 수행
- 예시) Netflix Eureka, ZooKeeper
- 장점
- 로드 밸런싱이 서비스 호출자에서 수행되어 유연성이 높음
- 서비스 간 직접적인 통신가능
- 단점
- 클라이언트가 서비스 위치를 조회하는 로직을 포함해야함
- 서비스 레지스트리가 다운되면 조회불가
서버 사이드 디스커버리
- API Gateway나 Load Balancer가 서비스 위치를 조회하여 요청을 라우팅
- 장점
- 클라이언트가 서비스 위치를 알 필요가 없음
- 로드 밸런서가 요청을 자동으로 분배
- 단점
- 로드밸런서에게 부하가 집중될 수 있음
Discovery Service 생성
Discovery Server를 생성하기 위해서는 유레카 서버 의존성을 필요로한다.implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-server'
위 의존성이 추가된 애플리케이션은 Discovery Server의 역할을 수행한다고 이해하면 된다.
또한 애플리케이션 진입점 클래스인 메인 클래스에 @EnableEurekaServer
어노테이션을 추가해주어야한다.
Eureka properties에 대한 설정은 아래의 공식문서를 확인할 수 있다.
https://docs.spring.io/spring-cloud-netflix/reference/configprops.html
참고자료
Spring MSA 강의 - https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%81%B4%EB%9D%BC%EC%9A%B0%EB%93%9C-%EB%A7%88%EC%9D%B4%ED%81%AC%EB%A1%9C%EC%84%9C%EB%B9%84%EC%8A%A4
Netflix Eureka GitHub - https://github.com/Netflix/eureka
Spring Cloud Eureka Document - https://spring.io/projects/spring-cloud-netflix
Spring Cloud Eureka GitHub - https://github.com/spring-cloud/spring-cloud-netflix
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!