Microservice Design 1일차 - 모놀리식 개발와 마이크로서비스 핵심 원칙

0

microservice

목록 보기
1/6

모놀리식 개발의 문제

---------    ---------    ------
|mobile |    |       |    |    |
|web    | -> |web app| -> | DB |
|desktop|    |       |    |    |
---------    ---------    ------

3 tier architecture는 다음으로 이루어진다.
1. client의 요청을 받는 presentation tier(mobile, browser)
2. bsuiness logic을 실행하는 backend(web application) tier
3. 고객의 데이터를 저장하는 data tier

참고로 frontend code는 사용자의 요청을 받아 처리함으로 web app에 해당하는 부분이다.

대부분의 경우 web application이 모놀릭하게 구성되어 있다는 것이다. 모놀릭하게 구성하는 아키텍처의 장점은 다음과 같다.
1. 개발이 쉽다.
2. 개발이 빠르다.

그러나, 너무 많은 개발자들이 생기고 시스템이 커지면, 의사결정이 어려워지고 코드 변경이 복잡해진다. 또한, 점점 코드가 복잡해지면서 개발의 어려움이 증가하게 된다. 이는 개발 속도가 느려지고, 개발의 난이도가 높이지는 것을 야기한다.

추가적으로 코드 베이스를 리팩토링하여 새로운 언어나 프레임워크를 적용하기 매우 어려워지며, 이는 레거시의 지속을 야기한다. 중요한 점은 모놀리식 어플리케이션을 모듈로 분리한다해서 결합도를 완전히 분리할 수 없다는 것이다.

따라서, 이러한 문제를 해결하기 위해서 마이크로서비스와 이벤트 드리븐 아키텍처가 등장하게 된다. 마이크로서비스는 다음의 이점을 제공한다.
1. 시스템의 확장성
2. 개발의 유연성

단, 마이크로서비스는 모든 문제를 해결하는 마법은 아니며, 대규모 시스템 개발의 문제를 해결하는 하나의 이론이라는 것에 유념하도록 하자.

마이크로서비스의 이점

마이크로서비스 아키텍처는 비지니스 로직을 하나의 모놀릭한 아키텍처로 두는 기존의 방식과는 달리, 비지니스 로직을 여러 단위로 나누는 것이다.

---------    ----------    ------
|       |    |web app1|    |    |
|mobile |    |--------|    |    |
|       |    |web app2|    |    |
|web    | -> |--------| -> | DB |
|       |    |web app3|    |    |
|       |    |--------|    |    |
|desktop|    |web app4|    |    |
|       |    |--------|    |    |
|       |    |web app5|    |    |
|       |    |--------|    |    |
---------    ----------    ------

각 서비스는 각 팀의 책임으로 분리하여 기능을 나눌 수 있다는 것을 나타낸다.

이렇게 논리적, 물리적으로 분리를 더 세분화 시킬 때의 장점은 다음과 같다.

  1. 조직의 확장성이 높아진다.
  2. 빌드 타임 및 test 단위도 독립적으로 동작하기 때문에 효율적이게 된다.
  3. 새로운 팀원의 통합도 빠르게 이루어질 수 있다.
  4. 개발 속도를 증진시킬 수 있다.
  5. 각 서비스들이 작게 나뉠 수 있으므로, 더 저렴하고 효율적으로 하드웨어를 사용할 수 있다.
  6. 코드에 대한 리팩토링과 대체가 쉽다.
  7. 시스템 안정도가 높다.

이는 모놀리식 아키텍처의 확장성 문제를 해결할 수 있다는 장점이 있다.

이렇게 마이크로 서비스가 3 tier 아키텍처의 문제를 해결해주지는 것 같지만, 안타깝게도 마이크로서비스는 여러 문제들이 있다.

  1. 분산 시스템의 어려움 -> 서로 다른 app간의 네트워크 인터페이스 연결은 네트워크 통신 문제가 존재한다.
  2. 분산 서비스는 unreliable하다는 문제가 있다.
  3. 분산된 app들을 합치는 일은 쉽지 않다.
  4. 성능 분석과 디버그가 어렵다.
  5. 책임 범위 설정에 있어 모호함이 있을 수 있다.

마이크로 서비스의 경계 - 핵심 원칙

모놀리식 아키텍처는 너무 큰 codebase로 인해 복잡성이 높아지고, binary size가 커졌으며, 하드웨어 비용이 높아졌다.

이러한 문제를 해결하기 위해서 마이크로서비스를 도입하는데, 일반적으로 기능 별로 모놀리식 아키텍처를 나눈다.

즉, business를 처리하는 layer부분을 다음의 3가지 단계로 나누는 것이다.

  1. frontend
  2. backend(상품 추천, 리뷰, 할인)
  3. data engineering(데이터 전처리)

따라서, 다음과 같이 동작하게 된다.

presentation layer -> frontend -> business logic layer -> data layer -> database

그러나 이는 마이크로서비스로 이향한 것이 아니라, 그저 3 tier architecture를 5 tier architecture로 바꾼 것일 뿐이다.

가령, business logic이 상품 추천, 리뷰, 할인 등의 기능을 담당하는데, 상품 추천에서의 API가 바뀌었다고 하자. 이는 frontend service에도 영향을 주는 문제가 생기고, 더불러 data가 바뀜으로 data layer에도 변화를 야기한다.

그렇다면, 우리는 문제를 isolation하는 것이 불가능하다는 것이다. 즉, 한 팀의 변화가 다른 팀에 모두 영향을 주고, 이는 마이크로서비스의 이점을 얻을 수 없다는 것이다.

이를 통해서 마이크로서비스 간 경계를 설정하는 첫 번째 원칙으로 이어진다.

Cohesion(응집성)

응집성은 서로 강하게 연결되어 있고, 함께 변화하는 것들은 함께 있도록 두어야 한다는 것이다.

즉, 코드의 변화가 영향을 주는 임팩트 결합도가 높은 component들은 하나로 묶어서 하나의 팀이 담당하도록 하라는 것이다.

가령, '상품 추천' 팀으로 따로 두어서, '상품 추천'에 대한 service 정의가 변화되어도 '상품 추천' 기능 내의 프론트 엔드와 bsuiness logic, data layer만 변화하도록 하는 것이다.

이렇게 만들면 cohesion(응집도)가 높다고 표현한다.

SRP(Single Responsibility Principle)

만약 모놀릭 시스템을 각 언어별로 나누어서 분리한다고 하자. 즉, 기능 중심이 아니라, '기술' 중심으로 나누는 것이다. 이렇게 될 때 각 기능이 여러 '기술'등을 기반으로 만들어질 수 있는데, 만약 특정 '기능'에 문제가 생겼을 때, 어디까지 영향이 끼쳤는 지 알기가 어렵고, 책임이 분산되어 있다. 이는 각 마이크로서비스가 '기술'별로만 분리되어있기 때문이다.

이에 따라 SRP는 다음의 원칙을 설정한다.

모든 마이크로서비스는 한 가지 일을 해야하고 그것을 아주 잘 수행해야한다는 것이다.

이 원칙을 잘따르면 어떤 문제에 대한 책임, 새로운 기능을 아주 명확히 설정할 수 있다.

SRP원칙에 따라 business logic부분을 세세한 기능 단위로 microservice로 나누었는데, 문제는 그렇게 나누었더니, 너무 각 servie마다의 통신 chain이 깊어지고 복잡해진다는 것이다.

이에 따라, 나오게된 3번째 이론이 바로 Loose Coupling이다.

Loose Coupling(느슨한 결합)

microservice들끼리는 거의 의존성이 없거나, 최소한의 통신만을 통해서 고유한 기능을 수행할 수 있어야 한다.

또한, 마이크로서비스의 크기는 그리 중요하지 않으며, 마이크로서비스는 가능한 작아야 한다는 것은 흔히 오해라는 것이다. 책임만 단일이면 된다는 것이지, app의 사이즈가 작아야한다는 것을 의미하는 것은 아니다.

마이크로서비스의 전제조건

  1. Cohesion(응집력)
  2. SRP(단일 책임 원칙)
  3. Loose Coupling(느슨한 결합)

이 3가지 원칙은 성공적인 마이크로서비스 아키텍처를 위한 전제 조건이 되는 것이다.

정리하자면, 각 마이크로서비스는 변화하는 부분에 대한 isolation을 위해 응집력있게 분리되고, 이는 기능 단위로 하나의 책임만을 가져야한다. 단, 이 경우 마이크로서비스의 크기(사이즈)는 상관없으며, 너무 작게 나누어 통신을 여러 번 하도록 할 필요는 없다.

다만, 이 3가지 원칙들은 얼마나 구체적으로 마이크로서비스를 나누어야 하는 지 지침을 내지 않는다. 따라서, 구체적으로 어떻게 나눌 수 있는 지에 대해서 알아보도록 하자.

마이크로서비스로 분해하는 구체적인 방법

마이크로서비스로 모놀릭 서비스를 나눌 수 있는 구체적인 방법은 2가지 정도로 정리할 수 있다.

  1. Business 기능에 따른 분리
  2. Domain/SubDoamin에 따른 분리

Business 기능에 따른 분리

Business Capability는 고객에게 제공하기 위한 기능이하고 생각할 수 있다.

이는 비기술적인 사람과의 대화를 통해 비즈니스 기능을 구체화하여 나누는 것이다.

가령, 온라인 쇼핑몰을 만든다면 다음의 기능들이 필요하다.
1. 상품 조회, 검색
2. 리뷰 관리
3. 주문 관리
4. 상품 배송

이 정의들을 하나의 마이크로서비스로 나누는 것이다. 그런데, 어떻게 이렇게 나눈 것들이 3가지 마이크로서비스 원칙을 지킨다고 할 수 있을까??

먼저 business 기능 별로 나뉘었기 때문에 SRP원칙은 지킨다는 것을 알 수 있다.

cohesion을 생각해보면 만약 상품에 대한 조회, 검색 API가 변화될 시에 우리의 변화가 영향을 끼칠 것은, 상품 조회, 검색 팀의 UI와 data처리 부분이다. 따라서, cohesion도 보장된다는 것을 알 수 있다.

다음으로 loosely coupled를 고려하면 명확하게 기능들 간에만 통신이 이루어지므로 복잡하지 않게 동작이 이루어진다는 것일 알 수 있다.

Domain/SubDomain에 따른 분리

이는 business 측면만이 아니라, 시스템을 전반적으로 바라보는 기법이다. 즉, 비개발자가 아닌 개발자의 이해도를 바탕으로 직관적으로 분리하는 것이다.

Domain/SubDomain의 타입으로는 다음이 있다.
1. core: 핵심 비지니스 로직을 대표하는 도메인으로, 우리 회사의 가장 핵심적이고 중요한 기능을 말한다.
2. supporting: 비즈니스 운영에 필수적이지만, core 기능을 제공하기 위해서 존재한다. 이는 어떠한 중요한 기능을 담지 않는다.
3. generic: 어떠한 business에도 특정되지 않고, 다른 회사들의 것을 사용할 수 있다.

이렇게 domain별로 나누면 개발자들의 특성에 따라 배치를 할 수 있다.

가령 온라인 스토어를 만든다고 하면 상품은 다른 기업들과 우리를 구분짓는 가장 중요한 도메인이므로 다음과 같이 나눌 수 있따.

  1. core subdomain: 상품 재고
  2. supporting subdomain: 주문, 재고, 배송
  3. generic subdomain: 리뷰, 검색, 이미지, ui, 결제

이렇게 각 도메인에 대해서 subdomain별로 마이크로 서비스를 나누고, 합쳐서 설계할 수 있다.

  1. product service
  2. order service
  3. inventory service
  4. shippling service
  5. reviews service
  6. payment service
  7. search service
  8. image service
    이렇게 subdomain 별로 나누고, 만약 payment service가 order service와 너무 강하게 연결되어 있다면, 이들을 하나로 묶어도 된다.

결론적으로 어떠한 것도 정답은 없다. 각 사업에 알맞게 수정하고, 적용하는 것이 중요하다.

다음으로 마이크로서비스로 전환할 때 어떤 방식으로 접근하는 것이 좋은 지 알아보도록 하자.

BigBang 접근

마이크로서비스 경계(바운더리)를 확실히 정하고, 새로운 기능 개발을 중단하고 전적으로 마이그레이션에 집중하도록 한다.

이는 겉보기에는 좋아보이지만, 실제로 생산성과 비즈니스 측면에서 최악이다.

  1. 마이크로서비스 이향에 있어서 너무 많은 개발자의 참여는 리팩토링을 더디게 만들고, 어렵게 만든다.
  2. 투입될 자원을 산정하는 것이 매우 어렵다. 즉, 마이크로서비스의 이향이 4개월 안에 이루어질 것이라고 생각했지만 실제로는 5개월이 걸릴 수 있다.
  3. 제품 매니저와 영업 직원들은 흥미를 잃고 비즈니스를 버리게 된다.

그래서 더 좋은 방식이 점진적인 이향이다.

incremental and continuous approach

마이크로서비스 이향을 통해 얻을 수 있는 장점이 확실한 기능들에 대해서만 먼저 분리를 하고, 확장성을 증진시킨다.

이렇게 먼저 마이크로서비스로 이향할 영역을 분리시켜 개발을 한다면, 버그도 덜 발생하고 이해하기 쉬우며, 더 좋은 방법이 된다.

그럼 마이크로서비스로의 이향에 있어서의 단계는 무엇이있을까??

steps to prepare for migration

  1. code test coverage를 늘리고, 보장한다.
  2. component API를 정의
  3. 원하는 코드를 격리시킨다.

실제로 그럼 어떻게 마이크로서비스로 이향하는 지 알아보도록 하자.

Strangler Fig Pattern

strangler fig pattern은 가장 유명한 pattern으로 나무에 자라는 덩쿨이 기존 나무를 감싸기 시작하여, 기존 나무를 대체해버리는 모습에서 유래된 것이다.

client  cleint  client
   |       |       |
   ------------------
   |Strangler Facade| (API Gateway)
   ------------------
            |
    -----------------
    | Monolithic App|
    -----------------

가장 처음에는 strangler facade라는 API gateway를 두어서 모든 traffic을 monolithic app에 전달하도록 한다.

개발이 진행되면서 monolithic app의 일부 기능을 새로 microservice로 만들고 철저하게 테스트하도록 한다. 이후에 배포되면, 일부 traffic을 새로 추가한 microservice로 돌리도록 하여, 동작의 차이와 문제를 확인하는 것이다.

client  cleint  client
   |       |       |
   ------------------
   |Strangler Facade| (API Gateway)
   ------------------
            |       |
-----------------  ------
| Monolithic App|  |MSA1|
-----------------  ------

만약, microservice가 문제없이 동작하면 해당 기능을 담당하는 monolithic app의 code부분을 삭제하고 microservice로 대체하여 부피를 줄어들게 하는 것이다.

이러한 단계를 반복하여 microservice들로 대체하는 것이다.

client  cleint  client
   |       |       |
   ------------------
   |Strangler Facade| (API Gateway)
   ------------------
      |       |     |
    ------  ------  ------
    |MSA1|  |MSA2|  |MSA3|
    ------  ------  ------

      x
-----------------  
| Monolithic App|  
----------------- 

단, 이렇게 변경할 때 새로운 언어와 프레임워크로 변화하지 않는 것을 추천한다. 이는 새로운 문제를 야기할 수 있기 때문이다.

0개의 댓글