2015. 12. 27.

Microservice에 대한 나만의 정리

서론


이글을 쓰고 있는 나는 마이크로서비스의 전문가가 아니다. 마이크로서비스는 커녕 마이크로서비스 이전에 나왔던 Service-Oriented Architecture(SOA) 조차도 설계하거나 구현하거나 적용해 본적이 없다.

그저 최근 1~2년간 뉴스리더나 각종 컨퍼런스에서 마이크로서비스라는 말을 들었고 흥미로운 접근 방식이라 생각했으며, 최근 springframework의 spring-cloud 프로젝트를 가지고 파일럿을 진행하는 과정에서 좀 더 관심이 생겼을 뿐이다.

파일럿을 진행하다보니 개념적으로 정립이 안되어있어서 그런지 혼란이 올때가 많이 있다.

그래서 생각을 좀 정리할 겸 마틴파울러의 2014년 마이크로서비스에 대한 글을 참고로하여 그 개념을 정리하려고 이 글을 쓰게 되었다. [원문 : Microservices - a definition of this new architectural term]

전문가는 커녕 굉장히 얕은 지식으로 이해를 하다보니 잘 못 이해한 부분도 있고, 영어실력이 그리 뛰어나지 않아 오해한 부분이 있을지도 모르겠다.

마지막으로 이 글은 일부 마틴파울러의 글을 번역해서 사용했지만, 전문 번역을 한 글이 아님을 밝힌다.


마이크로서비스란 무엇인가?


마이크로서비스 패러다임은 아래와 같다.

  • 작은 기능을 하는 독립된 작은 서비스를 독립된 어플리케이션으로 개발한 것이다.
  • 독립된 어플리케이션들은 다른 어플리케이션과 통신하여 좀 더 복잡한 서비스를 만들 수 있다.
  • 어플리케이션간의 통신은 경량 매커니즘으로 된 통신 방법을 이용해야 한다.
  • 독립적인 비즈니스 기능을 가지고 있어야 하고 독립적이면서 자동화된 방식으로 배포가 가능해야 한다.
  • 최소한의 중앙집중식 관리시스템에 의해서 관리가 가능해야 한다.
  • 서로 다른 기술로 만들어질 수 있어야 하고 독립된 저장소를 가질수도 있다.


뭔가 좋아는 보이지만 개념이 확 와닿지 않는다(최소한 나 같은 경우는 그랬다).

Monolithic service 와 비교를 통해 좀 더 알아보자.
우선 그림 하나를 보고 시작해 보겠다.

Monoliths VS Microservice




이 그림은 많이 봤을 것 같다. 거의 모든 자료나 발표자들이 설명에 사용하는 그림이니 말이다.

모놀리스 어플리케이션(Monolithic Application)개발 방식은 우리가 가장 많이 사용하고 있는 방식이다.

모놀리스 어플리케이션의 특징을 간단히 정리하면 다음과 같아.

  • 보통은 하나의 언어에 의해 개발된다.
  • 모든 리퀘스트는 하나의 프로세스에서 처리되어진다.
  • 언어에 따라 도메인 로직은 클래스나 펑션, 패키지 등으로 구분하여 개발을 진행한다.
  • 개발이 완료되면 전체 로직들에 대한 테스트가 진행되고 전체 프로그램이 빌드되서 서버에 배포된다.
  • 물리적인 서버 또는 가상화 서버에 동일한 인스턴스 전체가 배포되는 것으로 수평확장되며, 확장된 인스턴스들은 Loadbalancer 뒤에서 동작하게 된다.

모놀리스 어플리케이션은 지금까지 매우 훌륭하게 동작을 해 왔다.
예를 들어서 예전 게임 회사에 있을 때는 동일한 웹을 처리하는 여러대의 서버가 있었고, 그 서버들을 L4 스위치 를 이용해서 하나로 묶음으로써 많은 사용자를 처리할 수 있었다. 사용자가 적을 때는 서버를 L4에서 분리해 놓고 대기시키면 되고, 많을 때는 L4에서 분리된 서버를 다시 연결하거나 추가로 서버를 더 준비하고 추가하는 방식으로 처리가 되었다.

그런데, 최근의 개발의 패러다임이 변하면서 혼란이 생기기 시작한것이다.
여기서 변화라는 것은 여러가지가 있겠지만, 개인적인 생각에 가장 큰 변화는 애자일 개발 방법과 클라우드 서비스 두가지가 아닐까 생각한다.
사용자들의 요구사항은 더 빠르게 변화하기 시작했고, 더 작은 단위의 개발 수정이 일어나기 시작했고, 더 잦은 배포가 필요해지기 시작했다. 배포할 때마다 전체 어플리케이션은 다시 테스트되고 다시 빌드되고 다시 배포가 이루어져야 한다. 단지 하나의 모듈에만 수정이 이루어졌음에도 불구하고 전체 프로그램에 대해서 이 모든 일이 이루어져야 한다는 것이다. 서비스가 커질수록 빌드하고 테스트하고 배포하는 시간은 늘어나기 시작했다.(외국의 어떤 회사 같은경우는 빌드해서 배포까지 걸리는 시간이 2시간이 넘기도 한다고 한다)
또한, 확장을 하기 위해서는 전체에서 극히 일부분의 리소스가 부하를 가지고 가게 된다고 해도 전체 어플리케이션을 확장해야 한다.
클라우드로 서비스를 만들어 본 적이 없어서 정확한 부분은 잘 모르겠지만, 클라우드 서비스 중 서버를 이용하는 경우 서버 인스턴스를 늘리는 것은 굉장히 쉬운 것으로 알고 있다. 하지만, 일부 모듈의 부하를 위해서 전체 어플리케이션이 배포되고 확장된다는 것이 클라우드 서비스에서는 더 낭비요소가 심하지 않을까 하는 생각을 해본다.

마이크로서비스는 이런 혼란을 해결하기 위해 나온 개념이다.
위 그림 중 우측의 그림처럼 수평 확장은 개별 서비스 모듈 별로 가능하고, 요구사항이나 새로운 기능이 추가될 경우에도 해당 변경이 필요한 극소(모놀리스에 비해서)의 모듈에만 영향을 줄 수 있으며, 배포 또한 개별적으로 하는 것으로 앞에서의 혼란을 해결하려 한 것이다.

확장과 관련해서는 마이크로서비스의 확장을 X, Y, Z 축 확장으로 설명하곤 한다.

Microservice의 특성


그렇다면 좀 더 자세히 마이크로서비스의 특성을 한번 짚어봐야 하지 않을까?
마이크로서비스는 그 필요에 따라서 정말 다양한 구현 방식이 있고, 따라서 그 특성을 규정하기가 쉽지 않다. 그저 작은 서비스로 만들어야 한다는 것과 경량화 매커니즘에 의한 통신을 해야 한다 정도 외에 나머지는 필요에 의해서 만들어지는 것이다보니 그 구현에 따라서 특성도 제각각일 수 있다. 실제로 자료를 찾아보다보니 설명하는 사람들마다 많은 규칙을 정하고 그에 따라 구현방식을 이야기하면서 더 많은 특성을 정의하고 있었다. 내가 마틴파울러의 자료를 기본으로 설명하고 있는 이유가 이것인데, 마틴파울러는 이 부분에 대해서 가장 기초적인 특성이라고 볼 만한 것들을 정리해 놓았다.

Componentization via Services (서비스에 따른 모듈화)


우선은 마이크로서비스를 설명하기 위한 몇가지 개념을 먼저 정의해보자.
  • Component : 독립적으로 치환 가능하고, 변경(또는 업그레이드)가 가능한 소프트웨어 유닛
  • Library : 프로그램 내에서 링크되어 실행(In-memory function calls)되는 Component
  • Service : web service request나 remote procedure 등의 방식에 따라 통신하는 개별 Component. Service 는 독립적으로 배포가 가능해야 한다.
Service와 Library의 가장 큰 차이점은 독립적인 배포에 있다. 즉 여러개의 Library를 사용하는 application의 경우 library의 일부가 변경되는 행위는 전체 application에 영향을 미치게 된다. 하지만, service 별로 모듈화 되어있는 경우 특정 서비스가 변경이 이루어지는 경우 해당 service 에만 영향을 미치게 된다. 물론 service로 모듈화 된 경우에도 특정 service가 변경되는 경우 해당 service 를 이용하는 다른 service에 영향이 미치지 않는 것은 아니다. 단, 잘 설계된 마이크로서비스라면 이 영향을 최소화 할 수 있게 설계되어 있다는 것이다.
이쯤에서 나오는 개념이 cohesive service boundaries(응집된 서비스 영역)와 explicit component interface(명확한 모듈간 인터페이스)이다. 즉, 서비스의 영역을 잡을 때 최대한 외부 영향이 적은 범위에서 응집된 서비스 영역을 잡아야 하고, remote call interface를 정의함에 있어서는 명확하게 정의를 해야한다는 개념인데, 이 부분이 굉장히 어려운 개념이라고 생각한다.
특히 remote call interface를 정의하는 부분은 마이크로서비스의 단점이기도 하다. 아무래도 통신에 의한 프로그램 실행은 in-memory function call 에 의한 실행보다 비용이 많이 드는 부분이므로 이 부분을 염두에 두고 서비스를 모듈화 할 필요가 있다.

Organized around Business Capabilities (비즈니스 능력에 따른 조직구성)


보통 소프트웨어 개발 조직을 구성할 때 UI 팀, Server logic 개발팀, database 팀 등으로 나누는 경우가 일반적이다. 물론 우리나라도 요즘은 이런 형태를 탈피한 경우가 많이 있지만, 여전히 많다.
이쯤에서 Conway's Law 라는 법칙 한가지를 소개해보자.
Any organization that designs a system (defined broadly) will produce a design whose structure is a copy of the organization's communication structure.

시스템을 설계하는 조직들은 자신들의 조직 사이에 의사소통 구조(communication structure)를 모방한 형태의 설계를 만들곤 한다.

    -- Melvyn Conway, 1967
이 법칙은 여러가지 의미로 해석되고 적용이 가능하지만, 여기서는 조직 구성에 따라 해당 조직이 만들 수 있는 시스템이 정해진다는 의미가 가장 클 것으로 보인다. 기존의 개발조직의 문제점은 한가지 제품을 만들기 위해서는 여러팀이 협업을 해야 하고 협업에 필요한 시간과 비용이 많이 든다는 점이 있다.
마이크로서비스에서는 큰 문제가 될 수 있으며, 이 부분을 해결하기 위해서 복합 기능을 할 수 있는 팀을 제안하고 있다. 즉, 제품을 만들기 위해 일반적으로 필요한 디자인, 데이터베이스, 프로그래밍, 테스트, 시스템 운영이 가능한 팀을 구성해야 한다는 것이다. 또한, 각 팀을 구성할 때 기능이 아닌 비즈니스 기능에 맞춰서 팀을 구성해야 하다는 것이다.
뒤에 나오는 특성과 연결되지만, 하나의 팀이 자신들이 만든 제품에 대해서 모든 책임과 권한을 가져야 한다는 마이크로서비스의 특성을 위해서도 조직구성이 구현하려는 비즈니스 기능에 맞도록 구성될 필요가 있다.

Products not Projects (프로젝트가 제품은 아니다)


보통의 project model에서 볼 수 있는 형태는 개발팀에서 일정 프로젝트를 개발하여 완료하고 완료된 결과물은 운영팀이라고 불리는 조직에게 인계되는 것이다. 이 때부터 결과물은 개발팀을 떠나 운영조직에서 관리하게 된다.

마이크로서비스를 제안했던 사람들은 이 구조를 지양하고 개발하는 조직에서 모든 제품의 라이프사이클을 담당해야 한다고 이야기한다. 아마존의 "You build, you run it"의 개념이 이 생각을 대표한다고 할 수 있다.

이는 애자일 개발방법론에서도 자주 이야기되는 사용자 중심의 서비스 개발과도 통하는 개념으로 보이는데, 즉 개발을 담당하는 조직에서 사용자의 의견을 들으며 서비스를 운영하고 발전시켜 나가야 한다는 부분이라 하겠다. 또한, 이는 앞에서 이야기했던 서비스나 비즈니스 능력에 따른 조직구성과도 통하는 부분이라 할 수 있다. 개발하는 조직에서 서비스 또는 제품에 대한 모든 권한과 책임을 가지고 진행을 해야 한다는 것이다.

그렇다면 모놀리스 서비스에서는 이 부분이 적용이 되지 않을 것인가? 마틴 파울러는 꼭 그런것은 아니라고 이야기한다. 단지, 작은 서비스로 잘 조직화 되어있는 경우에 이 부분을 적용하기 쉽고 개발조직과 사용자 간의 관계를 만들기가 더 편하다고 이야기하고 있다.

Smart endpoints and dumb pipes


달리 한국어로 번역하기가 매우 힘든 특성이라 그냥 영어 그대로 제목을 달았다.
지금까지 알아본 특성들을 보면서 어디선가 많이 본 특성들이라고 생각했던 사람들이 있을 것이다.
그렇다. SOA(Service-Oriented Architecture)의 특성이나 개념과 매우 비슷하다. 아니, 어쩌면 같은 개념이다.

그럼 SOA 를 적용하면 되지 왜 마이크로서비스 아키텍처를 또 이야기하고 있는 것일까?
SOA를 구현해서 적용해 본 적이 없어서 완벽하게 이해할 수는 없지만, SOA 가 결국 실패했던 이유를 그 중심을 이루는 ESB(Enterprise Service Bus) 때문이라고 이야기하는 사람들이 많다.

특성상 서비스들이 여러개로 나뉘고 파편화되어 결국 서로간의 통신을 만들어서 처리해야 하는데, 이 때에 시간이 갈수록 더욱 힘들어지는 부분이 ESB 때문이라는 설명들이다. 또한, 필연적으로 추가되는 다양한 솔루션들과 장비들이 결국 ESB를 이용한 SOA를 구성하는데 어려움을 가져오게 되고, 결국 많은 조직과 실력있는 사람들 그리고 충분한 자금과 협력업체를 가지지 못한 업체들은 도입할 수 없는 시스템이 되고 말았다고도 들었다.

마치, 20여년전 쯤에 유행했던 EJB를 보는 듯한 느낌이다. 당시 획기적인 아키텍처로 각광 받았던 EJB가 결국 퇴출된 것처럼 SOA 도 최근에는 사람들의 관심에서 멀어지고 있는 듯이 보인다.

여기서 Springframework이나 Webwork같은 경량 프레임워크들이 EJB의 대안으로 등장했던 것처럼 SOA의 대안으로 등장한 것이 Microservice 이다. 일각에서는 Microservice를 SOA의 하나로 이야기하는 경우도 있는데 나는 일정부분 찬성하고 일정부분 반대한다.

아무튼 Microservice 는 SOA와 어떤 차별점을 가지면서 제안되었던 것일까?

이 차별점으로 등장하는 특성이 Smart endpoints 와 dumb pipes 이다.

Microservice의 서비스들은 앞에서도 설명했듯이 가능한 최대로 분리되어져야 하고  응집된 형태로 개발되어야 한다. 각 서비스들은 독자의 비즈니스로직을 소유해야한다. 서비스에 들어온 요청은 적절한 비즈니스 로직을 적용한 후에 응답으로 생산되어져야 한다.  이러한 일련의 동작들은 마치 유닉스의 필터와 같은 형태로 동작이 이루어져야 한다. (혹시 유닉스 명령을 안다면 cat log.txt | grep 2015-12-12 와 같은 형태를 아실 것이다. 완벽하게 동일한 개념은 아니겠지만, cat이나 grep의 경우는 서로 각 프로그램을 알지 못하지만 grep은 cat 어플리케이션에게서 적절한 결과를 받아서 본인이 최종 응답을 만들어 낸다. 아마도 이런 형태의 역할을 이야기하는 것으로 보인다. 내가 이해한 부분은 최소한 그렇다. 아마도 이것을 smart endpoint라고 표현한 것은 아닐까.)

이런 서비스간의 통신은 이전에 사용됐던 WS-Choreography 나 BPEL 또는 중앙집중적인 툴에 의해 관리되는 그런 통신 방식이 아닌 좀더 RESTish한 프로토콜에 의해 이루어져야한다.

보통 이런 프로토콜로는 두가지의 프로토콜이 제안되고 있는데 Resource API 를 이용한 HTTP Request/Response 프로토콜과 경량 Message Protocol 이다.

첫번째 프로토콜을 가장 잘 설명하는 표현으로 마틴파울러는
Be of the web, not behind the web
-- Ian Robinson
을 들고 있다. 즉, 웹 뒤에서 하지 말고 웹으로 하라는 말이다.
웹으로 만들어진 프로토콜을 이용하면, 한번 사용된 리소스를 캐싱할 수 있고 무엇보다도 이미 월드와이드웹에 모두 구현이 되어있다는 것이 장점이다.

두번째 프로토콜로는 경량화 메시지 버스(lightweight message bus)이다.
RabbitMQ 라던가 ZeroMQ 등을 통해서 구현되는 이런 메시지 버스는 비동기방식의 메시지 라우터 이상의 일을 해서는 안된다. 단순히 메시지 라우터로서의 역할을 하며 smart한 부분은 여전히 endpoint에 그 역할이 있다는 것이다.

이 부분을 길게 설명한 이유는 모놀리스에 비해서 마이크로서비스가 가지는 가장 난해하고 단점이 될 수 있는 부분이기 때문이다. 뭐, 서비스가 여러개의 어플리케이션으로 분리되는 과정에서 필연적으로 발생할 수 밖에 없는 부분이겠지만, 어쨌건 In-memory call에 의해 구현되어져 있는 부분을 RPC 로 변경해야 한다는 이야기이기 때문에 자칫하면 SOA가 퇴보할 수밖에 없었던 것과 같은 길을 걷게 될 가능성도 있는 것이다. 마틴 파울러의 이야기를 빌리면 fine-grained communication을 coarser-grained communication으로 변경하는 일인 것이다.

따라서 이 부분은 마이크로서비스를 설계함에 있어서 서비스를 분리해서 모듈화하는 것 다음으로 가장 중요한 부분이 되지 않을까 싶다.

Decentralized Governance (분산 지배구조)


중앙 지배구조라는 것은 하나의 테크놀로지를 이용하여 구현된 하나의 시스템의 특성이다.
테크놀로지의 하나로 볼 수 있는 언어를 예로 들면 동일 언어로 구현된 시스템을 이야기하는 것이다. 마틴 파울러는 모든 문제가 못일 수 없고 모든 해결법이 망치일 수 없다는 말로 표현하고 있다.
(Not every problem is a nail and not every solution a hammer)

잠깐 이야기한 것처럼 분산 지배구조라는 이상한 특성은 요즘 흔히 이야기하는 polyglot을 이야기한다. 하나의 커다란 Monolith component를 여러개의 서비스 단위의 component로 나눌때는 선택의 가능성이 생긴다는 이야기다. 서비스마다 가장 최적이라고 생각하는 solution이나 tool을 찾어서 적용할 수 있다는 것이다. 만약 어떤 서비스는 간단하게 node.js를 사용해서 개발할 수 있다면 그렇게 하고, C/C++이 최적이라면 그렇게 하고, 그것이 자바라면 자바로 개발을 하면 된다. 우리가 만드는 서비스에 적합한 데이터베이스가 기존의 시스템이 사용하던 오라클이 아니라 mongoDB 라면? 역시 그것을 이용해서 서비스를 만들면 된다. 물론 앞에서 서비스별로 개발조직을 정비했다면 해당 개발조직이 가장 잘 할 수 있는 것을 찾는 것도 하나의 방법이 되겠다.

이런 분산 지배구조는 해당 조직이 전체 서비스에 대한 종합적인 권한과 책임을 가질 수 있다는 앞에서의 조직구성과 관련된 특성으로도 연결이 된다. 우리 조직이 만든 제품을 우리가 책임지고 있기 때문에 우리가 선택하는 솔루션이나 툴에도 책임지고 관리가 가능한 것이다.

만약 우리가 다른 서비스에 요청하여 받아오는 결과물의 Contracts가 바껴서 우리가 서비스하는 시스템에 문제가 생긴다면? 그 서비스를 개발하는 팀에 기능 추가를 요청하거나, 아니면 우리가 변경사항에 맞게 우리 서비스를 수정해서 다시 서비스를 해야한다. 물론 상대편도 자기들의 제품을 사용하고 있는 사용자들을 위해 개발을 해야하겠지만, 보통은 후자가 되지 않을까 싶다.

Decentralized Data Management (분산 데이터 관리)


분산 지배구조에서도 설명했지만, 분산 데이터 관리 역시 각 서비스의 관점에서 다양한 데이터 관리 즉, 데이터 저장소 기술을 이용할 수 있다는 것이다. 앞 특성에서의 polyglot이 주로 언어적인 개념에 대한 설명이라면 polyglot persistence 에 대한 특성이라 하겠다.

물론 Monolith 에서도 polyglot persistence 를 사용할 수 있지만 일반적이지 않고, 마이크로서비스에서 더 많은 빈도로 나타나게 된다. 물론, 마이크로서비스라고 모두 polyglot persistence를 써야 하는 것도 아니고 말이다.

이 특성은 아래의 그림을 보면 이해가 빠르지 싶다.

이 특성에서의 중요한 부분은 사실 트랜잭션 처리와 관련된 부분이다.

각 서비스마다 다른 데이터베이스를 가지고 있으며, 같은 데이터베이스를 가지고 있다고 해도 처리하는 로직이 분리되어있는 상황에서 트랜잭션의 보장은 어떻게 할 것인가?
보통 Monolith 에서는 모듈별로 나누어진 서비스라 하더라도 하나의  트랜잭션을 공유하여 처리할 수 있도록 로직을 개발하여 데이터의 일관성을 보장하게 된다.
하지만, 마이크로서비스에서는 서비스가 분리된 상황에서 분산 트랜잭션을 적용해야 하는데 이는 대단히 힘들고 악몽같은 일이 될 수도 있다. 분산트랜잭션의 퍼포먼스 문제는 유명하니까 말이다. 즉, 마이크로서비스에서의 일관성 확보 전략에는 차이가 있을 수 밖에 없다.

마이크로서비스에서는 서비스 간에 분산 트랜잭션을 사용하지 않으면서 일관성을 유지하는 방법을 강조하고 있다. 즉, 일관성은 최종적인 일관성만을 보장하며, 이 것은 문제가 발생할 경우 조정 처리를 통해서 다루어져야 한다는 것을 인식고 일관성 보장수준을 결정해야 한다. 즉, 문제가 발생할 경우 사후 처리 또는 적절한 처리를 통해서 전체적인 일관성을 보장하도록 만들어야 한다는 것이다.
이 부분이 기존에 Monolith를 개발하던 조직에선 절대 용납이나 이해가 안되는 부분일 수 있다.(실제로 내가 다니는 회사에서 잠깐 마이크로서비스를 설명할 기회가 있었는데 이 부분에 대해서 설명하는 것이 가장 힘들었다. 결과적으로 실패했고, 아마도 앞으로도 힘들 수 있다. 확실한 예제를 만들어내기 전까지는...)
마틴 파울러 조차도 이 부분이 기존 개발팀에게 가장 큰 도전이 될 것이라고 이야기하고 있다.
하지만, 완벽한 데이터 일관성을 위해 비즈니스 기회를 놓치기 보다는 사후 처리를 통한 처리 딜레이가 생기더라도 비즈니스 기회를 획득하는 것이 더 중요하다는 설명으로 마무리 하고 있다. (The trade-off is worth it as long as the cost of fixing mistakes is less than the cost of lost business under greater consistency.)

Infrastructure Automation (인프라스트럭처의 자동화)


우선 일반적인 배포 자동화 솔루션의 빌드 파이프라인의 그림부터 보자.

어! 이건 모놀리스 에서도 하는 것 아닌가? 맞다.
모놀리스도 요즘은 이런 CD(Continuous Delivery)를 통해서 많은 이득을 얻고 있고 당연하게 여기기도 한다.  다만, 마이크로서비스 같은 경우는 좀 더 필수적인 요소로 자리한다는 점이다.
작은 서비스를 구성하고 사용자의 요구사항에 맞춰서 더 잦은 빌드와 배포가 이루어지게 되고 또한 클라우드를 사용하게 된다면 더 많은 자동화 툴과의 연계가 필요해 질 수도 있다.
가장 큰 차이점이라면 하나의 커다란 시스템을 배포하는 것이 아니라, 작은 모듈들이 개별적으로 배포되어진다는 부분으로 이 부분의 자동화가 구축되어있지 않다면 굉장히 지루한 작업들의 연속이 될 것이다.
두 시스템의 배포하는 모습을 간단히 표현한 그림은 다음과 같다.


대부분의 사람들이 알고 있듯이 CD의 전도사이기도 한 마틴파울러이지만 마이크로서비스를 설명함에 있어서 CD를 설명하는 것에는 말을 아끼고 있는데, 아마도 CD를 너무 당연하다고 생각하고 있는 것은 아닐까 싶다.

Design for failure (실패 또는 오류에 대한 설계)


이 특성은 역시 모놀리스 어플리케이션에서도 중요할 수 있는 부분이지만, 역시 마이크로서비스에서 특히 중요한 부분이다.

서비스 모듈들이 서로 통신하여 요청과 응답을 처리하는 과정에는 In-memory function call 보다도 더 많은 오류의 경우가 발생할 수 있다. 서버가 정상 상태가 아니라던가 특정 서비스가 Memory leak으로 죽는다거나 특정 서비스에 사용자가 몰려서 응답을 처리할 수 없다거나 특정 서버로 가는 네트워크에 문제가 생기는 등의 모놀리스에서는 발생하지 않을만한 오류들도 많이 발생한다.

이 특성 역시 모놀리스보다 마이크로서비스 개발의 복잡성을 증가시킬 수 있는 특성이다.

이런 오류가 발생할 때 사용자에게는 최대한 우아하게(graceful) 오류를 처리해서 보여줘야 하기 때문에 실패시의 오류 처리가 중요하다. 특정 서비스가 오류를 발생했다고 전체 정보가 모두 오류가 발생한다면 안된다.

특히 서비스 중단에 가까운 오류가 발생한 서비스에 계속해서 요청을 보내는 것으로 사용자에게 불편을 줘서도 안된다. 예를 들어 RestTemplate으로 통신하는 서버가 현재 응답을 못하고 있다고 해보자. 오류를 감지한 시점에서도 계속해서 요청을 보낸다면 사용자는 Connection Timeout이 발생할 때까지 무작정 대기해야 하는 문제가 발생할 것이다. 오류를 감지했다면 해당 서비스가 정상이 될 때까지는 요청을 보내지 말고 바로 오류처리를 진행해줘야만 사용자의 불편을 최소화 할 수 있다.

물론 무엇보다 중요한 것은 오류가 발생했을 때 빠르게 대응해서 오류를 신속히 복구하고 되도록이면 사전에 오류가 날만한 징조를 캐치해서 사전 대응을 하는 것이다. 내 서비스가 죽어서 다른 서비스들이 어려움을 겪는다면 안되지 않겠는가?

마지막으로 이런 오류가 발생해서 오류를 처리하고 해당 서비스에 대한 차단을 했다면, 오류가 복구되고 정상 서비스가 될 때 자동으로 모든 서비스가 함께 복구가 이루어져서 사용자에게 정상 서비스를 진행할 수 있도록 하는 것도 필요하다.

이런 전반적인 오류에 대한 처리를 위해 다양한 기술들이 선보인다.
Circuit breaker 를 이용해서 오류가 발생한 서비스를 분리하고, 해당 서비스가 다시 복구 됐을 때 정상 서비스를 제공하는 기술이라던가, fallback 메소드를 이용하여 오류가 발생한 경우 사용자들에게 우아하게 오류 상황을 전달한다던가, 각종 로그와 시스템 상황을 모니터링 할 수 있도록 해주는 대쉬보드라던가 하는 다양한 기술들이 필요하다.

사실 실제로 마이크로서비스를 특정 프레임워크를 이용해서 구현해보고자 한다면 초반에 이런 기술들이나 시스템들에 대한 이해를 진행하다가 좌절할 수도 있다. 예를 들어 Spring-cloud 프로젝트와 Netflix OSS를 적용하고자 한다면, 관련 인프라 서버만 해도 여러개가 들어가게 되며, 이 경우 각 서버의 기능과 적용방법을 알아가는데도 많은 노력이 필요하다. Spring-cloud가 많은 부분을 간편하게 만들어줬음에도 불구하고 말이다.

결론적으로 마이크로서비스에서는 이런 모니터링, 오류 처리, 복구와 관련된 다양한 설계가 필요하며 이것이 또한 마이크로서비스의 하나의 특성이라 하겠다.

Evolutionary Design (진화하는 디자인)


마이크로서비스를 적용하는 사람들은 진화하는 디자인에 대한 배경지식을 가지고, 변화가 늦춰지지 않도록 적절한 툴을 통해 변경을 관리해서 소프트웨어가 진화할 수 있도록 해야 한다.
여기서 말하는 변경 관리는 변경이 일어나는 경우를 줄이는 것이 아니라, 변경이 더 빠르게 자주 일어날 수 있도록 적절한 툴과 대응을 할 수 있도록 해주는 관리이다. 서비스를 모듈화 하는 작업을 할 때도 이 원칙이 지켜질 수 있도록 해야만 한다.

마이크로서비스를 적용하기 위해 서비스를 모듈화할때 문제는 어떤 조건 또는 어떤 규칙으로 서비스를 나눠야 하는가이다. 이때 가장 기본적으로 적용가능한 규칙은 독립적인 대체성(replaceability)과 업그레이드성(upgradeability) 이다.

마틴 파울러는 가디언지의 웹사이트를 예로 들고 있다. 가디언지의 웹사이트는 기본적으로 모놀리스로 개발되어져서 운영되고 있다. 하지만, 특정 스포츠 이벤트 등이 있을 때는 이 모놀리스의 API를 이용하여 가장 빠르게 개발 가능한 기술로 이벤트 페이지를 만든다. 그리고 그 스포츠 이벤트가 종료되면 해당 페이지는 손쉽게 제거된다.

특히 대체성 측면에서의 주안점은 일반적인 모듈화 규칙에 특별한 경우를 규정할 수 있다는 것이다. 만약, 어떤 변경이 일어날 때마다 두개 이상의 모듈에 영향을 준다고 보자. 변경이 반복될 때마다 같은 현상이 벌어진다면 해당 모듈들은 하나로 합쳐야 한다는 것을 보여주는 것이다.

잦은 변경은 마이크로서비스의 장점이다. 변경된 모듈만 새로 빌드해서 배포하면 되기 때문에 전체 시스템을 배포해야 하는 모놀리스에 비해서 빠른 빌드와 배포가 가능하다. 다만, 여기서 발생하는 단점이라면 해당 변경이 우리 서비스를 이용하는 시스템의 오류 가능성을 증가시킬 수 있다는 점이다. 전통적인 Integration 접근 방법에서는 버전을 통해서 해결을 하려고 한다. 하지만, 마이크로서비스에서는 버전 부여방식은 최후의 수단으로서만 이용해야 한다. 최대한 서비스 사용자(또는 시스템)에 문제가 없도록 개발하고 디자인함으로써 수많은 버전을 만들어내는 것을 피해야만 한다.

지금까지 마이크로서비스의 특성들에 대해서 알아보았다. 그렇다면 마이크로서비스는 정말 미래를 책임질 접근방식이 될 것인가?

Are Microservices the Future?


마틴 파울러는 이 부분에 대해서는 평가를 유보한 것으로 보인다.
아마존, 넷플릭스, 가디언 등등 이름만 들으면 알만한 기업과 또는 미국에서만 유명한 기업들 중에 많은 기업들이 이 방식으로 서비스를 개발하고 있고, 마틴파울러 조차도 몇몇 기업에 적용하여 긍정적인 효과를 보았음에도 평가 자체는 유보적인 입장을 보이고 있다.

첫번째 이유는 아직 마이크로서비스의 역사가 오래되지 않았기 때문이다. 아키텍처의 성공여부에 대한 판단은 몇년이 지난 후에야 나오기 때문이다.

두번째 이유는 아직 기술적인 성숙도가 충분하지 않다고 평가하기 때문이다. 서비스를 얼마나 잘 모듈화 할 수 있는지에 대한 기술 성숙도가 부족하기 때문이다. 만약, 서비스를 모듈화 했는데 기존 모놀리스 서비스 보다도 기술 대응이나 변경이 늦어진다면 어떻게 될 것인가? 모듈을 리팩토링 하는 것이 이전보다 더 힘들어졌다면 어떻게 될 것인가? 오히려 나중에 하위 호환성 문제가 발생한다거나 테스트하기 힘든 시스템이 될 뿐일 수 있다는 것이다. 이 부분은 결국 설계자와 팀의 기술 수준 문제이지만, 결국 마이크로서비스 접근 방식에 대한 문제로 부각될 가능성이 있다고 생각한다.

세번째 이유는 각 모듈들이 명확하게 구성되어있지 않다면, 내부의 복잡성이 결국 통신상의 복잡성으로 확대될 가능성이 있다는 것이다. 결국은 수많은 커넥션을 관리하기 위한 복잡성만 증가시키는 결과가 될 수 있다는 것이다. 뒤에도 설명하겠지만 마틴파울러는 이것 때문에 모놀리스를 만든 후에 마이크로서비스로 차근차근 변화하는 것에 대해서 경고하고 있기도 하다.

마지막으로 팀 기술 수준에 대한 문제를 지적한다.
모든 문제점 중에서 내가 가장 공감하는 부분이다. 내 자신이 선뜻 마이크로서비스로 개발하자고 말하지 못하는 이유이기도 하다. (내 자신의 기술 수준으로 볼 때 그럴만한 기술 수준이 안될 가능성이 크기 때문이고 지금 내가 이 글을 정리하면서 다시 한번 공부를 하고 있는 이유이기도 하다). 마틴파울러는 결국 낮은 기술수준의 팀은 낮은 수준의 제품만을 만들 수 있다는 것을 지적한다. 그것이 하나의 커다란 낮은 수준의 제품이냐 여러개로 나눠진 낮은 수준의 모듈들이냐의 차이일 뿐이라는 것이다. 마이크로서비스는 이런 경우 결국 더 안좋은 결론이 될 것이라고 이야기한다.

최근 마이크로서비스에 대한 몇개의 웹 강좌 동영상에서 연사들이 마이크로서비스부터 시작하지 말고 모놀리스에서 시작해서 그것을 모듈화 하라는 이야기를 들은 적이 있다.
이 견해에 대해서 마틴파울러는 위험하다고 이야기한다. 이유는 In-progress interface 가 훌륭하다고 해서 좋은 서비스 인터페이스가 되지는 않기 때문이다.

어쨌건, 마틴파울러도 분명 마이크로서비스를 해볼만 한 것으로 보고 있긴하다. 다만, 적용함에 있어서 불분명한 몇가지 자료에 의거해서 결정을 내려야 하는 도전이 될 것이라고 이야기하고 있다.

결론


여기까지가 마틴 파울러가 정리한 마이크로서비스의 개념이다.
앞서 서론해서 말했듯이 마틴파울러의 정리된 글을 얼마간 인용하고 얼마간은 내가 이해한 한도내에서 내 생각을 정리한 글이다. 틀린 부분도 있을 수 있으므로, 원문을 읽기를 다시 한번 추천한다.

다음 포스트는 언제일지 모르겠으나, 12 Factor APP 에 대한 정리를 할 것 같다.
12 Factor APP 은 예전 Agile Manifesto와 같이 개발에 있어서 12가지의 요소들에 대한 제안을 정리한 내용이다. 마이크로서비스를 구현함에 있어서도 여러가지 영향을 줄만한 요소들이 있으므로 정리를 한 후에 마이크로서비스에 대해서 더 알아볼까 한다.

추가글


마지막으로 한가지 내 개인적으로는 논란인 포스트를 하나를 소개하고자 한다.

Chris Richardson의 Does each microservice really need its own database? 라는 글이다.

마이크로서비스 제안자들은 서비스마다 데이터베이스를 독립적으로 소유해야 한다고 이야기한다.
이 글에서도 그래야만 한다고 이야기한다.
만약, 이미 모놀리스로 개발을 한 우리 같은 회사는 어떻게 하라는 이야기인가?
크리스 리차드슨은 이 문제에 대해서 세가지 방향을 이야기한다.
  • Private-tables-per-service – each service owns a set of tables that must only be accessed by that service
  • Schema-per-service – each service has a database schema that’s private to that service
  • Database-server-per-service – each service has it’s own database server.
하나의 데이터베이스를 가지고 있다면 서비스마다 소유하고 있는 테이블을 나누어라가 결론인 듯 하다.

서비스별로 데이터베이스를 가질 경우의 단점에 대해서도 대안을 제시하고 있다.

첫번째는 앞에서도 이야기한 트랜잭션을 통한 일관성 유지의 문제. 역시나 최종적으로 일관성을 유지하는 방식이나 이벤트 베이스 접근 방법을 이용하라고 조언한다.

두번째는 나도 팀원들과 이야기하다가 논쟁이 된 부분인데, 조인을 이용할 수 없어서 발생하는 문제점에 대한 것이다. 조인이 꼭 필요하다면 조인을 사용하라는 것이다. 물론 되도록이면 서비스 API 를 이용해야 하지만, 반드시 필요하다면 조인을 사용하라고 이야기한다. 이 부분은 CQRS나 View 를 대안으로 이야기하고 있기도 하다.

세번째는 모든 서비스가 공통으로 필요로 하는 공통 데이터는 어떻게 할 것인가 하는 부분이다. 이 부분은 해당 데이터를 제공하는 Service API 를 만들어 제공하거나, 또는 이벤트 기반으로 개발하여 해당 데이터가 필요할 때 데이터를 리플리케이션 하는 것을 제안하고 있다. 지인 중에 이와 비슷한 방식을 이용하여 서비스를 개발하고 있는 사람이 있다. 기본 데이터는 MySQL Database에 있지만, 특정 서비스에 요청이 들어오면 데이터가 복제된 MongoDB 에서 조회를 하고 없을 경우에는 MySQL Database에서 요청하여 데이터를 복제하는 방식으로 서비스를 분리하고 있다. 크리스 리차드슨이 이야기한 것이 이것과 동일한지는 모르겠지만 하나의 대안이 될 수 있지 않을까?

결론적으로 하나의 데이터베이스를 모든 서비스가 공유한다면 스키마 변경이나 기술 적용에 있어서 많은 제약이 생겨 마이크로서비스의 장점을 누릴 수 없다는 것이 크리스리차드슨의 주장이라 하겠다.

References


  • 마틴파울러 - Microservices - a definition of this new architectural term : http://martinfowler.com/articles/microservices.html
    ==> 이글의 기반이 된 글이다.
  • Pattern: Microservice Architecture : http://microservices.io/patterns/microservices.html
    ==> 이 글 외에도 다양한 마이크로서비스 구성요소들에 대한 설명이 있다. 
  • 바른모 블로그 - 마이크로서비스 소개 : http://barunmo.blogspot.kr/2014/12/microservice.html
    ==> 내 글처럼 긴 글이 아니라 아주 간단하게 정리를 잘해 놓은 글이다.
  • 안재우 - 마이크로서비스 아키텍처로 개발하기 : http://www.slideshare.net/saltynut/building-micro-service-architecture
    ==> SK Planet 에 근무하는 저자가 이야기하는 실질적인 사례를 담은 슬라이드
  • Chris Richardson - Plain Old Object Blog : http://plainoldobjects.com
    ==> 마이크로서비스에 대한 다양한 글이 있다.
  • Devoxx 2015 Principles Of Microservices by Sam Newman : https://www.youtube.com/watch?v=PFQnNFe27kU&list=WL&index=4
    ==> 이 글에 설명한 것을 좀 더 많은 자료를 통해서 설명해주고 있다.