마이크로서비스란?
작고 자율적으로 협업하는 서비스를 의미한다.
작고, 한 가지 일을 잘하는데 주력
- 새로운 기능으로 인해 코드가 추가될 수록 여러가지로 불편한 점이 많다.
- SOlLD 원칙 중 SRP와 동일한 접근 방식을 취한다.
- 충분히 작아서 더 이상 작아질 수 없는 크기 (존 이브스 - ‘2주 안에 재작성될 수 있는 것’)
자율성
- 동일 머신에 넣는 것을 피해야 한다.
- 서비스 사이의 모든 통신은 네트워크 호출을 통해 이루어진다.
- 서비스는 독립적이어야 한다. (변경, 배포)
주요 혜택
- 각 작업에 적합한 도구를 선택할 수 있다. (기술스택에 대한 제약을 받지 않는다.)
- 발생하는 문제를 격리시킬 수 있다. 결함 감내 시스템을 구축할 수 있다.
- 필요한 서비스만 확장할 수 있다.
- 필요한 서비스만 배포할 수 있다.
- 최적의 팀 크기와 생산성으로 인원을 최소화할 수 있다.
- 다양한 조합(웹, 앱, 웨어러블 등)이 가능하다.
- 레거시가 작아지기 때문에 빠르게 교체가 가능하다.
SOA(서비스 지향 아키텍처)
서비스의 최종 능력 집합을 제공하는 여러 서비스가 서로 협업하도록 하는 설계 접근 방식이다.
Monolithic Architecture -> (Service-Oriented Architecture -> Micro-Service Architecture)
SOA는 여러가지 이유로 각광받지 못 했다. 하지만, 이에 대한 특정 접근법인 마이크로서비스이 등장하면서 SOA에 적합한 시스템과 아키텍처를 보다 쉽게 이해할 수 있게 도와준다.
아키텍트에 대한 관점
고객의 손에 전해진 소프트웨어가 그대로 정체되는 일 없이 고객의 요구에 맞게 반응하고 적응해야 한다.
에릭 도넌버그 曰 “아키텍트의 역할을 건축가보다는 도시 설계자에 가까운 의미로 접근해야 한다.”
└─ ~특정 건물을 거기에 만들어라~ → 도시 구역화해라
프랑크 부쉬만 曰 “아키텍트는 개발자도 거줄할 수 있는 시스템을 만들 의무가 있다.”
└─ 시스템이 현재의 목적에 들어맞을 뿐만 아니라 미래의 플랫폼으로서 적합하다는 것을 보장해야 하고, 나아가 사용자와 개발자가 똑같이 행복해지도록 해야 한다.
구역화
아키텍트는 구역 내의 일보다 구역 사이에서 발생하는 이슈를 걱정해야 한다. (구역: 서비스 경계 또는 서비스의 대단위 그룹)
서비스마다 다른 스택을 보유하게 되면 팀원 고용 및 이동이 다소 어려워질 수 있다.
└─ 넷플릭스의 경우, 데이터 저장 기술을 카산드라로 통일했다.
원칙적인 접근법
시스템 설계상의 결정은 모두 트레이드오프와 관련이 있다.
└─ 성취해야할 목표에 기반하여 일련의 원칙과 실천 사항을 정의해야 한다.
- 전략적 목표: 비기술적인 부분과 기술적인 부분의 지향하는 바가 일치해야 한다.
- 원칙: 목표를 위해 해야 할 일을 정렬하는 규칙으로, 때로는 변경될 수 있다.
- 실천 사항: 원칙을 실행하는 방법
필수 기준
서비스와 서비스간에 어떤 부분이 일정해야 하는지를 식별
└─ 바람직하게 동작하는 서비스의 모습을 정의해야 한다.
- 모니터링: 서비스 세부 상태가 아닌 시스템 전체 상태를 볼 수 있어야 한다.
- 서비스 지표: Graphite
- 서비스 상태: Nagios
- 인터페이스: 서비스 간 인터페이스 기술의 갯수는 최소화해야 한다.
- 아키텍처의 안정성: 오작동하는 하나의 서비스가 전체를 망가뜨리게 해서는 안되며, 서비스들이 비정상적인 하위 호출로부터 자신을 잘 보호해야 한다.
기술 부채
더 큰 그림을 보고 균형을 이해해야 한다.
└─ 아키텍트가 지침을 제공하거나 정기적으로 검토되는 부채 로그를 관리하면서 체계화한다.
어디서 부터 마이크로 서비스화 해야 할까?
느슨한 결합과 강한 응집력을 가지고 있어야 한다.
- 느슨한 결합: 헙업하는 서버스에 관해 알 필요가 없다.
- 강한 응집력: 서로 연관된 행위는 한데 모아두어야 한다.
경계가 있는 콘텍스트
- : 모든 도메인은 다수의 경계가 있는 콘텍스트로 구성되며, 각 콘텍스트 내에는 외부와 통신할 필요가 없는 모델 뿐만 아니라 경계가 있는 다른 콘텍스트 외부와 공유되는 모델이 함께 존재한다.
- 명료한 경계에 의해 강제된 구체적인 책임
- 감춰진 공유 모델: 외부에는 명시적인 인터페이스를 제공하며, 각자만 알고 있어야 할 세부사항이 있다.
- 모듈과 서비스: 서비스 경계를 잘못 정하면 큰 비용이 들 수 있다.
- 성급한 분해: 기존 코드베이스를 마이크로서비스로 분해하는 것이 처음부터 마이크로서비스로 가는 것 보다 훨씬 쉽다.
비즈니스 능력
도메인이 제공하는 콘텍스트의 능력 관점에서 봐야 한다.
거북이 밑에 거북이(내포된 콘텍스트)
더 넓고 큰 단위의 콘텍스트 관점에서 생각한 뒤 접합부의 분리를 통한 혜택을 발견했을 때 내포된 콘텍스트에 따라 세분화해야한다.
비즈니스 콘셉트 관점에서의 커뮤니케이션
마이크로서비스 간에 전송되는 형태를 조직 간에 전송되는 형태와 동일시하는 것이 유용하다.
올바른 통합
자율성을 유지하면서 다른 것과 독립적으로 변경하거나 배포할 수 있어야 한다.
이상적인 통합 기술 모색
- 호환성을 깨뜨리는 변경을 피해야 한다.
- API가 특정 기술에 종독되지 않도록 기술 중립성을 유지해야 한다.
- 소비자가 서비스를 쉽게 사용할 수 있어야 한다. (ex. 클라이언트 라이브러리 제공)
- 내부의 세부 표현을 외부로 노출하도록 강요하는 그 어떤 기술도 피해야 한다.
동기와 비동기
동기: 요청/응답, 클라이언트가 요청을 시작하고 응답을 기다리는 것
비동기: 이벤트 기반, 일(사건)이 발생했음을 알림
오케스트레이션과 코레오그래피
복잡한 로직을 모델링하기 시작하면 각 서비스의 경계를 넘나드는 비즈니스 프로세스들을 관리하는 문제에 직면한다.
오케스트레이션 아키텍처: 프로세스를 안내하고 구동하는 하나의 중앙 두뇌에 의존
└─ 지나치게 많은 중앙 관리
코레오그래피 아키텍처: 시스템 각 부분에 작업 내용을 알리고 세부 사항을 수행
└─ 비동기 방식으로 이벤트를 발산
└─ But, 중간에 이슈 발생?
└─ 대응할 수 있는 모니터링 시스템을 구축 필요!
원격 프로시저 호출(RPC)
- RPC이란?
- 지역 호출을 통해 원격 서비스를 실행하는 기술
└─ SOAP, Thrift, Protocol Buffer와 같은 인터페이스 정의에 의존
장점
- 쉬운 사용성
단점
- 기술적 결합(ex. 상호호환성의 제약)
- 지역 호출이 아닌 원격 호출을 사용 할 수 있다.
└─ 해킹, 패킷 변형, 딜레이 발생( 네트워트를 신뢰하면 안된다.) - 취성( ≒ 메짐성)
└─ 스텁 동기화(?) (ex. 필드 추가/삭제가 모두 맞아야 한다,)
└─ Protocol Buffer, Thrift 등이 이를 보완하고 있다.
주의할 점
- 원격 호출을 추상화 X(네트워크가 완전히 은폐될 정도)
- 확장가능한 서버 인터페이스인지 확인
REST
웹에서 영감을 얻은 아키텍처 방식
└─ 자원이 외부에 보여지는 방식과 내부에 저장되는 방식이 완전히 분리
└─ 리차드슨의 성숙 모델
HTTP와 REST
- method 지원(
GET
,POST
,PUT
,DELETE
)
└─ ex.,/createCustomer
/editCustomer
- 지원 도구와 기술의 거대한 생태계 제공
└─ ex. HTTP 캐시 프록시, 로드 밸런서, 모니터링 도구 - 보안 통제 활용
- RPC 구현 가능
장점
- 단순한 포맷
- XML에 비해 상대적으로 간소하다.
단점
- 하이퍼 미디어 컨트롤을 정의할 수 없다.
└─ HAL으로 공통 표준 정의
HTTP기반의 REST의 단점
-
일부 웹 서버 프레임워크가 모든 HTTP 메소드를 제대로 지원하지 않는다.
└─ REST 방식의 제약이 걸릴 수 있다. -
성능적 이슈가 있다.
└─ 지연 시간으로 인한 부하
└─ Thrift, Binary Protocol의 비해 느리다.
└─ 타프로토콜에 비해 느리다.
비동기 이벤트 기반의 협업 구현
기술 선택
고려해야 할 주요 문제
- 이벤트를 발산하는 방법
- 생성된 이벤트를 찾는 방법
대안
- 메시지 브로커(ex. RabbitMQ)
- HTTP
비동기 아키텍처의 복잡성
이벤트 기반 아키텍처는 복잡하다!
└─ 느린 응답, 타 서비스의 오류 등에 대한 대처
└─ 적재적소의 모니터링, 프로세스 경계 추적할 수 있는 상관관계 ID
상태 기계로서의 서비스
핵심 도메인 개념의 수명주기를 명확히 모델링하는 것이 좋다.
반응형 확장
Rx(반응형 확장): 다수의 호출 결과를 조합하고 그 결과에 따라 연산을 실행하는 메커니즘
└─ 분산시스템과 잘 어울린다.
마이크로서비스 세계에서 코드 재사용의 위험과 DRY
DRY(Don’t Repeat Yourself): 시스템의 행동양식과 지식의 중복을 회피하는 모든 시도
- 재사용할 수 있는 코드 생성
- 중복된 코드를 추상화
- 공유 라이브러리 생성
하지만, 지나친 결합이나 작은 변경 사항으로 인한 잦은 배포는 불편함을 초래할 수 있다.
└─ 개별 마이크로서비스 내에서는 DRY을 위반하지 않아야 하지만 전체 서비스 간의 DRY 위반은 너무 걱정하지 않아도 된다.
클라이언트 라이브러리
SDK 수준의 분리하는 것은 좋다.
└─ 각 서비스를 항상 독립적으로 릴리즈할 능력을 유지해야 한다.
참조에 의한 접근
도메인 개체의 정보를 전달하는 방법
도메인 개체의 변경과 관련된 로직의 중요성을 알아야 한다.
└─ 오래 유지할수록 메모리가 틀릴 가능성이 높다.
└─ ex) 고객에게 이메일을 보낼 때, 보내는 과정 사이에 고객의 정보는 바뀔 수 있다.
=> 보내는 시점에서 고객의 정보를 가져오는 것이 옳다.
└─ 이벤트 기반 협업 고려하자.
└─ 이벤트 발생 자체 뿐만 아니라 무슨 일(히스토리)이 일어났는지도 알아야 한다.
항상 자원에 접근하면 서비스의 부하가 커질 수 있다.
└─ 캐시 컨트롤을 이용하자.
모놀리식 애플리케이션을 어떻게 분해해야 할까?
접합부가 중요하다.
접합부(seam): 코드베이스의 나머지 부분에 영향을 주지 않는 격리된 코드 부분
즉, 우리 코드에서 경계가 있는 콘텍스트를 인식하는 것이다.
분해하기
콘텍스트를 대표하는 패키지를 구성
└─ 코드 이동으로 인한 테스트가 필요하다.(동적 타입 언어는 더욱 세밀하게 진행해야 한다.)
분리해야 하는 이유
서비스를 점진적으로 조금씩 분리하는 것이 합리적이다.
- 콘텍스트만 신속하게 변경이 가능하다.
- 팀 구조의 자유로움
- 빠른 보안 처리
- 빠른 기술
뒤엉킨 의존성
‘콘텍스트의 코드가 시스템의 나머지 부분과 어떻게 엉켜져 있는가’ 에 대한 고려가 필요하다.
└─ 대개 데이터베이스가 엉켜있다.
데이터베이스
데이터베이스 수준의 제약을 보기 위해 시각화해야 한다. (ex. SchemaSpy)