마이크로 서비스 아키텍처
Last updated
Last updated
마이크로 서비스 아키텍처를 이해하려면 먼저 모노리틱 아키텍처 스타일에 대해서 이해해야 한다. 모노리틱 아키텍처 스타일은 기존의 전통적인 웹 시스템 개발 스타일로, 하나의 애클리케이션 내에 모든 로직이 들어가 있는 '통짜 구조'이다. ex)
위와 같이 온라인 쇼핑몰이 있을 때 톰캣 서버에서 도는 WAR 파일내에 사용자 관리, 상품, 주문 관리 등 모든 컴포넌트가 들어 있고 이를 처리하는 UX로직까지 하나로 포장돼서 들어가 있는 구조이다.
전체 애플리케이션을 하나로 처리하기 때문에 개발 도구에서 하나의 애플리케이션만 개발하면 되고 배포 역시 간편하며 테스트도 하나의 애플리케이션만 수행하면 되므로 편리하다.
모노리틱 아키텍처 문제점 작은 크기의 애플리케이션에서는 유용하지만, 규모가 큰 애플리케이션에서는 빌드 및 배포 시간, 서버의 기동 시간이 오래걸린다. 또한 프로젝트를 진행하는 관점에서도 한 두 사람의 실수는 전체 시스템의 빌드 실패를 유발하기 때문에 프로젝트가 커질수록 협업 개발하기가 쉽지 않다.
그리고 시스템 컴포넌트들이 서로 로컬 콜(Call-by-Reference) 기반으로 타이트하게 연결되어 있기 때문에, 전체 시스템의 구조를 제대로 파악하지 않고 개발을 진행하면 특정 컴포넌트나 모듈에서의 성능 문제나 장애가 다른 컴포넌트에까지 영향을 주게 된다.
마지막으로 특정 컴포넌트를 수정할 때 수정된 컴포넌트만 재배포 하는 것이 아니라 전체 애플리케이션을 재컴파일해서 전체를 다시 통으로 재배포 해야 한다. 이 때문에 잦은 배포가 있는 시스템은 불리하며 컴포넌트별로 기능 / 비기능적 특성에 맞춰서 다른 기술을 도입하고자 할때 유연하지 않다. 예를들어 전체 애플리케이션을 자바로 개발했다고 했을 때 파일 업로드/다운로드 와 같은 IO 작업이 많은 컴포넌트는 Node.js를 사용하는 것이 좋을 수 있으나, 애플리케이션이 자바로 개발되었기 때문에 다른 기술을 집어넣기가 매우 어렵다.
마이크로 서비스 아키텍처는 대용량 웹 서비스가 많아짐에 따라 정의된 아키텍처인데, 그 근간은 SOA에 두고 있다. SOA가 엔터프라이즈 시스템을 중심으로 고안된 아키텍처라면, 마이크로 서비스 아키텍처는 SOA 사상에 근간을 두고, 대용량 웹 서비스 개발에 맞는 구조로 사상이 경량화되고 대규모 개발팀의 조직 구조에 맞도록 변형된 아키텍처다.
마이크로 서비스 아키텍처의 구조 마이크로 서비스 아키텍처의 구조는 아래와 같은 모양을 따른다. 각 컴포넌트는 서비스라는 형태로 구현되고 API를 이용하여 타 서비스와 통신을 한다.
배포 구조 관점에서도 각 서비스는 독립된 서버로 타 컴포넌트와의 의존성 없이 독립적으로 배포된다.
예를 들어 사용자 관리 서비스는 독립적인 war 파일로 개발되어 독립된 톰캣 인스턴스에 배치된다. 확장을 위해서 서비스가 배치된 톰캣 인스턴스는 횡적으로 스케일이 가능(스케일 아웃)하고, 앞단에 로드 밸런서를 배치하여 서비스 간의 로드를 분산시킨다.
데이터베이스의 종류 자체를 다른 데이터베이스로 사용할 수도 있지만, 같은 데이터베이스를 사용하더라도 DB를 나누는 방법을 사용한다. 이 경우 다른 서비스 컴포넌트에 대한 의존성 없이 서비스를 독립적으로 개발 및 배포/운영할 수 있다는 장점을 가지고 있으나, 다른 컴포넌트의 데이터를 API 통신을 통해서만 가지고 와야 하기 때문에 성능상 문제를 일으킬 수 있고, 이 기종 데이터베이스 간의 트랜잭션을 묶을 수 없다는 문제점이 있다.
API Gateway API Gateway는 마치 proxy서버처럼 API들 앞에서 모든 API에 대한 엔드포인트를 통합하고 몇 가지 추가적인 기능을 제공하는 미들웨어다. (클라우드에서 작동하는 PaaS형태의 서비스로는 apigee.com이나 3scale.com 등이 있고, 설치형 제품으로는 CA사의 Layer7, Apache Service Mix, MuleSoft의 ESB 제품 그리고 WSO2의 API Platform 등이 있다.)
마이크로 서비스 아키텍처의 문제점 중 하나는 각 서비스가 다른 서버에 분리,배포되기 때문에 API의 엔드포인트, 즉 서버의 URL이 각기 다르다는 것이다.
사용자 컴포넌트는 http://user.server.com, 상품 컴포넌트는 http://product.server.com과 같은 분리된 URL을 사용하는데, 이는 API 사용자 경험 관점에서도 사용하기가 불편한다. 특히 마이크로 서비스 아키텍처는 될 수 있으면 컴포넌트를 업무 단위로 잘게 자르는 작은 덩어리의 서비스를 지향하기 때문에 컴포넌트의 UIRL 수는 더 많이 늘어날 수 있다.
API를 사용하는 클라이언트에서 서버 간의 통신이나 API 통신은 P2P형태로 토폴리지가 복잡해지고 거미줄 모양의 서비스 컴포넌트 간 호출 구조는 향후 관리의 문제를 일으킬 수 있다. 하나의 엔드포인트를 변경하였을 때 제대로 관리가 되지 않을 경우가 있다.
이러한 토폴로지의 문제점을 해결하기 위해 중앙 서비스 버스와 같은 역할을 하는 채널을 배치 시켜서 전체 토폴로지를 P2P에서 Hub & Spoke 방식으로 변환시켜서 서비스 간 호출을 단순화 할 수 있다.
오케스트레이션 마이크로 서비스 아키텍처 서비스 자체가 작은 덩어리 형태로 잘게 쪼개졌기 때문에 여러 개의 서비스를 묶어서 새로운 서비스를 API Gateway를 통해 구현할 수 있다.
사실 오케스트레이션을 API Gateway 계층에서 하는 것은 게이트웨이 입장에서 부담되는 일이다. 과도한 오케스트레이션 로직은 전체적인 성능 문제를 유발한다.
공통 기능 처리(Cross Cutting Function Handling) API에 대한 인증이나 로깅과 같은 공통 기능에 대해서 서비스 컴포넌트 별로 중복 개발해야 하는 비효율성을 유발할 수 있다. API Gateway에서 이러한 공통 기능을 처리하게 되면, API 자체는 비즈니스 로직에만 집중하여 개발 중에 발생할 수 있는 중복을 방지할 수 있다.
배포 마이크로 서비스 아키텍처의 가장 큰 장점 중의 하나가 유연한 배포 모델이다. 각 서비스가 다른 서비스와 물리적으로 완벽하게 분리되기 때문에 변경이 있는 서비스 부분만 부분 배포가 가능하다.
확장성 부하가 많은 특정 서비스에 대해서만 확장할 수 있어서 조금 더 유연한 확장 모델을 가질 수 있다.
성능 서비스 간의 호출을 API 통신을 이용하기 때문에 값을 JSON이나 XML에서 프로그래밍에서 사용하는 데이터 모델(자바 객체 등)로 변환하는 마샬링 오버헤드가 발생하고 호출을 위해서 이 메시지들이 네트워크를 통해서 전송하기 때문에 그만큼 시간이 추가로 소요된다.
메모리 각 서비스를 독립된 서버에 분할 배치하기 때문에 중복되는 모듈에 대해서 그만큼 메모리 사용량이 늘어난다. 예를 들어, 하나의 톰캣 인스턴스에서 사용자 관리와 상품 관리를 배포하여 운용할 경우, 하나의 톰캣을 운영하는 데 드는 메모리와 스프링 프레임워크와 같은 라이브러리를 사용하는 데 소요되는 메모리, 그리고 각각의 서비스 애플리케이션을 기동하는 메모리가 필요하다.
그러나 마이크로 서비스 아키텍처로 서비스를 배포하면 사용자 관리 서비스 배포와 상품 관리 서비스 배포를 위한 각각의 톰캣 인스턴스를 운용해야 하고, 스프링 프레임워크와 같은 공통 라이브러리도 필요하기 때문에 배포하고자 하는 서비스의 수만큼 중복된 양의 메모리가 필요해진다.
위의 두 문제는 반드시 발생하는 문제점이기는 하나 현대의 인프라 환경에서는 컴퓨팅 파워가 워낙 발달하였고 네트워크 인프라 역시 발전하면서 큰 문제가 되지 않는다.
테스팅이 더 어려움 서비스들이 분리되어 있고 다른 서비스에 대한 종속성을 가지고 있어서 특정 사용자 시나리오가 기능을 테스트하고자 할 경우 여러 서비스에 걸쳐서 테스트를 진행해야 한다. 이 때문에 테스트 환경 구축이나 문제 발생 시 분리된 여러 개의 시스템을 동시에 봐야 하기 때문에 테스팅의 복잡도가 올라간다.
서비스 간 트랜잭션 처리 구현상의 가장 어려운 점 중의 하나가 트랜잭션 처리다. 모노리틱 아키텍처에서는 RDBMS를 사용하면서 하나의 애플리케이션 내에서 트랜잭션이 문제가 있으면 쉽게 데이터베이스의 기능을 이용해서 롤백할 수 있었다. 여러 개의 데이터베이스를 사용하더라도 분산 트랜잭션을 지원하는 트랜잭션 코디네이터 등을 이용해서 쉽게 구현할 수 있었으나, API 기반의 여러 서비스를 하나의 트랜잭션으로 묶는 것은 불가능하다.
마이크로 서비스 아키텍처의 경우 금융이나 제조와 같이 트랜잭션 보장이 중요한 엔터프라이즈 시스템보다는 대규모 처리가 필요한 B2C 형 서비스에 적합하기 때문에 아키텍처 스타일 자체가 트랜잭션을 중요시하는 시나리오에서는 적절하지 않다.
그럼에도, 트랜잭션 처리가 필요할 경우 트랜잭션 실패 시 이를 애플리케이션으로 처리해 줘야 하는데, 이를 보상 트랜잭션이라고 한다. 앞의 계좌 이체 시나리오에서 말했듯이 출금하고 나서 다른 계좌로 이체하던 중 에러가 발생했을 때 명시적으로 돈을 원래 계좌로 돌려주는 에러 처리 로직을 구현해야 한다.
데이터 분리 데이터 저장 관점에서는 중앙 집중화된 하나의 통 데이터베이스를 사용하는 것이 아니라 서비스별로 별도의 데이터베이스를 사용한다. 보통 모노리틱 서비스는 하나의 전체 데이터베이스(보통 RDBMS)를 사용하는 경우가 일반적이지만, 마이크로 서비스 아키텍처의 경우 서비스가 API에서부터 데이터베이스까지 분리되는 수직적 분할 원칙에 따라서 독립된 데이터베이스를 가진다.