Microservice Architecture?

마이크로서비스 아키텍쳐(MSA)의 기본적인 접근은 하나의 어플리케이션을 작은 서비스의 모음으로 개발하는 방법을 말한다. 각각의 작은 서비스들은 자체적인 프로세스로서 동작하며 서로 가볍고 투명한 매커니즘(http와 같은)으로 통신한다. 각각의 서비스는 비즈니스 기능을 중심으로 구축되며 가능한 한 자동화된 배포를 통해 독립적으로 배포가 가능하다.

MSA vs Monolithic

Monolithic은 하나의 단단한게 뭉쳐진 어플리케이션으로 개발되고 배포된다. 각 기능들은 내부에서 class/package/namespace 등의 용어로 분리 개발되지만, 기본적으로는 하나의 어플리케이션을 구성한다. 단일 어플리케이션의 특성 상 모든 수정 및 업데이트내용은 서로에게 영향을 줄 수 있으며, 때문에 아주 작은 변경이라 할지라도 Monolithic에서는 서비스 전체에 반영을 해 주어야 한다. 이 내용은 모든 업데이트 내용이 어플리케이션 내부에서 서로 동기화되어 있다는 점이다.
MSA 어플리케이션은 서비스를 중심으로 작은 단위로 개발 및 배포되며, 서비스간의 명확한 경계를 만들어 서로 독립적으로 개발 및 배포될 수 있도록 한다. 이는 작은 변경은 필요한 서비스만 변경하여 전달할 수 있도록 하며, 각 도메인간의 연관성은 최대한 느슨하게 하여 서로 영향력을 적게 받도록 한다. 하지만 이러한 내용은 동일한 내용이 다수의 도메인에 속할 수 있기 때문에, 데이터 혹은 로직의 중복을 만들 수 있다. 또한 MSA는 다수의 어플리케이션이 서로 잘 작동될 수 있도록 하기 위해서 많은 고민이 필요하다.

Monolithic vs MSA Monolithic vs MSA

그래서 MSA를 왜 써야하는데?

MSA를 사용하는 이유는 매우 심플하다. 현재 개발하고 있는 비즈니스가 요구하고 있는가, 그리고 MSA로 개발했을 때 이득이 있는지를 확인해야 한다.
모든 상황에서 MSA가 무조건 유리하지 않다. MSA보다 Monolithic이 더 나은 경우도 많이 있기 때문에 무조건적으로 MSA를 선택하는 것은 나쁜 방법이다. MSA는 Monolithic에 비해 정말 많은 리소스가 소모된다. 첫 설계를 시작할 때 비즈니스의 범위를 정의하는 과정, 서비스의 구성 및 서로의 연관관계를 고려해야 한다. CI/CD에서도 더 많은 리소스가 필요하다. 하나의 Monolithic을 배포하는 것 보다는 여러개의 분할된 서비스를 배포하는 MSA가 더 많은 작업과 트러블들을 마주하게 된다는건 자명하다. 문제가 발생한 경우에는 어떠한가? 추적, 감시, 로그 시스템이 잘 구축되어 있지 않다면 어디서 문제가 발생하였는지 알기 어려운 경우가 허다하다.
여기까지만 보았을 때에는 왜 MSA를 쓰는가에 대해 회의감이 들 수 있다.

그럼에도 불구하고 우리가 MSA를 선택하는 경우에는 아래와 같은 이유가 있을 것이다.

  • 서비스를 제공하는 조직에 대한 역할과 책임의 분리가 필요할 때
  • 컴포넌트화된 각 서비스들에 대한 유연한 변화가 요구될 때

번외. 왜 MSA와 DDD가 서로 접목되는가?

상당히 다른 개념인 MSA와 DDD인데 왜 항상 두 가지를 같이 이야기하게 되는 것일까? MSA의 목적은 서비스를 작은 서비스의 모음으로서 개발하고, 각각의 서비스는 비즈니스 기능을 구현하고, 구현된 서비스들은 유기적이고 느슨한 관계 속에서 본래의 목적을 다한다. DDD는 비즈니스를 코드로 설명할 수 있으며, 반대로 코드를 비즈니스로서 설명할 수 있는 것이라고 말할 수 있다. 즉 실제 비즈니스를 기반으로 도메인, BoundedContext, ContextMap을 잘 만들고 이를 전파하는것이 목표이다.
자 그럼, MSA에서는 어떻게 서비스를 작은 서비스 모음으로 만들 수 있고 해당 내용을 모델링하여 전파할 수 있을까? DDD가 언급되는 이유는 여기에 있다. DDD의 목적을 다시 한번 잘 보면 비즈니스의 이해과 설계에 초점이 맞추어져 있다. 필요한 부분만을 모아서 정의한 도메인과 Boundary들, 그리고 BoundaryContext와 ContextMap은 MSA에서 서비스의 단위, 모델 설계, 서비스간의 관계와 필요한 기술을 설명할 수 있게 해주며, 나아가 서비스와 비즈니스간의 단단한 연결관계를 만들어주고 이를 비즈니스를 구성하는 구성원에게 전파하고 커뮤니케이션할 수 있는 수단(단어 혹은 상황들)을 제공해준다.

MSA의 특성들, 그리고 고려해야 하는 점

서비스의 컴포넌트화

어플리케이션을 구성하는 각각이 서비스들을 단일 개발 유닛인 컴포넌트로서 개발하는 것이다. 이로서 컴포넌트들은 서로 독립적으로 버전업 혹은 수정내용의 반영 등이 가능하다.
다만 각 서비스간의 커뮤니케이션을 위한 API 디자인이 가능한 한 세분화되어 있어야 하며, 이를 사용하는 서비스간 커뮤니케이션은 아무래도 Monolithic에 비해 비용이 더 들어간다. 또한 서비스가 책임지는 부분이 변경될 때에는 관련 서비스들에 대해 같이 변경되어야 하기 때문에 이 부분에 대한 어려움이 존재한다. 때문에 MSA에서는 서비스를 되도록 많이 나누는것이 능사가 아니다. 응집력이 있는 서비스를 만들고, 서비스간의 강한 관계성을 가능한한 낮추는 것이 여기서 말하는 컴포넌트화의 목적이다.

비즈니스의 분리

어쩌면 이 항목은 주제가 비즈니스에 초점이 맞추어져 있기 때문에 MSA보다는 DDD에 적합할지도 모른다. 하지만 MSA에도 적용되는 이야기가 있으니 작성해본다.

MSA는 다수의 서비스가 유기적으로 작동하는 것 에서부터 시작한다. 여기서 서비스는 어떻게 나누어지는가를 보면 비즈니스를 중심으로 나누어질 것이다. 비즈니스는 목표에 따라 하나 또는 여러 팀으로 나뉘게 된다. 그리고 여러 개의 팀은 각자의 역할, 책임을 가지게 된다. 그리고 이 말은 각자의 서비스 내에서 역할과 책임을 다해야 한다는 의미이기도 하다. 이 내용은 밑에서 나오는 오류가 발생할 것을 전제로 만들어지는 구조데이터와 트랜잭션에 관해서 에 이어진다.

오류가 발생할 것을 전제로 만들어지는 구조

MSA의 각 서비스들은 서로 연관관계가 있으면서도 독립적으로 작동해야만 한다. 서비스들이 서로의 작동에 영향을 줄 수 있지만, 장애 혹은 문제가 전파되어서는 안된다. 때문에 MSA에서의 서비스들은 모두 에러에 대한 내용을 전달하거나, 에러가 날 것을 전제로 작성해야만 한다. 그렇지 않으면 문제가 발생하였을 때 어디서부터 발생하였는지, 그리고 그 오류가 어디까지 전파되는지 알 수 없게 된다.
Monolithic의 경우에는 모든 로직이 서비스 내에 존재한다. 서비스 내에서의 오류는 개발자가 직접 테스트하는 것 만으로도 잡을 수 있다. 하지만 MSA는 서비스 간 통신이 기본 전제로 만들어지며, 이 통신이 문제가 발생하는 경우는 많다. 단순히 네트워크 장애 혹은 일부 서비스의 버전업 같은 경우도 여기에 속한다. 이러한 문제를 극복하기 위해 서비스의 감시와 복구에 대한 부분이 대두된다. 서비스는 언제든지 장애가 발생하여 정지될 수 있지만, 이것을 빠르게 인지하고 복구할 수 있는 시스템이 필요하다. 이를 위해서 우리는 실시간 모니터링 시스템을 구축하고, 가능한 경우에는 자동 복구 시스템을 구축하기도 한다.

데이터와 트랜잭션에 관하여

MSA하면 항상 트랜잭션의 문제에 대해 언급하곤 한다. 우리가 작성하던 수많은 Monolithic 서비스에서는 데이터 일관성을 위해서 트랜잭션을 매우 많이 사용한다. 하지만 MSA에서는 서비스간에 트랜잭션이 묶이는 경우도 있으며, 이를 위해 많은 서비스들이 하나의 트랜잭션에 묶이곤 한다.
계속 나오는 이야기이지만 MSA는 다수의 서비스가 관계를 통해 동작하는 하나의 큰 서비스이다. 그리고 이 서비스들의 역할은 너무나도 명확하다. 그리고 당연하지만 각각의 서비스는 자신이 관리하는 데이터를 각각 가지게 되고, 이 데이터들은 분산된 데이터 소스에 저장될 수 있다. 그리고 MSA에서는 여러 개의 데이터가 한번에 처리되어야 하는 경우도 물론 생긴다.
서비스에서 데이터를 관리할 때는 보통 ACID를 많이 지키려고 한다. 우리에게는 친근하면서도 엄격한 모델이지만, 문제는 이 ACID는 여러개의 boundedContext 안에서는 지켜지기가 쉽지 않다. 때문에 MSA에서는 최종 일관성이라는 개념을 도입하고, 이를 지키기 위해서 Two-Phase commit 혹은 SAGA 와 같은 분산 서비스 환경에서의 데이터 처리를 위한 패턴을 사용한다.

참고문헌