서비스 디스커버리 (Service Discovery)와 Eureka

핵심 인사이트 (3줄 요약)

  1. 본질: 서비스 디스커버리 (Service Discovery)는 클라우드 기반 마이크로서비스 아키텍처(MSA)에서 동적으로 생성되고 소멸하는 수많은 인스턴스의 IP 주소와 포트 정보를 중앙에서 동적으로 관리하고 라우팅할 수 있게 해주는 "동적 전화번호부" 역할이다.
  2. 가치: 인스턴스의 IP가 하드코딩될 경우 오토스케일링(Auto-scaling)과 장애 복구 시 통신이 단절되는 문제를 해결하며, 클라이언트가 논리적인 서비스 이름만으로 통신할 수 있게 하여 MSA의 인프라 결합도를 획기적으로 낮춘다.
  3. 융합: 과거에는 넷플릭스 유레카(Netflix Eureka) 같은 애플리케이션 레벨의 서비스 레지스트리가 주류였으나, 최근에는 쿠버네티스(Kubernetes)의 DNS 기반 내장 디스커버리나 Istio 같은 서비스 메시(Service Mesh) 계층으로 디스커버리 책임이 하방 이동(Shift-down)하고 있다.

Ⅰ. 개요 및 필요성 (Context & Necessity)

  • 개념: 서비스 디스커버리는 네트워크 상에 존재하는 마이크로서비스들의 네트워크 위치(IP 주소와 Port)를 등록(Registration)하고 검색(Discovery)하는 메커니즘이다. 주로 **서비스 레지스트리(Service Registry)**라는 데이터베이스를 통해 구현된다.

  • 필요성: 전통적인 온프레미스(On-premise) 환경에서는 서버의 IP와 Port가 고정되어 있어 설정 파일에 IP를 하드코딩해도 문제가 없었다. 하지만 클라우드 환경에서는 트래픽에 따라 인스턴스가 자동으로 늘어나거나 줄어들고(Auto-scaling), 컨테이너가 죽으면 새로운 IP를 할당받아 다시 뜨는 등 네트워크 정보가 끊임없이 변한다. 만약 서비스 A가 서비스 B를 호출할 때 B의 IP를 하드코딩해 두었다면, B의 IP가 바뀔 때마다 A의 코드를 수정하고 재배포해야 하는 치명적인 운영 한계에 봉착한다.

  • 💡 비유: 친구의 집이 매일 이사를 다녀 주소가 계속 바뀐다고 상상해보자. 내가 친구를 만나려면 매일 바뀐 주소를 알아내야 한다(하드코딩의 문제). 하지만 친구가 매일 아침 '동사무소(서비스 레지스트리)'에 바뀐 주소를 등록하고, 나는 동사무소에 전화해서 "길동이네 주소 알려주세요"라고 물어본 뒤 찾아가면(서비스 디스커버리) 주소가 아무리 바뀌어도 쉽게 친구를 만날 수 있다.

  • 등장 배경 및 발전 과정:

    1. 고정 IP 시대: DNS와 L4/L7 하드웨어 로드밸런서를 통한 정적 라우팅. 서버 증설 시 수동으로 설정 변경 필요.
    2. 클라이언트 사이드 디스커버리 (Netflix OSS): 넷플릭스가 클라우드로 이전하면서 동적 인스턴스 관리를 위해 Eureka를 개발. 클라이언트가 직접 레지스트리(Eureka)를 조회하고 소프트웨어 로드밸런싱(Ribbon)을 수행하는 방식이 유행함 (Spring Cloud Netflix).
    3. 서버/인프라 사이드 디스커버리 (Kubernetes/Service Mesh): 디스커버리를 개발자가 코드로 제어하는 것은 부담스럽다는 인식이 퍼지면서, 쿠버네티스의 Service 리소스(CoreDNS)나 Envoy 프록시 같은 인프라 계층에서 투명하게 처리하는 방식으로 진화함.
  • 📢 섹션 요약 비유: 옛날에는 수첩에 식당 전화번호를 직접 적어두었다면, 지금은 '배달 앱(서비스 레지스트리)'을 켜서 '치킨집(서비스 이름)'을 검색하면 현재 배달 가능한 가장 가까운 치킨집으로 자동으로 연결해주는 것과 같습니다.


Ⅱ. 아키텍처 및 핵심 원리 (Deep Dive)

구성 요소

요소명역할내부 동작관련 기술비유
서비스 레지스트리 (Service Registry)서비스 인스턴스의 네트워크 위치(IP, Port) 데이터베이스메모리에 인스턴스 정보 저장, 하트비트 수신Eureka Server, Consul, Zookeeper동적 114 전화번호부
서비스 제공자 (Service Provider)자신의 존재를 레지스트리에 등록시작 시 IP/Port 등록, 주기적으로 Heartbeat 전송Eureka Client (Provider)전화번호부에 번호 등록하는 식당
서비스 소비자 (Service Consumer)레지스트리에서 호출할 서비스의 주소를 검색레지스트리 정보를 캐싱하고, 클라이언트 로드밸런싱 수행Eureka Client, Ribbon(과거), Spring Cloud LoadBalancer식당 번호를 검색하는 손님

서비스 디스커버리의 2가지 아키텍처 패턴

서비스 디스커버리는 누가 라우팅을 결정하느냐에 따라 클라이언트 사이드와 서버 사이드로 나뉜다.

  1. 클라이언트 사이드 디스커버리 (Client-Side Discovery)
    • 클라이언트 애플리케이션 자체가 서비스 레지스트리(예: Eureka)에 질의하여 가용한 인스턴스 목록을 받아온다.
    • 클라이언트 내부에 내장된 로드밸런서(Client-side Load Balancer) 알고리즘(라운드 로빈 등)을 통해 최종 호출할 IP를 스스로 결정한다.
  ┌───────────────────────────────────────────────────────────────┐
  │        클라이언트 사이드 디스커버리 (Eureka 방식)                 │
  ├───────────────────────────────────────────────────────────────┤
  │                                                               │
  │                              [3] "User Svc" 목록 요청         │
  │                              [4] IP 목록 반환 (10.0.0.1, .2)  │
  │                            ◀─────────────────────             │
  │  ┌──────────────────────┐                     ┌─────────────┐ │
  │  │ Order Service        │                     │             │ │
  │  │ (Consumer)           │                     │             │ │
  │  │   ┌───────────────┐  │────────────────────▶│ Eureka      │ │
  │  │   │ Client Load   │  │   [1] 서비스 등록     │ Server      │ │
  │  │   │ Balancer      │  │                     │ (Registry)  │ │
  │  │   └───────┬───────┘  │                     │             │ │
  │  └───────────┼──────────┘                     └──────▲──────┘ │
  │              │ [5] 내부 로드밸런싱 후 통신                   │        │
  │              │     (예: 10.0.0.2로 직접 호출)             │        │
  │              │                                       │        │
  │              ▼                                       │        │
  │      ┌────────────────┐                      ┌───────┴──────┐ │
  │      │ User Svc (#1)  │                      │ User Svc (#2)│ │
  │      │ (10.0.0.1)     │                      │ (10.0.0.2)   │ │
  │      └────────────────┘                      └──────────────┘ │
  │                           [2] 나 살아있음! (Heartbeat)          │
  └───────────────────────────────────────────────────────────────┘

[다이어그램 해설] User Service 인스턴스들은 구동될 때 자신의 IP를 Eureka Server에 등록하고, 주기적으로 Heartbeat를 보내 생존을 알린다. Order ServiceUser Service를 호출하려 할 때, Order Service 내부에 탑재된 클라이언트 라이브러리가 Eureka에 "User Svc 목록 줘"라고 질의하여 IP 목록(10.0.0.1, 10.0.0.2)을 받아온다. Order Service 내부의 클라이언트 로드밸런서가 어떤 IP로 요청을 보낼지 결정하여 직접 HTTP 요청을 보낸다. (장점: 병목이 없다 / 단점: 클라이언트마다 디스커버리 라이브러리를 설치해야 하므로 언어 종속성이 강하다)

  1. 서버/인프라 사이드 디스커버리 (Server-Side / 3rd Party Discovery)
    • 클라이언트는 서비스 레지스트리의 존재조차 알 필요가 없다. 클라이언트는 논리적인 DNS 이름(예: http://user-service)으로 요청을 던진다.
    • 인프라스트럭처에 위치한 로드밸런서나 프록시(예: Kubernetes Service, AWS ELB, Istio Envoy)가 요청을 가로채서 레지스트리를 조회하고 적절한 백엔드 인스턴스로 라우팅한다.
  ┌───────────────────────────────────────────────────────────────┐
  │        서버/인프라 사이드 디스커버리 (Kubernetes 방식)              │
  ├───────────────────────────────────────────────────────────────┤
  │                                                               │
  │  ┌──────────────────────┐                                     │
  │  │ Order Service        │ [1] "http://user-service" 로 요청  │
  │  │ (Consumer)           │─────┐                               │
  │  └──────────────────────┘     │                               │
  │                               ▼                               │
  │                  ╔══════════════════════════╗                 │
  │                  ║   Infra Load Balancer    ║                 │
  │                  ║  (K8s Service / Envoy)   ║                 │
  │                  ╚══════════════════════════╝                 │
  │                               │                               │
  │                [2] 라우팅 ┌──────┴──────┐                       │
  │                          ▼              ▼                       │
  │                  ┌────────────┐   ┌────────────┐              │
  │                  │ User Pod #1│   │ User Pod #2│              │
  │                  │ (10.0.0.1) │   │ (10.0.0.2) │              │
  │                  └────────────┘   └────────────┘              │
  │                                                               │
  │  ▶ 장점: 코드는 "user-service"만 알면 됨. 언어나 프레임워크 무관!    │
  └───────────────────────────────────────────────────────────────┘
  • 📢 섹션 요약 비유: 클라이언트 사이드는 "내가 지도(레지스트리)를 보고 목적지(IP)를 골라 직접 찾아가는 방식"이고, 서버 사이드는 "택시 기사(인프라 로드밸런서)에게 'OO식당 가주세요'라고 말하면 알아서 데려다주는 방식"입니다.

Ⅲ. 융합 비교 및 다각도 분석

서비스 레지스트리 솔루션 비교

솔루션주 용도특징일관성 (CAP)
Netflix Eureka애플리케이션 레벨 레지스트리Spring Cloud와 완벽 호환, Java 생태계 중심AP (가용성 우선)
HashiCorp Consul서비스 메시간 통신 및 구성 관리Go 기반, 헬스체크 강력, K/V 스토어 내장CP (일관성 우선)
Apache Zookeeper분산 코디네이션 및 락 관리과거 Dubbo 등에서 사용, 설정 복잡CP (일관성 우선)
Kubernetes CoreDNS컨테이너 오케스트레이션 내장K8s 환경의 표준, L4 로드밸런싱 기본 제공인프라 종속

유레카 (Eureka)의 AP (가용성 우선) 특성 설계

분산 시스템의 CAP 정리(일관성, 가용성, 파티션 허용) 관점에서, Eureka는 **가용성(Availability)**을 최우선으로 설계되었다.

  • 자가 보존 모드 (Self-Preservation Mode): 네트워크 단절(Partition)로 인해 갑자기 많은 수의 마이크로서비스로부터 Heartbeat가 오지 않을 때, Eureka는 해당 서비스들이 일제히 죽은 것이 아니라 네트워크 문제라고 판단하여 해당 인스턴스들을 레지스트리에서 삭제하지 않고 보존한다. "잘못된 정보를 주더라도(일관성 희생), 아예 응답하지 않는 것(가용성 포기)보다는 낫다"는 철학이다.

  • 📢 섹션 요약 비유: 유레카의 자가 보존 모드는, 갑자기 동네 전화가 다 끊겼을 때 전화번호부에서 가게 번호들을 다 지워버리는 게 아니라, "아마 통신 장애일 거야. 일단 번호는 남겨둘게"라며 무리한 삭제를 멈추는 방어 본능입니다.


Ⅳ. 실무 적용 및 기술사적 판단

실무 시나리오

  1. 시나리오 — 배포 직후 트래픽 유실 (Warm-up 지연): Eureka 환경에서 새로운 인스턴스가 배포되어 등록되었으나, 클라이언트 측 유레카 캐시가 아직 갱신되지 않아 이전(삭제된) 인스턴스로 요청을 보내 Connection Refused 에러가 발생하는 상황.

    • 판단: Eureka의 AP 특성과 클라이언트 캐싱 주기로 인해 최대 3분 정도의 '레지스트리 불일치 창(Window)'이 존재할 수 있다.
    • 해결책: 클라이언트 로드밸런서에 재시도(Retry) 로직을 반드시 추가해야 한다. 이전 인스턴스 호출 실패 시 즉시 캐시 내의 다른 가용한 인스턴스로 재시도(Retry Next)하도록 설정하여 과도기적 불일치를 애플리케이션 단에서 은닉(Masking)해야 한다.
  2. 시나리오 — Java 외의 다국어(Polyglot) MSA 구축: 기존 Java/Spring 중심의 MSA에 Node.js(프론트엔드 BFF)와 Python(AI/ML 모델 서빙) 마이크로서비스가 대거 추가되는 상황. 기존처럼 Spring Cloud Eureka를 유지할 것인가?

    • 판단: 클라이언트 사이드 디스커버리(Eureka)는 모든 언어마다 유레카 클라이언트 라이브러리를 구현해야 하는 다국어 지원의 치명적 단점이 있다.
    • 해결책: 애플리케이션 레벨의 디스커버리를 버리고, 인프라스트럭처 레벨의 디스커버리(Kubernetes Service + CoreDNS 또는 Istio Service Mesh)로 전면 마이그레이션(Shift-down)해야 한다. 이를 통해 모든 언어의 애플리케이션은 디스커버리 로직 없이 논리적 DNS 호출만 구현하도록 단순화한다.

도입 체크리스트

  • 기술적: Eureka 서버 자체가 단일 장애점(SPOF)이 되지 않도록 다중 노드 (Peer-to-Peer) 클러스터링 구성이 되어 있는가?
  • 운영적: 시스템 인프라 환경이 이미 완벽한 쿠버네티스(Kubernetes) 환경인가? 그렇다면 굳이 애플리케이션 레벨의 Eureka를 유지할 필요가 없으므로 제거(Deprecation)를 검토해야 한다.

안티패턴

  • 인스턴스 IP 하드코딩 및 DNS 의존: 클라우드 네이티브 환경에서 hosts 파일이나 전통적인 DNS A-Record 설정 변경에 의존하여 라우팅을 제어하려는 시도. (DNS 캐싱 시간(TTL)으로 인해 오토스케일링의 동적 속도를 전혀 따라갈 수 없어 대규모 장애를 유발한다.)

  • 📢 섹션 요약 비유: 이사 가는 속도가 KTX 급(클라우드 오토스케일링)인데, 주소 변경 신고는 우편마차(전통적 DNS 변경 수동 작업)로 하는 셈이 되어, 손님(트래픽)이 빈집만 찾아가게 됩니다.


Ⅴ. 기대효과 및 결론

정량/정성 기대효과

구분고정 IP 및 하드코딩동적 서비스 디스커버리개선 효과
정량오토스케일링 반영 시간 수 분 ~ 수 시간 지연인스턴스 증감 즉시 레지스트리 자동 갱신오토스케일링 반응 속도 1초 이내로 단축
정량수동 설정 변경 중 휴먼 에러 발생 (연 수 회)제로 터치 (Zero-touch) 네트워크 설정설정 변경에 따른 장애율 0% 달성
정성IP 변경 시 의존 서비스들의 코드 수정 필요논리적 서비스명 통신으로 결합도 해소인프라 변동이 애플리케이션 코드에 영향 미치지 않음

미래 전망

  • 인프라로의 흡수 (Shift-down to Infrastructure): Spring Cloud Netflix Eureka 같은 프레임워크 종속적 기술은 점차 레거시로 분류되고 있다. 컨테이너 오케스트레이션(Kubernetes)이 사실상 IT 표준이 됨에 따라, 서비스 디스커버리와 로드밸런싱의 책임은 애플리케이션 코드에서 Kube-proxy, CoreDNS, 그리고 Envoy 같은 사이드카 프록시 (Service Mesh) 계층으로 완전히 하방 이동(Shift-down)했다.
  • 멀티 클러스터/멀티 클라우드 디스커버리: 단일 데이터센터나 단일 클러스터를 넘어, AWS, GCP, On-premise 등 이기종 환경에 흩어진 마이크로서비스들을 하나로 묶어 라우팅해주는 글로벌 서비스 디스커버리 (Global Service Discovery / Consul Federation 등) 기술이 부상하고 있다.

참고 표준

  • DNS 기반 서비스 검색 프로토콜 (RFC 2782): SRV 레코드를 사용하여 특정 서비스를 제공하는 서버의 위치를 찾는 인터넷 표준.
  • Kubernetes Networking 표준: K8s 내부에서 Service, Endpoints 리소스를 통한 DNS 기반 디스커버리 구현 구조.

서비스 디스커버리는 MSA가 성립하기 위한 가장 기본적인 네트워크 척추다. 기술사는 특정 솔루션(Eureka)의 사용법에 얽매이기보다, 애플리케이션 계층(Spring Cloud)과 인프라 계층(Kubernetes) 중 현재 조직의 성숙도와 다국어(Polyglot) 요구사항에 맞추어 디스커버리의 '위치'를 결정하는 아키텍처 의사결정 능력을 보여주어야 한다.

  • 📢 섹션 요약 비유: 초창기 스마트폰에는 내비게이션 앱(Eureka)을 따로 설치해야 했지만, 이제는 스마트폰 운영체제(Kubernetes)에 기본 내장된 지도(Service)만 써도 충분히 길을 잘 찾을 수 있게 진화한 것과 같습니다.

📌 관련 개념 맵 (Knowledge Graph)

개념 명칭관계 및 시너지 설명
마이크로서비스 아키텍처 (MSA)인스턴스가 수시로 생성/소멸하는 MSA의 동적 특성 때문에 서비스 디스커버리가 필수적으로 요구된다.
API 게이트웨이 (API Gateway)외부 클라이언트 요청을 받을 때, API 게이트웨이 또한 서비스 디스커버리를 조회하여 뒷단의 내부 마이크로서비스로 요청을 라우팅한다.
클라이언트 사이드 로드밸런싱Eureka 클라이언트가 레지스트리 목록을 받은 후, 라운드 로빈 등으로 요청을 분산하는 역할을 수행한다 (과거 Ribbon, 현재 Spring Cloud LoadBalancer).
쿠버네티스 (Kubernetes) Service애플리케이션 레벨의 디스커버리를 대체하여, 인프라 레벨에서 클러스터 IP와 CoreDNS를 통한 서버 사이드 디스커버리를 완벽하게 지원한다.
서비스 메시 (Service Mesh)사이드카(Sidecar) 프록시를 통해 서비스 디스커버리, 라우팅, 인증을 코드 수정 없이 인프라망에서 투명하게 처리하는 진화된 형태다.
CAP 정리서비스 디스커버리 시스템을 선택할 때, 일관성(Zookeeper/Consul)을 중시할지 가용성(Eureka)을 중시할지를 판단하는 이론적 척도이다.

👶 어린이를 위한 3줄 비유 설명

  1. 친구네 집(서버)이 주말마다 매번 이사를 가서 주소가 바뀌면 매번 찾아가기 너무 힘들겠죠?
  2. 그래서 친구가 이사 갈 때마다 동네 이장님(서비스 레지스트리/유레카)한테 "저 오늘 여기로 이사 왔어요!"라고 신고를 해요.
  3. 우리는 친구 주소가 바뀌어도 이장님한테 "친구 주소 좀 알려주세요"라고 물어보면 언제든지 바로 찾아갈 수 있답니다! 이것이 '서비스 디스커버리'의 마법이에요.