FrontEnd

MSA 핵심 디자인 패턴-saga, event sourcing, CQRS, BFF, Api Gateway

푸고배 2022. 4. 3. 15:49

Two Phase Commit

분산 DB 환경에서 쓰는 방법으로 주로 RDBMS에서 기능을 제공한다. Two-Phase Commit은 말 그대로 2 단계에 거쳐서 데이터를 영속화 하는 작업이다. 위 그림과 같이 여러 DB가 분산되었을 때, 트랜잭션을 조율하는 조정자(Cooredinator)가 존재한다. 조정자의 역할은 트랜잭션 요청이 들어왔을 때 두 단계를 거쳐 트랜잭션 진행을 담당하는 것이다.

첫 번째 단계는 Prepare이며, 이는 쉽게 말해 연관된 DB에게 데이터를 저장할 수 있는 상태인지 묻는 과정이다.

메시지를 받은 DB에서는 Commit 작업을 위한 준비를 진행한다. 이후 데이터를 영속할 수 있는 준비가 완료되면 조정자에게 준비가 완료되었음을 알리고, 반대로 Commit 할 수 없다면 불가하다는 메시지를 전달한다.

조정자는 첫 번째 단계에서 전달한 메시지에 대한 응답을 기다린다. 모든 메시지 수신이 완료되면 두 번째 단계인 Commit을 진행한다. Commit 단계에서는 조정자가 연관된 DB에게 데이터를 저장하라는 메시지를 송신하며, 수신받은 DB에서는 각자 DB에 데이터를 영속화한다.

모든 DB에서 트랜잭션 처리가 완료되면 전체 트랜잭션을 종료한다. 만약 두 단계를 거치는 과정에서 연관된 DB 중 하나의 DB라도 Commit을 할 수 없는 상황이라면, 모든 DB에게 Rollback을 요구한다. 트랜잭션을 종료하는 동시에 모든 DB 데이터가 영속화된다. 따라서 트랜잭션의 범위는 데이터를 처리하는 DB 전체이다.

MSA 환경에서 Two-Phase Commit의 문제점

Two-Phase Commit은 DBMS 간 분산 트랜잭션을 지원해야 적용이 가능하다. 하지만 NoSQL 제품군에는 이를 지원하지 않고, 함께 사용되는 DBMS가 동일 제품군(Oracle, MySQL, Postgres)여야 한다. 따라서 DBMS Polyglot 구성이 어렵다.

또한 Two-Phase Commit은 보통 하나의 API 엔드포인트를 통해 서비스 요청이 들어오고 내부적으로 DB가 분산되어있을 때 사용된다. 하지만 MSA 환경에서는 각기 다른 APP에서 API간으로 통신을 통해 서비스 요청이 이루어지기 때문에 구현이 쉽지 않다.

Saga Pattern

Saga 패턴은 트랜잭션의 관리주체가 DBMS가 아닌 Application에 있다. App이 분산되어있을 때, 각 App 하위에 존재하는 DB는 Local 트랜잭션 처리만 담당한다.

따라서 각각의 App에 대한 연속적인 트랜잭션 요청 및 실패의 경우에 Rollback 처리(보상 트랜잭션)을 Application에서 구현해야 한다.

Saga 패턴은 위 그림과 같이 연속적인 업데이트 연산으로 이루어져있으며, 전체 데이터가 동시에 영속화되는 것이 아니라 순차적인 단계로 트랜잭션이 이루어진다. 따라서 Application 비지니스 로직에서 요구되는 마지막 트랜잭션이 끝났을 때, 데이터가 완전히 영속되었음을 인지하고 이를 종료한다.

Two-Phase Commit과 다르게 Saga를 활용한 트랜잭션은 데이터 격리성(Isolation)을 보장해주지 않는다. 하지만 Application의 트랜잭션 관리를 통해 최종 일관성(Eventually Consistency)을 달성할 수 있기 때문에 분산되어있는 DB간에 정합성을 맞출 수있다. 또한 트랜잭션 관리를 Application에서 하기 떄문에 DBMS를 다른 제품군으로 구성할 수 있는 장점이 있다.

하지만 이러한 일관성을 달성하기 위해서는 프로세스 수행 과정상 누락되는 작업이 없는지 면밀히 살펴야하며, 실패할 경우 에러 복구를 위한 보상 트랜잭션 처리에 누락이 없도록 설계해야 한다.

Saga Pattern의 종류

1. Choreography-Based Saga

Choreography-Based Saga는 자신이 보유한 서비스 내 local 트랜잭션을 관리하며, 트랜잭션이 종료되면 완료 이벤트를 발행한다. 만약 그 다음으로 수행해야할 트랜잭션이 있다면, 해당 트랜잭션을 수행해야하는 App에서 완료 이벤트를 수신받고 다음 작업을 처리한다. 이때 Event는 Kafka와 같은 메시지 큐를 이용해서 비동기 방식으로 전달할 수 있다.

Choreography-Based Saga 방식에서는 각 App별로 트랜잭션을 관리하는 로직이 있다. 따라서 중간에 트랜잭션이 실패하면, 해당 트랜잭션 취소 처리를 실패한 App에서 보상 이벤트를 발생하여 Rollback 처리를 시도한다.

위와 같은 구성은 구축하기 쉬운 장점이 있으나, 운영자의 입장에서 트랜잭션의 현재 상태를 알기가 어렵다.

2. Orchestration-Based Saga

Orchestration-Based Saga는 트랜잭션 처리를 위한 Saga 인스턴스(Manager)가 별도로 존재한다. 트랜잭션에 관여하는 모든 App은 Manager에 의하여 점진적으로 트랜잭션을 수행하며 결과를 Manager에게 전달한다. 비지니스 로직상 마지막 트랜잭션이 끝나면 Manager를 종료하여 전체 트랜잭션 처리를 종료한다. 만약 중간에 실패하게 되면 Manager에서 보상 트랜잭션을 발동하여 일관성을 유지하도록 한다.

모든 관리를 Manager가 호출하기 때문에 분산 트랜잭션의 중앙 집중화가 이루어진다. 따라서 서비스간의 복잡성이 줄어들고 구현 및 테스트가 상대적으로 쉽다. 또한 트랜잭션의 현재 상태를 Manager가 알고 있기 때문에 롤백을 쉽게 할 수 있는 것 또한 장점이다. 하지만 이를 관리하기 위한 Orchestrator 서비스가 추가되어야 하기 때문에 인프라 구현의 복잡성이 증가되는 단점이 존재한다.

Event Sourcing

어플리케이션의 모든 상태 변화를 순서대로 이벤트로 보관하여 처리하는 개념이다. 이렇게 모든 상태를 이벤트의 흐름으로 처리하므로써 어플리케이션 개발을 간소화하고 분산환경에 적절히 대응할 수 있다.

모든 이벤트는 시간 순으로 기록이 되며 각 서비스들은 자신이 필요한 이벤트만 보고 있다가 최신의 상태를 유지하면 된다.

즉 회원관리 서비스는 회원의 생성 삭제, 또는 변경 이벤트만 참조하여 회원의 최신정보(snapshot)을 가지고 빠르게 최신 정보를 유지할 수 있으며, 상품 관련 서비스는 상품의 주문, 배송 등과 관련된 이벤트만 참조(Listening)하고 최신 상태를 유지한다.

이러한 Event Sourcing이 완벽하게 동작하려면 모든 이벤트는 기록되어야 하고, 이벤트를 받지 못하는 서비스가 없도록 재전송이 가능해야한다. 또한 이벤트는 자신의 서비스 또는 WAS 내에서만 받을 수 있는 것이 아니라 브로드캐스팅이 되어야 분산된 환경에서도 완벽하게 동작할 수 있어야 한다.

Event Sourcing의 이러한 특징 때문에 MSA 시스템을 적용할 때 Event 기반으로 구현하면 보다 손쉽게 구현이 가능하며, 각 서비스 간의 loosed coupling이 가능하다.

CQRS(Command Query Responsibility Separation)

Event Sourcing과 항상 함께 알아두어야 할 개념으로 CQRS가 있으며 간단히 설명하면 Command와 Query의 책임을 분리하자는 것이다.

Command는 일반적인 Database 기준으로 상태를 변경하는 Create, Update, Delete와 같은 메소드로 생각할 수 있으며, Query는 단순 데이터를 조회하기 위한 Read 메소드로 생각할 수 있다. 전에 설명한 Event Sourcing과 연계시켜 생각해보면 Event는 Command 즉 상태 변경되는 경우만 발생하고 그 경우만 기록하면 된다. 단순 Query인 경우는 모델의 정보를 변화시키지 않기 때문에 그 기능을 분리하여 좀 더 손쉽게 Event Sourcing Pattern을 적용할 수 있다.

또한 Event Sourcing 개념에 의해 생성된 최신 데이터(snapshot)은 CQRS에서 Query의 개념으로만 접근하여 조회하기 때문에 로직을 간략화하고 cache를 통해 조회속도를 손쉽게 개선할 수 있다.

Backend For Front(프론트엔드를 위한 백엔드)

최신 비즈니스 애플리케이션 개발, 특히 마이크로 서비스 아키텍처에서 프론트엔드 및 백엔드 애플리케이션은 분리되어 별도의 서비스로 동작하며 API 또는 GraphQL을 통해 연결된다. 애플리케이션에 모바일 앱 클라이언트도 있는 경우 웹 및 모바일 클라이언트 모두에 동일한 백엔드 마이크로 서비스를 사용하는 것은 문제가 된다. 모바일 클라이언트의 API 요구 사항은 화면 크기, 디스플레이, 성능, 에너지 소스 및 네트워크 대역폭이 다르기 때문에 일반적으로 웹 클라이언트와 다르다.

  • 특정 UI에 맞게 최적화가 가능하며, 더 높은 보안을 제공한다.
  • BFF는 비지니스 로직을 포함해서는 안되며 클라이언트 별 로직 및 동작만 포함해야하므로 신중한 설계 및 구현이 필요하다.
  • 애플리케이션에서 API 요구사항이 다른 여러 UI가 있는 경우 적합하다.

API Gateway

MSA는 큰 서비스를 잘게 쪼개어 개발/운영 하는 아키텍처로 하나의 큰 서비스는 수십~수백개의 작은 서비스로 나뉘어지며, 만약 이를 클라이언트에서 서비스를 직접 호출하는 형태라면 다음과 같은 문제점이 생길 수 있다.

  • 각각의 서비스마다 인증/인가 등 공통된 로직을 구현해야하는 번거로움이 있다.
  • 수많은 API 호출을 기록하고 관리하기가 어렵다.
  • 클라이언트에서 여러 마이크로 서비스에 대한 번거로운 호출을 해야한다.
  • 내부의 비지니스 로직이 드러나게 되어 보안에 취약해진다.

특히 이러한 문제점들은 마이크로 서비스의 갯수가 많아질 수록 기하급수적으로 늘어나게 된다.

어느 규모 이상의 마이크로 서비스 기반 어플리케이션에는 API Gateway를 도입하는 것이 효율적이다.

API Gateway는 API 서버 앞단에서 모든 API 서버들의 엔드포인트를 단일화 해주는 또 다른 서버이다. API에 대한 인증과 인가 기능을 가지고 있으며, 메시지의 내용에 따라 어플리케이션 내부에 있는 마이크로 서비스로 라우팅하는 역할을 담당한다.

API Gateway의 시작은 SOA의 핵심 인프라라고 할 수 있는 ESB(Enterprize Service Bus)에서 시작되었다. 따라서 API Gateway의 많은 부분들이 ESB로부터 승계되었다. ESB가 SOAP/XML 기반의 무거운 기능을 가졌다면, API Gateway는 REST/JSON 기반으로 보다 가볍게 설계된 것이 특징이다.

API Gateway의 주요기능

1. 인증 및 인가(Authentication and Authorization)

마이크로 서비스 아키텍처에서 각각의 서비스에 API 호출에 대한 인증 및 인가를 하는 것은, 같은 소스코드를 서비스 인스턴스들만다 심어주어야한다는 것을 의미한다. 이러한 경우, 소스의 중복이 심하여 유지 관리가 어려운 것은 물론, 로깅 및 모니터링을 관리하는 것도 매우 어려워진다. 이러한 이유로 인증서 관리나, 인증, SSL, 프로토콜 변환과 같은 기능들은 API Gateway에서 오프로드 함으로, 각각의 서비스의 부담을 줄이고, 서비스의 관리 및 업그레이드를 보다 쉽게할 수 있게 한다.

Authentication(인증)과 Authorization(인가)의 차이
Authentication은 유저가 누구인지 확인하는 절차(A라고 하며 접근하는 사람이 진짜 A인지 확인하는 절차)이고, Authorization은 어떠한 유저가 특정 자원에 접근하려 할 때, 그에 대한 접근 권한이 있는지 확인하는 절차이다.

2. 요청 절차의 단순화

여러 마이크로서비스를 대상으로하는 기능을 이용하려할 때, 만약 API Gateway가 없다면 클라이언트에서 여러 서비스들에 대해 요청을 진행해야 했을 것이다. 하지만 API Gateway는 여러 클라이언트의 요청을 단일 클라이언트의 요청으로 대체 가능하도록 한다. 따라서 클라이언트와 백엔드 간의 API 통신량을 줄여주어 대기 시간을 줄이고 효율성을 높여줄 수 있다.

3. 라우팅 및 로드밸런싱

API Gateway는 클라이언트로부터 접수된 메세지에 따라, API 호출을 적절한 서비스에 라우팅할 수 있는 기능이 있다. 또한, 서비스 인스턴스들에 대한 부하분산을 할 수 있다.

4. 서비스 오케스트레이션

오케스트레이션은 여러 개의 마이크로 서비스를 묶어 새로운 서비스를 만드는 개념이다. 오케스트레이션 로직을 과도하게 넣는 것은, API Gateway의 부담을 늘리는 것으로, 성능 저하를 일으킬 수 있어, MSA와 API Gateway에 대한 높은 수준의 기술적 이해를 바탕으로 이루어져야한다.

5. 서비스 디스커버리

API Gateway는 각 서비스를 호출하기 위해, 서비스마다의 IP주소와 포트번호를 알고 있어야 한다. legacy 환경에서는 고정적인 IP주소를 가지고 있기 때문에 크게 문제될 것이 없지만, 클라우드 환경에서는 동적인 환경에서 배포되기 때문에 서비스의 위치를 찾는 것이 어렵다. 이러한 서비스의 위치(IP주소와 포트번호)를 찾는 것을 "Service Discovery"라 하며, API Gateway에서는 서버사이드나, 클라이언트 사이드를 기준으로 하여 서비스 디스커버리를 구현할 수 있다.

API Gateway 적용시 고려해야할 사항

1. API Gateway 적용의 가장 큰 단점은 API Gateway를 내부 마이크로서비스와 결합한다는 것이다. 이러한 결합은 기존 SOA에서의 ESB(Enterprise Service Bus)에서 발생했던 문제점이 다시 발생할 수 있다.

2. API Gateway의 Scale-out 적용이 유연하게 일어나지 않을 경우, API Gateway가 병목지점이 되어 어플리케이션의 성능저하가 일어날 수 있다.

3. API Gateway라는 추가적인 계층이 만들어지는 것이기 때문에, 그만큼 네트워크 latency가 증가하게 된다.


Refference

18. Saga 패턴을 활용한 트랜잭션 관리 - 1

1. 서론 MSA 아키텍처를 구성하기 어려운 이유중 가장 큰 문제는 트랜잭션(Transaction)입니다. 기존 모놀로틱(Monolithic) 환경에서 DBMS가 기본적으로 제공해주는 트랜잭션 기능을 통해 데이터 Commit 혹

cla9.tistory.com

Event 기반 Microservices - Event Sourcing 및 CQRS

현재 IT업계에서는 기존 Monolithic 방식의 개발방식에 대한 많은 문제점을 인식하고 있는 상황입니다. 그에 따른 대안으로 Microservice Architecture나 Serverless Architecture등 다양한 방안 찾고 있지만 일..

cyberx.tistory.com

마이크로 서비스 아키텍처와 가장 중요한 10 가지 디자인 패턴

마이크로 서비스 아키텍처, 마이크로 서비스 별 데이터베이스, 이벤트 소싱, CQRS, Saga, BFF, API 게이트웨이, Strangler, 회로 차단기, 구성 외부화, 소비자 주도 계약 테스트 대규모 소프트웨어 시스템

ichi.pro

MSA 제대로 이해하기 -(3)API Gateway

지난 포스팅에서 MSA를 구성하는 아키텍처 요소에 대해 살펴보았습니다. Inner Architecture와 Outer Architecture로 나누어 설명을 드렸었죠. MSA 제대로 이해하기 - (2)아키텍처 개요 오늘은 그 중 Outer Archit

velog.io

반응형