Recommanded Free YOUTUBE Lecture: <% selectedImage[1] %>

Contents

마이크로 서비스 아키텍처

마이크로 서비스 아키텍처(MSA - Micro service architecture)는 서로 독립적인 여러 개의 프로세스로 나눈 다음, API를 이용해서 서로 연결해서 복잡한 애플리케이션을 구성하는 소프트웨어 디자인 패턴중 하나다. 서비스 기업들은 이전보다 더 빠른 주기로 업데이트된 서비스를 출시하고 평가 받기를 원한다. 이들 서비스는 PC, 모바일 기기, IoT기기들까지 대상을 한정하지 않는다.

(지금 내가 하고 있는)IoT 서비스를 예로 들어보자. 하나의 IoT 서비스에 TV, 냉장고, 에어컨, 세탁기, 카메라, 보일러, 전구, 스피커등의 기기들이 붙고 있다. 앞으로 어떤 종류의 기기들이 추가되더라도 IoT 서비스에 연결할 수 있어야 한다. 거기에 PC, iOS, 안드로이드, 자동차 네비게이션등 다양한 유저 애플리케이션을 붙일 수 있어야 한다. 다양한 각각의 기기를 처리하는 (서버)애플리케이션들은 그들 나름대로의 버전관리가 가능해야 할 것이다.

이런 서비스를 지원하기 위해서는 기능별로 애플리케이션을 서로 분리해서 서로에 대한 의존성을 제거하고, 독립적으로 개발 한후 연동하는 방식으로 아키텍처링 해야 할 것이다. 바로 마이크로 서비스 아키텍처링의 모습이다.

지금 당신의 상황

서버 사이드 애플리케이션을 개발한다고 가정해 보자. 이 애플리케이션은 다양한 종류의 데스크탑 브라우저와 모바일 브라우저 그리고 네이티브 모바일 애플리케이션을 지원해야 한다. 여기에 3rd party 개발자를 위한 API도 제공해야 한다. 다양한 애플리케이션에서 수집된 메시지들은 웹 서버나 메시지 브로커를 이용해서 통합 해야 하나. 이 애플리케이션은 HTTP, WebSocket, MQTT등 서로 다른 프로토콜로 전송된 요청을 처리해야 한다. 즉 데이터베이스에 접근하고, 다른 애플리케이션과 메시지를 교환해서 HTML/JSON/XML 형식의 응답을 만들어서 전송해야 한다.

이 애플리케이션은 아래와 같이 다양한 컴포넌트로 구성되는 hexagonal architecture 구조를 가질 것이다.
  • Presentation 컴포넌트 : HTTP 요청에 대해서 HTML 혹은 JSON/XML 메시지를 응답한다.
  • 비지니스 로직 : 애플리케이션의 비지니스 로직
  • 데이터베이스 엑세스 로직 : 요청을 처리하기 위한 데이터베이스 접근
  • Application integration logic : 메시징 레이어. (예. Spring integration)
이들 컴포넌트들은 하나의 서비스로 통합할 수 있지만, 논리적으로 서로 다른 기능영역에 포함된다.

문제

이 애플리케이션을 배치하기 위해서 어떤 아키텍처를 사용할 것인가.

요구 사항

  • 팀에 들어오는 새로운 멤버는 빠르게 개발에 참여할 수 있어야 한다.
  • 애플리케이션은 쉽게 이해할 수 있어야 한다.
  • 애플리케이션은 쉽게 수정할 수 있어야 한다.
  • 애플리케이션은 여러개의 머신에서 분산 수행할 수 있어야 하며, 단지 복사하는 것만으로 확장할 수 있어야 한다.
  • 애플리케이션에서 실행 중인 머신에 문제가 생기더라도 안정적인 서비스가 가능해야 한다.
  • 새로운 기술들과 언어들을 쉽게 적용할 수 있어야 한다.

해법

규모 확장성(scalability)을 결정하기 위해서 scale cube 모델을 널리 사용하고 있다. 이 모델을 이용해서 해법을 찾아보도록 하자.

Scale cube는 3개의 방향을 가지는 요소들로 구성이 된다. 이들 구성요소들을 조절해서 확장성(큐브의 크기)를 결정한다.
  • X axis - Horizontal duplication : 같은 일을 하는 녀석을 마구 복사해서, 많은 일을 처리하자.
  • Y axis - Functional decomposition : 기능을 분해해서 여러 머신에서 돌리게 해서, 많은 일을 처리하자. 한 녀석이 여러 일을 한꺼번에 처리하는 것보다 나눠주는게 빠르지 않겠는가. ?
  • Z axis - Data partitioning : 데이터베이스를 여러 서버군으로 쪼개자. 하나의 거대한 데이터베이스에서 데이터를 찾는 것 보다, 조그마한 데이터베이스에서 찾는게 효율적일 테니까. 데이터베이스 Shard라고 알려진 기술들이 여기에 포함된다.
마이크로 서비스 아키텍처는 Functional deomposition을 이용해서 규모 확장성을 꾀한다. 예를들어 물류관리 서비스를 개발해야 한다면, 수주 관리 서비스와 고객 관리 서비스를 분리해서 구성한다.

여기에서 분리는 물리적인 분리(컴퓨터 노드의 분리)까지를 의미한다. 따라서 HTTP/REST/AMQT와 같은 통신 프로토콜을 이용한 데이터 통신이 필요 하다. 데이터베이스도 서로 분리하며, 데이터베이스 리플리케이션이나 애플리케이션 영역에서의 이벤트 매커니즘등을 이용해서 데이터베이스를 관리해야 한다. 물론 강박적으로 모든 걸 분리할 필요는 없다. 패턴은 참조 할 만한 모델이지 법칙은 아니다. 데이터베이스를 분리할 경우 데이터베이스 관리가 복잡해지고 추가적인 개발이 필요할 수 있다. 또한 성능을 희생해야 할 수도 있다. 그냥 데이터베이스를 직접 공유하는게 나은 경우도 있다.(혹은 개발 초기에는 공유하고 나중에 분리하는 방법을 사용할 수도 있다.). 상황에 따라서 적절한 방법을 선택해야 한다.

개발 역시 독립적으로 이루어진다. 원칙적으로 물류관리 서비스 개발자는 수주 관리 서비스를 전혀 모르더라도 개발할 수 있다.

예제

E-commerce 서비스를 만든다고 가정해 보자. 이 서비스는 Inventory Service, Shipping Service, Accounting Service 그리고 StoreFrontUI 기능으로 구성된다. 앞단에는 Apache 웹 서버가 있고 뒷 단에 MySQL 데이터베이스가 놓인다.

Monolithic architecture 라면 아래와 같이 구성했을 거다.

장점

마이크로 서비스 아키텍처의 장점이다.
  • 각 마이크로 서비스는 낮은 결합도(느슨한 결합 )를 가진다.
    • 서비스를 쉽게 이해할 수 있고, 빠르게 개발에 참여할 수 있다.
    • 서비스를 빠르게 배포할 수 있다. 각 서비스 컨테이너도 더 빠르게 수행된다.
    • IDE를 이용한 개발성도 좋아진다.
  • 각 서비스는 서로 독립적이다. 따라서 서비스 별로 쉽게 업그레이드 할 수 있다.
  • 개발 확장이 용이하다. 서로 다른 기술을 가진 여러개의 팀을 구성할 수 있다. 각 팀은 피자 한판으로 한끼니를 해결할 수 있는 조그마한 규모로 유지할 수 있다. 또한 개발, 운영을 모두 포함한 탄력적 팀 운용이 가능하다.
  • Fault isolation 한 시스템의 구성. 하나의 서비스에 메모리 누수가 발생하더라도 다른 서비스에 영향을 미치지 않는다. 다른 서비스는 여전히 자신의 서비스를 수행할 수 있다. 모노리틱구조에서는 한 컴포넌트의 오동작이 전체 서비스에 영향을 미칠 수 있다.
  • 각 서비스는 독립적으로 개발하고 배치할 수 있다.
  • 기술 스택을 짭게 해서, 커뮤니케이션 비용을 절약할 수 있다.

단점

물론 장점만 있는 건 아니다.
  • 개발자들은 분산시스템에 따른 추가적인 복잡도를 이해하고 있어야 한다.
  • 개발자 툴들은 모노리틱 애플리케이션 개발에 적합하다.
  • 테스트가 어렵다.
  • 분산시스템은 애플리케이션의 배포와 관리의 복잡성을 높인다. 프로젝트 규모에 따라서, 분산시스템 개발자가 따로 투입되기도 한다.
  • 시스템 자원 특히 메모리 자원을 낭비할 수 있다.
  • 서비스간 통신 비용 때문에 모노리틱 애플리케이션 보다 느리게 작동하는 경우가 많다.

Scaling

Scaling 방식에서 모노리틱 아키텍처와 마이크로 서비스 아키텍처를 비교해서 고민해 보자. 아래 그림에서 왼쪽이 모노리틱, 오른쪽이 마이크로 서비스다.

모노리틱 애플리케이션은 모든 기능을 담고 있는 통짜 프로세스를 배포한다. 그리고 이들 프로세스를 복제하는 것으로 스케일링을 한다. 반면 마이크로 서비스 애플리케이션의 경우 필요한 기능을 가진 프로세스만 복제하는 방식을 따른다.

이 둘간의 장단점을 정리했다.
  1. 스케일링의 용이성 : 모노리틱이 더 편하다. 마이크로 서비스 애플리케이션의 경우 기능마다 가해지는 부하에 있어서 차이가 난다. 따라서 각 기능들을 모니터링 하면서, 스케일링이 필요한 기능에 대해서만 작업을 수행해야 한다. 즉, 마이크로 서비스는 좀 더 엄격한 스케일링 계획을 수립해야 한다.
  2. 효율성 : 기능별로 부하가 다를 수 있다. "인증과 검색"기능을 가진 WAS가 있다고 가정해 보자. 일반적으로 인증 보다는 검색기능이 더 많은 자원을 소모할 거다. 유저가 늘어나서 스케일링을 해야 한다. 전체 부하에서 인증이 차지하는 부하가 20%, 검색이 차지하는 부하가 80%인다. 이 경우 마이크로 서비스 애플리케이션이라면 "검색 기능"만 스케일링이 가능할 거다. 하지만 모노리틱은 모두 묶어서 스케일링을 해야 한다. 마이크로 서비스가 좀 더 효과적으로 스케일링이 가능하다.

비지니스 기능을 중심으로 하는 구성

여러 부분으로 구성되는 거대한 애플리케이션을 개발한다고 가정해 보자. 이런 프로젝트는 종종 "UI 팀, 서버 사이트 개발 팀, 데이터베이스 팀"과 같은 기술적인 관점에서 팀이 구성된다. 이들 팀들은 독립적으로 구성되는 경우가 많아서, 기능의 변경과 추가를 위해서는 많은 커뮤니케이션 비용을 투자해야 한다.

몇 개의 기능적인 팀들로 구성된 프로젝트를 경험해 봤다면, 서로 다른 팀들 간의 논의에 얼마나 많은 에너지를 들여야 하는지 알고 있을 것이다. 컴포넌트가 서로 단단하게 결합해 있어서, 다른 팀의 수정사항이 사이드이펙트를 일으킬 확률이 대단히 높다. 각 팀들은 자신이 맡은 컴포넌트만 책임지려하기 때문에, 변화에 보수적으로 대응 한다. 변화가 필요할 경우 모든 팀들이 동시에 움직여야 하며, 책임소재에 대한 정의를 명확히 해야 하기 때문에 팀간 조율에 많은 어려움이 따른다. 대기업의 SI 프로젝트의 경우 팀간 의견 조율을 전담하는 매니저라는 직책이 만들어지는 이유이기도 하다.

여기에 보안팀이라도 엮이게 되는 날엔 헬 오브 지옥을 경험하게 된다. Conway's Law 의 전형적인 예다.

마이크로 서비스는 비지니스 기능을 중심으로 구성된다. 비지니스의 요구에 따라서 UI, 스토리지 기타 다른 기능들이 협력해서 기능들을 재 구성해서 서비스를 만든다. UI, 데이터베이스 구축, 스토리지를 담당하는 각 팀은 다른 컴포넌트의 영향을 최소화 하면서 느슨하게 연결된다. 팀간 대화는 느슨한 컴포넌트간의 연결 프로토콜을 규격화한 API를 통해서 이루어진다. 이 API는 그 자체로 다른 컴포넌트의 기능을 설명하고 있기 때문에, 자연스럽게 전체 서비스의 흐름을 이해할 수 있다.

사용 기업들

  • Amazon
  • Netflix
  • SoundCloud
  • The Guardian

AWS와 마이크로 서비스

마이크로 서비스는 기본적으로 분산 시스템을 지향한다. 이러한 지향 점은 특히 클라우드 서비스와 일치하는 점이 있어서, 클라우드 기반으로 서비스를 개발 할 경우 마이크로 서비스 아키텍처는 핵심 아키텍처 원리로 적용 할 수 있다.

AWS를 예로 들어보자. 마이크로 서비스는 Scale cube에서 Y axis를 달성하기 위한 패턴이다. 여기에 X axis 패턴을 더하면, 실제 서비스에 적용할 수 있는 아키텍처 모델이 나온다. AWS의 ELB와 Autoscaling, SQS, Elastic Cache 등을 조합하면 쉽게 X axis 확장을 할 수 있다. X axis와 Y axis 조합의 결과로 유연하고 확장가능한 서비스를 만들 수 있다.

참고