[TIL] 241120 MSA 입문하기, Spring Cloud, Service Discovery, Road Balancing

MONA·2024년 11월 20일

나혼공

목록 보기
31/92

MSA

Microservice Architecture

  • 하나의 애플리케이션을 여러 개의 독립적인 서비스로 분리하여 개발, 배포, 유지보수하는 소프트웨어 아키텍처 스타일
  • 각 서비스는 특정 기능이나 비즈니스 도메인을 중심으로 나뉨
  • 독립적인 개발, 배포, 운영, 확장 가능
  • 서비스 간의 통신은 주로 HTTP, HTTPS, 메시지 큐를 통해 통신

특징

  • 독립적인 배포 가능성: 각 서비스는 독립적으로 배포할 수 있고, 다른 서비스에 영향 없이 업데이트 가능
  • 각 서비스는 적절한 기술 스택을 자유롭게 선택할 수 있음

모놀리식과의 비교

Monolithic Application(MA)

  • 하나의 코드베이스로 구성된 애플리케이션

장점

  • 배포가 간단
  • 하나의 데이터베이스를 사용하여 데이터 일관성 유지가 용이

단점

  • 특정 기능 확장을 위해서는 전체 애플리케이션의 확장이 요구됨
  • 작은 변경 사항도 전체를 재배포 해야 함
  • 새로운 기술 도입이 어렵고 특정 모듈에 종속적일 수 있음

MSA

주요 장점

  • 유연성: 독립적인 서비스 업데이트와 확장이 가능.
  • 속도: 특정 서비스의 빠른 개발과 배포가 가능.
  • 확장성: 각 서비스 단위로 필요에 따라 확장 가능.
  • 장애 격리: 하나의 서비스에 문제가 생겨도 전체 애플리케이션에 영향이 적음.

주요 단점

  • 복잡성 증가: 서비스 간 통신, 배포 관리, 모니터링이 어려움.
  • 네트워크 비용: 서비스 간 데이터 전송으로 인한 오버헤드.
  • 데이터 일관성: 분산된 데이터 관리로 인해 일관성을 유지하기 어려움.
특징모놀리식 아키텍처MSA
구조하나의 큰 애플리케이션 코드베이스로 구성됨.여러 독립적인 서비스로 나뉘어 있음.
개발 및 배포모든 코드 변경 시 전체 애플리케이션을 다시 빌드하고 배포해야 함.개별 서비스만 수정 및 배포 가능.
확장성애플리케이션 전체를 확장해야 하므로 비효율적.필요한 서비스만 확장 가능 (예: 특정 기능 트래픽 급증 시 해당 서비스만 확장).
유지보수코드베이스가 커질수록 유지보수가 어려움.작은 단위로 유지보수 가능하며, 다른 서비스에 영향을 최소화함.
기술 선택의 자유하나의 기술 스택에 의존함.각 서비스마다 적합한 기술 스택을 선택 가능.
의존성 관리모듈 간 의존성 증가로 인해 복잡도가 높아질 수 있음.서비스 간 느슨한 결합을 통해 의존성 감소.
장애 영향 범위하나의 모듈 오류가 전체 시스템에 영향을 미침.오류가 난 서비스에만 영향이 국한됨.
배우기 및 초기 설정초기 학습 및 설정이 상대적으로 쉬움.러닝커브 존재, 초기 설정이 복잡할 수 있음.
운영 복잡도단일 시스템 관리로 운영이 단순함.서비스가 많아질수록 네트워크, 배포 및 모니터링 관리가 복잡해짐.

요약

MSA는 규모가 크고 복잡한 시스템에서 특히 유용하다. 애플리케이션 개발 속도와 확장성을 요구하는 환경에 적합하다. 하지만 작은 팀이나 초기 단계의 프로젝트에서는 자원을 고려하여 모놀리식 아키텍처가 더 적합할 수도 있다.

Spring Cloud

  • MSA를 지원하기 위해 설계된 스프링 프레임워크의 확장 모듈
  • 분산 시스템의 복잡성을 줄리고 MSA의 개발, 배포, 모니터링을 효율적으로 할 수 있도록 다양한 도구와 서비스를 제공함

Spring Cloud

주요 기능

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

  • 도구: Eureka, Consul, Zookeeper
  • 각 마이크로서비스가 서로의 위치를 동적으로 파악할 수 있게 지원
  • 클라이어트는 서비스 디스커버리 서버를 통해 필요한 서비스의 위치를 조회함

API 게이트웨이(API Gateway)

  • 도구: Spring Cloud Gateway, Zuul
  • 클라이언트의 요청을 적절한 마이크로서비스로 라우팅하여 인증, 인가, 로깅, 요청 변환 등의 작업을 수행

설정 관리(Configuration Management)

  • 도구: Spring Cloud Config
  • 분산된 서비스의 설정을 중앙에서 관리할 수 있음
  • 환경별로 다른 설정을 유연하게 제공. 설정 변경 시 실시간으로 서비스에 반경 가능

로드 밸런싱(Load Balancing)

  • 도구: Ribbon, Spring Cloud LoadBalancer
  • 클라이언트가 여러 인스턴스 중에서 적절한 서비스로 요청을 전달하도록 지원

분산 트랜젝션 관리

  • 도구: Spring Cloud Sleuth, Zipkin
  • 서비스 간 호출을 추적, 분산 환경에서의 디버깅 및 모니터링을 지원

서킷 브레이커(Circuit Breaker)

  • 도구: Resilience4j, Hystrix(구버전)
  • 서비스 간 장애 전파를 방지하고 시스템의 복원력을 강화함
  • 호출 실패 시 백업 작업을 수행하거나 기본 값을 반환하는 등

메시징과 이벤트 기반 아키텍처

  • 도구: Spring Cloud Stream
  • 메시지 브로커(Kafka, RabbitMQ)를 통해 서비스 간 이벤트를 주고받을 수 있음

서비스 보안(Security)

  • 도구: Spring Security, OAuth2
  • 인증, 인가를 포함한 보안 설정을 쉽게 구성할 수 있도록 지원

볼드 처리된 부분을 많이 사용하지만, 현업 환경에서는 Hystrix나 Zuul을 사용하는 경우도 많음. 버전 업은 까다로운 일이기 때문에..

주요 장점

  1. MSA 구현 용이
  • MSA 개발을 위한 표준화된 도구를 제공하여 복잡한 분산 시스템의 설계와 구현을 단순화함
  1. 스프링 생태계 통합
  • Spring Boot와 자연스럽게 통합되며, 스프링 개발자에게 친숙한 방식으로 사용 가능
  1. 확장성 및 유연성
  • 다양한 클라우드 환경(AWS, Azure, Google Cloud)에서 원활히 작동하며 서비스의 확장성을 높임
  1. 오픈소스 커뮤니티 지원
  • 활발한 커뮤니티와 문서화 덕분에 학습 문제 및 해결이 비교적 용이

주요 모듈

모듈설명
Spring Cloud Config중앙 집중식 구성 관리. Git 등 외부 저장소를 지원.
Spring Cloud NetflixEureka, Hystrix, Ribbon 등의 Netflix OSS 기반 기술 통합.
Spring Cloud GatewayAPI Gateway 역할로, 요청 라우팅과 필터링 기능 제공.
Spring Cloud Stream메시지 브로커(Kafka, RabbitMQ)와 통합된 메시징 기능 제공.
Spring Cloud Sleuth분산 트랜잭션 추적을 위한 로깅, Zipkin과의 통합 지원.
Spring Cloud BusRabbitMQ 또는 Kafka를 통해 설정 변경이나 이벤트를 브로드캐스트.

서비스 디스커버리

  • MSA에서 각 서비스의 위치를 동적으로 관리하고 찾아주는 기능
  • 각 서비스는 등록 서버에 자신의 위치를 등록하고 다른 서비스는 이를 조회하여 통신
  • 서비스 등록, 서비스 조회, 헬스 체크 등의 기능

Eureka

  • 넷플릭스가 개발한 서비스 디스커버리 및 레지스트리 도구. MSA에서 각 서비스를 동적으로 관리.
  • 모든 서비스 인스턴스의 위치를 저장하는 중앙 저장소 역할을 하며, 서비스 인스턴스의 상태를 주기적으로 체크함
  • 특징
    • 서비스 레지스트리: 모든 서비스 인스턴스의 위치를 저장하는 중앙 저장소
    • 헬스 체크: 서비스 인스턴스의 상태를 주기적으로 확인하여 기용성을 보장

주요 개념
1. Eureka Server

  • 서비스 레지스트리 역할을 하는 중앙 서버
  • 마이크로서비스는 Eureka Server에 자신의 위치(host, port)를 등록
  • 서비스 간의 동적 디스커버리를 가능하게 함
  1. Eureka Client
  • Eureka Server와 통신하여 자신의 정보를 등록, 필요한 타 서비스를 조회
  • Eureka Client는 주기적으로 서버에 상태 정보를 보냄(heartbeat)
  1. Service Registry
  • Eureka Server는 등록된 모든 서비스의 정보를 저장하는 레지스트리이다
  • 서비스명, IP주소, 포트 번호 등과 같은 정보 저장
  1. Self-Preservation
  • Eureka는 네트워크 문제나 일시적인 장애 시에도 기존 서비스 정보를 유지하려 함
  • 일정 시간 동안 클라이언트의 상태 보고가 없더라도 서비스 정보를 삭제하지 않음

주요 기능
1. 서비스 등록 및 해제

  • 서비스 시작 시 Eureka Server에 자신의 정보 등록
  • 서비스 중단, 종료 시 삭제
  1. 서비스 디스커버리
  • Eureka Client는 필요할 때 Eureka Server에서 타 서비스의 위치를 조회, 통신함
  1. 로드 밸런싱
  • 클라이언트가 동일한 이름을 가진 여러 인스턴스 중에서 선택적으로 요청을 보낼 수 있음
  1. Falut Tolerance
  • 네트워크 문제가 발생해도 기존 정보를 유지하며, 복구 시 자동으로 동기화 됨

작동 흐름
1. 서비스 등록

  • 서비스 시작시 Eureka Client는 Eureka Server에 서비스명, 호스트 및 포트번호를 등록
  1. Heartbeat
  • 서비스는 주기적으로 Eureka Server에 생존신고를 함
  1. 서비스 조회
  • 타 서비스 간의 통신이 필요할 경우 Eureka Server에 요청을 보내 위치 정보를 조회
  1. 서비스 제거
  • 서비스 종료 시 Eureka Server에 이를 알리고 레지스트리에서 제거됨

장점

  • 동적 서비스 디스커버리: 서비스의 위치가 고정되어 있지 않아도 동적으로 탐색 가능
  • 확장성: 여러 인스턴스가 있는 환경에서도 효과적으로 작동
  • 강력한 장애 복원력: 네트워크 장애 발생시에도 레스트리가 삭제되지 않아 안정적인 서비스 운영이 가능
  • Spring Cloud와의 통합: Spring Cloud Netflix를 통해 쉽게 Eureka를 설정, 활용할 수 있음

주요 구성 요소

구성 요소설명
Eureka Server서비스 레지스트리 역할을 하는 중앙 서버. 모든 서비스 정보를 관리.
Eureka Client서버와 통신하여 자신의 정보를 등록하고 다른 서비스 정보를 조회.
Registry서비스의 이름, 주소, 포트 등의 정보를 저장.
RibbonEureka와 함께 사용하여 로드 밸런싱을 지원.

Eureka 설정, 사용하기

  1. 의존성 추가
  • Eureka Server
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-server'
  • Eureka Client
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'
  1. Eureka Server 설정
  • Eureka Server가 될 프로젝트에 @EnableEurekaServer 어노테이션 추가
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaServerApplication.class, args);
    }
}
  • application.properties 설정
spring.application.name=server

server.port=19090

eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
eureka.instance.hostname=localhost
eureka.client.service-url.defaultZone=http://localhost:19090/eureka/

+eureka.client.register-with-eureka: Eureka Client로 등록 유무-서버는 등록 x
+eureka.client.fetch-registry: 서버로부터 레지스트리를 가져올 것인가?-이게 서버임

실행 후 http://localhost:19090/ 에서 대시보드 확인 가능

  1. Eureka Client 설정
  • application.properties
spring.application.name=first

server.port=19091

eureka.client.service-url.defaultZone=http://localhost:19090/eureka/

포트를 다르게 설정해주고, 서비스 url을 설정함
그리고 실행하면 여기에 인스턴스가 추가되어야 하는데..
콘솔에 뭔가 심상치 않은(불길한) 경고가 계속 뜨고는

없었다.

진짜 힘들게 찾았는데 그냥 오타 문제였다.
properties에서 eureka.client.service-url.defaultZone을 eureka.client.service-url.defaulfZone으로 작성해버렸는데, 웬만하면 자동완성을 쓰는 게 좋다.


정상적으로 등록되면 이렇게 인스턴스 목록에 추가된다.

@EnableEurekaClient

위의 문제를 해결하기 위해 여러가지를 찾아보았는데, @EnableEurekaClient 어노테이션도 있다는 것을 알게 되었다.
나는 서버에만 @EnableEurekaServer을 추가하고 실행하였고 문제가 없었는데, 클라이언트에 @EnableEurekaClient는 필요없는 걸까?

  • @EnableEurekaClient 어노테이션 없이도 Eureka Client로 사용할 수 있다. Spring Boot 2.4 이상과 Spring Cloud에서 도입된 변경 사항 때문에.
  • 버전 업그레이드를 하며 자동 설정(Auto-Configuration) 메커니즘이 강화되면서 spring-cloud-starter-netflix-eureka-client 의존성 추가시 어노테이션 없이도 Eureka Client가 자동 설정된다.

그럼 @EnableEurekaServer은 필수인가? 얘도 없어도 되는건가?

-> 필수다. 왜?
1. Eureka Server 역할 활성화

  • 애플리케이션을 Eureka Server로 설정하기 위해서 필요한 설정을 추가하는 역할을 함
  • Spring Boot는 기본적으로 클라이언트 역할을 수행하므로, 서버 역할을 하려면 명시적으로 어노테이션을 사용해야 함
  1. Eureka Server 관련 빈 생성
  • Eureka Server에 필요한 Spring Cloud Netflix 구성요소(빈)을 활성화 하는 역할을 함
  1. 자동 설정(Auto-Configuration) 트리거
  • Spring Cloud Netflix의 EurekaServerAutoConfiguration 클래스를 활성화하는 역할을 함
  • 이 클래스는 Eureka Server의 핵심 로직과 관련된 설정을 제공

요약

@EnableEurekaServer를 추가하지 않고 애플리케이션 실행 시 이 애플리케이션은 단순한 Spring Boot 애플리케이션으로 동작하며 Eureka Server의 역할을 수행하지 않는다.
클라이언트처럼 자동 설정 매커니즘으로 대체되지 않아 명시적으로 선언해야 함.
대시보드도 못 봄.
클라이언트는 의존성 설정만으로 잘 굴러간다.

로드 밸런싱

  • 트래픽을 여러 서버(or 서비스) 인스턴스에 균등하게 분배하여 시스템의 성능, 안정성, 가용성을 높이는 기술
  • 특정 서버에 과도한 부하가 걸리는 것을 방지하고 장애 발생 시에도 시스템이 정상적으로 동작할 수 있게 함

주요 개념

  1. 클라이언트-서버 간 부하 분산
  • 여러 서버가 동일한 서비스를 제공하는 환경에서, 클라이언트 요청을 적절히 분산 처리함
  1. 확장성 제공
  • 트래픽 증가 시 서버 인스턴스를 추가하여 수평 확장을 가능하게 함
  1. 고가용성
  • 특정 서버가 장애를 일으키더라도 로드 밸런서가 요청을 정상 서버로 전달하여 서비스 중단을 최소화함

종류

구현 방식에 따른 분류

  1. 하드웨어 기반 로드 밸런싱
    • 네트워크 장비를 활용
    • 고성능, 높은 비용
  2. 소프트웨어 기반 로드 밸런싱
    • 소프트웨어를 이용해 요청을 분산
    • 오픈소스 솔루션(NGINX, HAProxy) 또는 클라우드 서비스(AWS Elastic Load Balancer 등)에서 제공
  3. DNS 기반 로드 밸런싱
    • DNS 서버에서 트래픽을 분산
    • 단순하지만 유연성이 부족
  4. 애플리케이션 레벨 로드 밸런싱
    • 애플리케이션 레벨에서 트래픽 분산(Spring Cloud Ribbon, Spring Cloud LoadBalancer 등)

책임 주체에 따른 분류

  1. 클라이언트 사이드 로드 밸런싱

    • 클라이언트가 직접 서버 목록을 관리하고, 요청을 어떤 서버로 보낼지 스스로 결정

    특징
    - 클라이언트가 로드 밸런싱 알고리즘을 수행
    - 클라이언트는 서버 목록과 연결 상태를 알고 있음
    - 서버 목록은 서비스 디스커버리에서 가져옴
    - Spring Cloud LoadBalancer, Ribbon, Feign 등을 사용

    장점
    - 중앙 로드 밸런서 없이 동작하여 부하를 줄일 수 있음
    - 서버 간 상태 정보를 클라이언트가 직접 반영 가능

    단점
    - 클라이언트에서 구현해야 하므로 복잡성 증가
    - 서버 목록의 변경 사항을 실시간으로 클라이언트가 받아야 함

  2. 서버 사이드 로드 밸런싱

  • 클라이언트의 요청이 중앙 로드 밸런서(서버)를 거쳐 적절한 서버로 분배됨

    특징
    - 클라이언트는 중앙 로드 밸런서만 알면 됨
    - 로드 밸런서가 서버 상태, 부하 등을 고려해 요청을 분배함
    - NGINX, HAProxy, AWS Elastic Load Balancer, GCP Load Balancing 등 사용

    장점
    - 클라이언트는 로드 밸런싱에 대해 알 필요가 없음
    - 중앙 관리로 서버 목록 및 상태 변경이 자동 반영됨

    단점
    - 로드 밸런서에 부하 집중될 수 있음
    - 로드 밸런서가 장애를 일으키면 전체 시스템에 영향을 줄 가능성 있음

비교

구분클라이언트 사이드 로드 밸런싱서버 사이드 로드 밸런싱
로드 밸런싱 주체클라이언트가 직접 서버를 선택.로드 밸런서가 서버를 선택.
관리 방식클라이언트가 서버 목록을 관리.로드 밸런서가 서버 목록을 관리.
복잡성클라이언트가 구현해야 하므로 다소 복잡.클라이언트는 단순하지만 로드 밸런서 설정이 필요.
실시간 상태 반영클라이언트가 서비스 디스커버리로 상태를 갱신.로드 밸런서가 상태를 자동으로 반영.
장애 처리클라이언트에서 직접 처리해야 함.로드 밸런서가 자동으로 장애 서버를 제외.
예시Spring Cloud Ribbon, Feign, Eureka 등.NGINX, HAProxy, AWS/GCP Load Balancer 등.

로드 밸런싱 알고리즘

  1. 라운드 로빈
    • 요청을 순차적으로 서버에 할당. 단순하고 균등한 분배
  2. 가중치 라운드 로빈
    • 각 서버에 가중치를 부여하여 트래픽 분산. 서버의 성능 차이 고려
  3. 최소 연결
    • 현재 연결 수가 가장 적은 서버로 요청 전달. 각 서버의 부하 상태 고려
  4. IP 해싱(IP Hash)
    • 클라이언트 IP를 기반으로 요청을 특정 서버에 고정 분배
    • 동일 클라이언트 요청이 항상 같은 서버로 연결되게 함
  5. 랜덤
    • 무작위 선택하여 요청 분배
  6. 대기 시간 기반
    • 응답 시간이 가장 짧은 서버로 요청 전달. 실시간 네트워크 상태를 고려함

로드 밸런싱의 장단점

장점

  1. 성능 최적화: 부하 분산으로 응답 속도 개선
  2. 장애 내성: 서버 장애 시 다른 서버로 요청을 전달
  3. 확장성: 트래픽 증가에 따라 쉽게 서버를 추가할 수 있음

단점

  1. 추가적인 복잡성: 로드 밸런서 구성 및 관리가 필요
  2. 지연 시간: 요청 분배 시간에서 약간의 지연 시간 발생
  3. 비용: 고성능 로드 밸런서나 클라우드 서비스 이용 시 비용 증가

Spring 환경에서의 로드 밸런싱

  1. RestTemplate + Ribbon
    • RestTemplate에 로드 밸런싱을 추가하여 요청을 여러 서비스 인스턴스에 분산함
  2. Spring Cloud LoadBalancer
    • Ribbon이 Spring Cloud LoadBalancer로 대체되었으며, 더 가벼운 구현체로 동작함
  3. Feign Client
    • 선언적 HTTP 클라이언트로 로드 밸런싱과 간단한 요청 처리가 가능함

Feign Client

  • Spring Cloud에서 제공하는 HTTP 클라이언트
  • HTTP 요청을 Java 인터페이스로 추상화하여 코드의 간결성과 가독성을 높임
  • Eureka와 같은 서비스 디스커버리와 통합하여 서비스 이름만으로 다른 서비스에 쉽게 요청을 보낼 수 있음

특징

  • 선언형: HTTP 요청을 위한 코드를 작성할 필요 없이 인터페이스와 어노테이션 만으로 구현 가능
  • 로드 밸런싱 지원: Ribbon 또는 Spring Cloud LoadBalancer를 통해 로드 밸런싱 가능
  • 직관적 API: 간단한 어노테이션으로 HTTP 메서드 및 엔드포인트 정의
  • Fallback 지원: 장애 시 대체 동작(기본 응답 반환 등) 정의 가능

Ribbon

  • 넷플릭스가 개발한 클라이언트 사이드 로드 밸런서. 서비스 인스턴스 간의 부하를 분산
  • 클라이언트 사이드 로드 밸런싱 수행. 다양한 로드 밸런싱 알고리즘 지원.

특징

  • 클라이언트 사이드 로드 밸런싱: Eureka로부터 서비스 인스턴스 리스트를 제공받아 로드 밸런싱에 사용
  • 로드 밸런싱 알고리즘: 라운드 로빈, 가중치 기반 등 다양한 로드 밸런싱 알고리즘 지원
  • Failover: 요청 실패 시 타 인스턴스로 자동 전환
  • 커스터마이징 가능: 로드 밸런싱 알고리즘 변경이나 사용자 정의 가능

Spring Cloud의 최신 버전에서는 Spring Cloud LoadBalancer로 대체되었다고 함

Feign Client와 Ribbon의 관계

  • Feign은 기본적으로 Ribbon을 사용하여 로드 밸런싱을 수행
  • Feign Client를 사용하면 별도로 Ribbon을 명시적으로 설정하지 않아도 자동으로 통합되어 동작함

Feign Client, Ribbon 적용하기

server -> order -> product(3)
order에 요청을 보내고, 각각 서로 다른 포트로 열린 product 서비스들이 라운드 로빈 알고리즘으로 요청을 수행함

  1. 의존성 추가
  • server
dependencies {
	implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-server'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
	testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}

dependencyManagement {
	imports {
		mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
	}
}
  • order, product
dependencies {
	implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'
	implementation 'org.springframework.cloud:spring-cloud-starter-openfeign'
}
  1. 설정
  • order
spring:
  application:
    name: order-service
server:
  port: 19091
eureka:
  client:
    service-url:
      defaultZone: http://localhost:19090/eureka/
  • product
spring:
  application:
    name: product-service
server:
  port: 19092
eureka:
  client:
    service-url:
      defaultZone: http://localhost:19090/eureka/
  1. Feign Client 인터페이스 작성
  • order
    호출할 서비스를 대상으로 하는 인터페이스 작성
@FeignClient(name="product-service")
public interface ProductClient {
    @GetMapping("/product/{id}")
    String getProduct(@PathVariable("id") String id);
}
profile
고민고민고민

0개의 댓글