[microservice-upLearning] Ⅳ. 마이크로서비스의 크기 조정: 서비스 경계

Ⅳ. 마이크로서비스 사이징 : 서비스 경계

적절한 마이크로서비스 경계 식별 - 성공적인 마이크로서비스 시스템 구축의 어려운 점

  • 커다란 코드 베이스를 더 작고/간단하고/느슨하게 결합하면 유지보수성이 향상되는 것은 누구나 직관적으로 이해 가능
  • 근본적인 질문의 답이 어려움 : 마이크로서비스 처음 접하는 팀이 많이 직면
    • 한 서비스가 다른 서비스가 시작되는 위치를 알기 위해 어떤 규칙을?
    • 코드를 부분으로 분할하는 방법과 위치는 어떻게 결정?
  • 경계 잘못정하면?
    • 이점이 크게 줄어든다
    • 경우에 따라서는 마이크로서비스 전환과 구축에 대한 노력을 물거품으로
  • 따라서 마이크로서비스 구현 실무자가 가장 궁금해하는 점 : 작은 마이크로서비스 모음으로 절절히 분할하는 방법
  • 해당 챕터는 : DDD, Event Stroming, universal sizing formula(범용 크기 조정 공식) 소개

4.1 경계가 중요한 이유, 중요한시기, 경계를 찾는 방법

마이크로의 절절한 크기는?

  • 국제 단위계에서 마이크로는 1000만분의 1을 뜻한다
  • 패턴의 이름에 마이크로로 시작하는 마이크로서비스에서 마이크로가 뜻하는 점은?
  • 충분히 작다는 것은 어느 정도를 이야기 하는가?
  • 단순 접근 시도 - 실제로 많은 시도가 되었으며 모두 많은 단점을 내포
    • SLOC - Source line of code
      • 역사적으로 노력과 복잡성의 척도로 어느정도 사용
      • 실제 크기, 실제 조작 가능한 코드 복잡성 파악을 위해선 미흡한 척도
      • ‘작은’ 서비스가 목표여도 SLOC 측적은 좋지 않은 방법이다
    • 함수 단위로 경계
      • 서비스 기능의 경계에 선을 긋고 소스 코드의 함수로 표현되는 세분화된 기능을 마이크로서비스 정의
      • AWS Lambda등 서버리스 함수 인기가 높아짐에 따라 더욱 매력적으로 느껴짐
      • 실제로 AWS 람다의 생산성 기반 팀이 이러한 서버리스 함수를 마이크로서비스로 선언
      • 이렇게 하면 많은 문제가 있으며 가장 큰 문제들은 다음과 같다
      • ch1에서 이야기한 MSA의 주요 목적은 무엇이었나?
        • 속도와 안정성 사이의 조화를 이루기 위해 복잡한 여러 팀의 환경에서 조정 비용을 최소화 하는 것
        • 따라서 서비스는 여러 마이크로서비스 팀 간의 조정 요구를 최소화화는 방식으로 설계되어야 한다
      • 만약 코드를 최소한 조정으로 이어지지 않는 방식의 함수로 세분화하면? → 잘못된 크기의 마이크로서비스 탄생
      • 코드를 서버리스 함수로 구성하는 방법이 조정을 줄일 것이라 가정? → 잘못된 생각임
  • Netflix, SoundCloud, Amazon등의 마이크로서비스 선구자들도 처음부터 세분화된 시작을 하지 않았다
    • 현재는 많은 마이크로 서비스를 가지고 있음
    • 처음부터 세분화된 마이크로서비스로 시작했다는 의미가 아님
    • 마이크로서비스의 높은 세분화 달성! 그리고
    • 관련된 복잡성 수준을 처리가능한 운영 성숙도를 달성!
    • 그 후 수년간의 개발 끝에 많은 수의 마이크로서비스를 최적화

초기에 마이크로 서비스를 너무 많이 생성하면 안된다

4.2 DDD 와 마이크로서비스 경계

샘 뉴먼 - 저서 마이크로서비스 아키텍처 구축에서 설계 모범 사례를 위한 기본 규칙을 소개

샘뉴먼은 경계를 정할 때 다음과 같은 설계를 위해 노력해야 한다고 제안했다

  1. 느슨한 결합
  • 서비스는 서로를 인식하지 않고 독립적이어야 한다
  • 한 서비스에서 코드를 수정하더라도 다른 서비스에 영향을 주지 않아야 한다
  • 서비스간 다양한 유형의 런타임 호출 제한: 빈번한 통신은 잠재적 성능 문제 외에 구성 요소의 긴밀한 결합으로 이어질 수 있으므로
  • ‘조정 최소화’ 접근 방식을 따르면 느슨한 서비스 결합의 이점으 ㄹ얻을 수 있음
  1. 높은 응집력
  • 서비스에 있는 기능은 관련성이 높아야 하며, 관련성 없는 기능은 다른 곳에 캡슐화 되어야 한다
  • 이러면 기능의 논리적 단위를 변경해야 하는 경우 한 곳에서 변경 가능
    → 변경사항을 릴리즈 하는 시간(중요 메트릭)을 최소화 할 수 있음
  • 반대되는 상황이라면? → 여러 서비스에서 코드 변경 필요 → 동시에 많은 서비스를 릴리스 해야함 → 비용 증가
  1. 비지니스 기능과 연결
  • 기능의 수정/확장의 대부분 요청은 비지니스 요구에 따라 이루어진다
  • 서비스 경계가 비지니스 기능의 경계와 일치하면 느슨한 결합/높은 응집력의 설계 요건을 쉽게 만족
  • 예)
    • 모놀리식 아키텍처 시대에 sw 엔지니어는 표준 데이터 모델(canonical data model)을 표준화하기 위해 노력
    • But? 현업 모델은 자부 변경되고 이를 표준화 하는 재작업 빈번 → 세부 데이터 모델은 오래 지속되지 않는다
    • 더 지속성이 높은 것은? 서브 시스템이 제공하는 비지니스 기능의 모음
      • 예) 회계 모듈은 시간의 지남에 따라 내부가 어떻게 진화하는지 상관없이 대규모 시스템에 원하는 기능의 집합을 항상 제공 가능

도메인 주도 설계 : DDD(Domain-driven design)

  • 위의 설계 원칙은 매우 유용 → 실무자 사이에 널리 채택
  • 하지만 높은 수준의 의욕만 앞선 원칙이며 실무자에게 필요한 서비스 크기에 대한 지침 제공없음
  • 더 실용적인 방법론이 없을까?
  • DDD는 MSA보다 훨씬 앞선 2003년에 도메인 주도 설계 책에서 소개 되었음
  • DDD 방법론의 핵심 : 복잡한 시스템을 분석할 때 전체 시스템을 대표하는 단일 통합 도메인 모델을 찾는 것을 피해야 한다

대규모 프로젝트에서는 여러 모델이 공존하고 있으며, 이는 많은 경우에 잘 작동한다. 서로 다른 모델은 각각의 컨텍스트 내에 존재한다

  • 제한된 컨텍스트 : bounded context

    • 복잡한 시스템은 기본적으로 여러 도메인의 모음임을 확인한후 해당 개념 도입
    • Eric Evans : 제한된 컨텍스트는 각 모델의 적용 가능성의 범위를 정의한다
    • 더 큰 시스템의 다른 부분을 구현하고 런타임에서 실행해도 시스템에 존재하는 독립적인 도메인 모델을 손상시키지 않는다
  • 유비쿼터스 언어 : Ubiquitous Language의 개념 확립
    → 제한된 컨텍스트의 최적화된 경계를 식별하는 공식 제공

    • 잘 정의된 도메인 모델은 도메인을 설명하는 저의된 용어 및 개념에서 공통 어휘를 제공한다
    • 주제별 전문가와 엔지니어는 공통 언어를 통해 비지니스 요구사항과 구현 고려 사항 사이의 균형을 이루며 함께 협업한다
    • DDD에서는 이러한 공통 언어를 유비쿼터스 언어 라고 한다
    • 동일한 단어는 다른 경계의 컨텍스트에서 다른 의미를 전달할 수 있다 - 예) account
      • ID 및 접근 관리 컨텍스트의 account는 인증 및 권한 부여에 사용되는 자격 증명 집합이다
      • 고객관리 컨텍스트의 account는 인구통계정보 및 연락처 속성의 집합이다
      • 재무회계 컨텍스트의 account는 지불 정보 및 과거의 거래 목록일 수 있다
    • 특정 도메인 모델의 재한된 컨텍스트(bounded context)내에서 용어(유비쿼터스 언어)의 유비쿼터스 의미에만 동의하면 되므로 문제가 되지 않는다
    • DDD에 따르면 어떤 용어가 의미를 바꾸는지 관찰하면 컨텍스트의 경계를 식별 가능
  • 모든 용어가 유비쿼터스 언어로 만들어지는 것은 아니다

    • 제한된 컨텍스트의 개념은 유비쿼터스의 언어의 일부이며 다른 모든 개념은 제외해야 한다
    • 그림 설명 : 잡 스토리 구문을 사용하여 유비쿼터스 언어의 핵심 용어 식별
      • 그림에서 강조된 주요 명사 : 관련 유비쿼터스 언어의 용어에 해당한다
      • 유비쿼터스 언어와 관련된 어휘를 식별하기 위해 잘 작성된 잡 스트리의 핵심 명사를 사용하는 방법을 적극 권장

4.2.1 컨텍스트 매핑

개요

  • DDD에서 복잡한 시스템을 단일 도메인 모델이 아닌 시스템에 공존하는 여러 독립 모델로 설계한다
  • 이러한 하위 도메인은 일반적으로 게시된 인터페이스 설명을 사용해서 서로 통신한다
  • context map : 더 큰 시스템에서 다양한 도메인의 표현과 이들이 서로 협업하는 방식
  • context mapping : 그림처럼 해당 협업을 식별하고 설명하는 작업

DDD는 bounded context를 매핑할 때 몇 가지 주요 유형의 상호 작용을 식별한다

1. 공유 커널 (shared kernel)
  • 가장 기본적인 유형
  • 두 도메인이 대체로 독립적으로 개발되고 각 도메인의 일부 하위 집합이 우연히 겹치는 경우 발생
  • 두 도메인은 공유 커널에 대해 협업하는데 동의할 수 있으며 여기에는 공유된 코드, 데이터 모델, 도메인 설명이 포함된다
  • 공유 커널은 MSA에 사용시 문제가 될 수 있다
    • 공유 커널을 사용하는 독립적인 두 팀은 서로간의 협업을 시작하기 위해 초기에 높은 수준의 조정이 필요하다
    • 이후에도 추가 수정을 위해서 지속적인 조정작업이 필요하다
    • MSA에 공유 커널 포인트가 많아질수록 긴밀한 조정이 늘어난다
    • MSA에 공유 커널 사용하는 경우에는 한 팀을 공유 커널의 기본 소유자(Primary owner)나 큐레이터(Curator)로 지정하고 다른 팀은 기여자(contributor)로 지정하는 것이 좋다
2. 업스트림-다운스트림 관계
  • 공유 커널에 대한 대안

  • 해당 유형에서 업스트림은 일부기능의 공급자 역할, 다운스트림은 해당 기능의 소비자 역할

  • 도메인의 정의와 구현이 겹치지 않음 → 공유 커널보다 느슨하게 결합된다

  • 조정 및 결합 유형에 따라 이 매핑은 여러 형태로 도입될 수 있음

    1. 고객-공급자
    • customer-supplier
    • 업스트림(공급자)은 다운스트림(고객)에게 해당 기능을 제공
    • 제공된 기능이 가치가 있는한 모두가 만족
    • but! 업스트림은 하위 호환성(hackward compatibility)에 대한 오버헤드 수반
    • 공급자 서비스 수정시 고객에게 문제가 발생하지 않도록 싱경 써야한다
    • 극단적으로, 고객은 업스트림이 (의도적이든 아니든)제공된 기능을 파괴하거나 고객의 미래 요구사항을 무시할 위험을 지니고 있음
    2. 순응주의자
    • conformist relation : 고객-공급자관계의 위험에서 극단적 경우
    • 이 관계에서 업스트림은 다운스트림의 요구사항을 ‘명시적으로 고려’하지 않는다 → 리스크를 고객이 감수하는 일종의 UAUOR 관계
      • UAUOR : use-at-your-own-risk
    • 업스트림은 다운스트림이 필요한 몇가지 중요한 기능 제공
      • 따라서 업스트림 제공 기능 변경시 다운 스트림은 업스트림의 변경 내용을 지속적으로 따라야만 한다
    • 발생시기
      • 일반적으로 대규모 조직/시스템에서 작은 서브 시스템이 더 큰 서브 시스템을 사용할 때 발생
      • 예시
        • 항공사 예약 시스템에서 작은 크기의 feature를 개발하고 여기에 결제 시스템 사용
        • 결제 시스템과 같은 엔터프라이즈급 시스템은 소규모의 새로운 서비스를 위해 별도의 개발을 제공할 가능성이 낮음
        • 그렇다고 결제 시스템을 혼자 직접 구현할 수도 없음
        • 이 경우 순응주의자 관계를 사용하거나 다른 실행 가능 솔루션으로 분리방법(separate way)를 사용한다
        • 분리방법의 의미 : 유사한 기능을 직접 구현한다는 의미가 아님
          → 지불시스템은 매우 복잡 → 작은 팀이 다른 목표의 사이드 업무로 구현
          → 회사에서 허용시 회사의 범위를 벗어나 상용 결제 공급 업체의 솔루션 이용가능
    3. 부패 방지 계층
    • anti-corruption layer = ACL
    • 다운 스트림은 업스트림으로부터 자신을 보호하기 위해 다운스트림과 업스트림의 유비쿼터스 언어 사이에 ACL이라 불리는 번역 계층을 만든다
    • ACL 생성은 필요한 경우 효과적인 보호 수단이 될 수 있다
    • but! 팀은 장기적으로 다운스트림에서 ACL을 유지관리 하는데 상당한 비용이 발생할 수 있음을 알아야 한다
    4. 오픈 호스트 서비스
    • 업스트림은 다운 스트림이 자신의 기능을 사용하고 있음을 알고 있는 경우 현재와 미래의 고객 요구사항을 조정하는 대신에 고객이 채택할 표준 인터페이스를 정의하고 공개한다
    • DDD에서는 이러한 업스트림을 오픈 호스트 서비스라고 한다
    • 승인된 다운 스트림을 통합할 수 있는 개방적이고 쉬운 프로토콜 제공
    • 해당 프로토콜의 하위 호환성을 유지하거나 명확하고 안전한 버전 관리 제공
    • 따라서 오픈 호스트 서비스는 별다른 문제없이 운영을 확장 가능
    • 실제로 모든 공용 서비스(API)는 이러한 접근 방식 사용
    • 예) 퍼블릭 클라우드 업체의 API
      • 일반적으로 고객이 수백만명 → 업스트림은 다운 스트림에 대해 구체적으로 알지 못한다
      • 이 경우 오픈 호스트로 운영 → 유용하게 서비스를 제공하고 발전시킬 수 있다

4.2.2 동기식 통합과 비동기식 통합

  • 컨텍스트 매핑은 위에서 설명한 도메인간의 관계 유형 외에도 제한된 컨텍스트간에 사용되는 통합 유형을 기반으로 구분 가능하다
  • 제한된 컨텍스트는 그림과 같이 동기식 또는 비동기식으로 통합할 수 있다
  • 동기식 통합
    • HTTP를 통해 구현된 RESTful API 서비스(일반적 패턴)
    • protobuf와 같은 바이너리 형식을 사용하는 gRPC 서비스
    • GraphQL 인터페이스 (최근 트렌드)
  • 비동기식 통합
    • 게시-구독 형식의 상호작용 패턴을 주로 사용
    • 해당 패턴에서 업스트림은 이벤트 생성 가능, 다운스트림은 관심있는 이벤트를 구독해서 처리하는 worker를 가진다
    • 단점: 구현 및 디버깅이 더 복잡하다
    • 장점: 서로 다른 기술 스택으로 구현한 경우에도 여러 수신기가 동일한 접근 방식과 구현 사용하여 이벤트 구독 가능 하므로 높은 수준의 확장성, 탄력성, 유연성을 제공가능

4.2.3 DDD 집합체

  • aggregate
  • DDD의 핵심 개념
  • 외부 소비자가 단일 단위로 볼 수 있는 관련 도메인 객체의 모임
  • 외부 소비자는 단일 엔티티만 참조하며 해당 엔티티는 DDD에서 집합체 루트(aggregate root)로 알려져 있다
  • 도메인은 집합체를 통해 도메인의 내부 족잡성을 숨기고 외부 소비자에게 ‘흥미로운’ 정보와 기능(인터페이스)만 노출 가능

4.3 이벤트 스토밍

개요
  • DDD 는 전체 시스템 수준의 분석(전략적 분석)과 복잡한 대규모 시스템의 상세 구성 요소 분석(전술적 분석)을 위한 강력한 방법론
  • MSA 초기에 DDD는 마이크로서비스 크기를 조정하기 위한 유일한 방법으로 알려짐 → 마이크로서비스 증가로 DDD 실천사례 증가
  • 오해와 진실 : DDD를 마이크로서비스 크리를 조정하는 도구로 사용되는 것은 무리가 있다
DDD는 어렵다
  • 복잡하고 비용이 많이 든다
  • DDD 수행하려면 아주 많은 지식과 경험이 필요하다
  • 에릭에반스의 DDD 책은 520 페이지이며 실제 이해하려면 최소 몇 권의 책을 더 읽고 여러 프로젝트에서 구현 경험을 쌓아야 한다
  • DDD는 팀 활동이며 시간이 많이 걸린다 → 정통한 소수 개발자 보유로 충분하지 않으며 실천이 어려움
이벤트스토밍
  • DDD에 개념에 기반을 두고 몇시간만에 제한된 컨텍스트를 찾을 수 있도록 도와준다
  • 주로 서비스 크기 조정을 위해 DDD의 낮은 적용 가능성을 위한 돌파구
  • DDD를 완전 대체는 불가능하지만 제한된 컨텍스트 발견에는 꽤 효과적인 프로세스

4.3.1 이벤트 스토밍 프로세스

  • 패스
  • 너무 간략히 나와서. 정리 거리도 안됨
  • 혹시 이부분에 관심이 있으신 분은 차라리 도메인 주도 설계로 시작하는 마이크로서비스 개발를 보면 굉장히 자세히 나와있습니다.
  • 아니면 이벤트 스토밍으로 검색하시면 나오는 블로그들 보면 오프라인/온라인에서 하는 실습이 있습니다

4.4 범용크기 조정 공식

  • 제한된 컨텍스트 : 마이크로서비스 크기를 정하기 위한 좋은 시작점
  • 마이크로서비스의 경계는 DDD와 이벤트 스토밍의 제한된 컨텍스트와 동의어는 아니다
  • 처음부터 완벽한 마이크로 서비스 경계를 구축할수 없으며 시간이 지남에 따라 마이크로서비스는 발전하고 경계도 일정하지 않게 된다
  • 크게 나눈(coarse-grained)설계로 시작 → 도메인에 더 많은 학습과 충분한 복잡성을 가진후 세분화 진행

4.4.1 범용 크기 조정 공식

→ 마이크로서비스를 세분화할 떄 권장하는 세가지 공식

  1. 제한된 컨텍스트를 사용할 수 있는 몇 개의 마이크로서비스로 시작한다
  2. 어플리케이션과 서비스가 성장함에 따라 마이크로서비스간 조정이 필요하다면 조정을 제거하기 위해 서비스를 분할
  3. 조정을 줄이기위한 올바른 궤적을 유지할 것 : 서비스 크기를 얼마나 ‘완벽하게’정하는지에 대한 현재의 상태보다 훨씬 더 중요

Related POST

공유하기