아키텍처는 확장성
신뢰성
관리성
테스트성
배포성
등 ~성
으로 끝나는 서비스 품질 요건에 영향을 미칩니다.
따라서 어떤 아키텍처를 사용하느냐에 따라 품질 요건을 얼마나 충족할 수 있을지 결정됩니다.
오늘은 마이크로서비스 아키텍처에 대해 알아보도록 하겠습니다.
💡본문에서 마이크로서비스 아키텍처를 MSA(MicroServiceArchitecture)라고 부르겠습니다.
MSA는 애플리케이션을 느슨하게 결합된 여러 서비스로 구성하는 아키텍처 스타일입니다.
💡여기서 서비스란 어떤 기능이 구현되어 단독 배포가 가능한 소프트웨어 컴포넌트입니다.
느슨하게 결합된 서비스는 구현 코드를 감싼 API를 통해서만 상호 작용하므로 클라이언트에 영향을 끼치지 않고 서비스 내부 구현을 바꿀 수 있습니다.
그리고 애플리케이션 개발 시간을 단축하는 효과가 있습니다. 개발자가 서비스를 이해하고 변경하고 테스트하기가 더 쉽기 때문입니다.
느슨한 결합이 잘 이루어지게 되면 작은 팀이 가장 짧은 시간에 다른 팀과 협동하는 부분은 최소로 하여 개발 가능한 서비스를 설계할 수 있을 것입니다.
MSA 특징
장점
- 지속적으로 배포할 수 있다
👉 MSA는테스트성
독립배포성
자율성
을 갖추고 있기 때문에 지속적으로 전달/배포를 할 수 있습니다. 이로 인해 제품을 시장에 더 빨리 제출하고 고객의 피드백에도 신속히 응대할 수 있습니다. - 결함 격리가 잘된다
👉 MSA에서 A 서비스의 결함이 B 서비스에 영향을 주지 않기 때문에 A 서비스에서 에러가 발생해도 B 서비스는 정상적으로 작동합니다. - 신기술을 시험/도입하기 쉽다
👉 서비스 규모가 작기 때문에 더 나은 언어와 기술로 얼마든지 재작성할 수 있습니다. - 확장하기 쉽다
👉 리소스를 다르게 분배하기가 쉽습니다. - 서비스가 작아 관리하기 용이하다
단점
- 서비스 분해가 어렵다
👉 MSA에 맞게 시스템을 여러 서비스로 분해하는지 구체적으로 정립된 알고리즘이 따로 없기 때문에 어떤 기준으로 서비스를 분해해야 하는지 결정하기 어렵습니다. 시스템을 잘못 분해할 경우 결합도가 높은 서비스들로 이루어질 수 있습니다. - 인프라가 복잡하다
👉 서비스 간 통신, 데이터 통일, 운영 등 여러 가지 측면에서 복잡하고 어렵습니다.
모놀리식 아키텍처 특징
장점
- 개발이 간단하다
👉 IDE 등 개발 툴은 단일 애플리케이션 구축에 초점이 맞추어져 있습니다. - 애플리케이션을 쉽게 변경할 수 있다
👉 코드, DB 스키마를 변경해서 빌드/배포하기 용이합니다. - 배포하기 쉽다
👉 개발자는 서버에 접속하여 빌드된 파일을 복사하면 끝입니다.
모놀리식 단점
- 너무 복잡하다
👉 기능이 많아질수록 서비스의 복잡도는 숨 막히게 증가합니다. 또한 변경사항이 있을 때마다 코드는 한층 더 복잡해지고 점점 이해할 수 없는 수준으로 변할 것입니다. - 개발이 더디다
👉 애플리케이션이 너무 커져서 개발자 IDE의 실행 속도도 느려지고, 자연히 빌드 시간도 오래 걸립니다. 따라서 코드를 고치고 빌드/실행 후 테스트하기까지 너무 많은 시간이 낭비되어 생산성을 떨어트리게 됩니다. - 지속적 배포가 불가능하다
👉 애플리케이션의 최근 트렌드는 지속적 배포입니다.
그러나 모놀리식에서는 여러 개발자가 같은 코드 베이스에 커밋을 하다 보니 종종 릴리스할 수 없을 때도 있습니다. 브랜치를 이용해서 해결하면 merge단계가 개발자를 괴롭힙니다.
따라서 점점 더 배포에 걸리는 시간이 길어지고 지속적인 배포가 불가능해집니다. - 확장하기 어렵다
👉 모듈마다 리소스 요건이 서로 다른데 모놀리식에서는 리소스를 다르게 배분하기 어렵습니다. - 신뢰하기 어렵다
👉 애플리케이션 자체가 워낙 커서 철저하게 테스트하기 어렵고, 테스트성이 부족하면 결국 프로덕션에 버그가 발생할 가능성도 높습니다. - 최신 기술 스택을 사용하기 어렵다
👉 최신 기술을 사용하고자 전체 모놀리식 애플리케이션을 재작성하는 것은 오랜 시간비용이 필요합니다. 또한 새로운 기술 때문에 현재 잘 작동 중인 기능이 고장 날 수 있는 리스크가 높기 때문에 어렵습니다.
💡테스트에 대한 차이
💡모놀리식은 처음엔 테스트가 쉽지만 복잡해질수록 어려워집니다.
💡MSA는 서비스가 작아서 테스트를 작성하기 쉽고 더 빨리 실행되지만 다른 서비스에 의존성이 있는 경우 테스트 하기가 어렵습니다.
MSA 정의
MSA를 정의하는 과정은 요청 식별
→ 서비스 식별
→ 서비스별로 API정의하기
순서를 따르게 됩니다.
요청 식별
아키텍처를 정의하기 위해선 애플리케이션의 요청들을 추출해야 하는데 각 요청들은 추상적인 도메인 모델 관점에서 정의됩니다. 도메인 모델들이 서로 협동하는 방식을 표현하면 요청들을 정의할 수 있습니다.
도메인 모델은 주로 사용자 시나리오의 명사에서 도출합니다. 아래 시나리오 예시를 통해 확인해보겠습니다.
주문하기
- 전제(Given)
👉 소비자가있다.
👉 음식점이 있다.
👉 음식점은 소비자의 주소로 제시간에 음식을 배달할 수 있다.
👉 주문 총액이 음식점의 최소 주문량 조건에 부합한다. - 조건(When)
👉 소비자가 음식점에 음식을 주문한다. - 결과(Then)
👉 소비자가 신용카드가 승인된다.
👉 주문이 PENDING_ACCEPTANCE 상태로 생성된다.
👉 생성된 주문이 소비자와 연관된다.
👉 생성된 주문이 음식점과 연관된다.
다음 주문하기 시나리오에 포함된 명사를 보면 Consumer(소비자)
, Order(주문)
, Restaurant(음식점)
, CreditCard(신용카드)
클래스가 필요할 것 같습니다.
주문 접수
- 전제(Given)
👉 현재 주문은 PENDING_ACCEPTANCE 상태다.
👉 주문 배달 가능한 배달원이 있다. - 조건(When)
👉 주문을 접수한 음식점은 언제까지 음식을 준비할 수 있다고 약속한다. - 결과(Then)
👉 주문 상태가 ACCEPTED로 변경된다.
👉 주문의 promiseByTime 값을 음식점이 준비하기로한 약속한 시간으로 업데이트 한다.
👉 주문을 배달할 배달원을 배정한다.
다음 주문접수 시나리오 에서는 Courier(배달원)
, Delivery(배달)
클래스가 필요할 것 같습니다.
다음과 같이 여러 시나리오에 대한 도메인추출을 반복하게 되면 아래와 같은 관계도를 그릴 수 있습니다.
이제 요청을 식별해야 하는데 보통 요청들은 시나리오의 동사에서 시작됩니다. 위의 도메인 모델에서 조금 더 생각해보면 아래와 같은 요청들이 필요하다는 것을 알 수 있습니다.
소비자
- 주문생성
음식점
- 주문 접수
- 음식 준비 완료
배달원
- 배달 접수
- 배달완료
- 현재 위치 전달
아래와 같이 자세한 명세표를 작성할 수도 있습니다.
**주문생성에 관한 명세표**
### 작업
- createOrder(소비자 ID, 결제 수단, 배달 주소, 배달 시간, 음식점 ID, 주문 품목)
### 반환 값
- orderID 등등
### 선행 조건
- 소비자가 존재하고 주문을 할 수 있다.
- 주문 품목은 음식점의 메뉴 항목에 들어 있다.
- 배달 주소/시간은 음식점에서 서비스할 수 있다.
### 후행 조건
- 소비자 신용카드는 주문 금액만큼 승인 처리되었다.
- 주문의 상태가 변화되었다.(PENDING_ACCEOTANCE)
이처럼 도메인 모델을 잘 정의하면 애플리케이션이 무슨 일을 하는지 알 수 있기 때문에 아키텍처를 정의하는데 매우 유용합니다.
또한 도메인들의 관계와 요청들을 정리하면 어떤 인터페이스를 만들지 알기 쉽습니다.
서비스 식별
요청을 식별했으면 어떻게 묶어서 하나의 서비스로 배포할지 생각해야 합니다.
이때 서비스를 식별하는 패턴 중 하나는 서비스를 비즈니스 능력에 따라 분해하는 것입니다.
보통 비즈니스 능력은 객체에 집중하며 하나의 객체를 여러 개의 비즈니스 능력으로 분해할 수 있습니다.
비즈니스 능력에 관련된 객체와 위에서 말했던 도메인 모델은 조금 느낌이 다릅니다.
도메인 모델은 시나리오를 통해 추출 됐다면 비즈니스 능력은 조직의 부서와 같다고 볼 수 있습니다.
물론 어느정도 겹치는 영역이 존재하겠지만 비즈니스 능력은 조직의 목표, 구조, 비즈니스 프로세스를 분석하여 식별합니다.
예시에서는 공급, 주문 접수 및 이행, 소비자관리 비즈니스 능력이 존재한다고 생각했고 객체와 능력을 표시했습니다.
공급
- 배달원 관리
- 음식점 정보 관리
소비자관리
- 소비자에 관한 정보 관리
주문 접수 및 이행
- 주문 관리
- 음식점 주문 관리
- 배달 관리
비즈니스 능력을 식별한 후 능력에 따라 또는 연관된 능력 그룹에 따라 서비스를 정의합니다.
이렇게 서비스를 거의 변하지 않는 비즈니스 능력에 따라 구성하면 비교적 안정적인 아키텍처를 구현할 수 있습니다.
서비스별로 API 정의하기
요청들과 서비스를 목록화했으니 다음은 각 서비스별 API(작업과 이벤트)를 정의할 차례입니다.
서비스 API는 외부 클라이언트 또는 타 서비스가 호출하는 요청과 서비스 간 협동을 지원하기 위해 호출 전용으로 만든 작업입니다.
서비스 API를 정의하려면 우선 각각의 요청을 서비스로 매핑한 후 그 요청을 구현하려면 어느 서비스가 서로 협동해야 할지 파악해야 합니다.
이때 noteUpdatedLocation()
의 경우 배달원 서비스에 배정되어야 할 작업으로 보이지만 배달원의 위치가 필요한 주체는 배달 서비스입니다.
어떤 작업이 제공하는 정보가 필요한 서비스에 그 작업을 배정하는 것이 더 합리적이라고 생각했습니다. 물론 반대의 경우도 있을 것입니다.
API를 정의하고 나니 서비스 하나로 전부 처리 가능한 요청(소비자 서비스의 createConsumer()
)도 있지만 대부분의 요청은 여러 서비스에 걸쳐 있습니다. 요청을 처리하는데 필요한 데이터가 여러 서비스에 흩어져 있는 것입니다.
createOrder()
는 각각의 서비스에서 다음과 같은 실행 조건들이 필요합니다.
- 소비자 서비스 : 소비자가 주문을 할 수 있는지 확인하고 소비자의 지불 정보를 획득합니다.
- 음식점 서비스 : 주문 품목이 올바른지, 소비자가 요청한 배달 주소/시간에 맞추어 해당 음식점이 준비 가능한지, 최소 주문량 이상인지 확인 후 주문 품목별 단가 정보를 조회합니다.
- 주방 서비스 : 식권을 생성합니다.
따라서 API를 온전하게 정의하려면 각 시스템 작업을 면밀히 분석해서 서로 어떻게 협동해야 할지 결정해야 합니다.
서비스 분해의 장애물
비즈니스 로직으로 서비스를 정의하는 것이 항상 옳은 것은 아닙니다. 상황에 따라 더 많은 서비스로 나눠야 할 수도, 반대로 서비스를 합쳐야 하는 경우도 있습니다.
다음과 같은 고려 사항을 알아보도록 하겠습니다.
- 네트워크 지연
- 동기 통신으로 인한 가용성 저하
- 여러 서비스에 걸쳐 데이터 일관성 유지
- 데이터의 일관된 뷰 확보
네트워크 지연
서비스를 여러 개로 나누면 서비스 간 왕복 횟수가 급증합니다.
따라서 서비스 간 너무 많은 네트워크 요청으로 지연이 발생할 수 있는지 고려해야 합니다
동기 IPC로 인한 가용성 저하
REST API와 같은 동기 호출 방식의 IPC를 사용하게 될 경우 소비자 서비스의 장애가 주문 서비스에도 영향을 끼치게 됩니다.
비동기 메시징으로 강한 결합도를 제거하고 가용성을 높이는 방법이 더 좋습니다.
여러 서비스에 걸쳐 데이터 일관성 유지
음식점이 주문을 접수하게 되면 소비자의 주문 상태와 배달 스케줄이 모두 업데이트돼야 합니다. 이때 두 업데이트는 원자적으로 일어나야 합니다.
이와 같이 서로 다른 서비스에 있는 데이터를 원자적으로 업데이트하는 일은 마이크로 서비스에서 흔하게 발생합니다. 따라서 사가 트랜잭션 및 분산 트랜잭션과 같은 방법으로 데이터의 일관성을 유지해야 합니다.
일관된 데이터 뷰 확보
서비스가 분해되면 DB가 여러 개 생기게 됩니다. 여러 DB에 걸쳐 일관된 데이터를 확보하기가 어렵습니다.
모놀리식에서는 ACID 트랜잭션의 속성 덕분에 어떻게 쿼리를 하든 일관된 데이터 뷰가 반환되지만, MSA는 전역 범위에서 일관된 데이터 뷰는 확보할 수 없습니다.
정리
- 아키텍처는 애플리케이션 개발 속도에 직접 영향을 주는 갖가지
`~성(-ilites)`
을 좌우합니다(예: 관리성, 테스트성, 배포성) - 모놀리식 아키텍처 패턴은 애플리케이션을
하나의 배포 단위
로 구성합니다. - MSA는 독립적으로 배포 가능하면서 자체 DB를 보유한 서비스들로 시스템을 분해합니다.
- MSA를 사용하면 자율적인 소규머 팀들이 작업을 병행할 수 있어서 개발속도가 빠릅니다.
MSA는 만병통치약이 아닙니다.
복잡성을 비롯하여 중요한 단점도 있습니다.- MSA를 검토할 때 인간적인 측면도 고려해야 합니다. 팀원이 느끼는 감정도 충분히 반영되어야 성공적인 전환이 가능합니다.
- MSA는 애플리케이션의
관리성
,테스트성
,배포성
을 높이는 아키텍처 스타일입니다. - MSA는 기술적 관심사보다 비즈니스 능력, 하위 도메인 등 비즈니스 관심사 위주로 구성됩니다.
Reference