1. Service Discovery란 무엇인가?
서비스 디스커버리(Service Discovery)는 분산 시스템 환경에서 동적으로 서비스를 등록하고 검색할 수 있는 메커니즘입니다. 마이크로서비스 아키텍처에서 서비스들은 독립적으로 배포되며, 각 서비스의 위치(IP 주소, 포트)는 동적으로 변경될 수 있습니다. 이런 상황에서 클라이언트가 매번 정확한 서비스 위치를 알기는 어렵기 때문에 서비스 디스커버리가 필요합니다.
서비스 디스커버리는 주로 두 가지 방식으로 구현됩니다:
- Client-Side Discovery: 클라이언트가 서비스 레지스트리에서 필요한 서비스를 직접 찾는 방식입니다.
- Server-Side Discovery: 클라이언트가 요청을 하면 서버가 적절한 서비스를 찾아 연결해 주는 방식입니다.
2. Eureka란?
Eureka는 넷플릭스가 개발한 Service Discovery 서버로, 마이크로서비스 아키텍처에서 널리 사용되고 있습니다. Eureka는 각 서비스가 자신의 위치를 Eureka 서버에 등록하고, 다른 서비스가 이 레지스트리에서 필요한 서비스를 검색할 수 있게 해줍니다.
Eureka의 기본 개념은 다음과 같습니다:
- Eureka Server: 서비스 레지스트리 역할을 하며, 각 마이크로서비스는 이 서버에 자신의 정보를 등록합니다.
- Eureka Client: 각 마이크로서비스가 Eureka 서버에 자신을 등록하고, 필요할 때 다른 서비스의 정보를 조회합니다.
- Heartbeat: Eureka Client는 주기적으로 Eureka 서버에 자신이 살아있음을 알리는 신호(하트비트)를 보냅니다.
- Self-preservation Mode: 네트워크가 불안정한 상황에서도 서비스가 삭제되지 않도록 하기 위한 Eureka의 보호 모드입니다.
Eureka는 CAP 이론에서 가용성(Availability)에 중점을 두고 설계되었습니다. 즉, Eureka 서버가 일시적으로 중단되더라도 클라이언트는 기존에 캐시된 서비스 정보를 사용해 서비스를 계속 연결할 수 있습니다.
3. Eureka를 활용한 Service Discovery 예제 (Gradle 프로젝트)
이제 실제로 Eureka Server와 Eureka Client를 사용해 간단한 서비스 디스커버리 시스템을 구현해보겠습니다.
프로젝트 구조
우리는 두 개의 프로젝트를 만듭니다:
- Eureka Server: 서비스 레지스트리를 제공하는 서버.
- Eureka Client: 등록 및 검색을 요청하는 마이크로서비스.
1) Eureka Server 설정
먼저, Eureka Server를 설정하겠습니다.
build.gradle (Eureka Server)
plugins {
id 'org.springframework.boot' version '3.1.0'
id 'io.spring.dependency-management' version '1.1.0'
id 'java'
}
group = 'com.example'
version = '1.0.0'
sourceCompatibility = '17'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-server'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:2023.0.0"
}
}
test {
useJUnitPlatform()
}
EurekaServerApplication.java
package com.example.eurekaserver;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
application.yml
server:
port: 8761
eureka:
client:
register-with-eureka: false
fetch-registry: false
spring:
application:
name: eureka-server
이렇게 Eureka 서버는 포트 8761에서 실행되며, 자체적으로 서비스에 등록하거나 레지스트리를 가져오지 않도록 설정합니다.
2) Eureka Client 설정
이제 Eureka 서버에 등록될 Eureka Client를 설정합니다.
build.gradle (Eureka Client)
plugins {
id 'org.springframework.boot' version '3.1.0'
id 'io.spring.dependency-management' version '1.1.0'
id 'java'
}
group = 'com.example'
version = '1.0.0'
sourceCompatibility = '17'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:2023.0.0"
}
}
test {
useJUnitPlatform()
}
EurekaClientApplication.java
package com.example.eurekaclient;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class EurekaClientApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaClientApplication.class, args);
}
}
application.yml
server:
port: 8081
spring:
application:
name: eureka-client
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
Eureka Client는 포트 8081에서 실행되며, Eureka 서버에 자신의 서비스를 등록합니다.
4. Eureka로 서비스 검색
Eureka에 등록된 다른 서비스의 정보를 가져오기 위해 Spring의 RestTemplate이나 WebClient를 사용할 수 있습니다. 예를 들어, 다른 서비스의 URL을 조회하는 간단한 코드를 작성해봅시다.
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class ServiceDiscoveryController {
private final DiscoveryClient discoveryClient;
public ServiceDiscoveryController(DiscoveryClient discoveryClient) {
this.discoveryClient = discoveryClient;
}
@GetMapping("/discover")
public List<ServiceInstance> discoverServices() {
return discoveryClient.getInstances("eureka-client");
}
}
/discover 엔드포인트로 요청을 보내면, eureka-client라는 이름으로 등록된 모든 인스턴스의 정보를 반환하게 됩니다.
5. 구현 시 주의 사항
- Eureka 서버의 내구성: Eureka 서버는 가용성(Availability)을 중점으로 설계되었지만, 하나의 서버가 단일 장애점(SPOF)이 되지 않도록 다중 인스턴스를 구성하는 것이 좋습니다.
- Heartbeat 주기: Eureka 클라이언트는 일정 주기로 하트비트를 보내야 합니다. 이 주기가 너무 길거나 짧으면 적절한 서비스 상태를 유지하기 어려울 수 있으므로, 환경에 맞게 조정하는 것이 필요합니다.
- Self-preservation 모드 주의: 네트워크가 불안정할 때 잘못된 서비스를 유지할 가능성이 있으므로, Self-preservation 모드가 언제 발동되는지 이해하고 설정을 적절히 관리해야 합니다.
Eureka는 마이크로서비스 간의 동적 연결을 효과적으로 지원하는 도구입니다. Spring Boot와 Eureka를 함께 사용하여 서비스 디스커버리를 구현하면, 서비스가 동적으로 등록되고 검색되며, 시스템이 유연하게 확장 가능합니다. 이를 통해 분산 환경에서도 안정적인 마이크로서비스 아키텍처를 구축할 수 있습니다.
'Spring' 카테고리의 다른 글
Spring Cloud Config로 설정 관리 자동화하기: 개념부터 Private Git 연동까지 (0) | 2024.10.08 |
---|---|
AI 애플리케이션의 미래를 바꿀 Spring AI Advisors를 만나보세요 (0) | 2024.10.08 |
서비스 장애로부터 안전망을 구축하라! Spring Boot에서 Resilience4j로 Circuit Breaker 구현하기 (0) | 2024.10.07 |
Spring Boot 2에서 3으로의 완벽한 마이그레이션 가이드: 새로운 변화와 적용 방법 (0) | 2024.10.04 |
Spring REST Docs vs Swagger: API 문서화를 완벽하게 관리하는 두 가지 방법 (0) | 2024.10.04 |