티스토리 뷰

2018년, 커스텀하기 쉬운 블록체인 엔진 개발 오픈소스 프로젝트인 it-chain 프로젝트를 진행했다. 블록체인 엔진을 만드는 프로젝트다 보니 그 규모와 복잡도는 지금까지 내가 수행한 프로젝트 중 제일 컸던 것 같다. 나는 처음부터 합류한 것은 아니고 중간에 컨트리뷰터로 처음 참여하게 되었다. 프로젝트 참여 후, 도메인과 기존 코드를 이해하고 삽질해가며 한 달 만에 첫 커밋을 작성했던 때가 아직도 기억에 남는다.

 

it-chain 프로젝트는 시간이 지난 지금에도 아키텍처적으로 참고하는 프로젝트 중 하나다. 그만큼 프로젝트 리더님이 아키텍처에 많은 신경을 썼으며 다양한 기술들이 결합된 프로젝트기 때문이다. 이 프로젝트를 통해 Domain Driven Design, Micro Service Architecture, Event Sourcing 등에 대해 처음 접하기도 했다. 오늘은 이 중 Micro Service Architecture에 대해 공부해보려 한다.

 


MSA 등장 배경, Monolithic Architecture

https://www.redhat.com/ko/topics/microservices/what-are-microservices

MSA의 등장 배경에는 Monolithic Architecture가 있다. 이는 아직 많은 프로젝트에서 사용 중인 형태로 전통적인 아키텍처를 지칭한다. 모놀리식 아키텍처는 여러 모듈이 하나의 프로젝트 내에 존재하여 하나의 애플리케이션으로 동작하는 것을 말한다. 비록 애플리케이션이 여러 계층에 따라 분리될지라도 결국 하나의 아키텍처로 패키징 된다.

 

이러한 모놀리식 아키텍처는 간단한 구조로 소규모 프로젝트에 적합하며 유지보수가 용이하다. 또, E2E 테스트가 비교적 쉽다. 그러나 서비스의 규모가 커짐에 따라 그 복잡성은 증가하여 모놀리식 아키텍처는 다음과 같은 문제점을 가진다.

 

  1. 적은 부분을 수정했어도 프로젝트 전체를 빌드, 테스트, 배포해야 하기 때문에 이에 걸리는 시간이 증가한다.
  2. 프로그램 자체에 대한 확장은 가능하지만 프로젝트 내 서비스를 부분적으로 확장하기 힘들다.
  3. 프로젝트에 사용된 기술을 따르기 때문에 새로운 기술 스택을 도입하기 어렵다.
  4. 하나의 기능 장애가 프로그램 전체의 기능 장애로 이어질 수 있다.

 

위와 같은 문제를 해결할 수 있는 방법으로 MSA가 등장한다.

 


마이크로 서비스 아키텍처 MSA

MSA는 프로그램을 여러 서비스로 나누어 조합하는 아키텍처다. 나누어진 각 서비스는 스스로 돌아갈 수 있으며 독립적으로 배포가 가능한 서비스를 말한다. 즉, 여러 작은 프로그램들이 큰 프로그램을 구성하는 형태다.

 

이런 형태를 통해 MSA는 다음과 같은 장점을 가진다.

 

  1. 서비스는 각자 독립적으로 동작하기 때문에 새롭게 구현하거나 수정한 서비스만 빠르게 빌드, 테스트, 배포할 수 있다.
  2. 각 서비스 별로 해당 서비스에 특화된 기술을 선택하여 사용할 수 있다. 즉, 폴리글랏 아키텍처로 구성할 수 있다.
  3. 서비스를 부분적으로 확장할 수 있다.
  4. 하나의 서비스 장애는 해당 서비스에서만 발생하기 때문에 다른 서비스에 영향을 주지 않고 복구할 수 있다.

 

A 서비스에 기능을 추가한다면 B, C 서비스는 그대로 동작하면서 A 서비스만 빌드, 테스트, 배포하면 되기 때문에 여기에 소요되는 시간을 줄일 수 있다. 그리고 각 서비스는 독립적으로 구현되므로 모두 다른 기술을 사용할 수 있다. A 서비스는 JAVA+Spring+MySQL 조합으로, B 서비스는 JavaScript + Node.js + Redis 조합으로 구성하여 각 서비스에 유리한 기술 스택을 골라 조합할 수 있다.

 

그러나 이에 따른 트레이드오프 역시 존재하며 이는 다음과 같다.

 

  1. 모놀리식 아키텍처는 메소드 호출을 통해 서비스끼리 상호작용할 수 있으나 MSA는 네트워크를 사용하여 상호작용하기 때문에 모놀리식에 비해 느리다.
  2. MSA는 각 서비스가 독립적이기 때문에 여러 서비스에 얽혀있는 트랜잭션을 처리하기 어렵다. 따라서 MSA는 Global 트랜잭션 대신 Local 트랜잭션으로 트랜잭션 경계를 나누는 것이 좋다.
  3. MSA는 모놀리식에 비해 인스턴스 수가 많아 비교적 관리가 어렵다.

 

위와 같은 이유로 MSA의 서비스들은 서로에 대한 의존성을 최소화해야 한다. 또 서비스 간의 상호작용은 REST, JSON, MQ 등의 방식으로 통신하는 것이 좋다.

 


MSA 구성을 돕는 요소들

Circuit Breaker

위에서 MSA의 한 서비스 장애는 다른 서비스에 영향을 주지 않고 복구할 수 있다고 했다. 이를 가능하게 도와주는 것이 Circuit Breaker다. Circuit Breaker는 서비스 사이에 위치하여 이들의 상호작용을 전달한다. 만약 A 서비스가 B 서비스에 의존하는 상황에서 B 서비스에 장애가 발생했다고 하자. 그럼 B 서비스의 장애로 A 서비스에도 장애가 발생할 수 있다. 이때 Circuit Breaker는 B 서비스의 장애를 감지하고 이에 대응한다. A 서비스의 요청에 Default 값으로 B 서비스 대신 응답함으로써 B 서비스 장애를 고립시키고 A 서비스의 장애를 예방할 수 있다.

 

API Gateway

https://docs.microsoft.com/ko-kr/azure/architecture/microservices/design/gateway

MSA의 프로그램은 여러 서비스로 분할된다. 만약 클라이언트가 이 서비스들을 모두 직접 호출해야 한다면 각 서비스에 대한 수많은 API 호출을 직접 클라이언트가 관리해야 한다. 또, 인증과 같이 서비스마다 공통된 로직에 대해 서비스별로 중복 구현될 수도 있다. 이와 같은 문제를 해결하는 API Gateway는 각 서비스의 Endpoint를 단일화하는 서비스로 인증과 같은 기능을 수행하기도 하며 요청에 알맞은 서비스로 라우팅 하는 역할을 한다.

 

Service Discovery

https://www.nginx.com/blog/service-discovery-in-a-microservices-architecture/

MSA의 독립적인 서비스 구성으로 개별 서비스에 대한 확장이 용이하다. 많은 요청을 처리하는 서비스는 여러 개의 인스턴스를 구성하여 트래픽을 분산할 수 있다. 그러나 인스턴스가 생성되면 해당 인스턴스에 요청을 라우팅 하기 위해 생성된 인스턴스 정보를 라우터에 알려야 한다. Service Dsicovery가 이를 관리하는 역할을 한다.

 

어떤 서비스에 대한 인스턴스가 생성되면 해당 인스턴스는 자신의 IP, Port와 같은 정보를 Service Registry에 전달한다. Service Registry는 등록된 인스턴스가 살아있는지 주기적으로 확인한다. 라우터는 Service Registry에 저장된 인스턴스 정보를 요청하고 저장한다. 클라이언트 요청이 들어오면 라우터는 Service Registry로부터 저장한 정보를 가지고 알맞은 서비스에 라우팅 하여 요청을 처리한다. 만약 새로운 인스턴스가 생성되면 자신의 정보를 Service Registry에 저장하게 되고 라우터는 Service Registry에 저장된 정보를 이용하기 때문에 새로 생성된 인스턴스 정보를 알 수 있다.

 

Backing Service

https://www.nginx.com/blog/event-driven-data-management-microservices/

어떤 비즈니스 프로세스를 처리하기 MSA의 여러 서비스들이 서로 통신해야 할 경우가 있을 수 있다. 이 프로세스가 하나의 트랜잭션으로 볼 수 있으나 이는 여러 서비스에 얽힌 트랜잭션이 돼버린다. 만약 어떤 서비스가 요청에 대해 정상적으로 처리하지 못한 경우 이에 대한 트랜잭션 복구가 쉽지 않다. MSA는 이를 해결하고자 Message Queue를 활용하여 트랜잭션을 분리한다.

 

MQ(Message Queue)는 비동기 방식으로 메시지를 주고받는 방법이다. Publisher가 메시지를 발행하면 Subscriber는 이를 받아 수행하도록 한다. MQ는 Publish 된 메시지는 Subscriber에 전달되는 것을 보장하는 특징이 있다. MSA는 MQ를 사용해 Event 메시지를 서비스끼리 주고받으며 처리한다. 이를 통해 서비스 간 결합도를 낮추고 서비스 실패에 대한 복구가 가능하다. 또, 여러 개의 MQ를 두어 Backing Service를 확장할 수도 있다.

 

Telemetry

MSA의 서비스들은 각자 독립적으로 실행되어 분산 환경을 만든다. 각 서비스 상태를 모니터링하고 관리하는 것의 어려움을 해결하고자 모니터링 서비스와 같은 역할이 필요하게 되었다. Telemetry는 MSA의 각 서비스 상태에 대한 모니터링 정보와 로그 정보들을 수집하여 쉽게 서비스를 관리하고 유지할 수 있도록 한다.

 


MSA를 맛보며...

MSA 구성은 모놀리식 아키텍처에 비해 훨씬 많은 것들을 구성하고 설정해야 한다. 많은 인프라 도구가 이를 지원하고 있으나 MSA의 복잡성 때문에 쉽지 않은 것 같다. 하지만 모놀리식과 비교했을 때 MSA가 주는 장점이 명확해 보여 정말 복잡하고 대규모의 시스템이라면 고려해볼 수 있다고 생각한다.

 

JAVA Spring 진영에서는 Spring Cloud를 이용하면 MSA 시스템을 빠르게 구축할 수 있다. 또, Netflix에서는 Eureka, Zuul, Ribbon과 같은 도구를 제공하여 MSA 구축을 지원하고 있다. 실제로 프로젝트에 적용해보며 공부해야겠다.

 

틀린 내용 피드백은 언제나 환영입니다 :)

 


참고

[MSA] #1 Monolithic Architecture 란?

[MSA] #2 Microservice Architecture 란?

[MSA] #3 Circuit Breaker

[MSA] #4 API Gateway

[MSA] #5 Service Discovery

[MSA] #6 Spring Cloud Netflix

MSA 제대로 이해하기 -(1) MSA의 기본 개념

MSA 제대로 이해하기 -(2) 아키텍처 개요

MSA 제대로 이해하기 -(3)API Gateway

MSA 제대로 이해하기 -(4)Service Mesh

MSA 제대로 이해하기-(5)Backing Service

MSA 제대로 이해하기-(6)Telemetry

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday