IT Paradigm/MSA

MSA(Microservice Architecture)란, 개념 이해하기

yeTi 2021. 7. 28. 14:41

안녕하세요. yeTi입니다.
오늘은 K-MOOC의 강좌인 Microservice 설계 및 구현을 듣고 내용을 정리한 것을 공유하고자 합니다.

강좌를 보고난 후 MSA를 하기 위한 설계적 방향성을 가질 수 있는 계기가 되었고, 어떠한 수준이 되어야 우리는 MSA를 하고 있다고 할 수 있는지도 이해할 수 있는 계기가 되어 개인적으로 유익한 시간이었습니다.

Microservice 개념과 특성

Biz민첩성과 아키텍처 요건

시스템이 비즈니스에 유연하게 대응하기 위해서는 새로운 서비스의 추가 및 변경이 용의해야하고 이벤트에 따른 트래픽의 증감에도 유연하게 대응해야 합니다.

이에 대응하기 위해서 하드웨어 수준에서는 클라우드 서비스를 활용하여 scale-up이나 scale-out을 유연하게 할 수 있는 기반을 마련하고, 소프트웨어 수준에서는 이에 적절하게 특정 서비스를 scale-out할 수 있는 구조를 가져야 합니다.

이러한 설계 방식을 Cloud Native Architecture라 칭하고, Cloud Native Computing Foundation (CNCF)에서 다양한 오픈소스를 개발하면서 일반 사용자에게 공유하고 있습니다.

참고 자료

일반적으로 마이크로서비스는 모노리스와 비교합니다.
두 아키텍처의 큰 차이점은 비즈니스를 구성하는 서비스의 단위로 볼 수 있는데요.

모노리스는 단일 서비스로 구성하는 반면에 마이크로서비스는 다양한 서비스의 조합으로 비즈니스를 구성하는 특징을 가지고 있습니다.

이는 비즈니스 민첩성의 연장선상에서 볼 수 있는데요.

모노리스 방식으로 개발된 서비스들은 단일 개발환경에서 단일 데이터베이스에 모든 데이터를 저장하기 때문에 scale-out시 인스턴스의 규모가 커지고 작은 변화에서 빌드 및 배포시간이 커지는 단점을 가지고 있습니다.

이를 마이크로서비스는 비즈니스를 작은 도메인으로 나누고 각 도메인에 따라 개발환경 및 데이터를 나눔으로써 scale-out시 인스턴스의 규모가 작고 특정 도메인의 변화는 해당 서비스만 빌드 및 배포하면 되는 장점을 가지고 있습니다.

두 아키텍처 방식은 보다 다양한 장/단점을 가지고 있지만 비즈니스 민첩성의 관점에서 차이점을 간략하게 확인했습니다.

MSA 특징

MSA(Micro Service Architecture)는 단순히 모노리스 아키텍처를 세분화하여 작은 단위의 서비스로 나누것이 전부는 아닙니다.

MSA는 설계적 관점뿐만 아니라 제품을 효율적으로 생산하는데 기반을 둔 조직문화도 고려해야하는데요.

다양한 기술스택을 가진 사람들이 모여 비즈니스의 빠른 의사결정과 개발 및 운영을 할 수 있도록 cross-functional 팀을 지향합니다.

또한 팀은 비즈니스의 전체 라이프사이클을 책임져야하기 때문에, 서비스 담당자와 소통이 수월한 개발 프로세스인 애자일을 활용하고, 개발외의 업무 부담을 줄이도록 배포 및 테스팅 프로세스를 자동화하는 devops 환경을 적극적으로 사용합니다.

데이터 관점에서도 특징을 가지고 있습니다. 모노리스 아키텍처에서는 모든 데이터가 하나의 데이터베이스에서 관리되고 이를 하나의 트랜젝션으로 처리할 수 있는데요.
MSA에서는 polyglot persistence 접근 방법을 선택하여 서비스별로 데이터를 나눠서 관리하고, 트랜젝션은 eventual transaction으로 서비스간 협업에 의하여 일시적으로 결과의 불일치가 있을 수는 있지만 결과적으로 데이터 일관성을 유지하는 방식을 취합니다.

서비스간 통신은 간결한 방식으로 rest apimessage queue를 사용합니다.

마지막으로 서킷 브레이크 패턴과 같이 실패를 위한 설계를 하여 더 이상 진행할 수 없는 환경에서도 대응할 수 있도록 하고 이를 위해서 실시간 모니터링 체계를 갖추어야 합니다.

Microservice Architecture

마이크로서비스 outer/inner 아키텍처 의미

소프트웨어 아키텍처란, 소프트웨어를 구성하는 요소와 그 구성요소 간의 관계를 정의한 것입니다.

예를 들어, 성능, 가용성, 보안, 유지보수성, 확장성 등등 비기능적 요소들을 어떻게 고려하여 시스템을 구축할 것인가에 대해 정의한 것으로 볼 수 있습니다.

그렇다면 MSA에서 고려되어야 하는 요소에는 무엇이 있을까요?
MSA를 클라우드라는 가상의 인프라를 활용하여 구조화하기 떄문에 클라우드의 특징을 고려해서 설계해야 합니다. 그렇다면 클라우드를 고려했을 때 가장 중요한 요소는 바로 비즈니스의 변화에 대응할 수 있는 유연성과 확장성입니다.

1_MSA_아키텍처
마이크로 서비스가 운영되는 환경을 정의하는 MSA 아우터 아키텍처와 실제로 비즈니스가 실행되는 각각의 마이크로서비스 내 구조를 정의하는 MSA 이너 아키텍처가 있습니다.

2_아키텍처_문서
최근에는 아키텍처 문서를 보면 솔루션의 아이콘을 그리는 경향이 있는데요. 이는 아키텍처링이 적절한 솔루션 및 오픈소스를 선택하는 과정임을 알 수 있습니다.

마지막으로 Cloud Native Landscape를 보면 클라우드 기반의 어플리케이션을 구축하기 위한 다양한 제품을 표현하고 있는데요. 이를 통하여 적절한 제품을 선택하여 아키텍처를 선택하여 조합할 수 있습니다.

마이크로서비스 outer 아키텍처

앞서 마이크로서비스의 outer 아키텍처는 마이크로서비스가 운영되는 환경을 정의하는 것이라고 했습니다.

인프라

MSA에서 인프라 환경은 클라우드 환경을 의미합니다.

여기서 아키텍트는 인프라 영역에 클라우드 사업자가 제공하는 IaaS를 선택할지, 기존 베어 메탈 장비나 가상 머신인 VM을 선택할지 결정해야 합니다.

플랫폼

인프라에서와 마찬가지로 클라우드 사업자가 제공하는 PaaS를 선택할지, 직접 오픈 소스로 프라이빗한 PaaS를 구축할지 결정해야 합니다.

먼저 가상 머신과 컨테이너 환경 중 적절한 것을 선택하고 오케스트레이션을 위한 제품을 선택하면 됩니다.

데브옵스 환경

데이옵스는 팀이 개발 및 운영에 책임을 가지는 상황에서 빌드 및 테스트, 배포 작업을 자동화하여 릴리즈 주기를 단축하고 배포 시간을 단축하기 위한 일련의 과정입니다.

보통 CI/CD 파이프라인을 구성하는 것으로 불리는데요.

개발자가 형상관리 툴에 코드를 merge하는 것을 시점으로 하여 빌드 및 정적 분석, 테스트, 배포 결과물까지 만드는것을 CI (지속적 통합)로 부르고

배포 결과물을 운영 서버에 전달하거나, 서비스에 적용하는것을 CD로 부릅니다.

이러한 자동화 파이프라인을 어떤식으로 구성할지를 아키텍트가 정의하는 것입니다.

기반 서비스

Netflix에서 MSA로 서비스를 운영하면서 겪은 시행착오를 개선하면서 만든 패턴들을 오픈소스화하여 Netflix OSS로 제공한것은 많은 개발자들이 알고 있습니다.

이러한 패턴들로 만들어진 서비스를 마이크로서비스를 구동하기 위한 기반 서비스라고 할 수 있습니다.

이러한 기반 서비스의 종류를 보면 다음과 같습니다.

  • API Gateway: 서비스의 단일 endpoint 제공 및 service mesh, 라우팅 등 API를 사용함에 있어 필요한 기능을 수행하는 패턴입니다.
  • BFF (Backend For Frontend): 다양한 종류의 클라이언트에 대응하기 위한 패턴입니다.
  • 서비스 디스커버리: 라우팅을 위해 마이크로서비스의 메타정보를 관리하는 패턴입니다.
  • 컨피그 서비스: 클라우드에서 돌아갈 애플리케이션은 하드웨어나 컨테이너에 종속된 정보를 가지지 않기 위해 환경 설정 정보를 관리하는 패턴입니다.
  • 인증/인가 서비스: 사용자에 대한 인증 및 인가에 대한 처리를 해주는 패턴입니다.
  • Circuit breakekr: 특정 서비스의 장애가 다른 서비스로 전파되지 않도록 요청을 차단하는 패턴입니다.
  • Fallback 처리: Circuit breaker 패턴에서 요청에 대한 차단 후 처리를 해주는 패턴입니다.
  • Log Aggregation, Exception tracking: 통합적으로 로그를 관리하는 패턴입니다.
  • Distributed Tracing: 분산 환경에서 요청을 추적해주는 패턴입니다.

마이크로서비스 inner 아키텍처

Application ArchitectureJAVA 엔터프라이즈 발전흐름을 보면, 개발 효율과 유연성을 위해 어플리케이션이 처리하는 기능들을 응집성 있게 각 영역으로 구분하고 이들 간의 의존관계를 최소화하려는 노력들을 하고 있습니다.

Application Architecture를 표현하는 방식에 있어 대표적으로 Layered Architecture가 존재하는데요.

Layered Architecture (계층형 아키텍처)란 Layer간에는 인터페이스로 호출함으로써 요청 규약을 명확히 정의하여 개발 효율성을 높이고 로직의 변경 및 확장을 용이하게 해주는 구조입니다.

하지만 Layered Architecture도 layer간 인터페이스가 일방향에 의존하기 때문에 의존하는 층에 영향을 받을 수밖에 없습니다. 이러한 단점을 해결하는 방안이 Hexagonal Architecture입니다.

Hexagonal Architecture (헥사고널 아키텍처)
3_hexagonal_architecture
위 그림과 같이 사용자 인터페이스나 저장소가 변경이 되어도 구현 처리를 하는 층이 그 차이점을 흡수해서 Business Logic 층이 영향이 없게 하는 구조를 말합니다.

그렇다면 Layered와 Hexagonal Architecture를 활용하여 마이크로서비스 각 Layer를 어떻게 구조화해야 되는지에 대해 상세히 살펴보도록 하겠습니다.

Presentation Layer에서 가장 많이 사용하는 것은 MVC 패턴입니다.
이는 마이스로서비스로 설계되면서 Front-End Microservice로 구조는 가져가는데요. Front End를 구성하는 UI 컴포넌트도 독립적으로 분해되면서 Server-side Page Fragment Composition 방식으로 발전됩니다.

Business Logic LayerTransaction Script 패턴Domain Model 패턴이 많이 언급됩니다.
Transaction Script 패턴 구조는 단순한 입출력 구조의 쉬운 업무 처리를 위한 마이크로 서비스 내부 구조로 활용하면 유용하며, 복잡한 비즈니스 로직을 잘 정리하여 핵심 서비스로 활용해야 할 경우에는 Domain Model 패턴 구조의 마이크로서비스 내부 구조를 취하면 효율적입니다.

Data Access Layer는 Hexagonal Architecture가 적용하여 어떤 저장소가 오더라도 Business Logic 층에 영향을 적게 받도록 하는 것이 중요합니다.

일반적으로 Business Logic 층이 Transaction Script 구조로 설계된 경우 SQL Mapper를 선택하고, 데이터 모델링을 먼저 수행하는 방식으로 진행을 하고, Business Logic 층이 Domain Model 구조로 설계된 경우에는 OR Mapper를 선택하고 도메인 객체 모델링을 먼저 수행하는 방식으로 진행하게 됩니다.

Event Driven Architecture

Microservice간 호출하는 방식은 동기식 방식비동기식 방식으로 구분할 수 있습니다.

RestAPI와 같은 인터페이스를 활용한 동기식 방식은 구현이 쉽다는 장점이 있지만, 특정 서비스의 장애가 다른 서비스의 장애로 전파될 위험을 가지고 있어 MSA에서는 적합하지 않습니다.

따라서 MSA에서는 Microservice간 이벤트를 전달하고 요청을 종료하는 비동기식 방식을 사용합니다. 이벤트 전달을 위한 매개체로는 Apache Kafka, RabbitMQ, ActiveMQ와 같은 메시지 브로커를 사용합니다.

이러한 방식은 메시지에 전달할 메시지 이벤트를 던지고 자신의 일을 처리하면 메시지 브로커가 전송을 보장하는 것인데요.
이벤트가 메시지가 브로커에 의해 중계되기 때문에 통신하는 서비스들이 물리적으로 동일한 시스템에 위치할 필요도 없고, 서로 프로세스를 공유할 필요도 없고, 심지어 동일한 시간대에 동시에 동작하지 않아도 됩니다.
따라서 서비스 요구에 따라 늘어나거나 줄어들 수 있는 탄력성이 높은 클라우드 플랫폼 환경에서 서비스가 다운되었을 때 또는 시스템을 더 확장해야 할 때 사용할 수 있는 매우 효과적인 방법입니다.

이러한 설계 방식을 Event Driven Architecture(이벤트 주도 아키텍처)라 하고, 이는 느슨한 결합으로 인해 확장성 및 수정 가능성에 많은 이점을 제공합니다.

계속해서 응용 패턴을 알아보겠습니다.

SAGA Pattern

MSA에서는 하나의 비즈니스 로직을 처리하기 위해서 다수의 microservice의 데이터를 변경해야하는 경우가 있습니다.
이 때, 모노리스 아키텍처의 경우에는 비즈니스로직 전체를 단일의 로컬 트랜젝션으로 처리가 가능하지만 MSA에서는 불가능합니다. 그렇다면 데이터 일관성을 어떻게 유지할 수 있을까요?

이 때 사용할 수 있는 방식이 SAGA Pattern입니다.

SAGA Pattern이란? 트랜젝션을 처리할 비즈니스 로직을 saga라는 논리적은 트랜젝션으로 간주합니다. 그리고 각 microservice에서는 로컬 트랜젝션으로 처리하고 다른 서비스에 보상 트랜젝션을 주는 방식으로 하나의 비즈니스 로직의 트랜젝션을 관리할 수 있습니다.

이러한 saga pattern에는 Choreography Saga PatternOrchestrator Saga Pattern이 있습니다.

Choreography saga pattern은 각 서비스가 비동기식으로 동작하며 자체적으로 로컬 트랜젝션을 처리한 후 다른 microservice에게 보상 트랜젝션을 위한 메시지를 제공하여 데이터 일관성을 유지하는 방식입니다.

이런 비동기적 과정은 순간적으로는 데이터의 일관성이 없을 수 있지만 결과적 일관성은 유지할 수 있습니다. 이를 Eventual Consistency라고 합니다.

Orchestrator saga pattern은 Orchestration 클래스가 직접 처리하는 방식입니다. Orchestration 클래스는 데이터 처리를 위해 동기식 API를 사용하며 다른 microservice의 결과를 수집 후 적절한 응답을 반환함으로써 트랜젝션을 일괄적으로 처리할 수 있도록 해주는 방식입니다.

Command Query Responsibility Segregation(CQRS) Pattern

서비스의 퍼포먼스와 효율을 높일 수 있는 방법중에 database에서 찾을 수 있는 것이 있습니다.

바로, 명령(추가/수정/삭제)과 조회의 역할 분리하는 것입니다. 일반적으로 서비스의 요청이 조회가 명령에 비해 압도적으로 많은 비율을 차지합니다. 또한 명령을 수행할때는 일반적으로 lock이 걸리기 때문에 순간순간 조회의 퍼포먼스가 떨어지는 경향이 있고, scale-out 관점에서도 조회를 위해서 하는 경우가 많은데 명령에 대한 부분도 같이 되기 때문에 scale-out에 대한 효율도 떨어질 수 있습니다.

이럴 경우에 CQRS Pattern을 적용할 수 있습니다.
CQRS Pattern이란? 쓰기 모델과 읽기 모델을 분리하여 서비스의 분리, 트랜젝션의 분리를 통하여 시스템의 부하 및 대기 시간을 줄이는 패턴입니다.

Event Sourcing Pattern

Event Sourcing Pattern이란? 이벤트 주도 설계와 CQRS 패턴을 접목하여 이벤트를 쓰기 전용으로 히스토리를 쌓은 후 읽기 전용 데이터에 구축하는 방식입니다.

이는 쓰기 서비스에서 객체모델을 관계형 모델로 변경하기 위해서는 쿼리라는 질의어로 변환시키는 과정을 제거하고 이벤트 자체를 순차적으로 그 상태를 포함하여 저장함으로써 쓰기 속도를 개선시키는 방식입니다. 또한 특정 시점의 상태를 재현할 수 있는 장점도 가지고 있습니다.

Domain Driven Design (도메인 주도 설계)

도메인이란? 비즈니스나 현실세계의 복잡한 문제를 의미합니다.

도메인 주도 설계란? 도메인의 가치를 최우선시하는 모델링 기법으로 크고 복잡한 도메인을 이해하고 탐구하는 활동을 하고, 이를 통해 발견된 다양하고 많은 도메인의 문제들을 해결하기 위한 소프트웨어 개발의 철학이라고 할 수 있습니다.

따라서 개발자들도 설계자와 함께 회의에 참석하고 분석 활동에 참여함으로써, 코드를 구현하기에 앞서 도메인과 도메인 모델을 명확히 이해하고 또 완전한 이해를 강조합니다.

이러한 활동을 함으로써 비즈니스의 중요도에 따라 서비스 경계를 설정하고 개발함으로 인해서 코드의 응집도를 높여 가동성이나 유지보수성을 높일 수 있습니다.

도메인 주도 설계에서는 마이크로서비스를 식별하는 전략설 설계와 식별된 마이크로서비스의 내부 구조를 상세하게 설계하는 전술적 설계를 할 수 있습니다.

전략적 설계

전략적 설계는 마이크로서비스를 식별하는 과정입니다.

따라서 제품 책임자와 도메인 전문가, 개발자 혹은 각 조직별로 사용하는 언어를 일치시키기 위한 유비쿼터스 언어를 정의하는 것이 중요하며, 서브 도메인(핵심, 지원, 일반)을 구분하고 이는 바탕으로 바운디드 컨텍스트를 정의하는것이 중요합니다.

이렇게 여러 개의 Bounded Context로 분할하게 되면 각 Bounded Context 간에 의사소통이 반드시 필요하게 됩니다. 때문에 바운디드 컨텍스트간 의사소통을 위해 관계를 찾아 통합하는 컨텍스트 매핑이 필요하게 되고 이는 아래와 같이 정의됩니다.

  • 공유 커널(Shared Kernel) : 바운디드컨텍스트 사이에 공통적인 모델을 공유하는 관계
  • 고객공급자 유형(Customer-Supplier) : 고객이 원하는 것을 공급자가 제공해 주는 관계
  • 준수자 유형(Conformist) : 공급자의 모델을 그대로 따르는 방식의 관계
  • 충돌방지계층(Anti-Corruption Layer) : 공급자와 모델과 고객 모델 사이에 번역 계층을 만드는 방법
  • 공개 호스트 서비스(Open Host Service) : 바운디드 컨텍스트에 접근할 수 있는 인터페이스를 정의하는 방법
  • 발행된 언어(Publiched Language) : 바운디드 컨텍스트의 규모와 상관없이 사용과 번역을 가능하게 하는 문서- 화된 정보교환언어를 제공하는 방법

이벤트 스토밍

그렇다면, 어떻게 서브 도메인을 도출할 수 있을까요?
이는 이벤트 스토밍 활동을 통하여 가능합니다.

이벤트 스토밍이란? 마이크로서비스를 도출하기 위한 시스템 안의 이벤트를 이해하는 도메인 전문가와 개발자들의 브레인스토밍 활동으로 프로젝트팀이 워크샵형태로 넓은 공간에 모여 소프트웨어를 구현하는 데 필요한 모든 것(UI, UX, 소프트웨어 아키텍처 그리고 데이터의 모든 부분)을 찾아낼 수 있습니다.

이벤트 스토밍의 주요 활동을 예로 들면 다음과 같습니다.

4_domain_event
도메인 이벤트를 식별한 이후 실행 프로세스나 외부 시스템, 핫스팟에 대한 식별을 합니다.

http://hoHome.ipdisk.co.kr:80//dl/10a37fd4b96978a351d845090d4db522/6100e41e/657465726e616c3b616d616e736d616e3737/7f1tUQyT5671b7r8ZikmlhPdu8NsYRD/5_command.png
그리고 도메인 이벤트를 기반으로 커맨드를 생성하고 사용자나 역할 부여합니다.

6_subdomain
마지막으로 도메인 이벤트를 기반으로 서브 도메인 설정합니다.

이렇게 식별한 서브 도메인을 기반으로 마이크로서비스를 식별할 수 있는데요. 아래와 같은 활동을 통하여 가능합니다.

7_entity_aggregate
먼저 EntityAggregate를 식별합니다. Entity는 이벤트 수행 결과를 표현하는 데이터 혹은 저장하는 데이터입니다.

8_bounded_context
바운디드 컨텍스트를 식별합니다.
경계를 설정할 때는 마이크로서비스로의 전환을 위해서 하나의 서비스에 어느 정도의 기능을 넣을지 충분히 고민하고 논의해서 정해야 합니다.
일반적으로 하나의 서브도메인은 하나의 바운디드 콘텍스트로 가져가지만, 그 이상의 서브도메인으로도 식별을 할 수 있습니다.

9_context_map
컨텍스트 맵을 그림으로써 컨텍스트간 관계를 알 수 있습니다.

10_get_microservices
식별된 바운디드 컨텍스트를 기반으로 마이크로서비스 식별합니다.

그 밖에 읽기 모델이나 User Interface의 정의를 통하여 보다 상세한 설계모델을 작성할 수 있습니다.

마이크로서비스의 명세

11_mapping_diagram
앞서 정의한 마이크로서비스 간의 연관관계를 식별하고 정의하는 활동을 마이크로서비스 맵핑이라고 하고 산출물로 맵핑 다이어그램을 생성할 수 있습니다.

12_microservice_spec
지금까지의 분석활동을 통하여 마이크로서비스의 스펙(API, User Story, Data, Risk, UI)을 정의할 수 있습니다.

전술적 설계

전술적 설계는 마이크로서비스(바운디드 컨텍스트로 식별된)의 내부 구조를 상세하게 설계하는 단계입니다.

따라서 다이어그램을 활용하여 마이크로서비스의 내부를 설계하고 각 마이크로서비스간의 관계를 표현하게 되는데요. 이렇게 다이어그램과 같은 도메인 모델을 그리기 위해 알아야 될 도메인 모델의 표준 패턴을 보면 다음과 같습니다.

  • 계층형 아키텍처(Layered Architecture) : 도메인 주도 설계에서는 도메인 계층을 분리하는 것이 매우 중요합니다.
  • 엔티티(Entity) : 고유한 식별자이면서 변화 가능성이 있는 객체
  • 값 객체(Value Object) : 개념적으로 식별성이 없고, 단순히 값만을 갖고 있는 객체
  • 표준 타입(Standard Type) : 대상의 타입을 나타내는 서술적 객체
  • 애그리게잇(Aggregate) : 업무상 관련 있는 객체들을 모아 경계를 명확히 정의하는 패턴
  • 저장소(Repository) : 애그리게잇의 개념적인 저장소
  • 도메인 이벤트(Domain Event) : 하나의 마이크로서비스 내부의 변경이 다른 마이크로서비스로 파생되는 작업이 필요한 경우 사용

도메인 모델의 표준 패턴은 다음 세 단계의 설계절차를 통하여 식별할 수 있게 됩니다.

  • 도메인 모델 설계 : 엔티티와 값 객체, 표준 타입을 식별하고 애그리게잇을 식별
  • 도메인 모듈 설계 : 서비스 인터페이스와 저장소 인터페이스를 정의
  • 마이크로서비스 설계 : 각 마이크로 서비스의 동기/비동기 연동을 설계

이상 도메인 모델링을 한 결과는 다음과 같이 표현될 수 있습니다.
13_전술적_설계_결과

또한 각 마이크로서비스를 설계하면 다음과 같이 표현될 수 있습니다.
14_마이크로서비스간_관계_설계_결과

참고문헌

Microservice 설계 및 구현