[Spring Cloud] Spring Cloud Eureka - 기본 개념 및 활용

mrcocoball·2024년 1월 11일
2

Spring Cloud

목록 보기
6/8

해당 포스트는 Spring Cloud에 속한 기술들에 대한 개념과 주요 기술에 대해 알아보고 실무에 적용했었던 내용을 정리하는 포스트입니다.

1. 개요

서비스 레지스트리와 서비스 디스커버리

MSA에서 마이크로서비스 관리와 운영을 위한 패턴 중 서비스 레지스트리와 서비스 디스커버리가 있습니다.

서비스 간 통신 시 서비스 위치 (포트, 호스트) 가 정적이던 전통적인 어플리케이션 / 아키텍처와는 달리 주소가 동적으로 변화하고 (클라우드 환경, 오토 스케일링, 컨테이너 기반의 배포 등) 서비스 역시 무수히 많은 마이크로서비스에서는 서비스 위치에 대한 관리가 많이 복잡해지게 되는데, 이를 해결하기 위해 서비스 레지스트리 패턴과 서비스 디스커버리 패턴이 등장하게 됩니다.

서비스 레지스트리와 서비스 디스커버리 패턴에 대한 설명은 다음과 같습니다.

  • 서비스 레지스트리 : 마이크로서비스 / 관리, 운영을 위한 기반 서비스의 주소와 유동적인 IP를 매핑하여 저장하는 패턴
  • 서비스 디스커버리 : 클라이언트가 여러 개의 마이크로서비스를 호출하기 위해 최적 경로를 찾아주는 라우팅 기능, 적절한 부하 분산을 위한 로드 밸런싱 기능을 제공해야 하는데 이와 관련된 패턴

Spring Cloud Eureka

Spring Cloud Eureka는 서비스 레지스트리와 서비스 디스커버리를 지원하기 위한 라이브러리이며 서버 / 클라이언트로 나뉩니다.

  • Eureka Server
    • REST API 기반으로 다양한 언어에서 사용 가능
    • 레지스트리의 모든 정보는 모든 Eureka Client에서 복제되어 있으며 가용 중인 모든 서비스들의 목록을 확인할 수 있고 30초마다 목록이 갱신됨
    • Eureka Client들에게 자신이 가지고 있는 Eureka Client들의 정보를 공유, Eureka Client들 (대표적으로 Spring Cloud Gateway, Ribbon, Zipkin 등) 에서 이를 사용
    • 일정 간격으로 Client로부터 핑을 전달 받아 가용 상태임을 확인하며 일정 횟수 이상 핑이 안 된다면 Eureka Client를 레지스트리에서 제외시킴
    • 가용 상태의 서비스 목록 확인 시 서비스의 이름을 기준으로 탐색, 로드 밸런싱을 위해 내부적으로 Ribbon (클라이언트 측의 로드 밸런서) 을 사용
  • Eureka Client
    • 서비스 시작 시 Eureka Server에 자신의 정보를 등록
    • 등록된 후 30초마다 레지스트리에 핑을 전송하여 자신의 가용 상태를 알림
    • 레지스트리로부터 다른 Eureka Client의 서비스 정보를 확인할 수 있음

2. 활용 예제

설정 : Spring Cloud Eureka Server

application.yml을 다음과 같이 설정합니다.
레지스트리에서는 각 서비스의 이름을 식별할 때 spring.application.name에 작성된 명칭으로 식별을 합니다.

server:
  port: 8761

spring:
  application:
    name: test-eureka-local
  config:
    activate:
      on-profile: local

eureka:
  instance:
    prefer-ip-address: true
  server:
    enable-self-preservation: true
  client:
    register-with-eureka: false # 자기 자신을 서비스로 등록하지 않음
    fetch-registry: true # 마이크로서비스 인스턴스 목록을 로컬에 캐시할 것인지 여부. 서비스 탐색 등의 목적.
    service-url:
      defaultZone: http://localhost:8761/eureka

management:
  security:
    enabled: false

ribbon:
  IsSecure: false

security:
  basic:
    enabled: true
  user:
    name: user
  password: secret

이후 어플리케이션의 메인 클래스에 @EnableEurekaServer 어노테이션을 부착합니다.

@EnableEurekaServer
@SpringBootApplication
public class ModuleEurekaApplication {

	public static void main(String[] args) {
		SpringApplication.run(ModuleEurekaApplication.class, args);
	}

}

서버 실행 후 접속 (로컬이므로 http://localhost:8761) 을 하여 서비스 인스턴스들을 확인할 수 있습니다.

설정 : Spring Cloud Eureka Client

application.yml을 다음과 같이 설정합니다.
위에서 설명한 대로 spring.application.name 을 통해 레지스트리 측에 전달할 서비스의 이름을 지을 수 있습니다.
또한, eureka.instance.instance-id의 값을 통해 인스턴스 고유 ID 표기 방식을 변경할 수 있습니다.
(기본값은 호스트 + 어플리케이션 이름 + 포트 번호)
eureka.client.serviceUrl.defaultZone은 Eureka Server의 주소 + /eureka/ 입니다.

# 각 마이크로서비스 공통 사항
spring:
  application:
    name: 마이크로서비스 이름

...

eureka:
  instance:
    instance-id: ${spring.cloud.client.hostname}:${spring.application.instance_id:${random.value}}
    leaseRenewalIntervalInSeconds: 10
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/

이후 어플리케이션의 메인 클래스에 @EnableDiscoveryClient 어노테이션을 부착합니다.

@EnableDiscoveryClient
@SpringBootApplication
public class SampleApplication {

    public static void main(String[] args) {
        SpringApplication.run(SampleApplication.class, args);
    }

}

이렇게 클라이언트 측에서 세팅을 할 경우 Eureka Server를 다시 확인하면 서비스가 정상적으로 등록되어있음을 확인할 수 있습니다.

Spring Cloud OpenFeign / Gateway와의 연계

Spring Cloud OpenFeign과의 연계
https://velog.io/@mrcocoball2/Spring-Cloud-Spring-Cloud-OpenFeign-%EA%B8%B0%EB%B3%B8-%EA%B0%9C%EB%85%90-%EB%B0%8F-%ED%99%9C%EC%9A%A9
이전 게시글에서는 Ribbon을 사용하지 않았으며 서비스 이름을 식별하지 못해 url을 직접 사용했었지만 Eureka Server / Client를 통해 서비스 이름을 식별할 수 있게 됩니다.

@EnableDiscoveryClient 어노테이션을 메인 클래스에 부착한 후 이전에 LoadBalancer나 Ribbon을 사용하지 않아 기본 클라이언트를 설정했던 설정 클래스를 주석 처리 합니다.

@Configuration
public class FeignConfig {

	/*
    @Bean
    public Client feignClient() {
        return new Client.Default(null, null);
    }
    */

}

그리고 url로 동기 통신 대상(localhost:8081)을 지정하던 인터페이스 설정을 서비스 이름(sample)으로 대체합니다.

// @FeignClient(url = "http://localhost:8081")
@FeignClient(name = "sample")
public interface SampleClient {

    @GetMapping("/api/v1/internal/sample/cases")
    ResponseEntity<List<CaseDTO>> getCaseList(@RequestHeader("Internal-Auth-Token") String token);

    @GetMapping("/api/v1/internal/sample/cases/{id}")
    ResponseEntity<CaseDTO> getCase(@RequestHeader("Internal-Auth-Token") String token,
                                    @PathVariable("id") Long id);

}

Spring Cloud Gateway와의 연계
https://velog.io/@mrcocoball2/Spring-Cloud-Spring-Cloud-Gateway-%EA%B8%B0%EB%B3%B8-%EA%B0%9C%EB%85%90
이전 게시글에서는 url로 직접 라우팅을 하였지만, lb://서비스 이름 으로 라우팅 및 로드 밸런싱이 가능해집니다.

@EnableDiscoveryClient 어노테이션을 메인 클래스에 부착한 후 기존 application.yml을 수정합니다.

spring:
  cloud:
    gateway:
      # 먼저 선언한 순서대로 필터가 적용됨 route 1 -> route 2
      routes:
      	# route 1, route의 id는 sample-internal
        - id: sample-internal
          #uri: http://localhost:8081
          uri: lb://sample
          predicates:
            - Path=/sample/api/v1/internal/** # /sample/api/v1/internal/** 로 들어오는 요청에 반응
          filters:
            - NotAllowedURIExceptionFilter # filter 로직 적용 (커스텀 필터)
        # route 2, route의 id는 sample
        - id: sample
          #uri: http://localhost:8081
          uri: lb://sample
          predicates:
            - Path=/sample/** # /sample/** 로 들어오는 요청에 반응
          filters:
            - RewritePath=/sample/(?<segment>.*), /$\{segment} # filter 로직 적용 (RewritePath 필터)

여기서 라우팅되는 하위 마이크로서비스 측에서 주소나 포트를 변경한 후 다시 실행하더라도 일정 시간 단위로 Eureka Server에서 핑을 보내 마이크로서비스의 정상 작동 여부를 확인하고 이용할 수 없는 서비스 정보는 삭제, 다시 실행된 마이크로서비스의 정보가 레지스트리에 등록되어 갱신되기 때문에 Gateway에서 정보를 바꾸지 않더라도 서비스 연결이 가능해집니다.

Appendix. 출처

https://cloud.spring.io/spring-cloud-netflix/reference/html/

profile
Backend Developer

0개의 댓글