AWS 기술 블로그
AWS 에 구축하는 클라우드 디자인 패턴 시리즈 2부: 연결성 및 조합
AWS에 구축하는 클라우드 디자인 패턴 시리즈 2부에서는 서비스간 연결과 조합에 관해서 이야기해보려고 합니다. 이미 많은 서비스들은 모놀로식 서비스에서 마이크로 서비스로 변화하고 있고 그에 따라 기존 모놀로식 서비스에서는 발견되지 않던 이슈들이 발생하고 있습니다. 특히, 서로 분리된 서비스들 간의 통신 방식에 대해 새로운 접근이 필요하게 되었습니다. 가장 기본적인 해결책으로는 메시지 큐를 생성하여 통신하는 방법이 있었으나, 현재는 이보다 더 다양한 방법론들이 제시되고 있습니다.
이 글에서는 서비스간 연결을 위한 가장 기본 개념부터 실제 현업에서 사용하는 패턴들까지 순차적으로 알아보도록 하겠습니다.
“이벤트”와 “메시지”
대부분 사람들은 “이벤트”와 “메시지”를 같은 의미로 사용하지만, 분산시스템 이론에서는 두 가지를 구분하고 있고 그 두 가지는 어떤 종류의 시스템이 통신 범위에 포함되는지에 따라 구별하고 있습니다.
메시지를 사용하는 아키텍처에서는 API를 설계할 때 특정 형식을 지정해서 사용합니다. 따라서 API가 호출되는 항목에 대해서 예상 응답이 사전에 정의되어 있습니다. 예를 들어서 배달앱에서 주문을 요청하는 API를 호출한다면 이 API에는 가게 상호, 메뉴 이름, 개수, 비용 등이 포함되어 있어야 합니다. 이 배달 앱은 매우 구체적인 정보를 가져와서 어떤 필드에 어떤 정보가 들어 있는지 알고 있습니다. 이런 요청을 서비스에 메시지를 전달한다고 정의합니다.
이벤트 기반 아키텍처에서는 일련의 복잡한 프로세스와 트랜잭션으로 구성된 서로 다른 시스템에 어떤 일이 발생했음을 “알리는 것”이 핵심입니다. 이벤트가 발생하게 되면 처리하기 위한 새로운 프로세스가 시작됩니다. 예를 들어서 조리가 완료되었다는 이벤트가 발생하면, 배송 서비스에서 고객의 집 주소를 확인해서 배송 서비스를 시작하는 새로운 프로세스가 시작됩니다. 이런 상태의 변화 또는 업데이트를 이벤트라고 정의합니다.
이벤트와 메시지 방식은 각각의 장단점이 있지만 이 글에서는 이벤트에 집중해서 설명합니다. 이벤트 방식이 가져다주는 장점에 대해서는 이 링크에서 확인하실 수 있습니다. 간략하게 요약하면 이벤트 기반 아키텍처를 통해 서비스 간 종속성을 제거할 수 있고 자신의 서비스를 더 유연하게 만들 수 있습니다.
마이크로 서비스에서 사용하는 두가지 이벤트 관련 패턴
(1) 코레오그라피(Choreography) 패턴
Choreography 패턴의 핵심 아이디어는 의사 결정 로직을 각 서비스 단위로 분산하고 각 서비스가 이벤트 발생 시 수행해야 할 작업을 각자 결정하는 것입니다.
온라인 쇼핑몰 서비스를 예로 들어보겠습니다. 고객이 주문하면 주문 서비스는 관련된 서비스가 구독하고 있는 이벤트를 발생시킵니다. 각 서비스는 주문 이벤트를 수신하고 그에 따라 해야 할 일을 수행합니다. 예를 들어 창고 서비스는 창고에 해당 재고가 있는지 확인하고, 배송 서비스는 배송 담당자에게 연락하여 상품을 픽업하는 프로세스를 시작합니다.
Choreography 설계 시스템에서 관련된 모든 서비스는 완전히 분리되어 있고, 독립적인 분산 아키텍처라고 할 수 있습니다.
(2) 오케스트레이션(Orchestration) 패턴
Orchestration은 시스템의 로직을 모델링하는 가장 간단한 방법입니다. Orchestration 패턴의 핵심 아이디어는 의사 결정 로직을 중앙 집중화하고 시스템에 단일 관리자(Orchestrator)를 두는 것입니다.
온라인 쇼핑몰을 예로 들면 Orchestration 패턴으로 구현한다며 주문 서비스가 발생한 이벤트를 받아 창고, 배송 서비스에 전달하는 과정을 Orchestrator가 중앙에서 수행합니다.
적용 대상 및 고려사항
Choreography 패턴과 Orchestration 패턴은 각각 장단점을 가지고 있습니다. 먼저 Choreography 패턴의 장점은 다음과 같습니다.
- 느슨한 연결 : 각 서비스가 독립적으로 서비스 수행
- 확장성 : 쉬운 기능 확장
- 유연성 : 서비스에서 다음 단계에 대해 자체적으로 결정
- 내결함성 : 한 서비스가 다운되더라도 다른 서비스에 영향을 미치지 않음
Choreography 패턴이 항상 장점만 있는 건 아닙니다. 특히 서비스 간 통합 가시성 측면에서 어려움이 있습니다. 동기식 서비스 구성에서는 하나의 요청에 대한 결과만으로 전체 시스템의 정상 여부를 판단할 수 있습니다. 반면에 비동기식 패턴, 그중에서도 Choreography 패턴은 하나의 이벤트의 완료가 전체 시스템의 정상을 보장하지는 않습니다. 따라서 서비스 가시성을 위해 이벤트가 발생해서 수행된 전체 비즈니스 프로세스를 추적 관찰하는 시스템이 별도로 필요하다는 어려움이 있습니다.
비동기 시스템을 구축할 때 Choreography 패턴이 좀 더 선호되지만, 그렇다고 Orchestration 패턴이 좋지 않다는 의미는 아닙니다. Orchestration 패턴은 트랜잭션과 관련된 서비스를 모델링하는 데 장점이 있습니다.
- 운영 편의성 : 하나의 관리자로 모든 로직을 관리
- 비지니스 흐름 제어 : 비지니스 로직 변경이 용이
- 모니터링 : 엔드 투 엔드 모니터링 단순화
패턴 선택 방법
아쉽지만 어느 패턴이 무조건 정답이라고 말하기는 어렵습니다. 서비스의 특성, 규모 등 여러 가지 변수에 따라 마이크로서비스에 적합한 커뮤니케이션 스타일을 선택해야 합니다. 우리 서비스가 정기적인 업데이트와 새로운 기능이 자주 업데이트 된다면 경우에는 Choreography 패턴이 더 적합합니다. 기존에 동작하는 있는 서비스에 영향을 최소화해서 기능을 업데이트 할 수 있기 때문입니다. 그렇지만 Choreography 패턴을 사용하더라도 서비스가 중앙 집중화되고 다른 서비스에 종속적으로 작성된 경우 업그레이드를 실행하기 어려울 수도 있습니다.
수천 개의 마이크로서비스 간에 높은 수준의 동기화가 필요한 매우 복잡한 작업 로직이 있는 경우 Orchestration 패턴을 사용하는 것이 바람직합니다. 복잡한 워크플로의 경우 이벤트를 각각 관리하는 것이 어렵고 이를 관리해 주는 관리자(Orchestrator)를 사용했을 때 생산성이 훨씬 높기 때문입니다.
마지막 방식은 Choreography 패턴과 Orchestration 패턴을 결합하는 하이브리드 방식이 있습니다. 이렇게 구성하게 되면 Orchestrator에 장애가 발생했을 때 생기는 단일 장애 지점 문제를 해결 할 수 있고 그러면서도 Choreography 패턴의 장점을 누릴 수 있습니다.
핵심은 하나의 패턴에 매몰되지 않고 우리 서비스 상황에 맞게 적용하는 것입니다.
SAGA 패턴을 사용해서 서버리스 분산 애플리케이션 구축하기
앞서서 Choreography와 Orchestration 패턴에 대해서 알아봤고, 이 장에서는 좀 더 현실 서비스에 가까운 방식을 알아보기 위해 SAGA 디자인 패턴을 Orchestration 구조로 구성하는 방식에 대해서 배워보려고 합니다.
먼저 SAGA 디자인 패턴은 여러 서비스에서 분산 트랜잭션의 데이터 무결성을 유지할 수 있도록 도와주는 패턴입니다. 분산 트랜잭션에서는 트랜잭션이 완료되기 전에 여러 서비스를 호출할 수 있습니다. 문제는 서비스가 서로 다른 데이터 저장소에 데이터를 저장하는 경우 데이터 저장소 간에 데이터 일관성을 유지하기가 어렵습니다. 예를 들어 온라인 쇼핑몰에 상품 주문이 들어왔을 때 상품 DB와 재고 DB 등을 모두 업데이트 해야 수량의 불일치가 발생하지 않습니다.
트랜잭션의 일관성을 유지하기 위해 관계형 데이터베이스에서는 2단계 커밋(2PC) 기능을 제공합니다. 첫 번째 단계인 준비 단계에서 트랜잭션의 참여 프로세스(참여자)에 트랜잭션을 커밋하거나 롤백할 것을 약속하도록 요청합니다. 모든 참여자의 준비가 끝나면 두번째 커밋 단계에서 참가자에게 트랜잭션을 커밋하도록 요청합니다. 참여자가 준비 단계에서 커밋에 동의하지 않으면 트랜잭션이 롤백 됩니다.
마이크로서비스로 설계된 분산 시스템에서는 트랜잭션이 여러 데이터베이스에 분산되어 있으므로 2단계 커밋 방식은 병목이 될 가능성이 높습니다. 이 경우 해결책 중 하나가 바로 SAGA 패턴을 사용하는 것입니다.
SAGA는 로컬 트랜잭션으로 구성됩니다. SAGA의 각 로컬 트랜잭션은 데이터베이스를 업데이트하고 다음 로컬 트랜잭션을 트리거합니다. 트랜잭션이 실패하면 사가는 보상 트랜잭션을 실행하여 이전 트랜잭션에서 수행한 데이터베이스 변경 사항을 되돌립니다.
이 SAGA 패턴은 우리가 이전 장에서 배웠던 Choreography와 Orchestration 패턴을 이용해 구성할 수 있습니다.
하이 레벨 아키텍처
SAGA Choreography
SAGA Choreography 패턴은 마이크로서비스에서 발생하는 이벤트에 따라 달라집니다. SAGA 참여자는 이벤트를 구독하고 이벤트 트리거에 따라 동작합니다. 예를 들어서 주문 서비스는 OrderPlaced 이벤트를 생성합니다. 인벤토리 서비스는 해당 이벤트에 구독하고 있기 때문에 OrderPlaced 이벤트가 발생하면 인벤토리를 업데이트합니다. 마찬가지로 참여자 서비스도 방출된 이벤트의 컨텍스트에 따라 작동합니다.
SAGA Orchestration
SAGA Orchestration 패턴에는 Orchestrator라는 단일 관리자가 있습니다. SAGA Orchestrator는 전체 트랜잭션 수명 주기를 관리하고 조정하고 트랜잭션을 완료하기 위해 수행해야 하는 단계들을 알고 있습니다. 각 단계를 실행하려면 참여자 마이크로서비스에 메시지를 보내 작업을 수행합니다. 참여자 마이크로서비스는 작업을 완료하고 Orchestrator에게 메시지를 보냅니다. Orchestrator는 수신된 메시지를 기반으로 트랜잭션에서 다음에 실행할 마이크로서비스를 결정하는 방식입니다.
AWS 기반의 구현
이 예제에서는 AWS Step Function, AWS Lambda를 사용해서 SAGA Orchestration패턴을 구현합니다. 트랜잭션이 여러 데이터베이스에 분산되어 있는 경우 AWS Step Function을 사용해서 SAGA Orchestration 을 구현할 수 있습니다.
이 패턴은 다이어그램에서 강조 표시된 각 작업과 항공편, 렌터카, 결제에 대한 세 개의 Amazon DynamoDB 테이블에 대해 별도의 Lambda 함수를 배포합니다. 각 Lambda함수는 트랜잭션의 확인 또는 롤백 여부에 따라 각 DynamoDB 테이블의 행을 생성, 업데이트 또는 삭제합니다. 이 패턴은 Amazon Simple Notification Service (Amazon SNS)를 사용하여 구독자에게 문자(SMS) 메시지를 전송하여 거래 실패 또는 성공을 알립니다.
결론
AWS 환경에서 마이크로서비스를 구축해서 운영 중 혹은 준비 중이시라면 서비스 간 연결에 대해서 많은 고민이 필요합니다. 그리고 이를 해결하기 위해 다양한 전략과 서비스 우선순위에 대한 논의가 수행됩니다. 만약 여러분께서 AWS 기반 마이크로 서비스를 준비 중이고 어디에서부터 시작하면 좋을지 모르겠다면 AWS 마이크로서비스 페이지를 통해 방향성 수립에 도움이 되는 지표들을 찾으실 수 있습니다.
또한 AWS에서 제공하는 이벤트 기반 아키텍처 페이지를 참고하신다면 이벤트 기반 아키텍처 구축을 위한 환경 구성부터 모범 사례 기반의 클라우드 아키텍처 현대화를 손쉽게 확인해 보실 수 있습니다. 이외에도 다음 블로그 시리즈에서 새로운 클라우드 디자인 패턴에 관해 확인하실 수 있고, 우리 서비스에 적용하는 방법에 대해 배워 보실 수 있습니다.
AWS 에 구축하는 클라우드 디자인 패턴 시리즈 1부: 안정성
AWS 에 구축하는 클라우드 디자인 패턴 시리즈 2부: 연결성 및 조합
AWS 에 구축하는 클라우드 디자인 패턴 시리즈 3부: 마이그레이션
AWS 에 구축하는 클라우드 디자인 패턴 시리즈 4부: API 관리
AWS 에 구축하는 클라우드 디자인 패턴 시리즈 5부: 데이터 관리