패턴 및 설계상의 일반적인 의사 결정
- 적절한 마이크로서비스 경계 설정
- DDD의 Bounded Context 개념
- Bounded Context는 일반적으로 결합도가 낮으며, 때로는 아예 연결돼 있지 않기도 한다. 하나의 마이크로서비스에 대응 가능
- 조직 구조도에 내재된 문제가 없다면 조직구조도에 따라 Bounded Context를 설정하는것이 현실적으로 가장 단순한 방법
- 하향식 도메인 분해도 Bounded Context를 도출하는데 도움이 된다.
- 마이크로서비스의 경계를 설계하는 가장 실용적인 방법은 가능한 여러가지 선택사항에 대해 서비스 리트머스 테스트를 했던 것처럼 손으로 시나리오를 돌려보는 것이다
- 자율적인 기능 : 입력을 받아 내부의 로직과 데이터로 계산해서 결과를 반환
- 암호화 엔진, 알림엔진 같은 유틸리티 기능이 적합
- 배포단위의 크기 : 관리할 수 있는 수준 이내로 유지할 수 있어야 함
- 분리하기에 가장 적합한 기능 또는 서브도메인
- 자원소모량, 소유비용, 비즈니스 효용성, 유연성 등이 분석 기준
- 폴리글랏 아키텍처
- 선택적 확장 : 모든 기능 모듈이 모두 동일한 수준의 확장성을 필요로 하지는 않는다
- 확장에 관한 요구사항을 기준으로 마이크로서비스의 경계를 결정
- 작고 애자일한 팀
- 전체 중에서 서로 다른 일부를 개발하는 데 집중할 수 있는 작고 집중력 있는 팀 구성을 통해 애자일 방식의 개발을 가능하게 해준다
- 단일 책임
- 책임을 비즈니스 범위 또는 하나의 기술 범위로 치환해서 생각하는 것이 더 현실적임
- 하나의 마이크로서비스는 여러가지 책임을 담당해서는 안 된다
- 복제가능성과 변경 가능성
- 마이크로서비스가 전체 시스템에서 최소한의 재작성 비용 투입만으로 쉽게 떼어져 나올 수 있는지를 기준으로 식별돼야 한다.
- 결합과 응집
- 기능분해도는 의존관계트리와 함께 마이크로서비스의 경계를 수립하는데 도움을 줌
- 트랜잭션 범위가 하나의 마이크로서비스의 범위를 넘어서 여러 마이크로서비스에 걸치지 않게 하는 것도 중요
- 이벤트를 입력으로 받아 내부적으로 몇개의 함수를 호출하고 최종적으로 새로운이벤트를 반환하는 방식으로 반응하는 것이 기본
- 마이크로서비스를 하나의 제품으로 생각하기
- 통신 방식 설계
- 먼저 요청-응답 동기방식으로 시작해서 나중에 필요하다고 판단될 때 비동기 방식으로 리팩터링
- 마이크로서비스 조립(composability)
- 오케스트레이션(orchestration) 방식과 연출(choreography)방식이 있음
- 여러개의 서비스를 모아 하나의 완전한 기능을 만들고 오케스트레이터가 중앙의 두뇌 역할 담당
- SOA에서는 ESB가 담당
- 연출방식은 이벤트 생산자가 이벤트를 생상하고 소비자가 대기하고 있다가 소비하는 방식
- MSA에서는 연출방식이 더 적합하지만 모든 사례에 연출방식으로 모델링하는 것은 불가능 함
- 마이크로서비스 하나에 얼마나 많은 종단점을 둘 것인가?
- 종단점의 수는 중요한 결정 사항이 아니다. 하나의 마이크로서비스는 하나 또는 그 이상의 종단점을 가질 수 있다. 마이크로서비스 크기에 적합하게 경계지어진 컨텍스트를 적절하게 설계하는 것이 훨씬 더 중요
- 가상머신 하나당 하나의 마이크로서비스 또는 다수의 마이크로서비스?
- 가상머신이 최대 사용치를 기준으로 두 걔의 서비스를 운영하기에 용량이 부족한가?
- SLA를 충족시키기 위해 서비스들이 별도로 처리돼야 하는가?
- 자원 요구사항이 서로 충돌되지는 않는가?
- 3가지 질무에 No라고 답할 수 있다면 가능하지만 서비스들이 서로 어떤것도 공유하지 않으며, OS 프로세스와 독립적으로 실행된다는 점이 보장돼어야 함
- 룰 엔진 공유 또는 내장?
- 룰 엔진은 생산성을 높이고, 룰, 사실정보, 용어의 재사용을 가능하게 하며, rete알고리듬을 통해 룰을 더 빨리 실행할 수 있게 해준다
- 룰이 단순하고 수적으로도 적고 서비스의 경계 내에서만 사용되며 룰 작성이 외부의 비즈니스 사용자에게 공개되지 않는다면 코딩하는것이 더 나을 수 있음
- 룰이 복잡하고 서비스 컨텍스트에 국한되며 비즈니스 사용자에게 부여되지 않는다면 서비스 내부에 내장된 룰 엔진을 두는 편이 더 낫다
- 룰이 비즈니스에 의해 작성되고 관리되거나, 룰이 복잡하거나, 룰이 서비스도메인 외부에서도 재사용된다면 외부에 있는 중앙저장소와 시스템 내부에 내장된 실행 엔진을 사용하는 것이 좋다
- BPM의 역할과 작업흐름
- 시스템과 사람의 상호작용을 자동화함으로써 전 구간의 여러기능에 걸쳐있는 비즈니스 프로세스를 모델링하는 상황에서 여러개의 마이크로서비스를 조합하는 상위 수준에서 사용가능
- 마이크로서비스가 데이터 스토어를 공유할 수 있는가?
- 공유데이터 모델, 공유 스키마, 공유테이블은 좋지 못한 방법이며, 마이크로서비스 개발을 재앙으로 이끌수도 있다
- 서비스 종단점 설계 고려 사항
- 계약설계
- KISS(keep it simple stupid) : 더 나은 양질의 서비스를 더 빠르게 구축하고 유지 관리와 교체에 드는 비용을 줄여준다. YAGNI(you ain't Gonna need it). 단순함 중시
- 소비자 중심 계약(Consumer Driven Contracts) : 사용자가 원하는 기대 사항을 서비스 제공자에게 테스트 케이스의 형태로 제공하게 함으로써 서비스 계약이 변경될 때마다 서비스 제공자가 통합테스트할 수 있게 해준다
- 프로토콜 선택
- 메시지지향서비스 : JMS/AMQP 프로토콜로 json 데이터 교환, 처리시간이 오래걸리는 작업에 적합
- HTTP : 호환성, 프로토콜 처리, 트래픽 라우팅, 로드 밸런싱, 보안시스템등에 적합
- REST : MSA 기본 선택 옵션,
- 최적화된 통신 프로토콜 : Avro, Protocol Buffers, Thrift 성능은 높이고 호환성은 떨어지게 됨
- API 문서화 : Swagger, RAML, API Blueprint
- 공유 라이브러리 처리
- 자율성과 자기완비성 원칙을 준수하기 위해 코드와 라이브러리를 복제해야 하는 상황이 있을 수 있다
- 비즈니스 범위나 분류관점에서 봤을때 하나의 마이크로서비스로 분류되지 않는 부분을 공통이라는 이유만으로 별도의 마이크로서비스로 떼어낸다면 효용보다 복잡성 증가라는 비용이 더 클 수도 있다
- 마이크로서비스에서 API게이트웨이 사용
- 서버는 화면까지 제공하기보다 RESTful서비스를 노출하는 방식으로 개발
- 계약에 대한 기대사항의 불일치, 하나의 페이지를 렌더링하기 위해 서버에 여러번의 요청을 날리는 문제점이 있음
- 해결방안
- HATEOAS방식으로 최소한의 정보만을 링크정보와 함께 전달
- 클라이언트가 요청 시 쿼리 문자열로 필요한 필드를 지정해서 요청
- 간접화 개념 사용, 클라이언트와 서버 사이에 있는 게이트웨이 컴포넌트가 데이터 소비자의 명세에 따라 데이터를 변환
- API 게이트웨이를 사용하는 목적에 따라 선택이 달라짐
- 리버스 프록시 용도 : Apigee, Mashery 같은 서비스를 공유되는 플랫폼으로 사용
- 트래픽 유형에 따른 세부적인 제어가 필요하고 복잡한 데이터 변환이 수반된다면 서비스당 독자저인 API게이트웨이 사용
- ESB 및 iPass와 마이크로서비스의 사용
- ESB의 역할
- MSA에 적합하게 기능이 제한된 ESB역할은 API 게이트웨이로 대체
- 오케스트레이션 역할은 ESB 버스에서 마이크로 서비스로 이동
- 프로토콜 중재 역시 더 범용적인 메시지 교환 방식사용으로 필요하지 않음
- 레거시 시스템과 연결해주는 어댑터 기능은 서비스 스스로가 구체적인 구현체를 제공하므로 필요 없음
- 엔터프라이즈 수준에서 레거시와의 통합과 솔루션 회사의 애플리케이션을 통합하는데 존재 가치가 있음
- 서비스 버저닝 고려사항
- 시맨틱 버전이 널리 사용됨
- 메이저, 마이너, 패치의 세가지 컴포넌트로 구성
- 메이저 : 호환이 안될수도 있는 대규모 변경
- 마이너 : 하위 호환을 유지하는 한도 내에서의 변경
- 패치 : 버그수정
- REST 버저닝 방식
- URI 버저닝 : 대부분이 사용
- 미디어 타입 버저닝
- 커스텀 헤더
- 크로스 오리진 설계
- 마이크로서비스에서는 서비스가 동일한 호스트나 동일한 도메인에서 운영된다는 보장이 없음
- 신뢰하는 다른 도메인으로부터의 크로스오리진 요청을 모든 마이크로서비스가 허용하게 하는 것
- 클라이언트가 신뢰하는 도메인에 API 게이트웨이를 두는 것
- 공유 참조 데이터 처리
- 상대적으로 정적이고 변경될 가능성이 전혀 없는 데이터는 각 서비스에서 하드코드로 관리
- 공통으로 사용되는 데이터를 별도의 마이크로서비스로 분리
- 데이터를 모든 서비스에 복제
- 필요한 데이터를 로컬에 캐시
- 마이크로서비스와 대규모 데이터 작업
- 데이터가 생성될때 사전 집계
- 배치 API
마이크로서비스의 과제
- 데이터 섬
- 마이크로서비스를 적용하면 데이터가 파편화돼 이질적인 데이터 섬으로 구성되는 상황을 맞이하게 됨
- 데이터웨어하우스 : 배치성
- 데이터 호수 : 하둡, NoSQL등을 이용한 준 실시간 처리
- 데이터 호수의 경우 데이터의 사용목적을 가정하지 않고 그대로 저장
- 데이터 전송 방안
- 데이터 처리상황이 발생할 때 이벤트 발송
- 스프링 데이터 플로우, kafka, flume등 사용
- 로깅과 모니터링
- 의존관계 관리
- 서비스 경계를 적절하게 설정해서 의존성을 낮춰라
- 의존성을 가능한 느슨하게 설계해서 변경에 의한 영향을 낮춰라. 또한 비동기 통신방식을 통해 서비스 간 상호작용이 일어나게 설계하라
- 서킷 브레이커 같은 패턴을 사용해서 의존성 문제의 전파를 차단하라
- 의존 관계 그래프 같은 시각화를 통해 의존 관계를 모니터링하라
- 조직 문화
- 관리체계문제
- 분권화된 관리체계 필요
- 더 나은 서비스를 만들기 위해 표준, 모범사례, 가이드라인 제정 필요
- 모든 이해관계자가 개발된 모든 서비스를 보는 것 뿐 아니라, 모든 문서, 계약, 서비스 수준 합의도 볼 수 있어야 한다
- 운영오버헤드
- 마이크로서비스 테스팅
- 서비스의 전 구간을 아우르는 동작을 평가하는 테스트는 어떻게 수행할 수 있을까?
- 서비스 가상화 또는 목킹
- 서비스 사용자 주도 계약
- 인프라스트럭처 프로비저닝
마이크로서비스 역량 모델
- 핵심역량
- 서비스 리스너(HTTP 리스너/메시지 리스너)
- 저장 기능
- 비즈니스 범위 정의
- 이벤트소싱
- 서비스 종단점 및 통신 프로토콜
- API 게이트웨이 : Zuul, Mastery, Apigee, 3scale
- 사용자인터페이스
- 지원역량
- 소프트웨어 정의 로드 밸런서 : Ribbon, Eureka, Zuul
- 중앙집중형 로그 관리
- 서비스레지스트리 : Eureka, Zookeeper, Etcd
- 보안서비스 : OAuth
- 서비스 환경설정 : 스프링 클라우드 Config, Archaius
- 테스팅 도구(anti-fragile, RUM) : netflix Simian Army
- 모니터링 및 대시보드 : Turbine, Hystrix Dashboard, AppDynamic, New Relic, Dynatrace, said, Sense, Spigo
- 의존 관계와 CI 관리 : CMDB
- 데이터호수 : 스프링 데이터 플로우, Flume, Kafka, HDFS, Cassandra
- 인프라스트럭처 역량
- 클라우드
- 컨테이너/가상머신
- 클러스터 제어 및 프로비저닝 : Mesos, Kubernetes
- 애플리케이션 라이프 사이클 관리 : Marathon
- 프로세스 및 통제역량
- 데브옵스
- 데브옵스 도구
- 마이크로서비스 저장소 : Nexsus, 도커레지스트리
- 마이크로서비스 문서화 : Swagger, API Blueprint
- 참조 아키텍처 및 라이브러리