대부분의 기업용 애플리케이션은 하나의 거대한 서비스 형태로 개발되어 왔다. 모놀리식 아키텍처(Monolithic Architecture)로 불리는 이 구조는 개발·관리가 용이하다는 장점이 있다. 그러나 시스템 규모가 커질 경우 복잡도도 증가해 코드의 이해와 분석이 어려워지고 작은 수정 사항에도 전체를 빌드·배포해야 하는 비효율이 발생하는 등 개선과 확장이 어려운 단점도 존재한다.
이에 대응하는 개념이 MSA(Microservices Architecture)이다. 경량화되고 독립적인 여러 개의 서비스를 조합하여 애플리케이션을 구현하는 방식으로 서비스마다 자체 데이터베이스를 가지고 동작하기 때문에 개발부터 빌드·배포까지 효율적으로 수행할 수 있다. 기업 입장에서는 개발과 유지관리에 소요되는 시간과 비용을 줄일 수 있어 활용도가 높아지는 추세이다.
본 아티클에서는 기업 애플리케이션이 서비스 경량화 구조 기반으로 진화하기 위해 MSA를 채택, 구현할 때의 고려 사항을 살펴보도록 하겠다.
서비스 경량화 구조를 위한 고려 사항
단위 서비스로 쪼개기
하나의 거대한 애플리케이션을 어떻게 분리해야 경량화 구조로 바꿀 수 있을까? MSA가 지향하는 서비스간 느슨한 결합(Loose Coupling)과 높은 응집성(High Cohesion)을 가지면서도 상호 의존 없이 단일 목적 기능을 수행하도록 하려면 어떻게 해야 할까?
도메인 주도 설계(Domain Driven Design)가 해답이 될 수 있다.
이해를 돕기 위해 많이 알려진 객체 지향 프로그래밍(Object Oriented Programming)의 ‘객체(Object)’와 도메인 주도 설계의 ‘도메인(Domain)’을 들어 설명하겠다. 객체와 도메인 모두 기본 사상은 비슷하지만 대상 범위에 차이가 있다. 객체는 추상화 또는 구체화할 수 있는 특정 요소만을 표현하는 반면 행위는 별개로 취급한다. 그러나 도메인은 행위를 포함하여 소프트웨어 이용자가 다루는 모든 것을 설명한다. 여기서 모든 것이 도메인이고 표현하는 방법을 모델(Model)이라 부르며 사용자 입장이나 관점 등의 상황에 따라 구분 짓는 것을 컨텍스트(Context)라고 한다. 바운디드 컨텍스트(Bounded Context)는 컨텍스트에 대한 업무 범위를 정의한다. 또한 컨텍스트 맵(Context Map)을 통해 바운디드 컨텍스트 간의 관계를 정한다.
보통 도메인 주도 설계는 업무(도메인) 전문가와 개발자가 함께 참여해 워크숍을 진행하면서 서비스 분리 전략을 수립한다. 이 때 중요한 것은 의사소통이다. 용어가 통일되어 있지 않으면 혼란이 발생해 동일한 용어를 서로 다르게 해석할 수 있다. 이를 방지하기 위해 업무 위주의 보편 언어(Ubiquitous Language)를 사용한다. 용어가 정리될 때 용어집(Glossary)으로 관리하면 소통이 원활해질 수 있다.
[그림 2]는 도메인 모델링을 도식화한 것이다. 요소 별 의미는 다음과 같다.
– Value Object: 비즈니스 용어를 나타내는 불변 객체
– Entity: 속성이 아닌 식별성을 기준으로 정의되는 도메인 객체, 여러 개의 Value Object로 구성(예: DB의 한 개 row)
– Service: 도메인 객체에 포함할 수 없는 오퍼레이션 연산을 갖는 객체
– Aggregate: 연관된 Value Object와 Entity의 묶음
– Factory: 복잡한 Entity, Aggregate를 캡슐화하여 여러 객체를 동시에 생성
– Repository: Aggregate의 영속성 및 등록·수정·삭제·조회 시 일관성 관리
도메인 모델은 UML(Unified Modeling Language)이나 BPM(Business Process Management)으로 전체적인 흐름을 표현하는 메인 모델 도메인을 먼저 설계한 후 이를 서브 도메인으로 분리하고 바운디드 컨텍스트와 컨텍스트 맵을 통해 모델 간 연관 관계를 정의한다.
결국 MSA 구조는 시스템 개발·운영 조직과 밀접한 관련이 있다. 도메인 주도 설계를 통해 경량화 서비스로 분리하고 업무 범위에 따른 그룹핑을 할 때 주의할 점은 기업의 조직 구조와 업무 단위를 고려해야 한다는 사실이다. 그래야만 애플리케이션 구조의 복잡성을 낮추고 개발 생산성은 높일 수 있다.
이와 같이 도메인 주도 설계를 활용하여 하나의 애플리케이션을 단위 서비스로 분리할 수 있으나 가장 올바른 방향은 애플리케이션 개발 시작 단계부터 MSA 및 도메인 주도 설계를 고려하는 것이다.
분리된 서비스의 통합 관리 방안 수립
애플리케이션을 업무 단위로 분리하였다면 경량화된 서비스 간 통신을 가능케 하고 전체 서비스를 관리하기 위한 서비스 메쉬(Service Mesh) 기반의 아우터 아키텍처(Outer Architecture)를 선정해야 한다.
MSA가 갖춰야 할 서비스 메쉬의 주요 기능은 다음과 같다.
– Configuration Management: 서비스의 재빌드·재부팅 없이 설정 사항을 반영(Netflix Archaius, Kubernetes Configmap)
– Service Discovery: MSA 기반 서비스 배포 시 서비스 검색 및 등록(Netflix Eureka, Kubernetes Service, Istio)
– Load Balancing: 서비스간 부하 분산(Netflix Ribbon, Kubernetes Service, Istio)
– API Gateway: 클라이언트 접근 요청을 일원화(Netflix Zuul, Kubernetes Ingress)
– Service Security: 마이크로서비스 보안을 위한 심층 방어 메커니즘 적용(Spring Cloud Security)
– Centralized Logging: 서비스 별 로그의 중앙 집중화(ELK Stack)
– Centralized Monitoring: 서비스 별 메트릭 정보의 중앙 집중화(Netflix Spectator, Heapster)
– Distributed Tracing: 마이크로서비스 간의 호출 추적(Spring Cloud Sleuth, Zipkin)
– Resilience & Fault Tolerance: MSA 구조에서 하나의 실패한 서비스가 체인에 연결된 전체 서비스들에 파급 효과를 발생시키지 않도록 하기 위한 계단식 실패 방지 구조(Netflix Hystrix, Kubernetes Health check)
– Auto-Scaling & Self-Healing: 자동 스케일링, 복구 자동화를 통한 서비스 관리 효율화
– Build/Deploy Automation: 서비스 별 빌드·배포 자동화(Netflix Spinnaker, Kubernetes Scheduler, Jenkins)
– Test Automation: 서비스에 대한 테스트 자동화
– Image Repository: 컨테이너 기반 서비스 배포를 위한 이미지 저장소
서비스 메쉬의 적용 방안으로는
1) REST API 호출 방식으로 서비스를 결합하는 스프링 클라우드(Spring Cloud) 기반 넷플릭스 OSS
2) 쿠버네티스(Kubernetes) 기반 클라우드 네이티브(Cloud Native) 서비스
3) 사이드카 프록시(Side-car Proxy) 패턴의 쿠버네티스 기반 이스티오(Istio) 솔루션
등을 검토할 수 있다. 이들 중 스프링 클라우드와 사이드카 프록시를 기반으로 하는 서비스 메쉬 구현에 대해 간략히 소개하겠다.
스프링 클라우드 기반 유레카(Eureka)
유레카는 단위 서비스들에 대하여 동적으로 서비스 등록(Service Registry) 및 서비스 디스커버리(Service Discovery)를 수행하고 로드밸런싱을 통해 서비스간 통신의 부하 분산 기능을 제공한다.
[그림 3]은 유레카를 활용한 서비스 부하 분산 처리 방식을 보여주고 있다.
애플리케이션 서비스가 시작될 때 유레카 서버의 레지스트리에 상태 정보를 등록한다. 각 서비스는 설정된 간격(기본 30초)마다 하트비트(Heartbeat) 방식으로 상태 정보를 유레카 서버로 전송하여 서비스 상태를 점검(Health check)한다. 만약 하트비트가 수신되지 않은 경우 레지스트리에서 해당 서비스 정보는 제거된다. 유레카에서의 부하 분산은 클라이언트-사이드(Client-side) 방식의 리본(Ribbon)을 사용한다.
사이드카 프록시 패턴으로 서비스 메쉬 구현
사이드카 방식은 본래의 애플리케이션 서비스 앞단에 서비스간 통신 제어를 담당하는 경량화된 프록시를 배치하는 디자인 패턴이다. 기본 애플리케이션을 수정하지 않고도 서비스간 통신에 해당하는 추가 기능을 수행할 수 있으며 서비스 모니터링은 시스템 매트릭(System Metric)과 같이 리소스 정보를 공유할 수 있는 구조로 개발 가능한 장점이 있다. 대표적인 사이드카 방식의 오픈소스 구현체로 Google, IBM, Lyft에서 주도하는 쿠버네티스 기반 솔루션인 이스티오가 있다.
[그림 4]는 이스티오의 작동 방식을 나타내고 있다. 데이터 플레인(Data plain)에서 엔보이(Envoy)가 사이드카로 배포되어 서비스의 인/아웃 통신 트래픽을 제어한다. 서비스 디스커버리 수행 방식은 컨트롤 플레인(Control Plain)의 파일럿(Pilot)이 서비스 레지스트리 정보를 가지고 있고 엔보이가 해당 레지스트리 정보를 참고하여 다른 서비스의 엔드 포인트(end point)로 접근하게 된다. 믹서(Mixer)는 접근 정책에 대한 통제와 서비스 모니터링을 위한 매트릭(Metric) 지표를 수집한다. 시타델(Citadel)은 인증·인가와 같은 보안을 담당하는 모듈로 TLS 인증서를 관리한다.
사이드카 프록시 패턴은 각 서비스 인스턴스 별로 프록시 서비스가 별도로 필요하기 때문에 서비스 인스턴스가 2배로 소요되는 단점이 있다. 하지만 프록시에 공통 기능을 별도로 구현함으로써 재사용성을 높여 비즈니스 서비스 개발에 집중할 수 있다는 이점도 가지고 있다.
서비스 메쉬 관련 자세한 내용은 에스코어가 발행한 다음의 MSA 관련 아티클을 참고하기 바란다.
– MSA와 분산 아키텍처 수용을 위한 방법: 서비스 메쉬와 이스티오
데이터 트랜잭션 관리
모놀리식 아키텍처로 개발된 애플리케이션은 하나의 통합 데이터베이스를 사용하며 데이터 일관성을 위한 트랜잭션 관리 기능은 DBMS가 제공한다. 반면 MSA 구조에서 분산된 서비스 간의 트랜잭션 관리는 매우 까다로운 영역이다. 이의 해결 방안을 간략히 소개한다.
2PC(2 Phase Commit)
2PC는 다른 용어로 TCC(Try Confirm Cancel)라고도 한다. 동작은 준비와 처리, 2단계로 실행된다.
1) 중앙의 코디네이터 제어 노드에서 글로벌 트랜잭션을 생성하여 대상 서비스들에게 잠금(Lock)을 요청한 후 모든 서비스로부터 응답을 받는다.
2) 모든 서비스에게 커밋을 전송한 후 서비스로부터 결과를 회신 받는다.
2PC는 매우 강력한 일관성 프로토콜을 제공하는 반면 트랜잭션을 요청 받은 서비스로부터 모두 완료 회신을 받기 전까지는 전체 서비스에 잠금(Lock)이 걸린다. 이로 인해 코디네이터 노드 혹은 대상 트랜잭션 노드가 다운될 경우 전체 시스템에 장애를 유발할 수 있어 MSA 구조에서는 그다지 추천되지 않는다.
SAGA 패턴
SAGA 패턴은 SEC(Saga Execution Coordinator)가 로컬 트랜잭션을 관리해주는 방식으로 하나의 로컬 트랜잭션 단위로 실행된다. 트랜잭션이 종료될 때 완료 이벤트를 수신하고 순차적으로 다음 로컬 트랜잭션을 실행하는 방식이다. 이 때 중앙의 SEC 노드가 다음에 실행할 로컬 트랜잭션을 결정한다.
트랜잭션 실행 중 실패가 발생하면 원래 트랜잭션에 대한 로그를 남기고 취소 상태를 설정한 후 보상 트랜잭션을 실행한다. 만일 보상 트랜잭션마저 실패한다면? 일정 횟수만큼 혹은 일정 기간 동안 보상 트랜잭션을 반복 재시도하게 된다. 각 마이크로서비스는 자신의 로컬 원자적 트랜잭션(Atomic Transaction)에만 집중하기 때문에 보류(Pending) 상태가 없다. 따라서 오래 걸리는(Long-Lived) 트랜잭션에 적합하다.
SAGA 방식은 시스템이 복잡한 경우 이벤트 메시지 관리가 어렵기 때문에 적용에 앞서 비즈니스 로직 상 꼭 필요한지를 충분히 검토해야 한다. 가장 좋은 방법은 앞서 소개한 도메인 주도 설계를 통한 서비스 분리·그룹화 시 트랜잭션 관리 측면도 함께 고려해 적절한 방안을 선택하는 것이다.
시사점
지금까지 서비스 경량화를 위한 MSA 설계 시 고려 사항을 소개하였다.
MSA는 서비스 별 모듈화로 확장이 용이하며 독립적인 개발·빌드·배포 체계를 갖춰 신속한 딜리버리가 가능하고 개발 생산성이 향상되는 장점을 가지고 있다. 반면 모놀리식 아키텍처에 비해 복잡도가 높으며 서비스가 분산되어 있어 트랜잭션 관리, 장애 추적 및 테스트 등이 쉽지 않은 단점도 있다.
그럼에도 불구하고 기업 비즈니스가 디지털로 전환되고 그 플랫폼으로 클라우드 서비스가 각광 받으면서 MSA의 활용도는 높아질 것으로 전망된다. 실제 국내에서도 인터넷 서비스 플랫폼과 대기업을 중심으로 MSA 기반의 애플리케이션을 구축하여 개발 생산성 향상 효과를 본 사례가 등장하고 있다.
MSA는 도입에 앞서 회사의 비즈니스와 IT 여건이 MSA의 가치와 부합하는지에 대한 분석이 선행되어야 한다. 도입이 결정되면 단순히 애플리케이션을 잘게 쪼개는 것이 아니라 조직 구조와 업무 기능을 감안하여 적절하게 그룹핑 하는 것이 중요하다. 따라서 개발자, 현업 담당자 및 프로세스 분석 컨설턴트 등 비즈니스 도메인과 소프트웨어 기술 영역이 밀착 협업하여 추진해야 한다. 이러한 과정은 상당한 리소스가 소요되고 여러 차례 시행착오를 거칠 수도 있다. 그렇기 때문에 평이한 수준의 IT 역량을 보유한 일반 기업이 MSA 도입에 나설 경우에는 시스템 아키텍처와 비즈니스 도메인을 두루 이해하는 전문 기술 업체의 도움을 받는 것을 고려할 필요가 있다.
# References
- https://martinfowler.com/tags/domain%20driven%20design.html
- https://en.wikipedia.org/wiki/Domain-driven_design
- https://jj09.net/domain-driven-design-tl-dr
- https://microservices.io/patterns/data/saga.html
- https://blog.knoldus.com/distributed-transactions-and-saga-patterns
손병창 프로
에스코어㈜ 소프트웨어사업부 엔터프라이즈플랫폼그룹
클라우드 아키텍처 및 플랫폼 개발을 담당하고 있습니다.
Register for Download Contents
- 이메일 주소를 제출해 주시면 콘텐츠를 다운로드 받을 수 있으며, 자동으로 뉴스레터 신청 서비스에 가입됩니다.
- 뉴스레터 서비스 가입 거부 시 콘텐츠 다운로드 서비스가 제한될 수 있습니다.
- 파일 다운로드가 되지 않을 경우 s-core_mktg@samsung.com으로 문의해주십시오.