인프라 아키텍처를 설계하기 위해 꼭 알아야 할 분산 시스템과 비동기 처리에 대해 알아보자.
🤔 모놀리식 아키텍처의 문제점
서버를 모놀리식 아키텍처로 설계하면, 특정 서비스에 장애가 발생한 경우 다른 모든 서비스 또한 사용할 수 없게 될 위험이 있다.
왜 그럴까? 모놀리식 아키텍처란 애플리케이션의 모든 모듈이 하나로 빌드되어 배포된 형태이다. 따라서 애플리케이션이 실행될 때, 어떤 모듈이 실행되더라도 모든 모듈이 다 들어있는 단일한 프로세스 위에서 돌아간다. 단일한 프로세스 위에서 각각의 모듈이 함수 호출로 상호작용하는 식이다. 이 형태로 설계하면 가장 큰 문제점이 '자원 부족', '장애 전파'이다.
1) 자원 부족
사실 자원 부족 문제는 동기 처리에서 특정 모듈이 I/O 작업 등에 의해 처리 시간이 길어질 때, 응답을 받을 때까지 스레드를 점유한 채 아무것도 하지 않고 대기하는 상황에서 주로 발생하는 문제이다. 그런데 모놀리식 아키텍처라면 더욱 이러한 자원 부족 문제가 심각하게 된다. 스레드가 쉽게 고갈되기까지 하기 때문이다. OS는 프로세스 단위로 CPU, Memory 자원을 할당한다. 그런데 프로세스 하나가 애플리케이션의 모든 모듈을 책임지고 있다면, 사용자 요청에 의해 생성된 스레드들이 각자 모듈에 할당될 텐데 당연히 스레드 풀 하나를 모든 모듈이 공유하기에는 버겁다.
2) 장애 전파
자원 부족 문제와 이어지는 얘기이다. 만약 특정 모듈에 메모리 누수가 있을 경우, 해당 모듈은 메모리를 계속 사용하게 되고 결국 메모리가 완전히 소진되면 애플리케이션 전체에 OOM 오류가 발생하게 되어 특정 모듈과 무관한 모든 기능이 함께 다운될 수 있다.
🧱 MSA
모놀리식 아키텍처의 이러한 문제를 해결하기 위해 MSA 아키텍처가 사용되기도 한다. 애플리케이션을 기능별로 나눈 후 독립적으로 빌드하여 배포하고, 각각 진짜 독립적인 하나의 서버처럼 DB도 붙여서 쓴다. 이제 각 모듈들은 함수 호출이 아닌 네트워크 통신으로 요청과 응답을 주고 받는다.
OS는 이제 나뉜 모듈들을 독립적인 프로세스로 보고 CPU, Memory 공간도 따로 할당해준다. 스레드 고갈이 발생할 확률이 줄어들고 장애 전파로 인한 애플리케이션 전체 다운 위험도 없어졌다!
모듈 간에 MQ를 끼워서 비동기 통신하게 만들면 더욱 더 안정적인 서비스를 만들 수 있다. 모듈 A가 모듈 B에게 요청을 보내는 속도가 엄청 빠른 반면 모듈 B가 처리할 일이 오래 걸리는 일이라서 A에게로의 응답이 늦어진다면, 동기 처리에서는 여전히 모듈 A에서 스레드 고갈이 발생할 위험이 있다. 그러나 A는 MQ에 B가 처리할 요청을 넣어두기만 하고 B는 MQ에서 요청을 꺼내서 처리하게만 하면 문제가 해결된다.
또다른 장점으로는 코드 수정 시 모듈별로 재빌드/배포를 할 수 있다는 점이다. 모놀리식 아키텍처의 경우 하나의 코드베이스로 뭉쳐져있었기 때문에 작은 코드 수정에도 거대한 코드를 항상 빌드/배포해야 했다. 그리고 인프라 스펙 관리도 각각 할 수 있다는 점에서 좋다.
단점으로는 분산 환경을 모니터링하고 관리하는 것과 데이터 일관성 유지가 모놀리식보다 어렵다는 점이다. 네트워크 지연 또한 발생할 수 있으며, 한 요청이 어떤 서비스를 거쳐 처리되었는지 추적하고 디버깅하기도 매우 어렵다. 따라서 MSA는 작은 규모의 프로젝트에 사용하기에는 전혀 적합하지 않다.
💡 MSA 말고 다른 방법은?
하나의 서비스에 장애가 발생해도 전체 시스템이 멈추지 않게 하기 위함이 MSA의 목적이라면, 굳이 관리 비용이 더 큰 MSA를 쓰지 않고 같은 효과를 주는 다른 방법을 써도 된다. 다음 사항을 먼저 고려해보자.
1) 비동기 처리
서비스 핵심 부하를 비동기처리하여 메인 서버의 안정성을 확보하자. 설명은 이미 위에서 했다!
2) 모니터링/복구 시스템 구축
서버의 장애를 빠르게 판단하고 빠르게 복구하여 업타임을 최대한 유지시키자. 서버 리소스 사용률, API 응답시간, 장애 발생 메트릭을 실시간으로 모니터링하여 잠재적인 문제를 사전에 파악하고, 특정 모듈의 헬스 체크가 실패하면 쿠버네티스를 통해 자동으로 해당 인스턴스를 재시작하도록 설정한다.
3) 로드 밸런싱
하나의 서버가 다운되더라도 로드 밸런서가 다른 서버에 트래픽을 옮겨서 서비스가 중단되지 않게 하자. 이를 도커로 컨테이너화하고 쿠버네티스로 관리하면 부하에 따라 자동으로 인스턴스 수를 늘릴 수 있다.
4) 특정 모듈만 따로 배포
특정 모듈을 독립된 컨테이너로 따로 만들어서 배포하여 메인 서버 프로세스와 격리하는 방법이다. 격리 모듈은 k8s에 배포하여 HPA를 통해 컨테이너 수를 auto-scaling할 수 있고, MSA처럼 자체 DB를 둘 수도 있다. 모니터링 툴도 따로 붙여서 메트릭을 수집할 수 있다.
오늘 공부를 하면서 MQ의 역할과 비동기 처리의 필요성, MSA에 대해서 좀 더 명확하게 알게 되었다.
나중에 내가 만들 서비스의 요구사항에 맞게 여러 기능들을 적용해볼텐데, 일단 개발하면서 필요하다 싶으면 점진적으로 도입해야겠다.
'프로젝트' 카테고리의 다른 글
| [OSSCA] 우분투 한국어 번역 오픈소스 기여하기 (0) | 2025.10.15 |
|---|---|
| 동시성 제어 방법 - 비관적 락, 낙관적 락 (0) | 2025.10.15 |
| [Next Step 사전과제] 도메인 설계(3) - 클래스 다이어그램 (1) | 2025.09.18 |
| [Next Step 사전과제] 도메인 설계(2) - 도메인 규칙 정의 (0) | 2025.09.18 |
| [Next Step 사전과제] 도메인 설계(1) - 행동과 책임 (1) | 2025.09.18 |