Microservice Architecture 도입 한 이유가 뭐니?
Microservice Architecture 도입
Microservice Architecture 를 한문장으로 이야기 해봐.
언젠가 이런 질문을 받은적이 있다.
A: "MSA 를 한 문장으로 표현한다면 뭐라고 할거니?"
나: "어~... 말하자면 긴데..."
위 질문을 받았을때 나는 올바르게 대답하지 못했다. 아마도 MSA 라는 단어가 가진 수많은 복잡도를 한마디로 녹여 넣어야 한다는 부담감 때문이라고 지금 내 스스로 위로 하고 있다. 수많은 생각이 머리속을 스쳐지나갔다. 모놀리식 아키텍처에서 어떻게 분리 해 내어야 하는지, 그리고 이렇게 분리해서 장애가 발생되면 어떻게 하지? 서로 통신 프로토콜은?, 타임아웃은?, 배포는?, 업무와의 관계는?, 운영은 누가하지? 이런 고려해야할 부분이 너무 많다는 것을 알기 때문이었다.
그럼 정말 MSA 를 한마디로 하면 무엇일까?
분리된 각 서비스들이 상호 커뮤니케이션을 통해서 공동의 목표를 달성하는 시스템 아키텍처
라고 이야기 할 수 있을 것이다.
MSA 는 최근 업계 표준으로 자리 잡고있다. 시스템 아키텍팅을 할 때 기본적으로 MSA 를 도입해야한다고 생각하고, 도입하는 것이 좀더 성숙한 아키텍처라고 이야기 하고 있는것 같다.
필자는 이번 아티클을 통해서 대표적인 시스템 아키텍처인 Monolithic Architecture 와 Microservice Architecture 를 어떻게 선택해야할지 함께 고민해 보고자 한다.
시스템 설계시 공통되는 요구사항
최근? 최근도 아니다. 이미 10여년 전부터 어플리케이션은 다음과 같은 요구사항을 수용하기 위한 구조로 설계 되고, 개발 운영되고 있다.
- 서비사이드 어플리케이션 개발자(혹은 Backend 개발자) 라는 전문 분야의 개발자로 개발이 진행된다.
- 프런트 엔드 개발 영역도 명확히 분리 개발 된다.
- 데스크탑 브라우저, 모바일 브라우저, 모바일 네이티브 어플리케이션, IoT 디바이스 등 다양한 디바이스와 커뮤니케이션 할 수 있는 엔드포인트를 제공해야한다.
- 대용량 트래픽을 수용하기 위한 Scale In/Out 과 Up/Down
- 짧은 Latency 를 필요로 하며, 이를 위한 동기, 비동기 방식의 API 제공
- 높은 Throughput 을 수용하는 시스템 제공
- REST API, Event Driven System 등의 제공
- 시스템 상황을 파악하기 위한 종합 모니터링 시스템
- 시스템 장애 최소화를 위한 시스템 설계
- 요구사항에 최적화된 개발 언어의 도입
- 데이터 컨시스턴시 유지
위 나열된 사항 이외에도 다양한 요구사항들이 존재하며, 이는 비즈니스 요구사항에 따라 좀더 정교한 방식의 방법들과 도구들이 도입되고 있다.
이는 Monolithic Architecture 든 Microservice Architecture 든 엔터프라이즈 어플리케이션이 갖추어야할 요구사항들이며, 각 아키텍처에 따라 조금더 적용이 용이하거나 어려움이 존재할 뿐, 그 요구사항이 대동소이 하다.
각 아키텍처는 어떠한 방식으로 이러한 요구사항을 수용하는지 알아보자.
Monolithic Architecture
모놀리식 아키텍처는 이미 대부분의 개발자에게 익숙한 구조이며, 하나의 시스템에 다양한 서비스 및 기능 모듈들이 상호 작용하며 어플리케이션의 목표를 달성하는 시스템이다.
모놀리식 아키텍처는 일반적으로 시스템의 기반이 되는 아키텍처이며, 여전히 인기있는 시스템 아키텍처이다.
Architecture
보통 모놀리식 아키텍처는 티어 혹은 레이어 구조를 가지며, Front Tier
> Backend Tier
> Persistance Tier
의 3단구조가 대부분이다.
<모놀리식 아키텍처: from https://microservices.io/patterns/monolithic.html>
프런트엔드 소스와 벡앤드, 퍼시스턴스 레이어 모두 하나의 war 파일에 묶어서 배포되는 경우가 많다.
최근에는 프런트엔드 기술 SPA(Single Page Application) 로 인해서 Backend Tier / Persistance Tier 가 묶여 war 파일로 배포 되고, Front Tier 의 경우 별도 배포하는 구조를 가지는 구조로 변경이 되었다.
모놀리식 아키텍처는 백엔드 티어에 모든 비즈니스 로직이 서로 연관되도록 구성이 되며, 시스템 상에 서비스들이 논리적으로 구조화 되는 형태를 갖는다.
논리적 구조화의 방안으로 비즈니스 워크 플로우에 따라 서비스들이 배치되고, 작업 요청에 따라 각 서비스들을 호출하는 구조이다.
특히 이러한 서비스들 혹은 공통 모듈들을 잘 구성하여 재사용성을 높이는데 많은 노력을 쏟는다.
장점
모놀리식 아키텍처는 단순한 구조가 가져오는 장점이 있다.
- 단순한 개발
- 시스템 구조가 비교적 단순해서 이해하기가 쉽다.
- 단일 코드 베이스에서 시스템을 모두 담고 있기 때문에 IDE 로 로드해서 전체 구조를 파악할 수 있다.
- 작은 시스템에서는 개발 유지보수가 편하다.
- 빠른 초기 개발
- 시스템 구조가 단순하고, 대부분의 서비스 모듈이 동일 코드 베이스 상에 존재하기 때문에 빠르게 개발을 진행할 수 있다.
- 단순한 디플로이
- 단일 WAR 파일로 배포 되기 때문에 배포 파이프라인이 단순하다.
- 쉬운 확장
- 시스템의 증설이 있을경우 단지 배포 템플릿을 통해 인스턴스 수를 늘여갈 수 있기 때문에 확장이 쉽다.
단점
모놀리식의 단순함으로 오는 단점역시 존재한다. 단순한 구조에서 점차 복잡해지면서 문제점들이 발생하기 시작한다.
- 코드베이스 관점
- 버젼 충돌: 여러 팀이 하나의 시스템을 수정하면서 오는 버젼의 충돌, 머지 컨플릭이 빈번하게 일어난다. 이는 인지하지 못한 시스템 장애의 원인이 된다.
- 코드 베이스 변경의 급격한 증가: 여러 팀이 소스를 수정 생성하면서 코드 베이스의 크기가 매우 빠르게 커진다. 이는 코드를 이해하기 어렵게 만들고, 전체를 이해 하려고 할 경우 개발자가 코드에 압도된다.
- 중복된 서비스들의 출현: 공통 모듈은 General 한 속성을 가진다. 이 의미는 General 속성은 특정 서비스에 특화되지 않는다는 의미이며, 이것은 중복 코드의 생산을 유도한다.
- 모듈간의 영역 구분이 어려움: 모듈과 모듈은 논리적으로 분리된 구조로 설계 된다. 그러나 시스템이 커지면서 다양한 모듈들을 호출하는 구조가 출현하면서, 각 모듈들의 오너쉽이 흐려진다.
- 시스템 배포 관점
- CI/CD 를 구성하기가 어렵다. 여러 팀이 동시에 연관 소스를 수정하거나 사용하는경우 CI/CD 에 대한 신뢰도가 떨어진다. 이는 배포 버젼의 신뢰도 역시 장담하기 어렵게 한다.
- 소스코드가 커지면서 CI 시간이 오래 걸린다. 특히 컴파일 오류를 유발하는 코드가 섞여 들어가는 경우 이를 찾아 수정하는데 시간이 더 걸린다. 필자의 경우 특정 팀 개발자의 테스트 코드가 깨지는 바람에 CI 최종 결과를 받는데 반나절 이상 걸린적이 있다. 첫째 누가 작성한 코드인지 확인을 해야했고, 두번째 해당 담당자에게 노티를 주는 것과, 세번째 그것이 수정되어 CI 에 들어오는 일련의 과정이 매우 번거로운 시간이 되었다. 이후부터 @Ignore 처리를 하고 노티한 적도 적지않게 발생되었다.
- 특정 팀의 요구사항으로 변경된 코드가 타 팀에 영향을 주게 된다. 특히 배포된 이후 한참 서비스하다가 발견되는 사이드 이펙트는 치명적이다.
- 팀간 배포 스케줄을 잡기 매우 어렵다. 시스템의 변경사항을 프러덕션 코드에 배포하기 위해서는 팀마다 배포 스케줄링을 마련해야한다. 필자의 경우 배포를 위해서 1주일을 소요한 적이 있다. 첫번째는 배포 시간을 2시간 잡았는데, 2시간 경과되어 롤백을 했으며, 두번째는 다시 배포 스케줄을 잡기 위해서 대기를 해야했다.
- 다양한 배포 전략을 구사하기 어렵다. 여러팀의 배포로 인해 카나리 배포, 블루그린 배포, 롤링 업데이트등 다양한 배포 전략에 큰 효과를 발휘하지 못한다. 그리고 롤백은 더 큰일이다. 내가 배포한 릴리즈 버젼 이후에 다른팀의 릴리즈가 배포 되어 있다면, 다른 팀은 처음부터 다시 배포 해야한다.
- 스케일링 관점
- 모놀리식 아키텍처의 스케일링은 단순하다. 그러나 단순함은 비효율을 가져온다.
- 단순한 1차원적 스케일 아웃만을 수행할 수 있다.
- 특정 모듈이나 서비스의 스케일 아웃을 위해서 필요 이상으로 많은 장비와 시스템 리소스 점유를 하게 된다. 보통 사용성이 낮은 기능역시 스케일 아웃으로 인해 리소스를 먹는다. 특히 데이터베이스 커넥션의 자원은 매우 아까운 리소스이다.
- 또한 인스턴스별로 사용하는 캐시의 적중율을 저하 시키고, 전체적인 메모리 소비를 증가 시키는 경향도 발생한다.
- 어플리케이션의 복제본의 증가는 트랜잭션 볼륨을 증가 시킨다. 어플리케이션마다 트랜잭션의 타임을 최소한으로 유지하기 위해 노력한다. 그러나 트랜잭션 타임이 긴 작업이 영향을 주면 빠르게 커넥션 풀을 소비하기 시작하며, 자원의 효율적 사용을 감소 시킨다.
- CPU 인텐시브, Memory 인텐시브에 등의 요구사항에 따른 확장이 불가능하다.
- 모놀리식 아키텍처의 스케일링은 단순하다. 그러나 단순함은 비효율을 가져온다.
- 기술 관점
- 동일한 기술을 이용하므로 전체 조직에 통합된 기술을 선정 및 컨벤션 유지에 시간이 걸린다.
- 고정된 기술 스택으로 필요에 의해 기술 스택 변경 및 프레임워크의 변경을 위해서는 처음부터 큰 시스템을 다시 작성해야한다.
- 공통 모듈의 버젼업이 매우 어렵다. 잘못된 버젼 변경은 치명적인 문제를 만들어 낼 수 있다.
이러한 단점이 보이기 시작할 때가 시스템 분리를 고민해야하는 시점이다. 이 포인트를 주의깊게 살펴 보는 것은 성공적인 아키텍처 도입의 좋은 시작점이다.
요구사항 달성을 위한 방법
- 데스크탑 브라우저, 모바일 브라우저, 모바일 네이티브 어플리케이션, IoT 디바이스 등 다양한 디바이스와 커뮤니케이션 할 수 있는 엔드포인트를 제공해야한다.
- SPA(Single Page Application), Native Application, Progressive Web App 과 같이 프런트엔드 기술의 발전으로 프런트엔드와 서버사이드를 완젼히 분리되어 개발이 진행
- 이를 위해 백엔드는 통일된 API (REST, GRPC 등) 을 활용하여 기능을 제공하며, 이는 모놀리식 아키텍처 역시 지원하는데 문제가 없다.
- 대용량 트래픽을 수용하기 위한 Scale In/Out
- 스케일 In/Out 은 모놀리식 아키텍처에서 가능 하지만 단점이 많다.
- 동일한 코드 템플릿을 이용하여 인스턴스를 수평적으로 늘여갈 수 있으나, 1차원적 확장으로 인해서 메모리, 캐시적중율 저하, 리소스 커넥션의 빠른 소비등의 다양한 이슈가 존재한다.
- 짧은 Latency 를 필요로 하며, 이를 위한 동기, 비동기 방식의 API 제공
- 동기/비동기 방식의 제공은 모놀리식 아키텍처에 구현에는 문제가 없다.
- 동기/비동기 방식의 업무는 서비스 성격이 다른 업무이기 때문에 사실 모놀리식 아키텍처내에 같이 소화할 필요가 없는 일이 대부분이다. 모놀리식에 우겨 넣어야 하는 경우 단순 스케일 아웃은 오히려 관리를 어렵게 만드는 원인이 된다.
- 높은 Throughput 을 수용하는 시스템 제공
- 높은 Throughput 은 일반적인 웹엡과는 다르게, 배치 처리등과 같은 서비스이므로 분리된 아키텍처가 일반적이다.
- 시스템 상황을 파악하기 위한 종합 모니터링 시스템
- 모놀리식 아키텍처의 장점중에 하나라면 모니터링이 비교적 쉽다는 것이다.
- 단일 시스템의 로그를 확인하거나, 시스템 메트릭을 확인하면 되기 때문에 단순한 메트릭을 제공하며, 이를 처리하는데는 공수가 생각보다 크지 않다.
- 그러나 한가지 고맨해야할 부분은 시스템 내부에서 일어나는 상호 커뮤니케이션을 모니터링 하는 것은 거의 불가능하거나, 매우 어렵다.
- 시스템 장애 최소화를 위한 시스템 설계
- 모놀리식의 최대 단점중에 하나는 장애의 전파가 매우 빠르게 일어난다는 것이다.
- 한 모듈의 메모리 릭은 전체 메모리 포화도를 빠르게 채워 나가며, 얼마 지나지 않아 시스템이 먹통이 될 가능성이 매우 높다.
- 시스템의 장애를 최소화 하기 위해서 모니터링을 강화하도록 시스템 메트릭을 관라하는 것이 방법이 될 수 있다.
- 요구사항에 최적화된 개발 언어의 도입
- 모놀리식 아키텍처 개발에서 개발 언어와 기술은 단일화 된다.
- 단일화 과정은 논쟁이 자주 일어나는 부분이며, 한번 결정되면 바꿀 수 없다.
- 데이터 컨시스턴시 유지
- 데이터 컨시스턴시 유지는 모놀리식 아키텍처에서는 비교적 단순하다.
- 단일 트랜잭션을 통해서 데이터를 저정하고, 로드 하기 때문에 관리가 쉽다.
- 단, 고려할 점은 가능하면 트랜잭션 타임을 최소화 해서 커넥션 풀의 소비를 완화 하도록 설계할 필요가 있다. 의외로 이 부분이 시스템 장애의 원인으로 자주 확인되기도 한다.
Microservice Architecture
마이크로 서비스 아키텍처는 엔터프라이즈 어플리케이션에서 필수 요소가 되어 가고 있는 아키텍처이며, SOA 에서 발전된 하나의 아키텍처이다.
작은 서비스들로 분리된 서비스들이 상호 커뮤니케이션을 통해서 공통의 니즈를 해결하기 위한 아키텍처이며, 최근 대부분의 중, 대형 서비스는 마이크로 서비스 아키텍처를 도입하고 있다.
특히 컨테이너 기반의 시스템 개발이 확대 되면서, MSA 는 필수적인 아키텍처가 되었다.
Architecture
마이크로 서비스 아키텍처는 작은 규모의 서비스를 수행하는 모놀리식 아키텍처의 모임으로 생각하면 이해가 쉽다.
각 서비스들은 각기 자신의 persistance layer 를 소유하고 있으며, 공통 프로토콜 (REST API, gRPC, AMQP) 등을 활용하여 상호 커뮤니케이션을 수행하게 된다.
<MSA 아키텍처: from https://microservices.io/patterns/microservices.html>
마이크로 서비스 아키텍처는 모놀리식 아키텍처로 부터 출발하는 것이 일반적이며, 시스템이 커지면서 다양한 문제점이 발생되는 시점부터 이를 해결하기 위한 방향으로 되입된다.
어떠한 장, 단점이 존재하는지 알아보자.
장점
모놀리식 아키텍처가 가지는 많은 문제점울 마이크로 서비스 아티켁처가 보완 하는 형태라 할 수 있다.
- 코드베이스 관점
- 버젼관리: 마이크로 서비스 아키텍처는 코드 베이스를 서비스 팀 자치적으로 관리한다. 하위 호환성만 유지 된다면 타 조직에 영향 없이 버젼을 업그레이드 할 수 있다.
- 단순한 코드 관리: 코드는 단순하게 관리된다. 단일 책임 원칙에 따라 자신이 처리해야할 소스 코드만을 작성하고 관리하면 되기 때문에 상대적으로 가벼운 코드로 서비스가 작성된다.
- 중복 코드는 서비스 내에서 최소화: 팀내 조직 인원들이 코드를 공유하고 리뷰 하므로 기능의 유무를 몰라 중복 작성하는 사항은 잘 발생하지 않는다.
- 명확한 서비스간 구분: 모놀리식 아키텍처는 모듈구분이 어려웠지만, MSA 는 명확한 자신의 역할과 책임이 있다. 이러한 명확한 비즈니스 관심사의 구분으로 서비스가 분리되는 것이 MSA 의 특징이다.
- 시스템 배포 관점
- CI/CD 를 구성하기가 어렵다. 팀은 자치권을 가지고 개발 및 운영을 한다. CI/CD 구성역시 매우 간편하고 쉽다. 서비스는 타 서비스와 완젼히 분리 되어 있기 때문에 CI/CD 역시 Isolation 된다.
- 가벼운 코드는 가볍게 CI 를 처리한다. 그리고 발생된 오류는 팀에 의해 즉각 발견되고, 바로 수정된다. 타 조직과 커뮤니케이션이 필요 없으며, 빠른 속도로 깨진 CI가 복구 된다. 이는 생산셩과 신뢰성을 향상 시킨다.
- 개발 배포는 자치적으로 이루어진다. 자치성은 언제 어디든지 배포가 수행될 수 있으므로 하루에도 여러번 배포를 해도 타 팀에 영향은 매우 적다.
- 다양한 배포 전략을 구사할 수 있다. 시스템 리소스가 허용하는 범위 내에서 다양한 배포 전략을 도입할 수 있으며, 롤백에 대한 이슈 역시 없다. 이전 버젼을 그대로 롤백 할 수 있다.
- 스케일링 관점
- 마이크로 서비스 아키텍처는 스케일 아웃을 필요에 따라 수행할 수 있다. 트래픽이 몰리는 서비스만 스케일 아웃하면 되기 때문에 리소스가 낭비를 최소화 할 수 있다.
- 데이터 베이스는 서비스에 따라 분리되기 때문에 트랜잭션 볼륨을 최소화 할 수 있다. 또한 커넥션 풀링의 소진역시 매우 적다.
- CPU 인텐시브, Memory 인텐시브에 등의 요구사항에 따른 확장이 용이하다. 필요한 서비스만 필요에 따라 요구사항에 맞게 확장/축소가 가능하다.
- 컨테이너 베이스 마이크로 서비스 아키텍처는 특히 노드 리소스를 효과적으로 사용할 수 있도록 할 수 있다. 여유가 있는 노드에 인스턴스를 실행할 수 있기 때문에 자원 효율성이 매우 높다.
- 기술 관점
- 다양한 니즈에 따른 기술을 사용할 수 있다. 특정 서비스는 웹 트래픽을 처리하기 위해 적합한 기술을 이용할 수 있으며, 특정 서비스는 리액티브 위주의 기술을 적용할 수 있는 장점이 있다.
- 서비스 상호간의 인터페이스만 정의된다면, 팀이 원하는 어떠한 기술이든 사용가능하며 이러한 구조를 폴리그랏 이라고도 한다.
- 마이크로 서비스의 경우 공통 모듈은 재사용 측면에서 매우 중요한 부분이지만, 마이크로 서비스에서는 크게 영향을 받지 않는다. 공통 모듈의 버젼을 과거 버젼으로 유지되도 되며, 버젼업이 필요한경우 최소 서비스 범위내에서 실험하고 배포가 가능하다.
- 시스템의 장애 고립
- 마이크로 서비스는 시스템의 장애를 자신의 서비스로 고립할 수 있는 장점이 있다.
- 하나의 서비스에서 메모리 릭이 발생되더라도, 전체 시스템에 영향도가 적다. 이를 위해 다양한 fault tolerance 전략을 자유자재로 사용할 수 있다.
단점
마이크로 서비스 아키텍처가 만능은 아니다. 당연히 분리된 서비스로 인한 단점이 존재하며, 이러한 단점을 보완하기 위한 리소스가 필요하다.
- 복잡도
- 마이크로 서비스는 아키텍처가 매우 복잡할 수 있다. 하나의 서비스는 다양한 서비스를 호출하게 되므로 호출관계가 매우 복잡하다.
- 분산 아키텍처리를 위한 기반 기술을 이해해야할 필요가 있다. (마이크로 서비스 아키텍처 패턴등 적용해야할 부분이 많다.)
- 개발 및 테스트
- 시스템 개발시에 상호 인터페이스를 잘 정의하지 않으면 이후 통합시 인터페이스 수정을 위한 추가작업이 필요할 수 있다.
- 통합 테스트가 매우 어렵다. 여러 서비스 홉을 넘나드는 경우 테스트가 쉽지 않으며, 이슈 발생시 이슈를 발견하고 해결하기 위한 시간이 든다.
- 네트워크 트래픽
- 마이크로 서비스 아키텍처는 트래픽 데이터의 흐름이 커질 수 있으며, 이러한 많은 데이터의 이동은 네트워크의 트래픽을 유발한다.
- N개의 서비스의 집합은 N*M 의 인터페이싱을 발생 시킨다.
- 모니터링의 어려움
- 마이크로 서비스는 여러 시스템의 시스템을 한번에 모니터링을 하기 쉽지 않다. 분산 트레이싱 기법이 필요하며, 일반적인 로그로는 워크 플로우를 관계를 찾기가 쉽지 않다.
- 데이터베이스 일관성 유지
- 마이크로 서비스는 분리된 서비스이므로 트랜잭션으로 묶을 수없는 상황이 발생한다.
- 마이크로 서비스에서 데이터베이스 일관성을 유지 하기 위한 패턴을 도입해야한다. 이는 추가적인 코드와 로직을 작성해야하며, 보완을 위한 서비스를 개발해야함을 의미한다.
- 팀 리소스
- 조직이 구성원이 작은 조직은 마이크로 서비스 도입을 충분히 고맨해야한다.
- 감당할 수 없는 시스템 분할은 매우 개발자들에게 큰 도전이 된다.
요구사항 달성을 위한 방법
- 서비사이드 어플리케이션 개발자(혹은 Backend 개발자) 라는 전문 분야의 개발자로 개발이 진행된다.
- 서비스에 특화된 팀들이 구성되며 팀은 자신의 서비스를 집중적으로 개발 및 유지 발전 시킬 수 있다. 또한 비즈니스 접점은 프런트 엔드 특화된 개발 팀원이 배치되어 시스템 개선이 이루어 진다.
- 데스크탑 브라우저, 모바일 브라우저, 모바일 네이티브 어플리케이션, IoT 디바이스 등 다양한 디바이스와 커뮤니케이션 할 수 있는 엔드포인트를 제공해야한다.
- 마이크로 서비스 아키텍처는 필요한 서비스는 API Gateway 를 통해서 제공되며, 단일 엔드포인트를 제공하도록 시스템이 구성된다.
- 또한 통일된 인터페이스 규약과 문서화가 되고, 사용측면에서의 편리함을 제공한다. (문서, 연결 방법등)
- 대용량 트래픽을 수용하기 위한 Scale In/Out 과 Up/Down
- 스케일 인/아웃 이 필요한 서비스만을 국한해서 인스턴스를 늘여가거나 줄일 수 있어 입체적인 확장이 가능하다.
- 시스템 특화된 머신을 도입할 수 있으며, 스케일 업/다운이 유연하게 이루어 질 수 있다.
- 짧은 Latency 를 필요로 하며, 이를 위한 동기, 비동기 방식의 API 제공
- REST API, Event Driven System 등의 제공
- 동기 비동기 방식의 서비스를 별도로 구성할 수 있으며 각 특성에 따라 시스템을 개선할 수 있다.
- 시스템 상황을 파악하기 위한 종합 모니터링 시스템
- 모니터링이 쉽지 않으며, 통합 모니터링, 개별 팀 모니터링을 위한 방법이 필요하다.
- 서비스가 많을수록 모니터링을 통합하기 위한 시스템과 구현을 위한 별도의 팀을 구성하는 경우도 많다.
- 시스템 장애 최소화를 위한 시스템 설계
- 서비스 시스템은 각 장애를 Isolation 하는 구조로 개발이 된다.
- 장애를 대응하기 위한 다양한 fault tolerance 전략이 존재하며, 다양한 전략을 도입하면 장애 상황을 최소화 할 수 있다.
- 요구사항에 최적화된 개발 언어의 도입
- 개발 언어의 선택은 매우 자유롭다.
- 폴리그랏의 구현으로 시스템 특화된 서비스를 위한 언어, 팀 공통의 언어등 선택의 다양성이 존재하며, 효율적인 시스템을 구헝할 수 있다.
- 데이터 컨시스턴시 유지
- 데이터 컨시스턴시를 위지하기 위한 SAGA 패턴 등의 방법으로 보상 정책으로 개발되여 결과적 일관성을 유지하도록 한다.
- 마이크로 서비스 아키텍처를 위한 보조 서비스
- 마이크로 서비스 아키텍처를 구성하면, 서비스를 찾을 수 있도록 하는 서비스 디스커버리와, API Gateway, 분산 트레이싱과 같은 추가적인 서비스들이 필요하다.
- 사실 코어 비즈니스 로직과는 분리되었다 뿐이지 마이크로 서비스 아키텍처를 위해 없어서는 안되는 기술이다.
선택기준
지금까지 모놀리식 아키텍처와 마이크로 서비스 아티켁처에 대해서 살펴 보았다.
각기 가진 장점과 단점이 존재하기 때문에 전략적인 선택이 필요하다. 가끔 시스템 아키텍처 회의를 참여하다보면 단순히 트랜드라는 이유로 마이크로 서비스 아키텍처 도입을 추진해야한다고 주장할때 혹은 모놀리식 아키텍처로 개발을 진행한다고 하면, 기술이 떨어지는 것처럼 생각하는 상황을 만날때 마다 내심 안타까운 마음이 들기도 한다. 몇가지 상황들을 살펴 보면 어떠한 아키텍터를 선택해야할 지 어느정도 길이 보이는데 말이다.
그럼 어떠한 아키텍처를 도입해야하는지 선택의 기준이 있을까? 정답은 없지만 고려해야할 사항들을 하나하나 살펴보면 선택이 수월할 수 있는 길은 있다.
지금부터 필자가 중요하게 생각하는 고려사항들을 알아 보자.
흔한 오해
첫번째 흔한 오해는 모놀리식 아키텍처의 단순함은 기술적으로 떨어진다는 오해를 가진 것이다.
모놀리식 아키텍처는 마이크로 서비스 아키텍처의 근간이다. 다만 모놀리식 아키텍처에 속해있는 다양한 비즈니스 로직들이 각자 분리된 서비스로 존재한다는 차이점만 있을뿐 시스템 구조는 대부분 유사하다. 모놀리식의 선택은 전략적인 선택일 경우가 더 많다. 개발 운영을 위해 리소스를 더 추가할 수 없을 때라면 모놀리식 아키텍처가 훨씬더 좋은 선택이다.
다른 한가지 오해는 마이크로 서비스 아키텍처는 최신 트랜드이고, 이정도는 적용해줘야 시스템이 안정적으로 유지할 수 있을꺼다 라는 생각이다.
마이크로 서비스 아키텍처는 분리된 모놀리식 서비스들의 집합이다. 그러나 여기에서 한가지 유의할 것이 있다. 시스템의 목적, 앞으로의 발전 방향을 어느정도 가늠하지 못한다면, 마이크로 서비스는 재앙이 될 수 있다는 것이다. 앞에서 나열한 다양한 장점이 있지만, 그 장점들을 살리기 위해서는 핵심 비즈니스 로직 뿐만 아니라 이를 통합하기 위한 보조 시스템역시 필요하다. 그런데 이러한 시스템을 구성하는데 리소스가 들고, 앞으로 어떻게 시스템을 개선해 나갈지에 대한 그림과 방법이 없다면, 좀더 고민해야한다.
이제는 이런 흔한 오해에서 벗어나서 좀더 전략적으로 접근해야한다. 아키텍처는 한번 정해지면 바꾸기가 쉽지 않다는 것을 명심해두고, 너무 기술 트랜드만 따라서 가지 않도록 주의하면서 나아가 한다.
근본 질문, 정말 필요한 것인가?
첫번째 질문은 근본 질문이다. 정말 MSA 가 필요한가? 고민을 해야한다.
가장 기본이 되는 시스템 아키텍처가 모놀리식 아키텍처라면, 여기서 MSA로 분리해야하는 사유를 명확히 해야한다. 단순히 트렌드니까? 이러면 위험하다.
우리는 장단점 비교에서 모놀리식 아키텍처의 단점을 알아 봤다. 초기에 빠른 개발을 위해서 모놀리식 아키텍처는 좋은 선택이다. 그러나 시간이 지나면서 시스템은 주요 서비스들로 세분화 된다. 세분화 된 시스템 서비스는 팀으로 분리되어 각자의 책임을 나눠 가지게 된다. 팀이 늘어나면서 각자가 가져가야할 책임과 역할이 나눠지다 보면, 어느새 시스템은 단일 코드 베이스에 운영하기가 쉽지 않은 상황이 오게 될 것이다.
한 팀의 변경사항이 다른팀의 서비스에 영향을 주는 간섭 현상이 생기고, 시스템 배포가 점점 어려워 지며, 알지 못하는 불안감 (내가 수정한 코드로 다른 팀이 고생을 하게 될것 같은)이 엄습해 오는 상황을 겪게 된다. 예전에 잘 지켜지던 코드 컨벤션이 팀이 나누어 지면서 지켜지지 않고 있거나, 코드 커밋시 컨플릭이 빈번히 발생하거나, 브렌치 관리가 점점더 여려워 지는 상황이 발생하기도 한다.
또 비슷한 서비스 코드인데, 서로 서비스마다 다르게 사용하고 있는 코드가 자주 발견이 되는 경우도 발생이 된다. 프런트 엔드 개발팀은 기획의 요구사항을 적용하기 위해서 변경을 시도하지만, 벡엔드 서비스의 차이로 인해서 새로운 엔드포인트가 필요하기도 하며, 새로운 서비스 화면을 개발 하려는데 이전에 만든 서비스와는 다른 기능들이 필요로 한 경우도 발생된다.
만약 위와 같은 이슈가 없이 현재 인원으로 서비스를 유지하고, 새로운 기능을 개발하는데 문제가 없다면 마이크로 서비스로 전환을 고민하지 않는것이 좋다.
그렇지 않고 이러한 상황이 발생이 된다면 이제는 분리를 고민해 봐야하는 시점이 된 것이다. 어떻게 분리할 것인지를 생각해야하며, 분리가 가져오는 이슈들을 확인하고 대응 방안 마련과 분리 플랜을 준비해야한다.
관심사의 분리 (SoC - Separation of Concerns)
관심사의 분리는 마이크로 서비스 아키텍처로 서비스를 분리하는 방법중에 하나이다. 예를 들면 쇼핑몰에서 관심사의 분리는 상품, 판매자, 구매자, 혜택, 주문, 결제, 배송, 취소/반품 등으로 각자의 관심에 따라 시스템을 분리하는 것이다.
전체 소핑몰은 하나이지만, 쇼핑몰을 구성하고 있는 요소들은 서로 다른 관심사를 가진다. 상품의 경우 상품을 등록하고, 상품 재고를 확인하여 상태를 변경하거나, 상품의 전시 및 검색 등이 주요 관심사이고, 그 관심사에 따라 시스템이 발전하게 될 것이다.
모놀리식 아키텍처에서 관심사를 분리해 내는 방법과, 처음부터 마이크로 서비스를 구성하여 분리된 관심사로 개발을 진행하고 통합하는 방법이 있을 것이다.
모놀리식 아키텍처에서 분리해 내는 과정을 거친다면, 코어 서비스 혹은 의사결정을 통해 선정된 서비스가 분리될 것이다. 보통 이럴때는 조직의 규모에 맞게 단계적으로 분리하는 전략을 취한다. 처음부터 마이크로 서비스를 구성하는 경우라면, 비즈니스 관점으로 분리된 서비스로 개발을 시작하게 되며 쇼핑몰이 분리된것처럼 많은 서비스를 한번에 개발할 수도 있고, 상품과 주문결제로 분리해서 서비스가 그룹 단위로 구성될 수도 있다.
위 예제와 같이 쇼핑몰 경우는 비즈니스 영역에 따른 관심사의 분리 대로 시스템 개발 운영이 진행이 된다. 관심사에 따라 팀이 분리되거나, 하나의 팀이 여러개의 서비스를 가져가는 경우가 될 것이다. 가끔 관심사의 분리를 기능단위나 레이어 단위로 수행하는 경우가 있다. 필자의 생각으로 비즈니스 단위의 분리가 아닌 기능단위 분리는 지양하는 것이 더 좋다고 생각이 든다. 이유는 기능 단위 분리가 가져오는 장점보다 단점이 더 많다고 생각이 들기 때문이다. 기능을 제공하는 조직이 있다고 생각했을때 우리는 몇가지 고민을 해야한다.
첫번째 기능이 앞으로 개선 벌전될 수 있느냐의 문제가 있다. 즉 기능 나름대로의 비즈니스 영역과 니즈의 변경이 존재하느냐에 따라 담당하는 팀의 존속이 결정되며, 팀이 유지가 되지 않는다면 해당 기능은 도태되게 된다. 즉, 서비스는 관리가 되고 발전이 되어야 하나의 서비스와 팀으로 분리 될 수 있다는 것이 개인적인 견해이다.
두번째 여러 서비스가 공통으로 사용하는 기능이 있는데, 이후 특정 서비스에서 해당 기능의 컨텍스트에 대한 해석이 달라지는 경우 문제가 발생할 수 있다. 우선 개발을 담당하는 조직이 해당 기능을 만들고 배포하는데 시간이 걸린다는 것이고, 무엇보다 어려운 것은 컨텍스트의 차이를 기능담당 팀에 이해를 시켜야 하는 문제가 생긴다. 이런 구조는 생산성을 저하시키고, 서비스의 발전을 저해하는 원인이 된다.
즉, 비즈니스 관심사에 따라서 동일한 데이터나 기능이 서로다른 용도로 사용할 수 있으므로, 보통은 기능단위보다, 비즈니스 단위로 서비스를 분리하는 것이 더 명확하게 변화를 주는 것이 더욱 수월하다. 이렇게 개발이 되는경우 동일한 소스가 중복으로 개발되는 것처럼 보이지만, 사실은 중복이 아니라 관심사에 따른 컨텍스트의 차이로 해석하는 것이 맞을 것이다.
관심사의 분리가 필요한 상황인지 고민하자.
조직 규모
시스템을 개발하고 유지하는데 인력 리소스는 항상 어려운 고민중의 하나이다. 초기 비즈니스 아이디어를 구현하는데는 인력 리소스에 대한 사항이나 조직 규모에 대한 고민을 그렇게 하지 않는다. 어디까지이나 개발 런칭이 주요 관심의 대상이고, 제품을 출시하고 나서 이후에 리소스를 고민하는 것이 일반적이기 때문일 것이다.
그런 의미에서 모놀리식 아키텍처와 마이크로 서비스 아키텍처는 초기 세팅되는 규모는 전혀 다르게 구성이 된다.
모놀리틱 서비스는 보통 1 ~ 2 팀정도에서 전체 기능을 다 만들기 때문에 작은 조직 인원으로 시스템을 개발 유지가 가능하다. 혹은 외주를 주는 경우에도, 최종 인수를 받을 수 있는 정도로 시스템을 구성하거나, 필요한 운영 인력을 배치하는 것이 보통이다.
그러나 처음부터 마이크로 서비스 아키텍처를 도입한다면 서비스 그룹마다 팀을 구성 해야하며, 제품이 완성되고 나서도 초기 개발 규모와 유사한 정도의 인력이 존재해야 한다.
만약 근본 질문을 할 정도의 규모가 되지 않는다면 마이크로 서비스 아키텍처로 전환하는 것을 권장하지 않는다.
3 ~ 4명이 운영하고 있는 서비스를 마이크로 서비스 아키텍처로 변환하는 이점과, 단점중 어느것이 더 큰지 생각해보면, 서비스의 분리 규모가 커지면 커질 수록 단덤이 더 클 것이다.
- 시스템을 유지 보수 하는 인력이 절대적으로 부족할 것이다. 즉, 유지 보수 비용이 절대적으로 증가하게 된다.
- 서비스의 장애가 발생하면, 전체적인 인과관계를 파악하는데 오랜 시간이 걸리게 된다.
- 서비스 개발 및 운영유지 보수를 진행하면서 신규 기능 개발 및 기능 개선의 리소스가 부족해진다.
조직 규모는 아키텍처 모델을 결정하는 매우 중요한 부분이다.
확장 가능성을 염두한 서비스 분리
시스템은 자체적으로 개선이 이루어 지는 것은 비즈니스 관점에서 매우 중요한 포인트이다. 마이크로 서비스 아키텍처는 서비스의 개선이 용이한 구조로 보통 구성이 되며 이러한 개선역시 자치적으로 수행된다.
상품 서비스를 제공하는 팀이라면 상품을 등록, 수정하는 기능을 더욱 사용자가 사용하기 쉽게 개발하거나, 상품을 프런트에 전시하는 방법과 정책역시 매번 변경되고 어 고객의 흥미를 끌기위한 방향으로 개선되기도 한다.
이런 경우 시스템의 기능을 확장하거나 피드백을 빠르게 제공해주기 위한 시스템을 구성하고자 한다면, 가능하면 단일 팀에서 기능 개발및 변경을 수행하는 것이 가장 좋다. 다른 팀으로 홉이 생기게 되면 원하는 컨텍스트에 맞는 기능을 획득하는데 시간이 오래 걸리거나 원하지 않는 결과가 올 수 있어 시스템 개선에 어려움이 생길 것이다.
즉, 지금까지 서비스는 자치적으로 발전할 수 있도록 구성하는 것이 필요하다.
필자가 있었던 조직은 취소 반품 서비스를 제공하는 팀이었는데, 취소/반품을 처리하고, 필요한 서비스와 상호작용 하면서 실제 고객에게 환불을 하는 임무를 수행했었다.
필자가 운영하는 서비스를 개선하기 위한 방법으로 취소/환불 보상 기능을 확장하고, 반룸되는 제품들의 데이터를 수집하고, 이들을 재고화 하는 기능을 만들었다. 이러한 기능들을 개발할때 외부에 필요한 정보를 조회하는 것 이외에 팀 내부적으로 시스템의 데이터를 수집하고, 이 데이터를 기반으로 서비스를 확장해 나갔다.
마이크로 서비스는 이렇게 서비스의 개선을 즉각적으로 빠르게 진행할 수 있어야 하며, 데이터를 기반으로 확장할 있도록 유연한 설계가 필요하다. 시스템 개선을 위해서 자치적으로 운영이 될 수 있도록 서비스를 분리를 고민해야한다.
맺음말
지금까지 모놀리식 아키텍처와 마이크로 서비스 아키텍처에 대해서 장 단점을 알아 보았고, 선택의 기준도 알아 보았다.
- 근본 적인 질문을 스스로 하자.
- 관심사의 분리를 통해 니즈를 파악하자.
- 조직 규모를 고려하자.
- 확장 가능성을 두어 서비스를 분리하자.
위 기준이 절대적인 기준은 아니며, 이것 이외에도 다양하게 고려해야할 사항들이 있을 것이다.
강조하고 싶은 것은 Monolithic 은 과거의 유물이 아니며, MSA 가 트랜드라고 해서 무조건 따르는 것은 위험하다는 것도 알아 보았다.
MSA 는 분리된 각 서비스들이 상호 커뮤니케이션을 통해서 공동의 목표를 달성하는 시스템 아키텍처
이다.
분리하는 이유를 생각하고, 팀의 규모와 앞으로의 확장성을 고려한 분리를 통해 성공적인 도입을 수행하는 것이 필요하다.
참고:
- https://microservices.io/patterns/monolithic.html
- https://ko.wikipedia.org/wiki/%EB%A7%88%EC%9D%B4%ED%81%AC%EB%A1%9C%EC%84%9C%EB%B9%84%EC%8A%A4