본문 바로가기

개발/서버 아키텍쳐

[강의 정리] 우아한모노리스(1) - 우아한테크세미나

좋은 강의들을 보고, 정리해보고자 한다.

첫 번째로 "우아한테크세미나에서 발표된 박용권님의 우아한모노리스"를 정리해 본다.

영상링크는 다음과 같다.

https://www.youtube.com/watch?v=SrQeIz3gXZg


  • 모노리스 -> 마이크로서비스로 넘어가면서 찢어진 두 개이상의 시스템을 어떻게 통합할까? 라는 관점에서 "이벤트" 기반을 도입해보았다.
  • 반대로 MSA를 모노리스로 옮기는 과정도 경험해 보았다. 
  • 시장에는 아키텍처 이야기를 다루는 책이 많지 않다. 하지만 MSA는 조금 예외다. 서점에 책이 너무 많다.
  • 하지만 보편적인 서버 아키텍처, 모노리스에 대한 책은 잘 없다. MSA책 안에 몇 장이 나올 뿐. 
  • 모노리스에 있는 이야기를 들려주는 강의! 

 

우린 마이크로서비스에서 모노리틱으로 갈아탔다.

이유는 크게 2가지였다.

  1. 기술부채 - 보통 MSA의 지향점이 모노리틱의 기술부채를 없애기 위함이라고 말한다. 하지만, 반대로 기술부채를 해결하기 위해서 모노리틱으로 넘어왔다. 
  2. 시스템과 조직의 관점

기술부채

  • 공유된 데이터 원본에 대한 문제가 있다.
  • 하나의 DB가 아닌, 테이블을 여러 개의 서비스가 사용하는 상황을 의미한다.
  • 스키마 조작 코드가 여러 서비스에 분산이 된다. 어떤 코드를 변경했을 때 다른 코드를 챙기지 못해서 장애 유발도 가능. (이런 부분 때문에 프로젝트 상에서 도메인 주도 개발, 헥사고날 아키텍처(포트앤어댑터) 등의 잘 짜여진 구조에서 DTO+Repository 모듈을 아예 따로 만들고 이를 여러 모듈에서 공유하는 것이 아닐까? - 이렇게 변경해보도록 하자)
  • 결국 중복과 안정되지 않은 코드.
  • 결국 시간이 지나면 한 가지 기능을 수행할 때 코드가 분산된다. 기능의 구현이 복잡해진다. 
  • 로직과 코드가 분산되어 있다보니 서로가 서로를 번갈아 호출하며 해결하는 경우가 발생한다.
  • 이렇게 되면 요구사항이 들어왔을 때, 빠르게 반영하지 못한다. -> 여러 서비스를 왔다갔다 진행해야 한다. 개발기간이 늘어나고, 테스트기간이 길어진다.
  • 그렇게 되고, 서비스 반영을 할때 2군데 이상의 서비스에 손을 대는 경우가 발생한다(이번의 문제였다고 생각한다. MQ에서 변수가 Integer -> Long으로 바뀜으로 인해, 문제가 발생했는데 이 경우 결국 정말 "동시에" 배포가 되지 않으면 문제가 해소가 되지 않을텐데. 좋은 방법이 있을까? 필드를 추가하는 것은 SerializableID가 존재하니까 문제가 되지 않을 것이다.)
  • 이때 타이밍이 중요하다.
너와 나, 우린 원래 하나였다. 원래 하나였는데, 억지로 찢어 놓을 것일 수도 있는 것이다.

조직과 시스템의 운영 관점

  • 처음에 개발팀의 인원이 많지 않았다. 소수였다. 그런데 3배에 가까운 서비스가 있었다. 
  • 이 경우 앞의 기술부채를 가속화 시킨다. 
  • 문제는 내가 A, B, C를 다 건드릴수 있다는 점이다.
  • 요구사항이 왔을 때 비즈니스를 수용하기 위해 만든거다. 요구사항이 왔을 때 일정의 압박과 비즈니스 요구사항이 동시에 왔다면?
  • 개발자는 결국 어디에 놓았을 때 가장 빨리 동작하게 될지를 고민하게 된다.
  • 그 곳에 그냥 요구사항을 갖다놓게 된다.
  • 결국 기술부채를 가속화 시킨다. 
그래서 결국 모노리틱으로 돌아갔다.
  • 기술부채를 개선하고 올바른 MSA로 개선하는 것도 방법이다.
  • 하지만 일주일에 몇 번이고 장애 직전까지 몰리는 경우가 존재했다.
  • 불안정한 서비스. 끌어안고 올바른 구조로 개선할때까지 얼마나 걸릴지 가늠이 안된다.
  • 하지만 돌아가는 것은 생각보다 빨리 갈 수 있을 것이라고 결정이 들었다.
필요하다면 다시 나누자

모노티릭으로 되돌아가는 여정... ( 초 간단 버전 )

  • 여러 저장소(Repo)에 나뉘어져 있었다.
  • 여러 저장소를 합치기 위해서 가능한 충돌이 덜 날 수 있는 구조로 프로젝트 정리를 했다. (이게 아키텍처 개선의 힘이 되지않을까?)
  • 그 후 저장소를 통합한다. 
  • 이때 2가지 선택지가 있었다. 
    • Git Repo merge
    • 아예 새롭게 복사 붙여넣기로 진행하기

 

Git 저장소 Merge를 결정.
  • 과거의 히스토리는 소중하다. 빌드시스템을 합치는 과정도 이 단계에서 설정. 의존성 설정 등
  • 패키지 구조를 정리한다.
  • 마지막 작업은 서비스 통합
  • 하나의 서비스로 통합할 때, 스프링 프레임워크의 구조를 활용했다.
Spring은 여러개의 Application Context를 계층구조로 가질 수 있다. 스프링부트도 동일.
스프링 Context 계층 구조

모노리틱은 나쁘고, 마이크로서비스는 좋다?

  • 대결구도로 많이들 비교한다.
  • 보통 MSA책에서는 모노리틱의 단점을 공격하고 해결할 수 있는 솔루션들을 제안한다. 
  • 사례를 언급할 때 항상 NETFLIX, AMAZON의 성공사례를 꼭 들려준다. 
  • 저런 큰 기업에서 MSA의 특징이 특대화 될 수 밖에없는 사례를 보여준다.
  • 대부분의 기업의 서비스는 저 회사들처럼 크지 않고, 환경도 다르다.
  • 그런데 MSA가 황금망치처럼 너무 과대 광고가 되고 있는 것 같다. 
  • 물론 좋다. 배민도 3년간 잘 변화하고 기대 이상의 효과를 얻어냈다.
  • 하지만 모든 우아한형제들의 서비스가 MSA일 필요는 없다. 
  • 오버헤드와 고통만 가중되는 경우도 있다.

모노리틱이 가진 주요 단점과 한계

  • 예상치 못한 결합(단점)
    • 의도하고 짠건 아니지만, 의도치 않게 강한 결합이 일어날 수 있다.
    • 이럴때, 높은 테스트 비용이 발생한다.
    • A기능의 변경이 B까지 영향을 받아서 테스트 실시
    • 그로 인해 아래의 늦은 출시 싸이클
  • 늦은 출시 싸이클(단점)
  • 높은 테스트 비용(단점)
  • 부족한 장애 내성(한계)
  • 단일 확장성(한계) - 똑같은 A시스템을 여러개로 늘리는 수 밖에 없다.

마이크로서비스가 주는 주요 혜택 살펴보기

  • 마이크로 서비스 책에 항상 나오는 법칙이 있다.
  • 콘웨이의 법칙(https://engineering-skcc.github.io/msa/DDD-ConwaysLaw/)
    • 시스템을 설계하는 조직은 조직의 의사소통 구조에 맞추어 시스템을 구조화 한다.
  • 조직 부합성 - 아키텍처 시스템의 구조를 조직의 구조에 맞추어 정렬. 정렬되기 시작하면 협업 시너지 효과 극대화
    • 조직의 변화와 시스템의 변화 유기적으로 반응
  • 확장성, 회복성, 배포 용이성 - 작은 서비스들의 집합. 개발/배포/확장의 관점에서 개별의 서비스별로 이런 것들을 진행할 수 있다. 독립적으로. 동시에 장애 전파도 잘 안되고, 격리 시킬 수도 있고, 적절하게 수위를 조절할 수도 있다. 
  • 대체 가능성, 기술 이기종성, 조합성 - 작은 서비스기에 대체 가능하고, 잘 조합될 수 있고, 특화된 기술들을 가질 수 있다. 

마이크로서비스는 자율적이다?

  • 스스로 원칙에 따라서 어떤 일을 하거나 등의 결정을 책임질 수 있다는 뜻이 자율적!
  • 독립적으로 갈길을 갈 수 있다는 뜻.

 

정말 그러할까?

아키텍처 스타일을 변경하면 문제가 해결된다? 

내부의 구조가 얽혀있기 때문에 단점이 발견되는 것이다. 모노리스는. 큰 진흙 덩어리라고 부른다. 

 

하지만.. 고민이 깊지 않다면..?

아키텍처 스타일이 문제를 해결해주지 않는다!

분산된 모노리스..

결국 분산된 모노리스... 가 되어버리는 것이다. 

  • 시스템이 주는 구조의 이점을 고려해서 잘 만들면 MSA의 장점을 가져갈 수 있다.
  • 하지만 악마는 세부적인 요소에 살아잇다. 언제 다시 나타날지 모른다.

응집과 결합을 다스리는 것이 먼저다

  • 높은 응집도 낮은 결합도는 70년대 소개된 원칙인데, 지금도 SW 품질을 결정하는 기준으로써 작용하고 있다.
  • SW는 태생적으로 복잡하다. 어떤 방법으로도 복잡성을 없앨수는 없다.
  • 의도치 않게 복잡한것과, 의도적으로 제어하는 것은 다르다.
  • 결국 복잡성을 제어하기 위해서 모든 구조는 나온다.

 

The goal of software delivery is to minimize the lead time to business impact. Everything else is detail.
  • 소비자에게 가장 빠르게 전달하는 것이 중요하다.
  • 다양한 방법론 아키텍처는 대부분 시스템이 변경이 필요하면, 이 변경을 최대한 빠르게 수용할 수 있고 지속적으로 받아들일 수 있고, 동시에 규모가 커지더라도 속도가 떨어지지 않게끔 하는게 중요하다. 
  • 시장에 뒤쳐지지 않고 살아남을 수 있는 것.

적은 비용으로 변경할 수 있는 구조 만들기

  • 응집이라는 것은 시스템의 변경이 필요할 때 하나의 요소에서 변하는 부분의 정도.
  • 한 가지 변화의 요청에 의해서 시스템 변경이 일어나는 부분이 전체면 응집도가 높은 것. 일부분만 변경이 일어나면 응집도가 낮은거다.
  • 주문/결제가 하나의 박스(서비스)에 있을 때, 주문의 변화 요청은 결제에 영향을 줄 수 있다. 
  • 어떤 박스의 변화가 다른 박스에 변화를 주면 결합도가 높은 것, 느슨한 결합, 강한 결합은 이런 개념이다.
서비스를 너무 잘게 쪼개도 변화가 다른 곳에 영향을 끼칠 수 있다. 결합도가 높은 것. 한 곳으로 모으면 응집도가 낮아지는 것. 밸런스를 잘 조절하자. 동시에 두 개를 잡으려면 밸런스를 잘 맞춰야한다. 어떤식으로는 변경은 비용을 지불해야 한다. 시간과 돈.
좋은 구조는 적은 비용으로 수용할 수 있는 것

주어진 상황에 따라 적절한 도구를 선택하자

좋고 나쁜건 없다. 유연하게 판단하자. 

 

그래서 모듈형 모노리스를 선택했다. (스프링과 함께)

비즈니스 영역에 따라 독립적인 모듈을 구성한다. 

네트워크 호출이 -> 로컬 호출로 변경된, 로컬 MSA라고 생각할 수 있다.

 

어떻게 모듈을 통합할 수 있을까? - Modular Monoliths

스프링을 통해 어떻게 모듈을 구성하고 통합할 것인지를 고려하기로 하였다. 

모노리스를 구성할줄 모르면, 결국 진흙 MSA를 구성할 수 밖에 없다

 


시스템을 단일->분산, 분산->단일의 유연한 시스템을 갖추기 위해서는 2가지가 필요하다고 생각.

  • 모듈화 - 단일 시스템이 규모가 커졌을 때 경계를 쳐서 복잡성을 줄여줄 수 있는 시스템.
  • 자바에서는 패키지로 표현을 하기도 한다. 시스템의 전체적인 구조를 한 번에 다 볼 필요가 없다. 조각조각만 본다. 복잡도가 낮아진다. 
  • 외부에 감춰야하는 내용을 숨기고 응집도를 높인다. 
  • 모듈은 스스로 자율성, 테스트도 되어야 한다. 

헥사고날 아키텍처

  • core는 도메인 모델, 비즈니스 로직.
  • 내벽을 형성하는 아이들을 포트(창구, 인터페이스)
  • Adapter는 결국 Interface의 구현체
  • Application의 핵심을 분리시킨다는 것. 관심이 없는 것을.

  • 클린 아키텍처와 같은 것들도 헥사고날과 비슷한 관점에서 이야기한다.
  • 내부는 도메인, 외부는 구체적인 구현기술로 구성 - 관심사를 떨어뜨려라.
  • 구체적인 구현기술들은 얼마든지 변경될 수 있도록 해라
 변경을 수용할 수 있는 시스템을 만들라는 뜻
  • 바깥의 것들은 얼마든지 바꿔칠 수 있다.
  • Rest, Method, MQ, RPC 얼마든지 대체할 수 있다.