마이크로 서비스 (MSA, MicroService Architecture)

마이크로 서비스는 한 가지만 아주 잘 처리하자는게 기본 원칙이다.
그렇기 때문에 비즈니스 도메인을 중심으로 모델링된 여러 마이크로 서비스들이 나오게된다.
독립적으로 배포 가능한 서비스들이 네트워크로 서로 통신한다.

마이크로 서비스의 특징

  • 독립적인 배포 가능성
  • 느슨한 결합
  • 자유로운 언어 선택
  • 각 컴포넌트가 갖는 자체 데이터베이스
  • 서비스 안전성과 유지보수성

독립적인 배포 가능성을 위해 느슨한 결합과, 자체 데이터베이스가 요구된다.
독립적인 배포를 통해 언어에 구애받지 않게 되며, 서비스 유지보수에 용이하고 안전성을 얻을 수 있다.

독립적인 배포 가능성 (Independent deployability)

다른 서비스를 활용하지 않고 마이크로서비스에 변경을 가하는 방식으로 서비스 환경에 배포할 수 있다는 개념이다.
서비스가 느슨하게 결합(loosely coupled) 되어 있음을 보증할 필요가 있고, 이를 위해 서비스간에 안정적인 계약이 필요하다.
인터페이스가 안정적이게 느슨하려면 서비스 경계를 찾아야한다.
그래서 도메인 중심의 모델링이 중요하다.

데이터 소유권 문제

몇몇 구현의 선택이 느슨한 결합 어렵게 만드는데 가장 자주 보이는 예시는 데이터베이스의 공유이다.

데이터 베이스 공유에 대한 원칙

  1. 정말 필요한 경우가 아니라면 DB를 공유하지 말자.
  2. 정말 필요해도 피할 수 있는 방법을 찾자.

어떤 이유로 변경될 수 있는 내부 구현 세부사항으로부터 안정적인 공개 계약으로 서비스를 매핑해야한다.
독립적 배포가능성에서는 안정적인 인터페이스가 필수이며 DB 공유는 독립적 배포 가능성을 위해할 수 있는 최악의 선택이다.

데이터를 보유한 서비스에 요청하는게 당연하다.
무엇을 공유하고 숨길지 결정해야하고, 이 인터페이스가 안정적으로 나와야 한다.
인터페이스가 바뀌면 사용하는 서비스들도 변경되어야 한다.

서비스와 데이터 소유권

MSA는 아니지만 내가 개발하는 모듈에서 배치와 같은 작업으로 멀티 서비스 구조를 갖게 되었을 때 데이터베이스 공유 문제로 이슈가 된적이 있다.

우리는 서비스 서버와 서비스 서버에서 발생한 메시지를 처리하는 배치 서버에서 동일한 데이터베이스를 공유했다.
초기엔 편하게 서비스를 개발하고 유지했는데, 키와 스키마 변경, 데이터 베이스 변경 등의 이슈 때마다 배포 순서를 신경써야 했다.
한번은 서비스 서버의 데이터 반영이 배치 서버에 반영되지 않아 사용자 데이터를 유실할 뻔 하기도 했다.

그 이후로 개발하는 배치에선 데이터베이스 공유를 하지 않는 방향을 기본으로 한다.
데이터의 안정성과 데이터에 대한 책임을 하나의 서비스가 갖는 것이 중요하다.
이건 어떤 편의성이 있더라도 교환해선 안되는 문제이다.

마이크로 서비스의 장점

  • 독립적인 배포로 시스템 확장과 견고성 개선
    • 독립적인 배포는 각각의 서비스에 대한 자체적인 빌드, 배포, 테스트, 모니터링, 확장, 롤백이 가능하다.
  • 서비스의 병렬 개발(콘웨이의 법칙)
  • 맡은 부분에만 몰입해서 개발
  • 다양한 기술적 선택
  • 아키텍처적 유연성

콘웨이의 법칙

시스템 구조는 그 시스템을 설계하는 조직의 통신 구조를 그대로 따라갈 수밖에 없다. - 콘웨이

‘조직의 통신 구조에 따라 제품을 만들게 된다.’는 현상이다.
모놀리스로 가게되면 모놀리스 구조의 큰 개발팀이 일해야 하는 구조가 되며, MSA로 가게되면 각각의 서비스 개발팀이 나뉘거나 독립적인 개발이 가능한 구조가 된다.

그런데 반대로 이미 만들어진 시스템 구조가 조직의 통신 구조에 영향을 미치기도 한다.
서비스와 팀이 아직 작을 때 모놀리스를 선택하게 되면 서비스와 팀이 커져서 팀을 나누게 될 때, 적절히 팀을 나눌 수가 없게 된다.
큰 모놀리스 서비스에서 업무 영역을 나누게 되면 커뮤니케이션 비용이 커질 수 밖에 없다.

마이크로 서비스 단점

  • 복잡성 증가
    • 전체 시스템의 복잡성이 증가한다.
  • 테스트 및 디버깅 비용 증가
    • 여러 서비스를 관통하는 acceptance 테스트를 작성하기 더 어렵다.
    • 이슈 등으로 디버깅이 필요한 경우에도 동일하다.
  • 네트워크를 사용하는 대기시간
    • 각 서비스를 거치기 때문에 아무리 효율적인 기술들(grpc 등)을 사용한다 하더라도 모놀리스에 비해 대기시간이 길어질 수 밖에 없다.
  • 예측할 수 없는 통신 문제
    • 서비스 통신, 네트워크, 서비스 다운 등의 문제에 대한 각각 서비스의 대응을 설계해야 한다.

결합도와 응집력

서비스는 응집력이 높고 결합도가 낮아야한다.
결합도와 응집력을 이해하는게 중요하다.

  • 결합도는 한가지를 바꾸면 다른 것도 바꿀 필요가 있는 방식.
  • 응집력은 관련된 코드를 그룹으러 묶는 방식.

구현 결합도 (implementation coupling)

가장 위험한 형태의 결합도지만 가장 쉽게 결합도를 낮출 수 있다.
위에서 예를 들었던 DB의 공유가 구현 결합도의 가장 고전적인 예시이다.
DB를 공유하면 스키마 구조, 쿼리, 행의 내용까지 모두 결합된다.

  • 인터페이스를 두고 해당 서비스에 요청하는 방식으로 결합을 낮춘다.

시간적 결합도 (temporal couping)

메세지가 전송되는 시점과 메세지가 처리되는 방식이 시간과 관련되어 있는 경우에 대한 결합도이다.

  • 캐싱등의 해결책이 있을 수 있다.

배포 결합도 (deployment coupling)

배포 결합도가 높은 서비스의 예를 들면 모놀리스이다.
모놀리스에서는 한 곳의 변경으로 전체 배포가 필요하다.

  • 배포에는 위험이 따르고 위험을 줄이는 방식으로 마이크로 서비스로 배포 결합도를 낮출 수 있다.

도메인 결합도 (domain coupling)

마이크로 서비스에서는 상호작용이 있기 때문에 도메인 결합도는 당연하다.
주문하려면 고객 주문 정보를, 배송하려면 고객 주소 정보를 알아야 한다.
마이크로 서비스에서는 이런 정보들이 각각 다른 마이크로 서비스에 있게되며 필요한 도메인만 사용하는 방식으로 도메인 결합도를 낮춘다.

  • 꼭 알지 않아도 되는 도메인은 메세지에 보내어 서비스에서 직접 도메인을 알지 않도록 결합도를 낮출수도 있다.

MSA의 방향

가능한 작은 인터페이스를 유지하는 것이 좋다.
그러나 처음 MSA를 한다면 규모를 작게하기 보단 작게 만들 수 있는 방향으로 모델링하되 컴포넌트의 개수를 너무 작지 않게 가져간다.
그리고 점진적으로 컴포넌트를 늘려갈 수 있도록 한다.

reference

  • 마이크로서비스 도입, 이렇게 한다 1장, 샘 뉴먼
  • 마이크로 서비스 아키텍처 1장, 우메쉬 램 샤르마