MSA로 전환, 어떤 기준으로 해야 할까?
아마존은 11.6초에 한 번씩 릴리즈를 배포한다. 여타 서비스들처럼 아마존도 처음에는 모놀리식으로 아키텍처를 구성하였지만 시스템이 커지면서 마이크로서비스아키텍처(MSA)로의 전환을 진행하였고 그 결과 빠르게 변하는 시장에 적응하면서 업계 1위 서비스로 성장할 수 있었다. 이런 성공 사례가 쌓이면서 근 5~6년 사이 어느 정도 규모를 가진 회사들은 대부분의 서비스를 MSA로 구성하고 있다.
MSA가 변화하는 요구사항에 기민하게 대응할 수 있는 아키텍처라는 점에는 이견이 없다. 이는 빠르게 변하는 세상에서 IT 서비스로 살아남기 위해서 이제는 필수로 선택해야 하는 옵션처럼 보인다. 하지만 현업에서는 MSA와 모놀리식의 충돌이 빈번하게 발생하고 있다.
모놀리식에서 MSA로 전환해야 하는 때는 언제인가?
MSA의 서비스 단위는 어떻게 나눠야 할까?
MSA로 전환하면 얻을 수 있는 이점은 무엇일까?
개발 리소스, 레거시의 상태, 인프라 지원, DDD 등 다양한 지식이 필요한 이런 물음에 합리적인 방향을 찾고자 자료가 필요했고 적절해 보이는 마이크로 서비스 패턴을 읽었다.
마이크로 서비스 패턴의 저자가 말하는 MSA의 단점과 장점 그리고 적용할 때 필요한 판단들을 배우고 여러 글에 걸쳐서 작성해보려고 한다.
모놀리식 아키텍처 패턴은 애플리케이션을 하나의 배포 단위로 구성한다.
마이크로서비스 아키텍처 패턴은 독립적으로 배포 가능하면서 자체 DB를 보유한 서비스들로 시스템을 분해한다.
단순한 애플리케이션은 모놀리식 아키텍처가, 크고 복잡한 애플리케이션은 마이크로서비스가 더 적합한 선택이다.
어드민 같은 작은 시스템에서는 서비스를 나누고 따로 관리하는 것보다 모아서 관리하는 것이 더 적합하다.
마이크로서비스 아키텍처를 채택하면 자율적인 소규모 팀들이 작업을 병행할 수 있어서 소프트웨어 개발 속도가 빠르다.
마이크로서비스 아키텍처는 만병통치약이 아니다. 복잡성을 비롯한 중요한 단점도 있다.
도메인 간 강하게 결합된 로직들은 수정과 배포가 같이 발생한다. 한 가지 요구사항에 여러 서비스가 수정되어야 하고 배포되어야 한다면, 과연 소프트웨어 개발 속도가 빠르다고 할 수 있을까? 잘못 설계된 도메인 단위로 나눠진 MSA는 분산 모놀리식에 가깝다.
마이크로서비스 아키텍처 패턴 언어는 마이크로서비스 아키텍처로 애플리케이션을 설계할 때 유용한 패턴의 모음집이다. 패턴 언어는 마이크로서비스 아키텍처 도입 여부를 결정할 때 유용하며 마이크로서비스 아키텍처를 효과적으로 적용하는 충실한 안내자이다.
소프트웨어 전달 속도를 높이려면 마이크로서비스 아키텍처만으로는 부족하다. 소프트웨어를 성공적으로 개발하려면 데브옵스 및 자율적인 소규모 팀들이 있어야 한다.
서비스 개발의 방향성을 결정할 수 있는 팀과 하나의 MSA 서비스는 1:1 관계여야 한다.
마이크로서비스를 검토할 때 인간적인 측면도 고려해야 한다. 직원들이 느끼는 감정도 충분히 반영되어야 성공적인 전환이 가능하다.
지금 당장 서비스 요구사항을 해결하기 바쁜데 MSA를 도입하여 얻는 이점에 모두가 공감할 수 있을까? MSA 전환이 시급한 지, 전환에 필요한 리소스는 충분한지 구성원들과 충분히 얘기를 나누고 팀원 모두가 공감하고 같이 움직일 수 있는가가 MSA 전환의 성패를 결정한다.
아키텍처는 애플리케이션 개발 속도에 직접 영향을 주는 갖가지 '~성(~ilties)'을 좌우한다.
마이크로서비스 아키텍처는 애플리케이션의 관리성, 테스트성, 배포성을 높이는 아키텍처스타일이다.
마이크로서비스는 기술적 관심사보다 비즈니스 능력, 하위 도메인 등 비즈니스 관심사 위주로 구성된다.
서비스를 분해하는 패턴은 크게 두 가지이다.
- 비즈니스 능력에 따른 분해: 비즈니스 아키텍처 기반
- 하위 도메인에 따른 분해: DDD 개념 기반
DDD를 적용하고 서비스마다 도메인 모델을 따로 설계하면, 의존 관계가 뒤엉켜 분해를 가로막는 만능 클래스를 제거할 수 있다.
기존 레거시를 분해하는 장애물은 아래 다섯 가지 형태로 나타난다.
- 네트워크 지연
분산 시스템끼리 통신은 네트워크 지연이 필연적으로 발생한다. 최대한 응집도를 높여 필요 없는 네트워크 통신을 줄이고 함수 통신으로 이뤄질 수 있도록 해보자.
- 동기 통신으로 인한 가용성 저하
서비스 간 통신은 타 서비스의 REST API 동기 호출이 가장 편한 방법이지만 타 서비스 중 하나가 불능일 때 관련 서비스 모두가 불능 상태로 빠질 수 있다. 비동기 메시징으로 강한 결합도를 제거하는 것이 방법이 될 수 있다.
- 여러 서비스에 걸쳐 데이터 일관성 유지
여러 서비스에 걸쳐 비동기 작업이 일어난다면 일관성을 유지해야 하는 과제가 생긴다. 과거에는 2 phase commit이라는 방식을 분산 트랜잭션에서 많이 사용했지만 요즘에는 saga 패턴을 많이 사용한다. 이는 4장에서 다룬다.
- 데이터의 일관된 뷰 확보
모놀리식 애플리케이션에서는 ACID 트랜잭션의 속성 덕분에 어떻게 쿼리를 하든 일관된 데이터 뷰가 반환되지만 MSA에서는 각 서비스 DB가 일관적이라 해도 커멘드 후 쿼리 호출 시점에 따라 데이터 뷰가 일관되지 않을 수 있다. 다행히 서비스를 잘 나누고 사가를 잘 적용한다면 이는 실제 문제로 나타나지 않는다.
- 분해를 저해하는 만능 클래스
서브 도메인으로 충분히 나눠지지 않는 경우, 한 도메인이 커다란 만능 클래스로 자리 잡을 수 있다. DDD를 도입하여 서브 도메인을 잘 나눠준다면 해결할 수 있다.
분해 지침에는 SRP, CCP와 같은 객체지향원칙 아이디어를 사용할 수 있다.
클래스의 책임을 정의하는 단일 책임 원칙(SRP, Single Responsibility Principle)과 클래스를 패키지로 구성하는 공동 폐쇄 원칙(CCP, Common Closure Principle)은 MSA의 서비스 단위에도 적용된다.
패키지 설계 시 적용 가능한 11개의 객체지향원칙을 MSA 서비스 분해에도 적용해보자.
http://butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod
1~2장에서 MSA가 장점과 단점, 그리고 MSA의 서비스로 서비스를 나눌 때 고려할 부분과 생기는 문제들에 대해서 알아봤다. 다음장부터는 1~2장에서 언급한 문제들의 해결 방법들과 실제 나누는 예제를 살펴본다.