Spring Boot 마이크로서비스를 Eureka Server에 등록하기

2025. 3. 14. 15:50Spring Microservice

🌟 Spring Boot 마이크로서비스를 Eureka Server에 등록하기

이전 글에서 Spring Cloud Netflix Eureka를 활용하여 Eureka Server를 구축하는 방법을 살펴봤습니다. 이번에는 마이크로서비스(organization-service, licensing-service)를 Eureka Server에 등록하는 과정을 자세히 알아보겠습니다. 🚀

Eureka Server는 MSA(Microservices Architecture) 환경에서 서비스 간 동적 탐색과 로드 밸런싱을 지원하는 핵심 역할을 합니다. 따라서 서비스들이 Eureka 레지스트리에 정상적으로 등록되어야 다른 서비스들이 이를 조회하고 통신할 수 있습니다.

이번 글에서는:
Eureka 클라이언트 설정
서비스를 Eureka 레지스트리에 등록하는 방법
IP 기반 등록 방식과 그 필요성
고가용성을 위한 Eureka 클러스터 설정 개념

까지 자세히 다룰 예정입니다. 💡

🎯 1. Eureka Client 등록이 필요한 이유

Eureka Server는 중앙 레지스트리 역할을 합니다.
하지만 Eureka Client가 등록되지 않으면 아무 의미가 없습니다!

각 마이크로서비스가 Eureka에 등록되면:
서비스 탐색(Discovery) 가능 → 다른 서비스가 내 서비스를 찾을 수 있음
동적 로드 밸런싱 → 여러 인스턴스가 실행 중이라면 Eureka가 적절한 대상 선택
서비스 장애 감지 → 다운된 서비스는 Eureka가 자동으로 제거

💡 즉, Eureka Server에 서비스를 등록하는 것은 마이크로서비스 환경에서 필수적인 과정입니다. 🚀

🏗 2. pom.xml에 Eureka Client 의존성 추가

마이크로서비스(organization-service, licensing-service)를 Eureka에 등록하려면,
각 서비스의 pom.xmlEureka Client 의존성을 추가해야 합니다.

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

spring-cloud-starter-netflix-eureka-clientEureka Client 기능을 활성화하는 핵심 라이브러리입니다.
✔ 이 의존성을 추가하면 Spring Boot가 자동으로 Eureka와 상호작용할 수 있도록 설정됩니다.

⚙️ 3. spring.application.name 설정

각 서비스는 Eureka에 논리적인 이름(= Application ID) 을 가지고 등록됩니다.
이 이름은 Eureka에서 서비스 탐색을 할 때 중요한 역할을 합니다.

📌 Organization Service (organization-service)

spring:
  application:
    name: organization-service  # 서비스의 논리적 이름
  profiles:
    active: dev
  cloud:
    config:
      uri: http://localhost:8071  # Spring Config Server 주소

📌 Licensing Service (licensing-service)

spring:
  application:
    name: licensing-service  # 서비스의 논리적 이름
  profiles:
    active: dev
  cloud:
    config:
      uri: http://localhost:8071  # Spring Config Server 주소

Eureka에 등록될 서비스 이름을 지정 (spring.application.name)
이름을 기반으로 다른 서비스들이 해당 서비스에 접근 가능

🚨 중요!
모든 서비스는 Eureka에 고유한 이름으로 등록되어야 합니다.
만약 여러 개의 서비스가 같은 이름을 가지면 충돌이 발생할 수 있습니다.

📝 4. Eureka에 서비스 등록 설정 (application.yml)

이제 각 서비스가 Eureka에 정상적으로 등록되도록 설정을 추가해야 합니다.

eureka:
  instance:
    preferIpAddress: true  # IP 기반 등록 활성화
  client:
    registerWithEureka: true  # Eureka에 서비스 등록
    fetchRegistry: true  # Eureka 레지스트리를 로컬에 캐싱
    serviceUrl:
      defaultZone: http://localhost:8070/eureka/  # Eureka Server 주소

✅ 설정 설명

🔹 eureka.instance.preferIpAddress: true

  • 기본적으로 Eureka는 서비스의 호스트명(Hostname)으로 등록합니다.
  • 하지만 컨테이너 기반 환경(Docker 등)에서는 랜덤한 호스트명이 부여되어 문제가 발생할 수 있습니다.
  • 따라서 IP 기반으로 등록하는 것이 더욱 안정적입니다.

🔹 eureka.client.registerWithEureka: true

  • Eureka Server에 서비스를 등록하는 설정입니다.

🔹 eureka.client.fetchRegistry: true

  • Eureka에서 주기적으로 레지스트리 정보를 가져와 캐싱합니다.
  • 기본적으로 30초마다 Eureka Server와 동기화됩니다.

🔹 eureka.client.serviceUrl.defaultZone

  • Eureka Server의 URL을 지정합니다.
  • 여기서는 로컬 환경의 Eureka Server(http://localhost:8070/eureka/)에 등록하도록 설정했습니다.

🎯 5. Eureka의 REST API를 활용한 서비스 등록 확인

Eureka의 강력한 기능 중 하나는 REST API를 제공하여 서비스 정보를 쉽게 확인할 수 있다는 점입니다.
이 기능을 이용하면 현재 등록된 서비스 목록을 조회하거나, 개별 서비스의 상태를 확인할 수 있습니다.

1️⃣ Eureka REST API 개요

Eureka는 기본적으로 RESTful API를 제공하여 현재 등록된 서비스 인스턴스를 조회할 수 있도록 합니다.
이를 통해 운영 중인 마이크로서비스의 상태를 직접 확인할 수 있습니다.

🔹 전체 서비스 목록 조회

GET http://<eureka-server>:8070/eureka/apps
  • 현재 Eureka Server에 등록된 모든 서비스 목록을 가져옵니다.

🔹 특정 서비스의 인스턴스 조회

GET http://<eureka-server>:8070/eureka/apps/<APPID>
  • 특정 서비스(APPID)의 모든 인스턴스를 조회합니다.
  • 예제: organization-service의 모든 인스턴스를 조회
GET http://localhost:8070/eureka/apps/organization-service

🔹 특정 인스턴스 상태 조회

GET http://<eureka-server>:8070/eureka/apps/<APPID>/<INSTANCE-ID>
  • 특정 서비스의 단일 인스턴스 상태를 확인할 수 있습니다.
  • INSTANCE-ID는 Eureka에 등록된 개별 인스턴스의 ID입니다.

2️⃣ 특정 서비스(organization-service) 조회

이제 organization-service의 등록 상태를 조회해 보겠습니다.
Postman을 이용하여 GET 요청을 보낸 결과는 아래와 같습니다. ✅

GET http://localhost:8070/eureka/apps/organization-service

📌 Postman 요청 설정

  • Headers:
    • Accept: */* ✅
    • Accept-Encoding: gzip, deflate, br ✅
    • Connection: keep-alive ✅

 

📌 Eureka 응답 데이터 (XML)

<application>
    <name>ORGANIZATION-SERVICE</name>
    <instance>
        <instanceId>0d97d48acdbc:organization-service:8081</instanceId>
        <hostName>172.18.0.6</hostName>
        <app>ORGANIZATION-SERVICE</app>
        <ipAddr>172.18.0.6</ipAddr>
        <status>UP</status>
        <overriddenstatus>UNKNOWN</overriddenstatus>
        <port enabled="true">8081</port>
        <securePort enabled="false">443</securePort>
        <countryId>1</countryId>
        <dataCenterInfo class="com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo">
            <name>MyOwn</name>
        </dataCenterInfo>
        <leaseInfo>
            <renewalIntervalInSecs>30</renewalIntervalInSecs>
            <durationInSecs>90</durationInSecs>
            <registrationTimestamp>1741938349289</registrationTimestamp>
            <lastRenewalTimestamp>1741939489340</lastRenewalTimestamp>
            <evictionTimestamp>0</evictionTimestamp>
            <serviceUpTimestamp>1741938349290</serviceUpTimestamp>
        </leaseInfo>
        <metadata>
            <management.port>8081</management.port>
        </metadata>
        <homePageUrl>http://172.18.0.6:8081/</homePageUrl>
        <statusPageUrl>http://172.18.0.6:8081/actuator/info</statusPageUrl>
        <healthCheckUrl>http://172.18.0.6:8081/actuator/health</healthCheckUrl>
        <vipAddress>organization-service</vipAddress>
        <secureVipAddress>organization-service</secureVipAddress>
        <isCoordinatingDiscoveryServer>false</isCoordinatingDiscoveryServer>
        <lastUpdatedTimestamp>1741938349290</lastUpdatedTimestamp>
        <lastDirtyTimestamp>1741938349151</lastDirtyTimestamp>
        <actionType>ADDED</actionType>
    </instance>
</application>

✅ 주요 응답 데이터 설명

<status>UP</status> → 서비스가 정상적으로 등록되어 실행 중
<port enabled="true">8081</port> → 서비스가 8081 포트에서 실행 중
<homePageUrl> → 서비스의 기본 URL 제공
<healthCheckUrl> → /actuator/health을 통해 헬스 체크 가능
<vipAddress> → Eureka에서 인식하는 서비스 논리적 이름

💡 즉, 현재 organization-service가 8081 포트에서 정상적으로 실행 중이며,
Eureka Server에서 이 서비스의 상태를 "UP"으로 인식하고 있습니다. 🚀

3️⃣ JSON 형식으로 응답 받기

Eureka는 기본적으로 XML 형식으로 데이터를 반환하지만,
JSON 형식으로도 응답을 받을 수 있습니다.

📌 JSON 응답을 받는 방법

  • Accept HTTP 헤더를 application/json으로 설정
  • 예제 요청:
GET http://localhost:8070/eureka/apps/organization-service
Headers:  
Accept: application/json

📌 JSON 응답 예제

{
    "application": {
        "name": "ORGANIZATION-SERVICE",
        "instance": [
            {
                "instanceId": "0d97d48acdbc:organization-service:8081",
                "hostName": "172.18.0.6",
                "app": "ORGANIZATION-SERVICE",
                "ipAddr": "172.18.0.6",
                "status": "UP",
                "overriddenStatus": "UNKNOWN",
                "port": {
                    "$": 8081,
                    "@enabled": "true"
                },
                "securePort": {
                    "$": 443,
                    "@enabled": "false"
                },
                "countryId": 1,
                "dataCenterInfo": {
                    "@class": "com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo",
                    "name": "MyOwn"
                },
                "leaseInfo": {
                    "renewalIntervalInSecs": 30,
                    "durationInSecs": 90,
                    "registrationTimestamp": 1741938349289,
                    "lastRenewalTimestamp": 1741939249306,
                    "evictionTimestamp": 0,
                    "serviceUpTimestamp": 1741938349290
                },
                "metadata": {
                    "management.port": "8081"
                },
                "homePageUrl": "http://172.18.0.6:8081/",
                "statusPageUrl": "http://172.18.0.6:8081/actuator/info",
                "healthCheckUrl": "http://172.18.0.6:8081/actuator/health",
                "vipAddress": "organization-service",
                "secureVipAddress": "organization-service",
                "isCoordinatingDiscoveryServer": "false",
                "lastUpdatedTimestamp": "1741938349290",
                "lastDirtyTimestamp": "1741938349151",
                "actionType": "ADDED"
            }
        ]
    }
}

XML 대신 JSON 형식으로 데이터를 받을 수 있어 가독성이 향상
REST API와의 연동이 쉬워짐 (예: React, Angular, Vue.js 등과 연계 가능)

4️⃣ Eureka API 활용 예시

Eureka의 REST API는 서비스 레지스트리 정보를 관리하고 조회하는 데 유용합니다.
아래는 몇 가지 유용한 API 활용 예시입니다.

🔹 모든 서비스 조회

GET http://localhost:8070/eureka/apps

등록된 모든 서비스 목록을 확인 가능

{
    "applications": {
        "versions__delta": "1",
        "apps__hashcode": "UP_2_",
        "application": [
            {
                "name": "LICENSING-SERVICE",
                "instance": [
                    {
                        "instanceId": "47d288fe2a1f:licensing-service",
                        "hostName": "172.18.0.4",
                        "app": "LICENSING-SERVICE",
                        "ipAddr": "172.18.0.4",
                        "status": "UP",
                        "overriddenStatus": "UNKNOWN",
                        "port": {
                            "$": 8080,
                            "@enabled": "true"
                        },
                        "securePort": {
                            "$": 443,
                            "@enabled": "false"
                        },
                        "countryId": 1,
                        "dataCenterInfo": {
                            "@class": "com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo",
                            "name": "MyOwn"
                        },
                        "leaseInfo": {
                            "renewalIntervalInSecs": 30,
                            "durationInSecs": 90,
                            "registrationTimestamp": 1741938349431,
                            "lastRenewalTimestamp": 1741939549532,
                            "evictionTimestamp": 0,
                            "serviceUpTimestamp": 1741938349431
                        },
                        "metadata": {
                            "management.port": "8080"
                        },
                        "homePageUrl": "http://172.18.0.4:8080/",
                        "statusPageUrl": "http://172.18.0.4:8080/actuator/info",
                        "healthCheckUrl": "http://172.18.0.4:8080/actuator/health",
                        "vipAddress": "licensing-service",
                        "secureVipAddress": "licensing-service",
                        "isCoordinatingDiscoveryServer": "false",
                        "lastUpdatedTimestamp": "1741938349431",
                        "lastDirtyTimestamp": "1741938349361",
                        "actionType": "ADDED"
                    }
                ]
            },
            {
                "name": "ORGANIZATION-SERVICE",
                "instance": [
                    {
                        "instanceId": "0d97d48acdbc:organization-service:8081",
                        "hostName": "172.18.0.6",
                        "app": "ORGANIZATION-SERVICE",
                        "ipAddr": "172.18.0.6",
                        "status": "UP",
                        "overriddenStatus": "UNKNOWN",
                        "port": {
                            "$": 8081,
                            "@enabled": "true"
                        },
                        "securePort": {
                            "$": 443,
                            "@enabled": "false"
                        },
                        "countryId": 1,
                        "dataCenterInfo": {
                            "@class": "com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo",
                            "name": "MyOwn"
                        },
                        "leaseInfo": {
                            "renewalIntervalInSecs": 30,
                            "durationInSecs": 90,
                            "registrationTimestamp": 1741938349289,
                            "lastRenewalTimestamp": 1741939549348,
                            "evictionTimestamp": 0,
                            "serviceUpTimestamp": 1741938349290
                        },
                        "metadata": {
                            "management.port": "8081"
                        },
                        "homePageUrl": "http://172.18.0.6:8081/",
                        "statusPageUrl": "http://172.18.0.6:8081/actuator/info",
                        "healthCheckUrl": "http://172.18.0.6:8081/actuator/health",
                        "vipAddress": "organization-service",
                        "secureVipAddress": "organization-service",
                        "isCoordinatingDiscoveryServer": "false",
                        "lastUpdatedTimestamp": "1741938349290",
                        "lastDirtyTimestamp": "1741938349151",
                        "actionType": "ADDED"
                    }
                ]
            }
        ]
    }
}

 

🔹 특정 서비스(organization-service)의 모든 인스턴스 조회

GET http://localhost:8070/eureka/apps/organization-service

현재 실행 중인 organization-service 인스턴스를 확인

 

🔹 특정 인스턴스 상태 확인

GET http://localhost:8070/eureka/apps/organization-service/0d97d48acdbc:organization-service:8081

특정 인스턴스의 상태 및 메타데이터 확인 가능

 

🚀 5. Eureka Dashboard에서 서비스 확인

위 설정이 완료되면, 서비스를 실행하고 Eureka Dashboard에서 정상 등록 여부를 확인할 수 있습니다.

🔎 Eureka Dashboard 확인

브라우저에서 http://localhost:8070/ 에 접속하면 Eureka 대시보드를 확인할 수 있습니다! 🎉

  • organization-service
  • licensing-service

위 서비스들이 정상적으로 Eureka에 등록된 것을 확인할 수 있습니다.

 

🔄 6. Eureka의 고가용성(High Availability)

🌍 다중 Eureka 서버 설정

단일 Eureka 서버는 싱글 포인트 장애(SPOF, Single Point of Failure) 문제가 있습니다.
이를 해결하기 위해 여러 개의 Eureka 서버를 클러스터링 할 수 있습니다.

eureka:
  client:
    serviceUrl:
      defaultZone: http://eureka-server-1:8070/eureka/,http://eureka-server-2:8070/eureka/

여러 Eureka 서버를 지정하여 고가용성 보장
서버가 다운되더라도 다른 서버가 정상적으로 동작

Eureka 클러스터링을 설정하면 각 서버가 서로의 정보를 공유하여 장애 발생 시에도 서비스 등록/조회가 가능하게 됩니다. 🔄

📌 Eureka 클러스터 구성은 별도 글에서 더 자세히 다룰 예정입니다.

🎯 결론

이번 글에서는 Spring Boot 마이크로서비스를 Eureka Server에 등록하는 방법을 살펴봤습니다. 🚀

Eureka Client 의존성 추가 (spring-cloud-starter-netflix-eureka-client)
spring.application.name 설정하여 서비스 ID 지정
Eureka에 서비스 등록 (registerWithEureka: true)
IP 기반 등록 (preferIpAddress: true) 사용하여 컨테이너 환경 최적화

Eureka Restful API 사용
Eureka Dashboard에서 서비스 확인 가능

💡 이제 마이크로서비스가 Eureka 레지스트리에 정상적으로 등록되었으므로,
다음 글에서는 Eureka Client를 통해 다른 서비스와 동적으로 통신하는 방법을 알아보겠습니다! 🎯