Amazon, eBay, Netflix 같은 많은 조직들에서 Microservice Architecture Pattern이라고 알려져 있는 기술을 채택하여 이러한 문제를 해결하고 있다. Microservice Architecture는 하나의 괴물같은 monolithic application을 만드는 대신에, 더 작고 서로 연결되어 있는 서비스들의 집합으로 application을 나누는 것이다.
일반적으로 서비스는 order management(주문관리), customer management(고객관리), 등과 같은 별개의 특징이나 기능 집합으로 구현되어 있다. 각각의 microservice는 그 자체에 다양한 어댑터에 대한 비즈니스 로직을 가진 6각형의 아키텍처를 가지는 mini-application이다. 어떤 microservice는 다른 microservice나 application client에 의해서 호출되는 API를 제공하고, 다른 microservice는 Wen UI를 구현할 수도 있다.
런타임시에는 각각의 instance는 클라우드 VM이나 Docker container 상에 실행되기도 한다.
예를 들면, 앞서 기술된 시스템을 분해하면, 다음과 같은 다이어그램으로 표현할 수 있다.
Application의 개별 기능은 각각의 microservice로 구현되어 있다. 더욱이 web application은 더 간단한 web application의 집합으로 나누어져 있다. (택시 호출 서비스 예제에서 보면, 한 명의 운전 기사가 한 명의 승객과 매칭되는 경우). 이것은 특정 user, device, 혹은 특별한 use case별로 개별 경험을 더 쉽게 배포하게 한다.
각 backend service는 REST API를 제공하고, 대부분의 service들은 다른 service가 제공하는 API를 사용한다. 예를 들어, Driver Management(기사 관리)는 잠재적 이동에 대해 활동 가능한 운전 기사에게 알려주는데 Notification Server를 사용한다. UI service들은 web page들을 렌더링하기 위해 다른 서비스들을 호출한다. 서비스들은 메시지 기반의 communication에 async방식을 사용한다. 서비스간의 communication에 대해서는 추가로 나중에 자세히 다룰 것이다.
몇몇 REST API는 기사나 승객에 의해 사용되는 mobile app에서 이용한다. 그러나 이러한 app들은 backend service이 직접 접속할 수 없다. 대신에 communication은 API Gateway로 알려진 중재자를 통해 가능하게 된다. API Gateway는 로드밸런싱, caching, access control, API metering, monitoring과 같은 부분들을 처리해야 한다. 이러한 부분들은 nignx를 사용하여 효과적으로 구현할 수 있다.
Microservice architecture pattern은 scalability(확장성) 3D Model에 대한 최고의 책인 "The Art of Scalability"에 나오는 Scale Cube의 Y축 Scaling에 대응된다. 다른 2개의 Scaling 축은 load balancer 뒤에서 실행중인 여러 개의 동일한 application 복사본으로 구성된 X축 Scaling과 요청의 속성(예를 들면, row의 primary key나 고객의 identity)이 특정 서버에 대한 요청으로 route하는데 사용되는 Z축 Scaling(혹은 data partitioning)이 있다.
Application들은 일반적으로 3가지 Scaling 타입을 함께 사용한다. Y축 Scaling은 이 섹션 첫번째 그림에서 보여주는 대로 application을 microservice로 분해한다. Runtime 시점에 X축 Scaling은 load balancer 뒤에서 처리량과 가용성을 위해 각 서비스에 대해 여러 개의 instance를 실행한다. 어떤 application들은 service를 partition하기 위해 Z축 Scaling을 사용하기도 한다.
다음 다이어그램은 Trip Management 서비스가 Amazon EC2 서버 상에 Docker로 어떻게 배포되는지를 보여준다.
Runtime시에 Trip Management 서비스는 여러 개의 instance로 구성되어 있다. 각 서비스 instance는 Docker container로 제공된다. 가용성을 높이기 위해서 docker container들은 여러 개의 cloud VM에서 돌아간다. Service instance 앞단에는 여러 instance로 요청을 분산하는 nginx와 같은 load balancer가 있다. Load balancer는 caching, access control, API metering, monitoring과 같은 여러 가지 관심 사항들을 취급할 수 있어야 한다.
Microservice Architecture Pattern은 특히 application과 database 사이의 관계에 중요한 영향을 준다. 서로 다른 service간 하나의 database schema를 공유하는 것보다는 각각의 서비스가 독자적인 database schema를 갖는다. 다른 한편으로 이러한 접근은 Enterprise-Data Model 관점에서는 이상하다. 또한, 어떤 데이터들은 종종 중복되기도 한다. 그러나 서비스별 database schema를 갖는 것은 microservice를 통해 혜택을 보기 원한다면, 본질적인 부분이다. 왜냐하면, microservice는 느슨한 연결(loose coupling)을 보장하기 때문이다. 다음 다이어그램은 예제로 제시된 application에 대한 database architecture를 보여주고 있다.
각각의 서비스는 자체적으로 데이터베이스를 가지고 있다. 게다가, 서비스별로 소위 polyglot
persistence architecture(상황에 맞게 최적화된 구조로 데이터를 구분하여 저장하는 아키텍처)라고 불리는 가장 최적의 데이터베이스 타입을 사용할 수 있다.
예를 들어, 잠재적인 승객에게 가장 가까운 운전 기사들을 찾아야 하는 Driver Management(기사 관리)는 geo-query(지리기반 질의)를 지원하는 데이터베이스를 사용해야만 한다. 표면적으로는 MSA는 SOA와 유사하다. 두가지 모두 서비스들의 집합으로 구성된 아키텍처이다. 하지만, MSA는 WS 표준과 ESB가 없는 SOA라고 생각하면 된다.
Microservice 기반 application은 WS 표준보다 굉장히 단순하고, REST와 같은 가벼운 프로토콜을 사용한다. 또한 ESB 사용을 지양하고, microservice 자체적으로 ESB 유사한 기능을 구현한다. Microservice Architecture Pattern은 정규 스키마(canonical schema)와 같은 SOA의 다른 부분도 거부한다.
Marching Towards Monolithic Hell (통짜 구조로 인한 지옥으로 행진)
불행하게도 이런 간단한 접근 방법은 심각한 한계가 있다. Application이 성공적일수록 시간이 지나면서 점차적으로 크기가 커지게 된다. 각 단계별로 개발팀은 많은 코드를 추가하여 약간 더 많은 스토리를 구현하게 된다. 몇 년 후에는 초기에 작고 간단했던 application이 괴물처럼 커지게 될 것이다. 극단적인 예를 하나 들자면, 최근에 수백만 라인의 코드로 구성된 수천개의 JAR 사이에 있는 의존성을 분석하는 Tool을 개발하는 개발자와 이야기를 나눈 적이 있다. 그런 괴물을 만들어 내기 위해서 오랫동안 수많은 개발자들이 많은 노력을 들여 협조했을 것이라고 확신한다.
한번 Application이 커지고 나면, 개발 조직은 고통의 세상에 빠지게 된다. Agile 개발 방법론과 Delivery에 대한 어떤 시도도 허우적거리게 될 것이다. 커다란 문제 중 하나는 Application이 극도로 복잡해졌다는 것이다. 간단히 말하자면, Application이 너무 커서 한 명의 개발자가 전체를 이해할 수 없다. 결과적으로 버그 수정이나 새로운 기능을 개발하는 것은 어렵고, 시간이 오래 걸리게 되었다. 한술 더 떠서 아래쪽으로 향하는 소용돌이처럼 되기 쉽다. Codebase가 이해하기 어렵다면 원하는 대로 변경을 할 수 없다. 결국에는 괴물과 같은 이해하기 어려운 거대한 진흙 덩어리(Big Ball of Mud)에 직면하게 될 것이다.
방대한 규모(Sheer size)의 Application은 개발을 느리게 할 것이다. Application이 커질수록, 시작하는데 걸리는 시간은 길어진다. 예를 들면, 최근 조사에서 어떤 개발자들은 application을 시작하는데 12분이 걸렸다고 보고되었다. Application 실행하기까지 40분이 걸렸다는 일화도 들은 적이 있다. 개발자들이 application server를 재시동해야 한다면, 동작하기까지 오랫동안 기다려야 하고, 생산성은 악화될 것이다.
규모가 커졌을 경우 또다른 문제는 복잡한 monolithic application을 지속적으로 배포할 경우 장애물이 된다는 것이다. 오늘날 SaaS Application을 예술적으로 만들기 위해서 하루에도 몇번씩 제품에 변경을 가해야 한다. 복잡한 monolith application에는 이렇게 하기가 극도로 어렵다. 왜냐하면 일부 수정사항을 업데이트 하기 위해서 전체를 재배포 해야하기 때문이다. 앞서 언급한 application 실행 시간의 길이는 더 언급할 필요도 없다. 또한 변경에 따른 영향도를 일반적으로 파악하기 어렵기 때문에 어마어마한 수동 테스트를 해야할 것이다. 따라서 지속적인 배포는 다음 작업을 불가능하게 할 것이다.
Monolithic application은 다른 모듈간에 서버의 자원 사용에 대한 충돌이 발생했을 때 확장하기 어렵다. 예를 들면, CPU를 집중적으로 사용하는 이미지 처리를 하도록 구현된 하나의 모듈이 Amazon EC2에 최적화된 instance에 배포되어 있고, 또다른 모듈은 In-Memory 데이터베이스를 사용하고, EC2 메모리 최적화 instance에 적합할 경우, 이러한 모듈들이 하나의 Application에 함께 배포되어 있기 때문에 하드웨어 선택에 있어서 타협을 해야만 한다.
Monolithic Application의 또다른 문제는 신뢰성(reliability)이다. 모든 모듈들이 동일한 프로세스 내에서 동작하기 때문에 어떤 모듈에 메모리 누수(memory leak)와 같은 버그가 있을 경우, 잠재적으로 전체 프로세스가 다운될 수 있다. 더욱이 application의 모든 instance가 동일하기 때문에 그런 버그는 전체 application의 가용성(availability)에 영향을 줄 것이다. 마지막으로, 그렇지만 앞에 이야기한 것과 마찬가지로 중요한 것은 monolithic application은 새로운 framework과 language를 받아들이기에 극도로 어렵다는 것이다. 새롭고 더 좋은 ABC framework을 사용하여 전체 application을 재개발하는 것은 시간적으로나 비용적으로나 굉장히 비싸다. 결과적으로 새로운 기술을 받아들이기에 거대한 장벽이 있다. 여러분이 처음 프로젝트 시작할 때 적용한 기술이 무엇이건 간에 새로운 기술을 받아들일 수 있는 여지가 없다.
요약하자면 : 만약 여러분이 괴물같이 거대한 통짜로 개발된 주요 비즈니스 application (business-critical application)을 가지고 있다면 개발자들은 거의 이해하기 어렵다. 재능이 뛰어난 개발자들을 고용하여 거의 쓸모가 없고, 비생산적인 기술들을 사용하여 어렵게 구현하게 된다. Application은 확장하기도 어렵고, 신뢰성도 떨어진다. 결과적으로 application의 agile 개발 및 배포가 불가능하다.
자, 그럼 이제 무엇을 할 수 있을까?
댓글을 달아 주세요
댓글 RSS 주소 : http://www.yongbi.net/rss/comment/748