Book/도메인 주도 설계로 시작하는 마이크로서비스 개발

도메인 주도 설계로 시작하는 마이크로서비스 개발 part 2-2.

silver_sun 2024. 4. 1. 20:58

2-1 에 이어 작성한다. (https://silvers-dev-note.tistory.com/46)

 

2.3~2.4 마이크로서비스의 아키텍처와 패턴

  • 외부 아키텍처
    • 인프라 영역, 플랫폼 영역, 애플리케이션 영역
  • 내부 아키텍처
    • 비지니스 애플리케이션(백엔드 서버 구성이라고 볼 수 있다.)

 

마이크로서비스를 구현하기 위한 방법과 패턴

크게 인프라, 플랫폼, 애플리케이션. 이렇게 3가지 단으로 나누어 패턴을 분류하고 있다. 

인프라

  • 컨테이너
    • 애플리케이션을 분리하여 여러개의 작은 서비스를 배포해야하는 마이크로서비스 특성에 맞게, 컨테이너 환경에 사용하여 인프라를 구성하는 것이 좋다. (책에서는 가장 많이 쓰이는 도커를 예시로 들고 있다.)
  • 컨테이너 오케스트레이션
    • 여러개의 컨테이너들을 보다 손쉽게 관리하기 위해 컨테이너 오케스트레이션 툴을 사용하는 것이 좋다. (책에서는 쿠버네티스를 예시로 들고 있다.)
    • 컨테이너 오케스트레이션은 확장, 장애 대응, 모니터링 등 다양한 기능들을 지원한다.
  • 다양한 클라우드 인프라 서비스
    • AWS, Azure, GCP 등 많은 곳에서 클라우드 서비스를 제공하고 있다. 상용화된 클라우드 서비스들이 상당히 많아 비용, 기술, 형태 등등 나에게 맞는 것을 찾아 효율적으로 사용할 수 있다.
      • IaaS(Infrastructure As a Service)
        • 가상 머신, 스토리지, 네트워크 같은 인프라를 필요한 만큼 적시에 제공하는 서비스. 사용자는 이러한 인프라를 이용해 개발 환경을 구성한 후 애플리케이션을 배포한다.
        • 예 ) AWS EC2, GCP Compute Engine, Azure VM
      • CaaS(Container as a Service)
        • 컨테이너 기반 가상화를 사용해 컨테이너를 업로드, 구성, 실행, 확장, 중지할 수 있는 서비스. 애플리케이션을 바로 구동할 수 있는 환경을 제공한다는 점에서 PaaS와 유사하지만, 다른 환경에도 이식 가능한 컨테이너 기반 가상화를 제공한다.
        • 예 ) AKS(Azure Kubernetes Service), AWS EKS, GKE, AWS ECS
      • PaaS(Platform as a Service)
        • 복잡함 없이 애플리케이션을 곧바로 개발, 실행, 관리할 수 있는 플랫폼 환경을 서비스 형태로 제공한다. IaaS 위에 실제로 애플리케이션이 실행될 수 있는 미들웨어나 런타임까지 탑재된 환경이라 생각하면 된다.
        • 예 ) Azure Web App, Google App Engine, Cloud Foundry, Heroke …
    • 클라우드 서비스는 크게 3가지 유형으로 나뉘고, 책에서는 유형별로 먼저 탐색을 하면 필요한 서비스를 찾는 것에 도움이 될 것이라고 안내하고 있다.

 

플랫폼

MSA 시스템을 구성하는 수많은 마이크로서비스를 하나하나 수동으로 빌드하고 배포하는 비효율적인 과정을 없애기 위해 자동화할 수 있는 플랫폼 패턴을 가져가는 것이 중요하다.

  • CI/CD 파이프라인 설계
    • 책에서 예시로 보여주는 배포 파이프라인 절차

  • 이처럼 배포 파이프라인 절차를 정하고, 클라우드 서비스를 이용하여 하나의 파이프라인을 수많은 마이크로서비스 서버에 적용할 수 있다.
    (예를 들어, yml 파일에 파이프라인 절차를 작성하고 해당 yml 파일을 마이크로서비스를 관리하는 클라우드 서비스에 사용하여 세부적인 정보들만 각 서버에 맞는 환경변수로 작성해준다면 동일한 파이프라인을 손쉽게 여러 곳에 적용할 수 있다. )
  • 마이크로서비스 관리/운영 패턴
    • 마이크로서비스를 도입하여 성공한 대표적인 사례 중 하나가 넷플릭스이다. 넷플릭스에서는 자신들이 어떤 식으로 마이크로서비스를 구성하고 관리하였는지 오픈소스를 통해 공유하고 있다. (넷플릭스 OSS)
    • 넷플릭스 OSS를 쉽게 사용할 수 있도록 스프링진영에서 개발한 것이 스프링 클라우드이다. 스프링 부트 + 스프링 클라우드의 조합은 마이크로서비스를 비교적 쉽게 구축할 수 있는 대표적인 관리/운영 패턴으로 자리매김하고 있다.
  • 서비스 디스커버리 패턴
    • 백엔드 MSA 서비스의 명칭과 유동적인 IP정보를 매핑하고, 매핑된 정보를 저장하는 저장소를 두어 관리하는 패턴.
  • API 게이트웨이 패턴
    • 클라이언트가 API 를 호출할 수 있는 단일 진입점(게이트웨이)을 만들어, 클라이언트는 해당 진입점으로만 요청을 취하고, 진입점에서 요청에 알맞는 서버로 요청을 보내는 방식.
    • 게이트웨이에 모든 API 요청에 대한 공통 기능을 수행하도록 구현한다면 더욱 효율적인 게이트웨이를 사용할 수 있다.
      • API 게이트웨이에서 할 적절한 기능
        • 레지스트리 서비스와 연계한 동적 라우팅, 로드 밸런싱
        • 인증 / 인가
        • 로그 집계 및 로깅
        • 메트릭 데이터 추출 및 확인
        • 트레이싱 서비스와 연계한 서비스 추적.
        • 모니터링 서비스와 연계한 장애 격리(서킷 브레이커 패턴)
    👍 API 게이트 웨이는 스프링 클라우드의 spring API gateway service 를 통해 비교적 쉽게 구현할 수도 있다 👍
    쿠버네티스 + 인그레스 조합으로도 구현할 수 있다.
  • BFF 패턴
    • BFF(Backend For Frontend) = 프런트엔드를 위한 백엔드 패턴으로, 웹, 앱 등등 … 클라이언트 유형에 따라 진입점(게이트웨이)를 설정하는 방식이다.
    • 이러한 방식을 통해서 각각의 클라이언트에게 꼭 필요한 기능들만 체크할 수 있게 할 수 있고, 공통된 부분들은 공통 게이트웨이를 통해 공통적으로 묶어 처리할 수 있다.
    • 클라이언트마다의 진입점을 분리해놓았기 때문에 각 클라이언트에 변경사항이 발생했을 경우에 타 클라이언트에 영향을 미치지 않고 작업할 수 있다.
  • 외부 구성 저장소 패턴
    • 데이터베이스 연결 정보, 파일 스토리지 정보 같은 외부 환경 설정 정보들을 외부 저장소에 저장하고, 해당 외부 저장소를 사용하는 방식.
      스프링에서는 spring Config 를 이용하면 보다 손쉽게 구현할 수 있다. 쿠버네티스에서는 이러한 방식을 쿠버네티스 ConfigMap 으로 제공하고 있다.
  • 인증/인가 패턴
    • 인증 / 인가는 공통된 보안이므로 모든 MSA 서버마다 구현하는 것은 상당히 비효율적이다. 그렇기에 인증 / 인가를 제공하는 서버 하나를 구현하고 인증 / 인가가 필요할 땐 해당 서버를 통하는 방식이다.

      일반적으로 MSA에서는 다음과 같은 패턴들을 사용한다고한다.
    • 세션 방식
      • 각자의 서비스에 세션을 저장하지 않고, 공유 저장소에 세션을 저장하고 모든 서비스가 동일한 사용자 데이터를 얻도록 한다. (보통 세션 저장소로는 레디스나 멤캐시드를 사용한다.)
    • 토큰 방식

토큰 방식

    • ➕ OAuth 를 사용하여 타 애플리케이션과 나의 인증 서버를 연계하는 방법도 있다.
  • 서킷 브레이커 패턴
    • 장애가 발생한 서비스를 격리해서, 타 서비스에 영향을 미치지 않도록 처리할 수 있는 방법.
    1. 임계값 설정
      1. 요청 실패에 대한 임계값을 설정하고 해당 값이 초과하면, 장애가 발생한 해당 서비스를 호출하려는 모든 시도를 실패하게 만들어, 어차피 실패할 요청에 대한 응답을 기다리는 대기 시간을 발생하지 않도록 한다.

폴백(fallback) 메서드 지정

  1. 폴백 메서드를 지정해두면 폴백 메서드가 자연스럽게 장애를 처리하고, 사용자는 특정 서비스에 장애가 발생했는지 눈치채지 못하게 된다. 폴백 메서드는 장애 발생시 대체할 수 있는 로직을 구현해놓을 수 있기 때문에 어떻게 구현을 해놨느냐에 따라 사용자는 장애 발생여부를 전혀 눈치채지 못할 수도 있다.
💡 폴백 메서드(fallback method)특징
Fallback 메서드는 일반적으로 서비스 호출이 실패하거나 예외를 발생시킬 때 호출되는 대체 메서드이다. 주로 분산 시스템에서 사용되며, 일부 서비스가 다른 서비스와 통신할 때 발생할 수 있는 장애 상황에 대비하여 구현된다.

특징
비상 상황 대처: 주요 서비스 호출이 실패했을 때, 대체 로직을 수행하여 시스템의 안정성을 보장한다. 이를 통해 전체 시스템의 성능을 유지하고 서비스 중단을 최소화할 수 있다.
설정 가능한 대체 로직: Fallback 메서드는 호출자가 원하는 대체 동작을 수행하도록 구현된다. 이를 통해 장애 상황에 대한 적절한 대응을 설정할 수 있고, 일반적으로는 기본값 반환, 캐시된 데이터 사용, 다른 서비스를 호출하는 등의 대체 동작을 수행한다.
서비스 내결함성: Fallback 메서드를 사용하면 일부 서비스의 장애가 전체 시스템에 영향을 미치는 것을 방지할 수 있다. 장애가 발생한 서비스 대신 대체 로직을 수행함으로써 사용자에게 최소한의 서비스를 제공할 수 있다.
코드 유연성: Fallback 메서드를 사용하면 코드의 유연성을 높일 수 있다. 서비스 간 의존성을 최소화하고, 각 서비스가 자체적으로 장애를 처리할 수 있도록 구성할 수 있다.

🚨 주의 🚨
Fallback 메서드가 일부 기능을 임시로 대체하는 것이므로, 장애가 해결되면 반드시 해당 기능을 복원해야한다.
Fallback 메서드가 호출되는 경우를 모니터링하고 로깅하여 시스템의 상태를 파악할 수 있어야 한다.
  • 모니터링과 추적 패턴
    • 당연하게도 애플리케이션의 장애를 감지할 수 있는 모니터링 기능의 필요하다. 그러나 마이크로서비스 특성상 장애 발생 시 서킷 브레이커 같은 패턴을 사용하게 되는데, 그러러면 모든 마이크로서비스들의 상태를 파악해야만한다. 즉, 모든 마이크로서비스를 한 눈에 모니터링 할 수 있는 방법이 필요하다.
    • Spring Cloud - Hystrix Dashboard
      스프링 클라우드를 사용한다면, 스프링 클라우드의 라이브러리인 히스트릭스를 이용해, 손 쉽게 대시보드를 구성할 수 있다.

Hystrix Dashboard

  • 이외에도 자바 진영에서는 Resilience4j 를 이용할 수도 있다.
  • 쿠버네티스를 사용한다면 이스티오를 사용할 수도 있다.

분산 트레이싱 서비스

  • 책에서는 집킨이라는 오픈소스 프로젝트를 예시로 들고 있다.스프링 클라우드의 분산 트레이싱 서비스인 Sleuth 과 호환이 가능하다. 스프링 클라우드를 사용한다면, 의존성만 추가해주면 집킨을 손 쉽게 구현할 수 있다.
  • implementation 'org.springframework.cloud:spring-cloud-sleuth-zipkin'
  • 집킨 → https://zipkin.io/
💡 분산 트레이싱 서비스
분산된 마이크로서비스 아키텍처에서 서비스 간 통신을 추적하고 모니터링하기 위한 도구. 각 요청의 수명을 추적하여 성능 문제를 해결하고, 시스템의 동작을 이해하는 데 도움을 줄 수 있다.
Zipkin, Jaeger, AWS X-Ray, OpenTelemetry 등이 있다.
  • 로그 집계 패턴
    • 서버가 탄력적으로 변화하는 마이크로서비스에서는 로그가 끊여서는 안되며 이벤트 스트림으로 처리가 되어야 한다. 또한, 특정 인프라나 기술에 의존하게 되지 않도록 로그 스트림과 비지니스 서비스는 분리되어, 서로에게 관여하지 않아야 한다.책에서는 대표적으로 ELK 스택을 설명하고 있다.
      그래서 MSA 에서는 중앙화된 로그 집계 패턴을 사용한다.
    • ELK (Elasticsearch Logstash Kibana) 
      • Elasticsearch
        • 분산형 검색/분석 엔진
        • 정형, 비정형, 위치 정보, 메트릭 등 원하는 방법으로 검색을 수행하고 결합 할 수 있다.
      • Logstash
        • 로그 집합기
        • 데이터 처리 파이프라인. 다양한 소스에서 동시에 데이터를 수집해 변환한 뒤, 특정 보관소로 데이터를 보낸다.
      • Kibana
        • 시각화
        • 히스토그램, 막대 그래프, 파이차트 등을 표현. 위치 데이터, 시계열 분석, 그래프 관계 탐색 등을 지원한다.
    (개인 경험으로, 분산 트레이싱 서비스처럼 요청의 수명을 추적하는 기능 또한 ELK 를 통해서도 확인 할 수 있었다. 상세한 API 호출 내역를 확인 할 수 있고, 어떤 class 를 통하는지 등등 상세한 내역을 확인 할 수 있다. )
  • 서비스 메시 패턴
    • 초창기 MSA 를 구현할때에는 API 게이트웨이, 서비스 레지스트리, 컨피그 서비스와 같이 운영 관리를 위한 여러 개의 기반 서비스를 별도로 각각 만들어야했다. 하지만 최근, 서비스 메시 패턴을 통해 그러한 번거로움을 줄일 수 있게 되었다.
      책에서는 대표적인 서비스 메시 패턴 구현체로 이스티오(Istio)를 설명한다.
      • 이스티오 Istio
        • 서비스 디스커버리, 라우팅, 로드 밸런싱, 로깅, 모니터링, 보안, 트레이싱 등의 기능을 제공한다.
        • 기본적으로 이스티오는 쿠버네티스에 탑재되어 서비스 메시 기능을 지원한다.
        • 이스티오는 애플리케이션이 배포되는 컨테이너로부터 완전히 격리되어 별도의 컨테이너로 배포되는 사이드카(Sidecar)패턴을 적용하고 있다.

 

애플리케이션

  • 마이크로 서비스 통신 패턴
    • 비동기 통신 방식을 사용하자.
      비동기 방식은 동기방식처럼 응답을 기다리지 않기 때문에, 완결성을 보장 할 수 없다. 그래서 우리는 메세지 브로커를 활용한다.
      메세지 브로커는 프로듀서, 컨슈머 사이에 구독이 발생하고, 구독하고 있는 컨슈머들에게 프로듀서가 메세지를 전달하는 방식의 메커니즘을 지니고 있다. 대표적인 메세지 브로커는 Kafka, RabbitMQ, ActiveMQ 등이 있다.
    • 메세지 브로커는 
      • 처리 규모에 따라 확장이 가능하다.
      • 통신하는 서비스들이 물리적으로 같은 시스템에 위치할 필요가 없다.
      • 서비스들끼리 프로세스를 공유할 필요가 없다.
      • 서비스들이 동시에 동작하지 않아도 된다.
      ⇒ 서비스 요구 사항에 따라 유연하고 탄력적으로 반응할 수 있다.
  • 저장소 분리 패턴
    • 각 마이크로서비스는 각자의 비지니스를 처리하기 위한 데이터를 직접 소유해야한다는 방식이다. 즉, 각 마이크로서비스는 마다의 DB를 지니도록 한다.
      • 정보은닉
        • 다른 서비스에 직접 노출하지 않고 각자가 공개한 API를 통해서만 접근한다.
      • 폴리글랏 저장소
        • 저장소가 격리돼 있기 때문에 각 저장소를 자율적으로 선택할 수 있다.
      • 영향도
        • 데이터를 통한 변경의 파급 효과를 줄여, 서비스를 독립적으로 만든다.
    • 그렇기 때문에 다음과 같은 효과를 가질 수 있다.
  • 분산 트랜잭션 처리 패턴
    • 각 마이크로서비스 마다 DB를 지니고 있게 된 만큼 데이터 일관성을 지키는 것 또한 어려워졌다. 이렇게 나뉘어진 환경에서 일관성을 지키기 위해서는 분산된 서비스를 하나의 트랜잭션으로 묶지 않고, 각 로컬 트랜잭션과 보상 트랜잭션을 이용해 비지니스 및 데이터 정합성을 맞출 수 있다. 그러한 패턴으로 사가(Saga) 패턴이 있다.
    • 이벤트 처리를 통해, 트랜잭션을 나누고 이벤트의 응답값을 통해 롤백이나 다음 과정을 진행하는 등의 액션을 선택하는 방식으로 구성한다.
  • CQRS 패턴
    • CQRS (Command Query Responsibility Segregation) (CQRS에 대한 내용은 포스팅 참고.)
      1. 하나의 저장소에 쓰기 모델과 읽기 모델을 분리하는 방식으로 쓰기 서비스와 조회 서비스를 분리.
      2. 물리적으로 쓰기 저장소와 조회 저장소를 분리.
      쓰기 모델에서 업데이트가 발생하면 읽기 모델에도 해당 사항이 반영되어야할 것이다. 이러한 부분 역시, 이벤트를 통하여 처리한다. (이러한 방식을 이벤트 주도 아키텍처라고 한다고 한다.)그냥 API 를 가지고 통신해도 될텐데 왜 CQRS 패턴을 사용하는걸까?하지만 CQRS 패턴을 도입한다면, 그때그때 이벤트를 발생시켜 주문 내역(R)을 생성할 수 있다.
    • API 통신으로 인해 하나의 주문 내역을 만들기 위해서는 제품, 주문, 고객, 배송 4가지의 서비스와의 통신이 필요하다. 주문 내역은 그 4가지 서비스와 비교적 강한 결합을 띄고 있는 것이다.
    • 이벤트를 통한 처리이기때문에 하나의 모델로써 관리하는 것처럼 즉각적으로 반영될 수 없겠지만, 어느 시점이 되면 결과적으로 일치하게 되고, 책에서는 결과적 일관성을 추구하는 것이라고 설명하고 있다.
    • CQRS 방식을 통해, 쓰기 모델과 읽기 모델을 다음과 같이 두 가지 방법으로 설계할 수 있다.
  • 이벤트 소싱 패턴이벤트 소싱 패턴은 CQRS 패턴의 연장선으로, 쓰기 최적화를 수행할 수 있는 방법으로, 데이터의 상태 트랜잭션 자체를 저장하는 전략이다.
    event_id entity_state entity_type data
    100 장바구니 생성됨 장바구니 {….}
    101 품목 1 추가됨 품목 {….}
    102 품목 2 추가됨 품목 {….}
    103 품목 1 삭제됨 품목 {….}
    이벤트 저장소를 따로 생성하여, 이벤트가 발생하면 해당 저장소로 이벤트 row 를 생성한다.이 전략으로 얻을 수 있는 이점
    • 메세지 브로커와 데이터베이스를 분리하지 않고 하나로 사용할 수 있다.
    • 메세지를 보내는 등.. 부가적인 과정이 없어, 복잡성이 줄고 쓰기 속도가 향상된다.
    • 상태를 저장하기 때문에 정확한 감사 로깅을 제공받을 수 있다.
    • 객체의 예전 상태를 파악하고 재구성하는 것이 간단해진다.
    • 외부 애플리케이션으로 이벤트를 전달하는 것도 간단해진다.
  • 변경이 이루어져야하는 모든 서비스들은 이벤트 저장소를 바라보고 있다가, 자신에게 맞는 이벤트 row 가 생성됐을 경우 작업을 수행하는 구조이다.
  • 설명하자면, 다음과 같이 해당 트랜잭션이 가지는 상태 자체를 저장한다는 말이다.
  • 데이터는 원자성을 지켜야하는데, 이벤트 소싱 패턴을 통해 CQRS 패턴을 사용하면서도 원자성을 지킬 수 있다. (데이터베이스에 데이터를 저장하는 과정과 읽기 서비스로 메세지를 보내는 작업을 언제나 완전하게 진행하여 함께 실행되도록 하는 것이다.)
  • 프론트와 서버사이에도 마이크로서비스를 위한 패턴이 필요하다.

 

MSA 프로젝트 레퍼런스

https://www.dtoncloud.com/landing/board_customerCase_list.html?bltnNo=11569663449355

https://devocean.sk.com/blog/techBoardDetail.do?ID=163544