MSA Level Up

devty·2023년 11월 7일
0

MSA

목록 보기
14/14
post-thumbnail

쓰게 된 계기

  • 회사에서 사내 발표를 진행하였는데, 추가적으로 MSA를 도입하려는, MSA가 궁금한 사람들을 위해 PPT 발표 자료를 정리해서 올렸다.

Monolithic Architecture란?

  • MSA가 파생되기 전에는 주로 사용하던 아키텍처는 Monolithic(모놀로식) Architecture 입니다.
  • Monolithic Architecture는 모든 애플리케이션의 컴포넌트들이 하나의 단일 패키지로 통합되어 있는 구조를 의미합니다.
  • 이러한 구조에서는 화면, 데이터베이스, 비즈니스 로직 등 모든 컴포넌트가 같은 시스템에 위치하고, 이들 간의 커뮤니케이션은 프로세스 내부에서 이뤄집니다.

Monolithic Architecture의 장단점

  • 장점
    1. 개발, 테스트, 배포가 비교적 간단하며, 이해하기 쉽습니다.
    2. 어떤 서비스든지 개발되어있는 환경이 같아서 복잡하지 않습니다.
  • 단점
    1. 코드베이스가 커지면 유지 보수가 어려워집니다.
      • 유지보수할 때 문제가 생긴 곳을 고치면, 다른데도 같이 고장나는 경험이 있지 않은가요?
    2. 신규인원 투입시 전체 서비스를 이해하는데 시간이 오래걸립니다.
      • 저희도 처음 들어왔을 땐 사진과 같은 경험을 한번씩은 해본적이 있죠?
    3. 서비스의 확장이 어렵다. 특정 부분만 확장하려 해도 전체 애플리케이션을 확장해야 합니다.
    4. 하나의 서비스에 대한 장애가 전체 서비스에 장애로 퍼질 가능성이 높습니다.
      • 왼쪽 사진 처럼 하나의 서비스에 장애가 발생하면 오른쪽 사진 처럼 전체로 퍼질 가능성이 큽니다.

MSA란?

  • MA의 단점으로 극복하기 위해 MSA가 탄생되었는데 여기서 MSA란? 풀어서 MicroService Architecture 입니다.
  • 해석하면 각각을 마이크로하게 나눈 독립적인 서비스를 연결한 구조를 말합니다.
  • MA 단점들을 극복하고 더 빠르게 더 큰 규모로 확장하기 위해 MSA가 탄생했습니다.

MSA의 장단점

  • MSA 장점
  1. 서비스별 개별 배포가 가능하여 배포 시간이 단축됩니다.
    • 배포 사진(모노 레포 상태 입니다.)
      • 사진을 보면 알 수 있듯이 왼쪽은 모놀로식으로 배포한 시간이고 오른쪽은 MSA로 배포한 시간입니다.
      • 빌드 및 배포 시간에 차이가 유의미하게 나타나는걸 볼 수 있다.
  2. 특정 서비스에 대한 확장성(scale-out)이 유리합니다.
  3. 일부 장애가 전체 서비스로 확장될 가능성이 적습니다.
  • MSA 단점
  1. 설계가 어렵습니다.
  2. MSA는 모놀리식와 다르게 서비스가 모두 분산되어 있기 때문에 상대적으로 많이 복잡하다.
  3. 트랜잭션 및 데이터 관리가 어렵습니다.
    • 서비스가 분리 되어 여러 DB를 사용한다면 데이터 관리가 어렵고 DB 컬럼에 Join이 어려워집니다.
  • MSA에도 단점이 있어도 경험해보는게 좋은 이유는 무엇일까요?
    • 앞서 얘기한 모놀리식 아키텍처 단점에서 얘기한 유지보수, 확장성 등은 오랜기간 지속되면 기술 부채로 이어질 가능성이 큽니다. 따라서 기술부채를 줄이는 데 도움을 줄 수 있고
    • '유연성'과 '확장성'을 통해 빠르게 변화하는 비즈니스 요구사항에 더 잘 대응할 수 있게 되기 때문입니다.

MSA 도입시 고려할 사항

  • 실제로 MSA를 도입한다면 고려사항 어떤것이 있을까요?
    • MA → MSA로 전환하는 방법에는 여러가지 기술스택이 필요합니다.
  • 크게 생각해봐야할 것이 3가지가 있을 것 같습니다. 3가지는 아래와 같습니다.
  1. 서비스 분리
    • 전체 어플리케이션을 분리하려면 어떤 서비스가 독립적인 서비스가 될 수 있을지 생각해봐야합니다.
      • 그림을 보고 알수있듯이 MA는 큰 덩어리 하나이고 MSA는 그 큰 덩어리를 세분화 해서 나눈걸 볼 수 있습니다.
      • MSA에서 지향하는 방식은 분리된 서비스들은 각 서비스가 단일 책임을 가져야하므로 각각의 마이크로서비스는 서로 느슨한 결합이 되어야합니다.
      • 느슨한 결합을 주도하기 위해 MA에서는 계층형 아키텍처를 사용했다면 MSA에서는 DDD 도메인 주도 개발, Clean Arch(클린 아키텍처)를 지향합니다.
  2. 두번째로는 서비스 간 통신
    • 이렇게 서비스를 나눈다면 독립성은 서비스 간의 통신을 복잡하게 만들 수 있습니다. 서비스 간에 데이터를 공유하거나 상호 작용해야 하는 경우, 이를 어떻게 해결 할 수 있을까요?
      • 예를들어, 사진처럼 주문 서비스와 재고 서비스가 있다고 가정을 한다면 주문 서비스에서 주문을 처리 했을 경우 재고 서비스에서 주문한 상품의 재고를 차감시켜야 됩니다.
        • 해당 경우에서 주문 서비스만으로 다 처리가 불가능 하므로 서비스간 통신을 위한 무언가가 필요합니다.
    • 이런 부분을 해소해 주기 위해 몇가지 방법이 있습니다.
      1. Neflix에서 만든 라이브러리인 FeignClient가 있고
      2. Message queue 기반인 카프카 존재합니다.
  3. 세번째로는 트랙잭션 관리
    • MAS에서 Error가 발생했다면?
      • 모놀리식 아키텍처에서는 한 개의 데이터베이스가 모든 비즈니스 로직을 처리합니다. 이런 구조에서 ACID 트랜잭션(원자성, 일관성, 고립성, 지속성을 갖는 트랜잭션)이 자연스럽게 지원됩니다.
      • 하지만 마이크로서비스에서는 각 서비스가 자체 데이터베이스를 가질 수 있어 분산 트랜잭션을 처리하는 것이 어렵습니다.
        • 사진처럼 주문 서비스는 잘 처리가 되었는데 재고 서비스에서 주문한 상품의 재고가 부족할 경우는 어떻게 처리해야할까요?
    • 이런점을 보안하기 위해 MSA에서 분산 트랙잭션을 관리하기 위한 사가 패턴이 존재하고 있습니다.

1. 서비스 분리

  • 앞서 얘기한 도입 시 고려야할 사항중에 첫번째인 DDD, 클린아키텍처에 설명하겠습니다.
  • Clean Architecture는 계층형 아키텍처의 단점을 보안하고 MSA가 지향하는 독립적인 서비스의 확장성과 유연성을 위해 탄생한 아키텍처입니다.
  • 여기서 계층형 아키텍처는 단점을 보안한다고 얘기했는데요 계층형 아키텍처는 다들 익히 알고있는 MVC 패턴이고 단점을 간단하게 집고 넘어가겠습니다.
    1. 데이터 베이스 주도 개발을 유도합니다.
      • 우리는 위와같이 계층형 아키텍처를 구현하지 않는가요?
      • 이렇게 구현을 하였다면 영속성 서비스에 강한 결합이 생깁니다.
      • 강한 결합이 생기면 안 좋은 점은 한 계층에서 변경이 발생하면 다른 계층에도 영향을 미쳐 코드 수정이 불가피해집니다.
    2. 실무적으로 동시에 작업하기가 어렵습니다.
      • 다들 그림과 같은 경험이 많이 있지 않나요?
        • 내가 수정한 영역과 다른사람이 수정한 영역이 동일해서 해당 파일에 충돌하여 push, merge가 불가능한 경험 다들 한번 쯤은 있으시죠?
      • 계층형 아키텍처는 controller, service, repository로 크게 분류해서 작업하는데 하나의 기능 개발을 할 때 2명 이상이 들어가서 작업하는 것이 많이 힘듭니다.

Clean Architecture란?

  • Clean Architecture 구조
    • 그림을 보듯 알수있듯이 화살표가 입력, 출력포트를 기준으로 디펜젼시 인젝션 의존성 역전을 지원하고 있습니다.
    • 라이트한 버전이라 디테일한 Clean Arch에 대해 설명하지 않겠습니다.
  • 찾아보니 클린 아키텍처는 언어와 프레임워크에 종속적이지 않더라고요.
    • 파이썬에 django, fastapi
    • 자바와 코틀린에 Spring
    • 등에서 사용이 가능합니다.

Clean Architecture 장점, 단점

  • 장점
    1. 유연성과 확장성에 용이합니다.
    2. 여러명이 동시에 작업하기 좋습니다.
      • 기능별로 뚜렷하게 분리된 구조를 가지고 있기에 여러 개발자가 동시에 작업하기 좋습니다.
    3. 테스트코드 작성이 용이합니다.
      • 이것도 마찬가지로 기능별로 뚜렷하게 구분된 구조를 가지 있기에 모킹을 사용하여 유닛 테스트를 쉽게 수행할 수 있습니다.
  • 단점
    1. 학습 곡선 가파릅니다.
    2. 오버 엔지니어링이 있습니다.
      • 작은 프로젝트나 단순한 애플리케이션에 Clean Architecture를 적용하면, 과도한 설계와 복잡성을 초래할 수 있습니다.

2. 서비스 간 통신

  • 앞서 얘기한 도입 시 고려야할 사항중에 두번째인 각각의 마이크로서비스들의 통신에 대해 설명하겠습니다.
  • 마이크로서비스의 통신은 크게 3가지가 있는데 1가지만 얘기하겠습니다.
  • Message queue (Kafka, RabbitMQ)
    • Message queue는 인스턴스가 데이터를 서로 교환할 때 사용하는 통신 방법입니다.
    • MQ 동작 원리를 간단하게 설명하겠습니다.
      • 그림을 보면 알수 있듯이 3가지가 있습니다.
      • producer(프로듀서) : 정보를 제공하는 역할로, 메시지를 생성하고 특정 토픽과 같이 큐에게 보냅니다.
      • consumer(컨슘) : 정보를 제공받는 역할로, 특정 토픽을 구독하고 해당 토픽에 대한 메시지를 받아와서 비즈니스 로직을 처리합니다.
      • Queue(큐) : producer의 메세지를 임시 저장 및 consumer에 제공하는 곳입니다.

MQ의 장점, 단점

  • 장점
    1. 비동기를 지원하며 서비스간 낮은 결합도를 지원합니다.
    2. 프로듀서, 컨슘은 서비스를 원하는대로 확장할 수 있어 확장성에 용이합니다.
  • 단점
    1. 시스템의 복잡성을 증가시킬 수 있습니다. 메시지 전송, 수신, 라우팅, 에러 처리(트랙잭션)등과 같은 추가적인 로직과 관련된 기능들을 구현해야 합니다.

3. Transaction 관리

  • 모놀로식에서 Transaction 관리
    1. 비즈니스 로직에 트랙잭션(데이터베이스에 상태 변화)이 필요한 작업을 수행한다. → 트랙잭션 시작
    2. 트랜잭션 안에서 여러개의 쿼리나 명령이 실행되며, 중간에 어떤한 오류도 발생하지 않아야한다.
    3. 모든 연산이 성공적으로 완료되면 트랜잭션은 커밋 상태가 된다.
      • 커밋은 트랙잭션 내의 모든 변경사항이 데이터베이스에 영구적으로 저장되는 것을 의미한다.
    4. 만약 트랜잭션 중간에 오류가 발생하거나 어떤 이유로 트랙잭션을 취소해야하는 경우 롤백이 발생한다.
      • 롤백은 트랙잭션 내에서 일어난 모든 변경사항을 취소하고, 데이터 베이스를 트랙잭션 시작 전으로 돌리는 과정이다.
    • 이러한 모든 Flow가 1PC(1-Phase Commit)이라고 한다.
    • 모놀로식 아키텍처에서는 데이터베이스 연결과 트랙잭션 관리가 일반적으로 단일 서비스 내에서 이루어지기 때문에 ACID 속성을 유지하는 것이 비교적 간단하다.

MSA에서 Transaction 필요한 이유

  • MSA에서는 여러 서비스가 존재하는데 각 서비스별로 각각의 고유의 DB를 가지고 있다.
    • MSA가 지향하는 점으로 각 서비스엔 각각의 DB를 가지는걸 지향한다.
  • 두개 이상의 서비스에서 DB 상태 변화가 일어날 때 하나 이상의 서비스에서 오류가 발생한다면 두 서비스 모두 롤백을 해줘야하는데 서비스가 다르기에 자체적으로 ACID를 지원하지 않는다.
  • 따라서 이런 부분을 해소해주기위해 MSA에서 새로운 Transaction 방식을 채택해야한다.

MSA에서 Transaction 관리

  • 이번 내용은 서론이 조금 길었는데 앞에 내용을 이해해야지 MSA에서 Transaction 관리를 어떻게 하는지 파악하기가 더 쉽다!
  • 그리고 진짜로 MSA에 여러 경험이 있는건 아니지만 Transaction 관리하는게 제일 어려웠던 것 같다…
  • 다시 돌아와 MSA에서 Transaction 관리로는 여러 방법이 있다.
    1. 2PC(2-Phase Commit)
    2. Saga Pattern
      • Ochestration Pattern
      • Choregraphy Pattern
  • 위 몇가지 방법을 이론적으로 먼저 소개하겠다.

2PC(2-Phase Commit)

  • 2PC(2-Phase Commit or N-Phase Commit)는 분산 시스템에서 트랜잭션을 관리하는 알고리즘이다.
  • 1 단계 : 준비단계(Prepare Phase)
    1. 코디네이터(Coordinator)는 모든 참여자에게 트랜잭션을 준비하고 커밋할 준비가 되었는지 묻는 Request-To-Prepare 메시지를 보냅니다.
    2. 참여자(Participant = Managers)는 트랜잭션을 준비하고, 커밋할 준비가 되면 Yes로 응답하고, 그렇지 않으면 No로 응답합니다. Yes로 응답한 참여자는 트랜잭션을 롤백하지 않고 잠시 대기합니다.
  • 2 단계 : 커밋 단계 (Commit Phase)
    1. 코디네이터(Coordinator)는 모든 참여자(Participant = Managers)로부터 Yes 응답을 받으면, 모든 참여자에게 Commit 메시지를 보내 트랜잭션을 완료하도록 합니다.
    2. 만약 어떤 참여자라도 No로 응답하거나 응답이 없다면, 코디네이터(Coordinator)는 모든 참여자(Participant = Managers)에게 Rollback 메시지를 보내 트랜잭션을 취소합니다.
  • 1 단계인 준비단계가 필요한 이유
    • 모놀리식에서는 어차피 본인들의 인스턴스를 공유 → 트랜잭션 적용하려는 DB가 트랜잭션이 가능한 상태지인지 알아야할 필요 X → 전체(1개)의 어플리케이션이 1개의 DB를 사용하기 때문이다.
    • MSA에서는 인스턴스 분리로 인해 대상 DB가 트랜잭션이 가능한 상태인지 미리 확인해야한다. → 전체(여러개)의 어플리케이션이 각 고유의 DB를 사용하기 때문이다.

2PC(2-Phase Commit) 장점, 단점

  • 장점
    1. 모든 참여자가 트랜잭션에 대해 합의하므로 데이터 일관성이 유지된다.
    2. 여러 서비스 또는 데이터베이스에서 동작하는 분산 트랜잭션을 관리할 수 있다.
  • 단점
    1. 모든 참여자가 커밋을 완료할 때까지 대기해야 하므로 시스템 전반의 성능이 저하됩니다.
    2. 알고리즘이 복잡하여 구현 및 유지 관리가 어려울 수 있습니다.
    3. 참여자 중 하나가 실패하거나 응답이 느리면, 다른 모든 참여자가 해당 참여자의 응답을 기다리는 동안 데드락이 발생할 수 있습니다.
    4. 참여자들은 트랜잭션의 최종 커밋/롤백이 결정될 때까지 자원을 보유해야 합니다. 이는 시스템에 부하를 줄 수 있습니다.
    5. NoSQL에서는 지원하지 않는다.

Saga Pattern

  • 드디어 왔다. MSA에서 제일 어려운 트랙잭션 관리 중에서도 Saga Pattern이다.
  • Saga 패턴이란 마이크로서비스들끼리 이벤트를 주고 받아 특정 마이크로서비스에서의 작업이 실패하면 이전까지의 작업이 완료된 마이크서비스들에게 보상(complemetary) 이벤트를 소싱함으로써 분산 환경에서 원자성(atomicity)을 보장하는 패턴입니다.
  • Saga에는 두가지 패턴이 존재하는데 위에서 설명했듯이 Choreography, Orchestration 두가지가 존재한다.

Choreography-based Saga

  • 분산 트랜잭션을 책임지는 중계자(Saga Manager)가 존재하지 않는 방식이다.
    1. 로컬 트랜잭션을 처리하고 다음 서비스에게 이벤트 전달한다.
    2. 성공, 실패를 큐로 응답으로 넣어준다.
    3. 실패 시 보상 트랜잭션 발행한다.
  • 장점
    1. 각 서비스가 독립적으로 작동하므로 시스템이 더 분산적이다.
    2. 각 서비스가 자체적으로 단계를 처리하므로, 개별 서비스 변경이 더 쉽다.
    3. 서비스간에 느슨한 결합이 유지되므로, 한 서비스의 변경이 다른 서비스에 큰 영향을 미치지 않는다.
  • 단점
    1. 트랜잭션이 많은 서비스를 포함하는 경우, 전체 흐름을 이해하거나 디버깅하기가 어려울 수 있다.
    2. 각 서비스가 독립적으로 작동하므로, 전체 트랜잭션의 일관성을 유지하는 것이 어려울 수 있다.
    3. 오류가 발생하면, 이미 완료된 단계를 롤백하는 로직이 복잡해질 수 있다.

Orchestration-based Saga

  • 하나의 orchestrator가 전체 트랜잭션의 흐름을 관리합니다.
  • Orchestration는 각 서비스를 순차적으로 호출하고, 오류가 발생하면 적절한 롤백 로직을 호출하여 트랜잭션을 복구합니다.
    1. 트랜젝션에 관여하는 모든 서비스는 중계자에 의해서 점진적으로 트랜잭션을 수행하며 결과를 중계자에게 전달한다.
    2. 그렇게 진행하다 마지막 트랜잭션이 끝나게되면 중계자를 종료하면서 전체 트랜잭션 처리를 종료한다.
    3. 실패 시 보상 트랜잭션 발행한다.
  • 장점
    1. 중앙 중계자가 있으므로 전체 트랜잭션 흐름이 명확하고 일관됩니다.
    2. 오류 처리와 롤백 로직이 중앙에 있으므로, 오류 관리가 더 간결하고 효과적일 수 있습니다.
    3. 전체 흐름이 한 곳에서 관리되므로, 코드 유지보수가 더 용이할 수 있습니다.
  • 단점
    1. 중앙 중계자가 시스템의 복잡성을 증가시킬 수 있습니다.
    2. 중앙 중계자는 서비스 간의 결합도를 높일 수 있으며, 시스템의 유연성을 제한할 수 있습니다.

Transaction Pattern 선택

  • 두 패턴(Choreography, Orchestration)은 어떤 프로젝트에서 사용하는게 나은지는 프로젝트의 특성에 따라 나뉠수 있다고 생각한다.
  • 내가 생각했을 땐 Choreography-based Saga가 결합도 측면에서 조금 더 MSA가 지향하는 방식이 아닐까 하지만 단지 결합도 측면만 봐서는 안 될 것 같다.
  • 왜냐하면 유지보수 및 관리적인 측면에서 서비스가 커지면 커질수록 Orchestration-based Saga는 개발 리소스가 적게 들 것이다.
  • 또한, 추적성 측면에서 중앙 중계자를 통해 명확한 트랙잭션이 추적이 가능하고 제일 크다고 느낀점은 바로 오류 처리 및 롤백 측면이다.
    • 해당 부분은 Orchestration Saga가 직접 롤백을 관리하기에 각각의 서비스를 자체적으로 롤백하지 않아도 돼서 이 부분이 제일 맘에 들었다.

결론

infrastructure architecture

  • 위에서 말했던 모든 마이크로 서비스를 합친 인프라 아키텍처입니다.
    • 사진에 보이듯이 유레카 클라이언트로 등록된 도메인 마이크로서비스는 6개이고 서비스간 통신은 Kafka를 사용하고 Proxy Server 역할로 Spring Cloud API Gateway를 사용하였고 모두 합쳐 서버는 7개를 사용하였습니다.
    • 또한, API Gateway에 Client로 등록을 해주기 위해 Eureka Server를 따로 두었습니다. 여기서
      • API Gateway는 라우팅 기능을 이용하여 각 서비스로 통신을 보내줍니다. 비유를 하자면 기차와 같고
      • Eureka는 라우팅을 통해 갈 수 있게 길을 연결을 해주는 서비스입니다. 비유를 하자면 철길과 같습니다.
    • Redis는 Redission을 이용하여 전체 서비스에 세션을 관리합니다.
    • 그리고 API Gateway 각 서비스중 주문서비스만 로드밸런싱을 담당해주고 있습니다.

마지막으로

  • 마지막으로 제가 여태 어떤 이유로 MSA가 좋은지만 설명했는데 꼭 MSA가 무조건 좋다는 절대절대 아닙니다.
  • 그리고 저희가 자주 방문하는 사이트중 하나인 스택 오버플로우라는 사이트가 있습니다.
    • 해당 사이트는 한달에 1억명 방문하는 사이트인데 MSA를 사용하지도 않고도 잘 운영되고 있죠.
  • 이런걸 보았을 때 상황에 맞춰서 합리적인 판단이 든다면 MSA를 경험해보는 것도 좋을 것 같습니다.
profile
지나가는 개발자

0개의 댓글