마이크로서비스의 정의
늘 그렇듯이 무언가에 대해서 이야기 하기 위해서는 먼저 정확한 정의가 필요하다. ThoughtWorks의 James Lewis와 Martin Fowler는 Mircoservices라는 기사에서 다음과 같이 정의하고 있다.마이크로 서비스는 서비스 디자인스타일로서 작은 서비스의 결합을 통해 하나의 응용프로그램을 개발하는 방법으로, 각각의 서비스는 독립적인 비즈니스 로직으로 구성되며, 완전 자동화된 개발/배포환경에 의해 각각 독립적으로 배포될 수 있습니다. 최소한의 중심적인 관리 체계가 있으며, 이 시스템은 각각 다른 프로그래밍 언어, 다른 데이터스토리지 기술로 작성하는것이 가능합니다.
밥아저씨Robert C. Martin 또한 트위터에 다음의 짧은 글로 마이크로서비스에 대한 정의를 내리고 있다.
단일체Monolithic 대 마이크로서비스의 이분법은 잘못된 것입니다. 그것(마이크로서비스)은 단독으로 실행 가능하며 독립적으로 배포 가능한 특징을 지닙니다.
어디서 많이 들어본 이야기 아닌가? 그렇다. 불과 2,3 년 전 까지만해도 가장 핫한 기술 트렌드 였던 SOA(Service Oritented Architecture)를 떠올리며 기시감Déjà Vu을 느끼는것은 당신만이 아닐것이다. 이 이야기는 잠시후에 자세히 다뤄 보기로 하고 우선은 마이크로서비스의 특징을 좀 더 살펴보도록 하자
왜 마이크로서비스인가?
많은 개발자들은 마이크로서비스 인기의 가장 큰 이유로 시스템 규모와 복잡성에 대한 관리를 꼽는다. 마이크로서비스는 서비스를 충분히 작은 크기로 나누어 개발하되 상호 연계를 통해 좀 더 복잡하고 거대한 시스템을 만들어 갈 수 있다. 그리고 그러한 서비스들은 서비스의 양과 복잡성 뿐만이 아니라 스케일링에도 높은 자유도를 지니게 되는데 이러한 특징들은 오늘날 널리 확산되고 있는 클라우드 컴퓨팅이나 고확장성 시스템의 요구조건에 정확히 부합하고 있다.잘 알려진 마이크로서비스의 특징과 장점은 다음과 같다.
- 각각의 마이크로서비스는 심플하며, 각각의 비즈니스 요구사항에 특화되어 있다 - 개발자가 관리하는 스코프가 명확해지게되며, 이를 토대로 소프트웨어의 복잡성을 제어하는것이 가능해진다. 최근의 마이크로서비스들이 지향하는 방향은 UI와 컨트롤, 도메인로직이 별도의 마이크로서비스로 구성되어 완전히 독립적으로 개발이 가능한 방향으로 나아가고 있다는 점 이다. 즉, 웹프론트 개발자들은 서버사이드 처리를 몰라도 아무런 문제가 되질 않는다는 점 이다.
- 각각의 마이크로서비스는 개별 팀에서 독립적으로 개발/배포가 가능하다 - 개발팀의 운영과 스케줄링에 있어서 높은 자유도를 가져다준다. 또한, 시스템의 규모가 커짐에 따라 추가로 발생하게 되는 오버헤드가 일정수준으로 관리가 가능해진다는 점도 빼 놓을 수 없는 마이크로서비스의 장점이다.
- 각각의 마이크로서비스는 다른 프로그래밍언어, 다른 도구를 사용하여 개발 할 수 있다 - 요구사항을 구현하기위해 최적화된 언어와 아키텍처의 선택이 가능해진다.
- 각각의 마이크로서비스는 다른 데이터 저장소를 사용할 수 있으며 서로 느슨하게 연결된다. - 언어선택과 마찬가지로 요구사항에 최적화된 DB의 선택이 가능해지며 각각의 마이크로서비스들은 서로간의 인터페이스에만 집중할 수 있게된다.
- 고속 개발에 최적화 되어 있다 - DevOps와 결합된 각각의 마이크로서비스는 심플한 구조를 지니는 만큼 개발속도와 개선에 있어서 높은 효용성을 지닌다. 자동화된 유닛 테스트와 시나리오테스트는 빠른 배포주기에도 불구하고 뛰어난 품질을 유지할 수 있게끔 도와준다.
빠르고 지속적인 업그레이드는 치열한 IT비즈니스 경쟁에 있어서 가장 강력한 무기가 된다. 출처 : Doctor Who |
SOA와 마이크로서비스
결론부터 이야기 하자면 SOA와 마이크로서비스는 동일한 발상이다.그렇다면 왜 SOA가 아닌 마이크로서비스라는 새로운 용어를 꺼내들고 있는가? 필자의 생각으로는 여러 프로젝트에서의 실패로 SOA라는 용어 자체에 부정적인 이미지가 많이 입혀지게 된 것이 큰 이유가 아닐까 추측해 본다. SOA는 본래의 사상을 살릴 발상이나 구현이 제대로 확립되기도 전에 마케팅적 미사여구에 매몰되어 무엇이든 가져다 붙일 수 있는 마법의 기술로 변화되어 버렸다. 그리고 여러분들도 잘 알다시피 무엇이든 할 수 있다는 말은 아무것도 할 수 없다는 말과 동일하다.
2000년대 중반 시스템 개발의 현신적인 패러다임으로 각광받던 SOA는 2009년경에 이미 부정적인 이미지가 표면화되기 시작했다. 출처 : SOA is Dead; Long Live Services |
대표적인 실책중에 하나는 SOA가 서비스 독립적이라는 말을 하면서도 기존의 단일체 시스템에서 철칙처럼 여겨졌던 중앙집중식 데이터 관리 방식을 어떻게든 이어가려 하고 있었다는 점 이다. 흔히 ACID으로 대표되는 트렌젝션 신뢰성에 대한 원칙들은 SOA에서도 아무런 의심 없이 지켜져야 할 요소로 여겨지고 있었다. 이러한 데이터의 무결성을 유지하기 위해서 two-phase commit과 같은 분산 트랜젝션이 등장하게 되었는데, 분산 트랜젝션은 시스템 규모가 커지면 커질수록 성능에 치명적인 저하를 가져온다.
단일체 시스템이 가질 수 밖에 없는 근본적인 제약을 그대로 지닌채 발상의 전환 없이 세상에 등장한 SOA는 출생부터 험난한 앞길이 예고되어 있었는데, 결국 장미빛 희망만으로 SOA를 채택했던 (정확히는 비싼돈을 들여서 ESB를 도입했던)많은 프로젝트들이 애초부터 고려되지 않은 다른 서비스들과의 연동에서 오는 오버헤드와 단일 트랜젝션 처리에 대한 롤백과 같은 이루어 질 수 없는 ACID준수에 대한 요구사항을 만족시키려고 발버둥치다 좌초되거나 본래의 목표를 잃어버린채 표류하는 운명을 맞이한다.
ESB와 API Gateway : SOA와 마이크로서비스처럼 사실상 동일한 개념이라고 봐야 한다. 간단한 스크립트 만으로 서비스들을 엮어 새로운 서비스들을 구성할 수 있게 해 준다는 개념은 얼핏 듣기에 매력적으로 들리지만 실제 문제영역에서 제대로 그 역할을 제대로 구현하는데에는 섬세한 모델링을 통한 접근이 필요하다. 이러한 집합체에 대한 문제는 도메인 주도 설계(이하 DDD)에서도 비중있게 다루고 있으므로 일독을 권한다.
마이크로서비스의 구현요소들
이러한 마이크로서비스를 구현하기 위해서는 어떠한 기술적 요소들이 필요할까? 마이크로서비스 구현을 위한 핵심요소들은 다음과 같다.- 데이터 분산 관리 : 분산트랜젝션을 말하는것이 아니다. 중앙집중형 데이터 관리 방식에 대칭되는 의미로서 이 용어를 사용하며 종래의 한가지 DB에 데이터를 집중시키는 방식에서 벗어나 필요에 따라 다양한 DB를 사용하는 이른바 Polyglot Persistense도 마이크로 서비스에서는 보다 쉽게 도입이 가능해 진다.
마이크로 서비스의 퍼시스턴스 구성 예 출처 : martinfowler.com |
- RESTful / AscyncProcess : 느슨한 결합과 처리의 비동기화를 위해 가장 효과적인 인터페이스이다. 비동기처리는 대규모 웹 서비스를 위해서는 필수 불가결한 존재이며 기존의 DB를 중심으로 이뤄지던 트랜젝션과는 다른 접근 방식 가져야 한다.
출처: 4 Developing Asynchronous Web Services |
- 폴리그랏 아키텍쳐 : 임백준님의 폴리글랏 프로그래밍이나 마틴파울러의 Polyglot Parsistense로 대변되는 이른바 멀티 랭귀지, 멀티 파시스턴스를 이용한 개발 스타일이다. 각각의 서비스 목적에 맞추어 효율적인 언어와 플랫폼을 선택 할 수 있다는 장점이 있지만 한편으로는 언어나 프레임워크별로 발생하는 코드의 중복이라던지 관리해야 하는 기술의 스코프가 늘어난다는 문제가 있다. 이 점에 대해서는 잠시후에 좀 더 자세히 살펴보도록 하겠다.
- DevOps : 대다수의 마이크로서비스 관련 문서에서 필수로 꼽는것이 바로 이 DevOps이다. DevOps는 CI에서 좀더 진화된 형태로, 개발, 테스트, 배포를 모두 자동화 시켜 개발 사이클이 끊임없이 순환되도록 함으로서 개발의 속도를 최대화 시키는 개발스타일 이다. 마이크로서비스의 경우 배포가 서비스의 수 만큼 이루어지게 될 뿐만 아니라 테스트 또한 각각의 서비스가 연동되어 발생하는 집합체Aggregate의 수 만큼 필요하게 된다. 이를 일일이 사람의 손으로 제어하는것은 엄청난 비용이 소요되며 이러한 문제점을 해소하기 위해 필연적으로 요구되는것이 바로 DevOps이다.
- 클라우드 컴퓨팅: 독립된 배포라던지 각각의 서비스별로 관리 가능한 확장성부분에 있어서 마이크로서비스는 클라우드 컴퓨팅과 궁합이 잘 맞는다. 특히 Docker로 대표되는 컨테이너 방식의 개발은 DevOps와 어우러져 빠른 개발과 서비스 안정성, 유연한 확장성 이라는 세마리 토끼를 잡는데 최적의 솔루션으로 각광받고 있다.
- 실패를 염두에 둔 설계Design for failure : 앞에서도 열거한 비동기 처리와 데이터의 분산관리는 필연적으로 한가지 처리가 단일 트랜젝션에 묶일 수 없다는 전제를 가져온다. 즉, 여러 마이크로서비스를 이용한 어떠한 복합기능이 도중에 실패하였을경우 이를 단일체시스템처럼 간단히 롤백하기가 쉽지 않다. 게다가 물리적으로도 여러개의 서버 인스턴스가 운영되게 되므로 시스템 전체로 보았을때 개별 마이크로서비스 인스턴스가 처리에 실패하더라도 전체 서비스가 문제 없이 움직일 수 있도록 가용성을 높이기위한 고려가 필수적으로 이루어져야 한다.
마이크로서비스의 문제점과 그 해결책 - 도메인 주도 설계
위에 열거한 바와 같이 많은 장점들을 지닌 마이크로서비스이지만 세상에는 공짜가 없는 만큼 치뤄야 할 대가가 있다. 기술적으로도 새로운 요소들의 도입이 검토되어야 하지만 서비스에 대한 관점이나 개발 사이클의 관리라던지 팀 구성과 같은 개발 전반에 걸친 발상의 전환이 필요하다.Eric Evans가 저술한 도메인 주소 설계(이대엽역, 이하 DDD)에서는 이러한 마이크로서비스 설계와 구현에 필요한 실질적인 지식과 조언을 종합적으로 제공하고 있다. DDD의 핵심 개념중 하나인 도메인의 격리와 제한된 컨텍스트는 바로 마이크로서비스와 직접적으로 대응이 되고 있으며 해결 방안에 대한 아이디어또한 동일하다.
만약 진지하게 마이크로 서비스의 도입을 검토하고 있다면 서두에서 언급한 Martin Fowler의 Microservices와 함께 DDD의 일독을 강력히 추천한다. 특히 10장에서 소개하는
- 서비스 범위 설정 문제: 각각의 마이크로 서비스를 구성하는 범위는 어느정도가 적절할까? 단순히 크기의 문제가 아닌 어디서부터 어디까지를 묶어야 독립적으로 운영 가능한 서비스가 되느냐 의 문제이다. 이에대해서 DDD는 적절한 서비스범위에 대한 전략적인 조언을 제한된 컨텍스트Bounded context에서 제시하고 있다. 중요한것은 동일한 문제영역을 나타내는 모델이 한개 이상 존재할 수 있으며 이러한 문제영역을 올바르게 이해하는데 필요한것은 이렇게 되어야만 한다는 이상론에 집착할 것이 아니라 실제 문제역역이 어떻게 동작하고 있는가에 대해 있는 그대로를 관찰하고 이를 바탕으로 서비스를 구성하는것이 중요 한다는 점이다.
제한된 컨텍스트에 대한 예제. 위 그림과 같이 실제 문제영역에서 '고객'이나 '제품'이 서로 다른 컨텍스트에서 독립된 형태로 존재하는것은 흔히 있는 일이다. 출처 : martinfowler.com |
- 레거시 시스템과의 공존에 대한 고려: 대부분의 시스템 개발에서 마이크로서비스 아키텍처가 전면적으로 도입되는 상황이라고 하여도 기존의 (특히 단일체)시스템들과의 공존은 필연적으로 존재하기 마련이다. 이러한 상황에서 기존 시스템들과의 연계를 어떻게 해 나가야할지를 결정하는것은 상당히 중요한 문제이다. 다행이도 이미 SOA에서 많은 노하우들이 축적되어 있는 상황이므로 우리는 이에대한 조언과 사례를 쉽게 찾을 수 있다.
- 운영오버헤드: 마이크로서비스는 엄청나게 많은양의 배포작업이 수반된다. 단순 계산만으로도 늘어나는 서비스의 수 만큼 필요하게 되며 릴리즈가 개별적으로 이루어지는 특성상 이를 별도의 운영팀에서 일괄적으로 관리하는것은 불가능에 가깝다. 따라서 배포에 수반되는 일련의 작업들을 철저하게 자동화 시키지 않으면 살인적인 작업 오버헤드에 직면하게 될 것이고, 이를 해소하기 위해 마이크로서비스 개발에 있어서 DevOps의 도입은 필수요소이다.
- 인터페이스 불일치: 마이크로서비스에서는 서비스간의 통신에 사용되는 인터페이스에 의존하게 되는데, 각각의 서비스의 인터페이스를 변경하는것에 대한 영향범위를 파악하는것은 때에 따라서 만만치 않은 작업이 될 수 있다. 마이크로서비스 환경에서는 아주 작은 인터페이스 변경이라 할 지라도 수많은 구성요소를 수정해야 할 가능성이 있다는 것이다.
또 한가지 마이크로서비스상에서 발생하기 쉬운 중요한 인터페이스 관련 문제는 서비스 외부로 제공하는 인터페이스가 제공자가 의도하지 않은 형태로 곧잘 사용되기도 한다는 점 이다.
이러한 인터페이스의 불일치 문제는 전체 시스템의 인터페이스 맵을 만들어 관리해 나가는것으로 각 어느정도 해결이 가능할 것이지만 근본적으로는 각 서비스를 만들어 나가는 팀 간에 발생한느 커뮤니케이션 비용을 얼마나 효과적으로 제어 할 수 있느냐에 인터페이스의 품질이 크게 좌우된다.
- 코드 중복: 단일한 언어나 프레임웍을 사용하여 개발된다고 하면 공유 라이브러리를 통해서 코드의 중복을 해결 할 수 있지만 여러 언어를 사용하여 개발이 진행되는경우 코드중복은 필연적으로 발생할 경우도 있다. 다언어 개발은 이러한 개발/관리상의 오버헤드가 충분히 고려된 이후에 득실을 따져서 도입하여야 한다. 한가지 희망적인 소식은 JVM이나 .net fw상에서 동작하는 여러 언어들은 각각의 환경에서 동작하는 언어 상호간에 참조에 대한 호환성을 제공하는경우가 많다. 따라서 개발효율등을 위해 다언어개발을 고려해야 한다면 이러한 라이브러리 호환성을 함께 고려함으로서 코드중복을 피해나갈 수 있을것이다.
한편으로는 실용적인 관점에서 생각했을때 다언어 개발이 가져다주는 이점이 코드중복의 오버헤들르 상쇠시켜 주는 경우를 생각해 볼 수 있다. 이상론에 따라 코드중복을 무조건적으로 배척하기 보다는 실용적인 관점에서 손익계산을 따져볼 필요가 있다는 것이다.
- 데이터 중복: 위의 코드중복이 다언어 개발에서 고려되어야 하는 문제라고 한다면 데이터 중복은 분산 데이터 관리 측면에서 고려 되어야 한다. 이 문제에 대해서는 사용하는 데이터 영속화의 솔루션에 따라 다양한 기술적/모델링적 접근방법이 있으므로 적당한 방법을 취하는것이 중요하다. 단, 코드의 중복과 마찬가지로 데이터의 중복이 절대적으로 해악만을 가져오는것은 아니다. 구현에 따르는 비용과 성능이 가져오는 이득을 잘 따져서 전체 시스템의 관점에서 문제에 접근하는 방식이 필요하다.
- 분산시스템의 복잡성과 비동기성: 마이크로서비스 아키텍처는 기본적으로 네트워크를 기반으로한 비동기 통신에 기반하여 전체 시스템이 동작하게 되므로 프로그램을 시간의 흐름에 따라 선형으로 바라보는 구조적 프로그래밍에서, 각각의 마이크로서비스들간의 관계를 중심으로 바라보는 객체지향 프로그래밍으로의 패러다임 전환이 필요하다. 다른 마이크로서비스의 문제들과 마찬가지로 DDD는 이 문제에 대한 많은 아이디어들을 제공한다. 구현적인 측면에서는 비동기성과 관련하여 최근 주목받고 있는 Actor Model(또는 Reactive 프로그래밍)을 주목할 필요가 있다.
- 테스트의 까다로움: 비동기로 동작하는 마이크로서비스의 특성상 개별 서비스에 대한 테스트는 만들기가 수월하지만 런타임 환경상에서 비동기 상호작용을 테스트하기는 무척이나 까다롭다. 실제로 많은 마이크로서비스 경험자들이 제품으로서 릴리스하기에 충분한가 라는 의문에 대해서 객관적인 답안을 제시하는것이 어렵다는것을 토로하고 있는데, 이 부분은 SOA진영에서 어느정도 해답이 제시되고 있으므로 이를 참고로하면 좋을것이다.
마이크로서비스 개발을 위한 개발 팀 운영에 대해서
마지막으로 마이크로서비스 개발의 장점중에 한가지로 꼽히는 독립적인 개발 배포의 장점을 살린 개발 팀의 독립적인 운영에 대해서 살펴보면서 글을 마무리 하고자 한다.원래 이 부분은 Server Side Architecture Group에 올라온 조대협님의 질문에 대답해볼 요량으로 처음 손을 대기 시작했다. 그런데 막상 필자 또한 답을 찾기가 쉽지 않았다. 그러다가 마이크로서비스에 대한 자료들을 정리하며 이번 포스팅을 작성하던 도중 어느정도 생각들이 정제되었다는 확신이 들었기에 내용을 여기에 적어 보고자 한다.
필자가 주목하는 점은 마이크로서비스 아키텍처가 지니는 특징들인 독립적인 팀 운영과 아키텍처 수립등 중앙집권적 체계를 벗어나 스스로 결정하고 책임져 나간다는 점이 스스로 결정하고 그 결정에 대해 책임진다는 자기 조직화 팀Self-Organising Teams의 주요 특징과도 일치한다는 점 이다. 즉, 마이크로서비스 아키텍처는 단일체 개발에서 보여지는 커다란 조직의 톱니바퀴로서의 역할을 강요당하는 것이 아닌 모든 멤버들이 주체가 되어 개발을 주도적으로 이끌어 나갈 수 있게 해 주는 중요한 토양을 마련해 준다는 점 이다.
필자는 다음의 특징들을 통해 마이크로서비스가 개발팀이 자기 조직화 팀으로서 역할을 하기에 좋은 토양을 제공해 준다고 생각한다.
- 지식노동자에게 최적화된 팀 규모에 적합한 개발 규모제공: 팀의 규모는 생산성과 효율 문제 뿐만이 아니라 팀 자체의 성격을 결정짓는 중요한 요소이다. 게다가 작은 규모는 변화에 대한 수용과 대응에 있어서 훨씬 효율적이다.
팀 인원수가 많아지면 필연적으로 무임승차자가 등장하게 된다. 이러한 무임승차자는 단순히 -1 만큼의 효과만 가져 오는것이 아니라 열심히 일하는 다른 팀원들의 사기도 저하시킨다. |
- 팀 운영에 대한 자율성: 작은 팀 규모는 운영에 있어서도 보다 많은 자유를 가져다준다. 권한없이 주인의식이 생기기를 기대하지 말라.
- 아키텍처 선택에 대한 자율성: 아키텍처선택에 대한 권한 위임은 개발자에 대한 신뢰의 표현이기도 하다. 사용할 아키텍처를 스스로 정한다는 것은 모든 능력있는 개발자들의 소망이기 때문이다.
- 상향식 개발: SOA실패의 원인 으로 상당수 지적되는 것이 비즈니스 주도 개발이라는 이유로 택해진 하향식개발이다. 이러한 하향식 개발은 결국 무늬만 분산형 개발이고 단일체 개발의 광범위한 계획과 일괄적인 정책 적용을 강제하게 된다.
참고자료
마이크로서비스에 대해서
- Microservices (James Lewis, Martin Fowler)
- 대용량 웹서비스를 위한 마이크로 서비스 아키텍쳐의 이해 (조대협의 블로그)
- Microservices: It’s not (only) the size that matters, it’s (also) how you use them (tigerteam.dk)
- Microservices - Not A Free Lunch! (Benjamin Wootton)
- Scalable Microservice Architectures - QCon San Francisco 2014 (아직 행사가 열리기 전이어서 컨퍼런스 내용은 공개가 되어있지 않지만 Netflix를 비롯해 마이크로서비스 개발에 있어서 선도적인 기업들의 노하우가 상당수 공개될것으로 기대되고 있다.)
레거시 시스템에서의 마이그레이션
- System Integration Made Easy (Mulesoft)
- Migrating to Cloud Native with Microservices (Adrian Cockcroft)
개발팀에 대해서
- SOA From the Bottom Up - The Best Approach to Service Oriented Architecture (Mulesoft)
- 스크럼 개발팀의 최적 인원수는 몇명일까? (MoreAgile)
- Why Do We Need Self-Organising Teams? (InfoQ)
- The Top-Down vs Bottom-Up SOA Debate Revisited (InfoQ)
데이터 중복에 대한 해결책
- Automated conflict resolution - enabling masterless data distribution (Rune Skou Larsen, Trifork)
- Conflict Resolution Concepts and Architecture (Oracle)
- Conflict Management (CouchDB)
장애를 고려한 설계
- Build Scalable Systems That Handle Failure Without Losing Data (MSDN Magazine) - 장해에도 굴하지 않고 데이터를 지켜내는 확장가능한 시스템을 만드는데 필요한 포괄적인 아이디어를 제공하고 있다. 분량이 만만치 않지만 충분히 읽을만한 가치가 있다.
리엑티브 프로그래밍
- The introduction to Reactive Programming you've been missing (@andrestaltz)
- An Introduction to Functional Reactive Programming (@cmeik)
- Functional Reactive Programming in the Netflix API (@benjchristensen)
- Principles of Reactive Programming (Coursera)
테스팅
- SOA Testing Techniques (crosschecknet)
- Testing asynchronous service calls (IBM)
- Jersey Test Framework (Jersey 2.13 User Guide)
댓글 없음:
댓글 쓰기