MSA 설계 시 고려사항

·2022년 1월 31일
0

최근 회사에서 MSA 프로젝트에 들어가기 시작하면서 MSA에 대해 공부하기로 했다. 일단 개념부터 익혀보자!

MSA(Microservices Architecture)


경량화되고 독립적인 여러 개의 서비스를 조합하여 애플리케이션을 구현하는 방식으로 서비스마다 자체 데이터베이스를 가지고 동작하기 때문에 개발부터 빌드·배포까지 효율적으로 수행할 수 있다. 기업 입장에서는 개발과 유지관리에 소요되는 시간과 비용을 줄일 수 있어 활용도가 높아지는 추세이다.

그렇다면 기업 애플리케이션이 서비스 경량화 구조 기반으로 진화하기 위해 MSA를 채택, 구현할 때의 고려사항을 살펴보도록 해보자~

서비스 경량화 구조를 위한 고려사항

단위서비스 쪼개기

하나의 거대한 애플리케이션을 어떻게 분리해야 경량화 구조로 바꿀 수 있을까?
MSA가 지향하는 서비스 간 느슨한 결합(Loose Coupling)과 높은 응집성(High Cohesion)을 가지면서도 상호의존 없이 단일목적 기능을 수행하도록 하려면 어떻게 해야 할까?

도메인 주도 설계(Domain Driven Design)

객체지향 프로그래밍(Object Oriented Programming)의 '객체(Object)'와 도메인 주도 설계의 '도메인(Domain)'을 들어 설명하겠다. 객체와 도메인 모두 기본 사상은 비슷하지만 대상 범위에 차이가 있다. 객체는 추상화 또는 구체화할 수 있는 특정 요소만을 표현하는 반면, 행위는 별개로 다룬다. 그러나 도메인은 행위를 포함하여 소프트웨어 이용자가 다루는 모든 것을 설명한다. 여기서 모든 것이 도메인이고 표현하는 방법을 모델(Model)이라 부르며 사용자 입장이나 관점 등의 상황에 따라 구분짓는 것을 컨텍스트(Context)라고 합니다. 바운디드 컨텍스트(Bounded Context)는 컨텍스트에 대한 업무 범위를 정의합니다. 또한 컨텍스트 맵(Context Map)을 통해 바운디드 컨텍스트 간의 관계를 정합니다.

• 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) 사이드카 프록시(Sidecar Proxy) 패턴의 쿠버네티스 기반 이스티오(Istio) 솔루션

등을 검토할 수 있다. 이번에는 1)스프링 클라우드와 사이드카 3)프록시를 기반으로 하는 서비스 메쉬 구현에 대해 간략히 소개하겠다.

스프링 클라우드 기반 유레카(Eureka)

유레카는 단위 서비스들에 대하여 동적으로 서비스 등록(Service Registry) 및 서비스 디스커버리(Service Discovery)를 수행하고 로드밸런싱을 통해 서비스간 통신의 부하 분산 기능을 제공한다.

유레카를 활용한 서비스 부하 분산 처리 방식을 보여주고 있다. 애플리케이션 서비스가 시작될 때 유레카 서버의 레지스트리에 상태 정보를 등록한다. 각 서비스는 설정된 간격(기본 30초)마다 하트비트(Heartbeat) 방식으로 상태 정보를 유레카 서버로 전송하여 서비스 상태를 점검(Health check)한다. 만약 하트비트가 수신되지 않은 경우 레지스트리에서 해당 서비스 정보가 제거된다. 유레카에서의 부하 분산은 클라이언트-사이드(Client-side) 방식의 리본(Ribbon)을 사용한다.

사이드카 프록시 패턴으로 서비스 메쉬 구현

사이드카 방식은 본래의 애플리케이션 서비스 앞단에 서비스간 통신 제어를 담당하는 경량화된 프록시를 배치하는 디자인 패턴이다. 기본 애플리케이션을 수정하지 않고도 서비스간 통신에 해당하는 추가 기능을 수행할 수 있으며 서비스 모니터링은 시스템 매트릭(System Metric)과 같이 리소스 정보를 공유할 수 있는 구조로 개발 가능한 장점이 있다. 대표적인 사이드카 방식의 오픈소스 구현체로 Google, IBM, Lyft에서 주도하는 쿠버네티스 기반 솔루션인 이스티오가 있다.

이스티오의 작동 방식을 나타내고 있다. 데이터 플레인(Data plain)에서 엔보이(Envoy)가 사이드카로 배포되어 서비스의 인/아웃 통신 트래픽을 제어한다. 서비스 디스커버리 수행 방식은 컨트롤 플레인(Control Plain)의 파일럿(Pilot)이 서비스 레지스트리 정보를 가지고 있고 엔보이가 해당 레지스트리 정보를 참고하여 다른 서비스의 엔드 포인트(end point)로 접근하게 된다. 믹서(Mixer)는 접근 정책에 대한 통제와 서비스 모니터링을 위한 매트릭(Metric) 지표를 수집합니다. 시타델(Citadel)은 인증·인가와 같은 보안을 담당하는 모듈로 TLS 인증서를 관리한다.

사이드카 프록시 패턴은 각 서비스 인스턴스 별로 프록시 서비스가 별도로 필요하기 때문에 서비스 인스턴스가 2배로 소요되는 단점이 있지만 프록시에 공통 기능을 별도로 구현함으로써 재사용성을 높여 비즈니스 서비스 개발에 집중할 수 있다는 이점이 있다.

데이터 트랜잭션 관리

모놀리식 아키텍처로 개발된 애플리케이션은 하나의 통합 데이터베이스를 사용하며 데이터 일관성을 위한 트랜잭션 관리 기능은 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 노드가 다음에 실행할 로컬 트랜잭션을 결정한다.

SAGA 방식은 시스템이 복잡한 경우 이벤트 메시지 관리가 어렵기 때문에 적용에 앞서 비즈니스 로직상 꼭 필요한지를 충분히 검토해야 한다. 가장 좋은 방법은 앞서 소개한 도메인 주도 설계를 통한 서비스 분리·그룹화 시 트랜잭션 관리 측면도 함께 고려해 적절한 방안을 선택하는 것이다.

결론

MSA는 서비스별 모듈화로 확장이 용이하며 독립적인 개발·빌드·배포 체계를 갖춰 신속한 딜리버리가 가능하고 개발 생산성이 향상되는 장점을 가지고 있다.

MSA는 도입에 앞서 회사의 비즈니스와 IT 여건이 MSA의 가치와 부합하는지에 대한 분석이 선행되어야 한다. 도입이 결정되면 단순히 애플리케이션을 잘게 쪼개는 것이 아니라 조직 구조와 업무 기능을 감안하여 적절하게 그룹핑하는 것이 중요하다. 따라서 개발자, 현업담당자 및 프로세스 분석 컨설턴트 등 비즈니스 도메인과 소프트웨어 기술 영역이 밀착 협업하여 추진해야 한다. 이러한 과정은 상당한 리소스가 소요되고 여러 차례 시행착오를 거칠 수도 있다. 그렇기 때문에 평이한 수준의 IT 역량을 보유한 일반기업이 MSA 도입에 나설 경우에는 시스템 아키텍처와 비즈니스 도메인을 두루 이해하는 전문 기술업체의 도움을 받는 것을 고려할 필요가 있다. 다음에는 실제 소스 구현 부분을 알아봐야겠다.

참고: https://www.samsungsds.com/kr/insights/1239180_4627.html

profile
코딩하는 은행원 !

0개의 댓글