Spring Cloud로 구현하는 MSA 인프라 아키텍처 - (1) Service Registry & Discovery

KIM Jongwan·2025년 12월 3일

1. 왜 Service Registry가 필요한가?

모놀리식 아키텍처에서는 하나의 애플리케이션이 단일 배포 단위로 묶여 있다.
애플리케이션 프로세스가 하나고, 보통 고정된 포트에서 동작하기 때문에 “서비스 위치”를 관리하는 것은 어렵지 않다.

[단일 애플리케이션] → http://app.example.com:8080

반면 MSA(Microservices Architecture)에서는 도메인 중심으로 여러 개의 독립적인 서비스가 분리되고, 각 서비스가 각각의 배포 단위를 가진다. 여기에 클라우드 컴푸팅 기술이 더해지면 다음과 같은 특징이 생긴다.

  • 서비스 인스턴스 수가 동적으로 증가하거나 감소

  • 각 인스턴스의 IP / Port가 고정되어 있지 않음

  • 배포·롤링 업데이트마다 인스턴스 구성이 지속적으로 변경

규모가 작은 환경에서는 환경 변수나 설정 파일에 서비스 URL을 직접 적어 두는 방식으로 어느 정도 버틸 수 있다.
하지만 실제 아마존이나 넷플릭스처럼 수백~수천 개의 마이크로서비스를 운영하는 환경에서는

“모든 서비스 인스턴스의 IP/Port를 사람이 직접 관리한다” 는 것은 사실상 불가능하다.

이런 배경 때문에 등장한 것이 Service Registry / Service Discovery 패턴이다.


2. Service Registry & Service Discovery 패턴 정리

2.1 Service Registry (서버 레지스트리)

Service Registry(서비스 레지스트리)는 마이크로서비스 인스턴스의

  • IP / Port
  • 서비스 이름
  • 메타데이터(환경, 버전, zone 등)

를 중앙에서 저장하고, 각 인스턴스를 서비스 이름으로 식별할 수 있도록 매핑 정보를 관리하는 컴포넌트다.

간단히 말해:

“catalog-service라는 이름으로 어떤 인스턴스들이 어디에서 떠 있는지”
를 알고 있는 전화번호부 역할을 한다.

2.2 Service Discovery (서버 디스커버리)

Service Discovery(서비스 디스커버리)는 클라이언트가

“order-service야, catalog-service로 요청 하나 보내줘.”

라고 서비스 이름만으로 요청을 보냈을 때,

  • Service Registry에서 실제 인스턴스 목록을 조회하고

  • 헬스 체크 / 로드밸런싱 정책을 적용해

  • 어느 인스턴스로 보낼지 결정하는 메커니즘을 의미한다.

즉, 클라이언트 입장에서는 더 이상 http://10.0.1.23:8080 같은 구체적인 주소를 알 필요가 없고, “catalog-service에게 보내라” 는 논리적인 이름만 알고 있으면 된다.


3. Spring Eureka 소개

넷플릭스는 자사 인프라에서 이 문제를 해결하기 위해 Eureka라는 이름의 Service Registry & Service Discovery 서버를 개발해 사용해 왔다.

이후 Spring 진영에서는 이를 손쉽게 사용할 수 있도록 Spring Cloud Netflix 프로젝트에 통합(2015.03)했고, Spring Boot 애플리케이션에서도 Eureka 기반의 서비스 등록·조회 기능을 쉽게 사용할 수 있게 되었다.

Eureka는 크게 두 가지 역할로 나뉜다.

  • Eureka Server

    • Service Registry 역할

    • 서비스 인스턴스 정보(IP, Port, 상태 등)를 저장하고 관리

  • Eureka Client

    • 실제 비즈니스 로직을 수행하는 각 마이크로서비스

    • 기동 시 자신을 Eureka Server에 등록하고, 다른 서비스 위치가 필요할 때 Eureka로부터 조회

Spring Boot에서의 의존성은 대략 다음과 같이 추가한다.

// Eureka Server
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-server'

// Eureka Client
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'

4. Eureka Server 기본 구성

4.1 서버 애플리케이션 만들기

먼저 Eureka Server로 동작할 Spring Boot 애플리케이션을 하나 만든다.
여기에 @EnableEurekaServer 애노테이션을 추가하면 된다.

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

이제 설정 파일(application.yml)에서 서버 포트와 기본 설정을 잡아준다.

server:
  port: 8761

spring:
  application:
    name: eureka-server

4.2 단일 Eureka Server로 동작시키기

Eureka Server는 내부적으로도 Eureka Client 역할을 수행한다. 즉, 다른 Eureka Server에 자신을 등록하거나, 레지스트리를 조회할 수 있다.

하지만 단일 레지스트리 서버로만 사용할 때는 자기 자신을 다시 Eureka에 등록할 필요가 없기 때문에 Client 기능을 끄는 것이 일반적이다.

eureka:
  client:
    # 서버 자신을 Eureka에 등록하지 않음
    register-with-eureka: false
    # 다른 레지스트리로부터 레지스트리를 가져오지 않음
    fetch-registry: false

register-with-eureka: false

  • “나는 다른 Eureka Server에 등록하지 않겠다”

fetch-registry: false

  • “다른 Eureka Server로부터 레지스트리를 가져오지 않겠다”

5. Eureka Server가 Client 역할을 하는 이유

그렇다면 왜 Eureka Server도 Client 역할을 할 수 있도록 설계되어 있을까?

고가용성(HA)이 필요한 환경에서는 Eureka Server 역시 다중 인스턴스로 구성하고, 각 서버가 서로를 peer로 인식해서 레지스트리를 복제한다.

서버가 1대만 있으면 장애 시 전체 시스템에 큰 영향을 미친다.
서버를 2대 이상 두고 서로 레지스트리를 동기화하면,

한 대가 장애가 나더라도 나머지 서버가 계속 Service Registry 역할을 수행할 수 있다.

이를 위해 Eureka Server도 Eureka Client로서 동작하면서 서로를 service-url.defaultZone에 등록하는 구조를 사용한다.


6. 자주 사용하는 Eureka Server 설정

6.1 인스턴스/헬스 체크 관련 설정 (eureka.instance)

Eureka Server 자체도 “하나의 서비스 인스턴스”이기 때문에,
다른 서비스들과 마찬가지로 eureka.instance 설정을 사용할 수 있다.

eureka:
  instance:
    hostname: eureka-server-1           # 실제 환경에 맞는 호스트/DNS
    # prefer-ip-address: true           # 필요 시 IP 기반으로 등록
    health-check-url-path: /actuator/health
    status-page-url-path: /actuator/info
    lease-renewal-interval-in-seconds: 30
    lease-expiration-duration-in-seconds: 90
  • hostname

    • Eureka에 등록될 때 사용할 호스트 이름

    • 실제로는 보통 클라이언트 서비스에서 도메인 기반 등록을 할 때 더 많이 활용

  • health-check-url-path

    • Eureka가 인스턴스 상태를 확인하기 위해 호출하는 URL

    • Spring Boot Actuator를 사용하면 /actuator/health로 지정하는 것이 일반적

  • status-page-url-path

    • 상태 페이지 URL (Eureka 대시보드에서 링크로 노출)
  • lease-renewal-interval-in-seconds

    • 클라이언트가 Eureka에 하트비트(heartbeat)를 보내는 주기

    • 기본 30초 수준. 너무 짧게 줄이면 트래픽 부담 증가

  • lease-expiration-duration-in-seconds

    • 이 시간 동안 하트비트가 오지 않으면, Eureka가 해당 인스턴스를 죽었다고 판단하는 시간

6.2 자기 보호 모드 설정 (eureka.server)

Eureka Server는 네트워크 이슈 등으로 인해 하트비트가 갑자기 줄어드는 경우에도,
정상 인스턴스를 함부로 레지스트리에서 제거하지 않도록 자기 보호 모드(Self-Preservation)를 제공한다.

eureka:
  server:
    enable-self-preservation: true
    renewal-percent-threshold: 0.85
    eviction-interval-timer-in-ms: 60000
  • enable-self-preservation

    • 기본값은 true이며, 자기 보호 모드를 활성화한다.

    • 네트워크 장애 등으로 하트비트가 일시적으로 급감하더라도 일정 기준까지는 인스턴스를 바로 제거하지 않는다.

    • 운영 환경에서는 보통 true 유지가 일반적이고, 개발 환경에서는 디버깅을 위해 false로 두기도 한다.

  • renewal-percent-threshold

    • 예상되는 정상 하트비트 대비, 어느 정도 비율 이상이 유지되어야 “정상”으로 볼 것인지 기준
  • eviction-interval-timer-in-ms

    • 만료된 인스턴스를 레지스트리에서 실제로 제거하는 주기 (기본 60초 수준)

개발 단계에서 인스턴스를 자주 재기동하면, “이미 죽은 인스턴스가 대시보드에서 한동안 남아 있는” 현상을 볼 수 있는데, 대부분 이 self-preservationeviction 주기 설정 때문에 발생한다.


7. Eureka Server 다중 인스턴스(peer) 구성 예시

Eureka Server를 2대로 구성하는 예시를 보자.
각 인스턴스는 서로를 peer로 등록해 레지스트리를 복제한다.

7.1 eureka-server-1 설정

spring:
  application:
    name: eureka-server

server:
  port: 8761

eureka:
  instance:
    hostname: eureka-server-1
    lease-renewal-interval-in-seconds: 30
    lease-expiration-duration-in-seconds: 90

  client:
    # peer 구성이므로 두 옵션 모두 true
    register-with-eureka: true
    fetch-registry: true
    service-url:
      # "내가 등록/조회할 대상" → 반대편 서버 주소
      defaultZone: http://eureka-server-2:8762/eureka/

  server:
    enable-self-preservation: true
    eviction-interval-timer-in-ms: 60000

7.2 eureka-server-2 설정

spring:
  application:
    name: eureka-server

server:
  port: 8762  # 1번과 다른 포트 (LB 앞에 두면 동일 포트도 가능)

eureka:
  instance:
    hostname: eureka-server-2
    lease-renewal-interval-in-seconds: 30
    lease-expiration-duration-in-seconds: 90

  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      # 이 서버는 1번 서버를 peer로 본다
      defaultZone: http://eureka-server-1:8761/eureka/

  server:
    enable-self-preservation: true
    eviction-interval-timer-in-ms: 60000

두 설정의 핵심은 다음 한 줄이다.

eureka.client.service-url.defaultZone
  • “이 인스턴스가 등록/조회할 Eureka Server는 어디인가?” 를 지정한다.

  • peer 구성에서는 서로의 주소를 defaultZone에 등록하여 레지스트리 정보를 양방향으로 동기화한다.


8. 정리

여기까지 정리한 내용을 다시 한 번 요약해보면:

  • MSA & 클라우드 환경에서는 서비스 인스턴스의 IP/Port가 동적으로 변하기 때문에 사람이나 설정 파일만으로는 서비스 위치 관리가 불가능에 가깝다.

  • 이를 해결하기 위해 Service Registry / Service Discovery 패턴이 등장했고, 넷플릭스는 이를 구현한 Eureka를 운영해 왔다.

  • Spring에서는 Spring Cloud Netflix Eureka를 통해 Spring Boot 애플리케이션에서도 손쉽게 Eureka 기반 서비스 등록/조회 기능을 제공한다.

  • Eureka는

    • Eureka Server: 레지스트리 관리, 서비스 등록/조회
    • Eureka Client: 실제 비즈니스 서비스, 자신 등록 + 다른 서비스 조회
      로 역할이 나뉜다.
  • Eureka Server는

    • 단일 서버 모드에서는 register-with-eureka=false, fetch-registry=false
    • 다중 서버(HA) 구성에서는 서로를 peer로 등록하여 레지스트리를 복제한다.
  • 실제 운영 환경에서는

    • health-check-url-path, lease-interval, self-preservation 등
      몇 가지 핵심 옵션을 이해하고 적절히 튜닝하는 것이 중요하다.

0개의 댓글