아티클/개발
[아키텍처] MSA는 서버를 여러 개 띄우는 게 아니다
오웬
한동안 PM2로 프로세스를 나누고 Nginx로 연결해둔 구성을 보면서, 이것도 MSA와 비슷한 것 아닐까 생각했다.
하지만 프로세스가 여러 개라는 사실만으로 MSA가 되는 것은 아니었다.
중요한 것은 서버의 개수가 아니라, 각 서비스가 어떤 업무를 책임지고 얼마나 독립적으로 변경·배포될 수 있느냐였다.
지금 내 구조와 무엇이 다른지, 그리고 언제 분리할 이유가 생기는지 정리해봤다.
구분의 기준은 서버 개수보다 배포 단위다
모놀리식은 여러 업무 기능이 하나의 애플리케이션으로 묶여 함께 배포되는 구조다.
반드시 서버나 실행 프로세스가 하나여야 하는 것은 아니다. 같은 애플리케이션을 여러 인스턴스로 실행할 수도 있고, 여러 데이터 저장소를 사용할 수도 있다.
반면 MSA는 업무 경계를 기준으로 서비스를 나누고, 각 서비스가 독립적으로 변경·배포될 수 있도록 설계하는 방식이다.
주문 기능을 바꿀 때 회원이나 상품 서비스를 함께 배포하지 않아도 되는 구조를 지향하는 것이다.
이렇게 보면 같은 모놀리식 애플리케이션을 복제해 로드밸런서 뒤에 배치하는 것은 MSA가 아니라 스케일 아웃이다.
| 구분 | 스케일 아웃된 모놀리식 | MSA |
|---|---|---|
| 실행 구조 | 같은 애플리케이션을 여러 인스턴스로 실행 | 업무별 서비스를 각각 실행 |
| 배포 단위 | 애플리케이션 전체 | 변경한 서비스 |
| 확장 단위 | 애플리케이션 인스턴스 | 필요한 서비스 |
| 주요 설계 과제 | 애플리케이션 내부의 결합 관리 | 서비스 간 계약과 의존성 관리 |
같은 애플리케이션을 여러 개 실행하는 것과, 서로 독립적인 애플리케이션으로 나누는 것은 다른 선택이었다.
MSA에서 얻으려는 것은 독립성이다
MSA를 선택하는 이유는 단순히 코드를 작은 덩어리로 만들기 위해서가 아니다.
특정 기능에만 부하가 몰리면 그 서비스의 처리 용량을 늘리고, 한 팀이 담당 기능을 수정할 때 다른 팀의 배포 일정에 덜 의존하도록 만드는 것이 목적에 가깝다.
다만 서비스를 분리했다고 이런 이점이 자동으로 생기지는 않는다.
결제 서비스가 중단돼도 상품 조회는 계속 제공하도록 설계할 수 있다. 하지만 상품 조회가 매번 결제 서비스의 응답을 기다리는 구조라면, 결제 장애가 상품 조회에도 영향을 줄 수 있다.
모놀리식에서도 특정 인스턴스의 장애가 반드시 모든 인스턴스의 중단으로 이어지는 것은 아니다. 반대로 MSA에서도 공유 DB나 긴 호출 체인 때문에 장애가 전파될 수 있다.
따라서 MSA는 장애 격리를 가능하게 하는 구조이지, 장애 격리를 자동으로 보장하는 구조는 아니다.
타임아웃, 서킷 브레이커, 자원 제한 같은 장치가 함께 필요한 이유도 여기에 있었다.
DB를 나눈다는 것은 데이터의 소유권을 나눈다는 뜻이다
MSA를 공부하면서 처음에는 서비스마다 DB 서버를 따로 두는 것이라고 이해했다.
하지만 더 중요한 것은 물리적인 DB 서버의 개수가 아니라, 어떤 서비스가 어떤 데이터와 변경 규칙을 책임지느냐였다.
예를 들어 주문 데이터는 주문 서비스가 관리하고, 다른 서비스는 정해진 API나 이벤트를 통해 필요한 정보를 얻는다. 다른 서비스가 주문 테이블을 직접 수정하는 방식은 피하는 것이다.
같은 물리적 DB 서버를 사용하더라도 데이터 저장 영역과 접근 권한을 분리할 수 있다. 반대로 DB 서버를 따로 두었어도 서로의 내부 구조에 강하게 의존한다면 독립성은 낮을 수 있다.
데이터를 분리하면 기존처럼 여러 업무의 테이블을 자유롭게 JOIN하거나, 하나의 로컬 트랜잭션으로 묶는 접근을 그대로 유지하기 어려워진다. 필요한 데이터를 어떻게 조합하고 변경 결과를 어떻게 일치시킬지 다시 설계해야 한다.
DB를 분리하는 것은 저장 위치를 바꾸는 일이 아니라, 데이터에 접근하고 변경하는 계약을 만드는 일이었다.
독립성을 얻는 대신 분산 시스템의 비용을 감당한다
모놀리식 내부에서는 함수 호출로 끝나던 일이, 서비스 경계를 넘으면 네트워크 통신이 된다.
그러면 단순히 응답이 느려지는 것 외에도 새로운 질문이 생긴다.
- 상대 서비스가 응답하지 않으면 얼마나 기다릴 것인가.
- 처리는 끝났지만 응답만 유실됐다면 다시 요청해도 되는가.
- 여러 서비스 중 일부만 성공했다면 어떻게 복구할 것인가.
- 문제가 생겼을 때 어느 서비스에서 시작됐는지 어떻게 찾을 것인가.
서비스마다 배포와 모니터링을 관리하고, API 호환성과 데이터 정합성도 챙겨야 한다.
서버를 쪼개면 코드가 단순해질 수는 있지만, 전체 시스템의 운영까지 단순해지는 것은 아니다.
애플리케이션 내부의 복잡도를 줄이는 대신, 서비스 사이의 통신과 운영에 새로운 복잡도가 생기는 셈이다.
그래서 언제 분리할 것인가
“서버 하나로 감당할 수 없을 때 MSA로 바꾼다”는 기준만으로는 부족하다. 처리 용량 문제는 모놀리식을 스케일 아웃하거나 병목을 개선해 해결할 수도 있기 때문이다.
오히려 다음과 같은 요구가 반복되는지 살펴보는 편이 낫겠다.
- 특정 업무만 독립적으로 확장해야 하는가.
- 서로 다른 팀이 각자의 기능을 자주 배포해야 하는가.
- 함께 배포해야 하는 구조가 실제로 개발 속도를 막고 있는가.
- 서비스별 데이터 소유권과 통신 규칙을 정할 수 있는가.
- 분리 이후의 장애 추적과 운영을 감당할 수 있는가.
처음부터 전부 나눌 필요도 없다. 모놀리식 안에서 업무별 모듈과 의존성을 정리하고, 독립성이 필요한 부분부터 점진적으로 분리할 수 있다.
반대로 경계가 정리되지 않은 코드를 서버만 나눠 배치하면, 여전히 함께 수정하고 배포해야 하는 분산된 모놀리식이 될 수도 있다.
지금 내 서버에서는 서비스를 더 나누는 것보다, 모놀리식 내부의 역할과 업무 경계를 먼저 정리하는 편이 우선이라고 생각한다.
MSA를 도입했는지가 아니라, 지금 겪는 문제가 무엇이고 그 문제를 분리로 해결할 수 있는지가 먼저다.
당분간은 서버 개수보다 코드 안의 경계부터 잘 나눠보려고 한다.
댓글 0개
댓글은 로그인 후 작성할 수 있어요.
로그인하기아직 작성된 댓글이 없습니다.