레이어드 아키텍처로 잘 나눠도 트래픽이 몰리면 한계가 온다. 일부 기능의 수요가 급증했는데 전체를 함께 확장해야 하는 상황이다. 그때 나오는 두 갈래다.
모놀리식
하나의 프로젝트, 하나의 프로세스에 전부 들어 있다. 전통적인 형태이고 소규모 테스트에서는 이쪽이 합리적이다. 개발 환경이 같아 복잡하지 않고, 개발과 빌드와 배포와 테스트가 쉬우며, 서비스 간 통신이 네트워크를 타지 않아 빠르다.
확장하려면 전체를 확장해야 한다. 보통 서버 스펙을 올리는 수직 확장이다. 배포도 느려서 한 줄 고쳐도 전체를 다시 빌드하고 올린다. 배포가 느린 문제는 무중단 배포로 던다.
확장 문제는 패턴 안에서도 완화할 수 있다. 멀티 모듈로 나눠두면 나중에 서비스가 커졌을 때 확장하기 쉽고, 응집도는 올라가고 결합도는 내려간다.
MSA
하나의 큰 애플리케이션을 작은 서비스 단위로 쪼개고, 각 서비스가 하나의 업무를 맡아 경량 API로 통신한다. 서비스마다 알맞은 기술과 언어를 고를 수 있고, 서버나 프로세스에 장애가 나도 격리와 복구가 쉬워 전체로 번질 가능성이 적다.
규모가 정하는 선택
| 모놀리식 | MSA | |
|---|---|---|
| 빌드와 배포 | 느리다 | 서비스별로 빠르다 |
| 일부 장애의 영향 | 전체가 멈춘다 | 그 서비스만 멈춘다 |
| 확장 | 전체를 함께 | 필요한 서비스만 |
| 내부 호출 | 메서드 호출 | 네트워크를 탄다 |
| 트랜잭션 | 하나로 묶인다 | 어렵다 |
| 데이터 조회 | 조인하면 된다 | DB가 나뉘어 어렵다 |
| 테스트 | 전 구간이 쉽다 | 어렵다 |
| 이해 가능성 | 커지면 아무도 모른다 | 각각은 이해할 수 있다 |
| 관리 | 단순하다 | 어렵다 |
규모가 답을 정한다. 서비스가 작을 때 MSA는 과하고, 커진 뒤에는 모놀리식이 감당되지 않는다. 중간 단계로 모놀리식을 유지하면서 멀티 모듈로 나누는 선택지가 있다.
쪼갠 뒤에 남는 문제
서비스마다 로그가 따로 남으니 하나의 요청을 추적할 수 없다. ELK처럼 수집과 파싱과 시각화를 묶은 도구로 한곳에 모아 검색할 수 있게 한다. 데이터 콜렉터를 Fluentd로 바꾼 EFK 조합도 같은 자리다.
여러 서비스에 걸친 작업은 하나의 트랜잭션으로 묶을 수 없다. 그래서 묶기를 포기하고 다른 방법을 쓴다. 보상 트랜잭션은 실패했을 때 되돌리는 작업을 따로 수행해 일관성을 유지하고, 복합 서비스는 트랜잭션을 지원하는 네이티브 프로토콜로 두 시스템을 묶어 API로 노출한다. 유스케이스를 하나 두고 최종 결과가 맞는지 검증하게 하는 방법도 있다.
실무에서 가장 큰 장벽은 비용이다. DAU가 높지 않은데 무리하게 전환하면 서버와 모니터링과 로그 수집이 전부 배로 늘어 비용 폭탄을 맞는다.