2장 : MSA의 이해
2.1 : 리액티브 선언 : 현대 애플리케이션이 갖춰야 할 바람직한 속성들
리액티브 시스템을 만들기 위한 요소
-
응답성 : 사용자에게 신뢰성 있는 응답을 빠르고 적절하게 제공하는 것
-
탄력성 : 장애가 발생하거나 부분적으로 고장 나더라도 시스템 전체가 고장 나지 않고 빠르게 복구하는 능력
-
유연성 : 시스템의 사용량에 변화가 있더라도 균일한 응답성 제공, 시스템 사용량에 비례해서 자원을 늘리거나 줄이는 능력
-
메시지 기반 : 비동기 메시지 전달을 통해 위치 투명성, 느슨한 결합, 논블로킹 통신을 지향하는 것
리액티브 시스템이 반드시 갖춰야 할 공통적인 특성 : 아키텍처 유연성
시스템을 구성하는 구성요소 간의 관계들이 느슨하게 맺어져 있어 언제든지 대체되거나 추가 확장될 수 있는 특성
2.2 : 강 결합에서 느슨한 결합의 아키텍처로의 변화
예전에는 아키텍처의 구성요소들을 각 기업이나 특정 벤더의 제품에 전적으로 의존해서 구축했다
특정 벤더의 제품에 수정이 필요한 부분이 있다면 개발을 하고, 해당 벤더의 프레임워크가 변경된다면 의존하는 애플리케이션 또한 변경해야 했다
이에 따라 쉽게 변경하거나 확장하지 못한다는 단점이 따라온다
최근에는 클라우드 환경하에서 필요한 영역에 적절한 솔루션을 선택하고 조합하는 개방적인 방식으로 바뀌고 있다
2.3 : 마이크로서비스의 외부 아키텍처와 내부 아키텍처
마이크로서비스 아키텍처는 인프라, 플랫폼, 애플리케이션으로 구분된다
-
외부 아키텍처 : 인프라, 플랫폼, 애플리케이션 영역에 있는 구성요소 및 관계를 정의하는 것
-
내부 아키텍처 : 마이크로서비스가 제공하는 API, 비즈니스 로직, 이벤트 발행, 데이터 저장 처리 등 구조화에 대한 내용
2.4 : MSA 구성요소 및 MSA 패턴
인프라 구성요소 : 마이크로서비스를 지탱하는 하부구조 인프라를 구축하는 데 필요한 구성요소
플랫폼 패턴 : 인프라 위에서 마이크로서비스의 운영과 관리를 지원하는 플랫폼 차원의 패턴
애플리케이션 패턴 : 마이크로서비스 애클리케이션을 구성하는 데 필요한 패턴
인프라 구성요소
퍼블릭 클라우드와 베어 메탈, 프라이빗 클라우드 환경
맨 하부의 시스템의 기반이 되는 인프라를 구축해야 한다
물리적인 베어 메탈 장비를 구매 ? 가상화 환경을 선택 ?
마이크로서비스는 어떠한 환경에서도 유연하도록 구성돼야 하므로 특정 인프라를 고집하지 않는다
베어 메탈을 고려한다면 프라이빗 클라우드 환경을 구축하는 것을 의미한다
VM과 컨테이너
가상 머신에서는 운영체제 패치 설치나 관련 라이브러리 설치 같은 오버헤드가 지속적으로 발생
마이크로서비스 같은 작은 서비스를 패키지하고 배포하기에는 컨테이너 환경이 더 적합
도커 컨테이너의 이점
-
이식성 : 어떠한 호스트 커널이나 플랫폼 버전에 상관없이 사용 가능하며 동일하게 동작
-
신속성 : 크기가 작고 가볍기 때문에 빠르게 배포 가능, 문제 발생 시 수정할 필요 없이 새로 가동
-
재사용성 : 동일한 환경을 재사용해서 쉽게 설정 가능하기 때문에 개발, 테스트, 스테이징, 프로덕트 환경을 동일한 환경으로 구축
컨테이너 오케스트레이션
컨테이너를 관리하기 위한 기술
자동 배치 및 복제, 장애 복구, 확장 및 축소, 컨테이너 간 통신, 로드 밸런싱 등
이러한 기술을 컨테이너 오케스트레이션이라 한다
대표적으로 쿠버네티스가 있다
-
자동화된 자원 배정 : 각 컨테이너가 필요로 하는 CPU 와 메모리를 쿠버네티스에 요청하면 컨테이너를 노드에 맞춰 자동으로 배치
-
셀프 치유 : 컨테이너의 이상 유무를 점검해서 실패한 경우 자동으로 교체하고 재스케줄링
-
수평 확장 : 일정 CPU 및 메모리 사용량을 초과하면 자동으로 확장
마이크로서비스 운영과 관리를 위한 플랫폼 패턴
개발 지원 환경 : 데브옵스 인프라 구성
기존에는 수동으로 테스트, 오류 수정, 스테이징 환경 배포 등등 시간이 엄청 오래 걸렸다
-
데브옵스 : 개발과 운영을 병행 가능하게끔 높은 품질로 소프트웨어를 빠르게 개발하도록 지원하는 빌드, 테스트, 배포를 위한 자동화 환경
-
CI : 자동으로 통합하고 테스트하고 리포트로 남기는 활동
-
CD : 실행 환경에 자동으로 배포하는 것
빌드/배포 파이프라인 설계
빌드/배포 파이프라인 : 빌드/배포되는 과정 동안 수행해야 할 태스크가 정의된 것
레포지토리 - 빌드&유닛 테스트 - 정적 분석 - 통합 테스트 - 배포 - 마이크로서비스
클라우드 같은 가상환경이 대중화 되면서 완전한 자동화 가능해졌다
하지만, 모든 과정을 자동화하는 과정은 매우 힘든 작업이기에 일부는 자동화, 일부는 수동화 처리할 수도 있다
애플리케이션마다 빌드/배포 파이프라인 또한 다르게 설정할 수 있다
스프링 클라우드 : 스프링 부트 + 넷플릭스 OSS
-
모든 마이크로서비스는 인프라에 종속되지 않도록 데이터베이스, 파일 등에 저장된 환경 설정 설정 정보를 형상관리 시스템에 연계된 ‘Config 서비스’ 에서 가져와 설정 정보를 주입한 후 클라우드 인프라의 개별 인스턴스로 로딩
-
로딩과 동시에 ‘서비스 레지스트리’에 자신의 서비스명과 클라우드 인프라부터 할당받은 물리 주소를 매핑해서 등록
-
클라이언트가 ‘API 게이트웨이’를 통해 마이크로서비스에 접근하고, 이때 API 게이트웨이는 적절한 라우팅 및 부하 관리를 위한 로드 밸런싱을 수행한다
-
또한 API 게이트웨이에서 클라이언트가 마이크로서비스에 접근하기 위한 주소를 알기 위해 ‘서비스 레지스트리’ 검색을 통해 서비스의 위치를 가져온다
-
동시에 API 게이트웨이는 클라이언트가 각 서비스에 접근할 수 있는 권한이 있는지 ‘권한 서비스’와 연계해 인증/인가 처리를 수행한다
-
이러한 모든 마이크로서비스 간의 호출 흐름은 ‘모니터링 서비스’와 ‘추적 서비스’에 의해 모니터링되고 추적된다
서비스 단일 진입을 위한 API 게이트웨이 패턴
여러 클라이언트가 여러 개의 서버 서비스를 각각 호출하게 된다면 매우 복잡한 호출 관계가 만들어진다
이를 위해 단일 진입을 위한 API 게이트웨이를 제공한다
-
레지스트리 서비스와 연계한 동적 라우팅, 로드 밸런싱
-
보안 : 권한 서비스와 연계한 인증/인가
-
로그 집계 서비스와 연계한 로깅 ex) API 소비자 정보, 요청/응답 데이터
-
메트릭 ex) 에러율, 평균/최고 지연시간, 호출 빈도
-
트레이싱 서비스와 연계한 서비스 추적 ex) 트래킹 ID 기록
-
모니터링 서비스와 연계한 장애 격리
스프링 API 게이트웨이 서비스를 활용할 수 있다
BFF 패턴
- Backend for Frontend
API 게이트웨이와 같은 진입점을 하나로 두지 않고 프런트엔드의 유형에 따라 각각 두는 패턴
모바일, 웹, 이기종 앱 등 클라이언트 종류에 따라 최적화된 처리를 수행할 수 있게 구성
외부 구성 저장소 패턴
데이터베이스 연결 정보, 파일 스토리지 정보 같은 내용을 애플리케이션에 포함하면 변경 시 반드시 재배포 해야 한다
그러면, 서비스를 중단해야 하고 여러 마이크로 서비스가 동일한 구성 정보를 사용하는 경우에도 일일이 변경하기 어렵고 불일치 가능성이 있다
이에 따라 일관되게 변경 가능하도록 관리를 하는 외부 저장소 패턴을 이용한다
애플리케이션이 배포되는 환경이 매번 달라지기 때문에 코드에서 사용하는 환경 설정 정보는 코드와 완전히 분리되어 관리해야 한다는 원칙을 가리킨다
인증/인가 패턴
- 중앙 집중식 세션 관리
각자의 서비스에 세션을 저장하지 않고 공유 저장소에 세션을 저장하고 모든 서비스가 동일한 사용자 데이터를 얻게 한다
이때 보통 레디스, 멤캐시드를 사용한다
- 클라이언트 토큰
토큰은 사용자의 브라우저에 저장된다
토큰은 사용자의 신원 정보를 가지고 있고 서버로 요청을 보낼 때 전송되기 때문에 서버에서 인가 처리를 할 수 있다
- API 게이트웨이를 사용한 클라이언트 토큰
API 게이트웨이가 외부 요청의 입구로 추가 된다
인증/인가 처리하기 위한 별도의 전담 서비스를 만들어서 다른 서비스의 인증/인가 처리를 위임할 수 있다
이를 통해 각 리소스 서비스가 자체적으로 인증/인가를 처리하지 않고 업무 처리에 집중할 수 있다
장애 및 실패 처리를 위한 서킷 브레이커 패턴
A라는 서비스가 B라는 서비스를 호출해서 자신의 서비스를 제공하는데,
B 서비스에서 장애가 발생하면 동기 요청의 성격상 A는 계속 기다리게 된다
이때 A 서비스까지도 장애가 발생한 것처럼 사용자가 느끼게 된다
이때, 서킷 브레이커 패턴은 B 서비스 호출에 대한 연속 실패 횟수가 임곗값을 초과하면 회로 차단기가 작동해서 이후에 서비스를 호출하려는 모든 시도를 즉시 실패하게 만든다
애플리케이션 패턴
마이크로서비스 통신 패턴
- 동기 통신 방식
동기 호출 방식은 클라이언트에서 서버 측에 존재하는 마이크로서비스 REST API 를 호출할 때 사용되는 기본 통신 방법
다양한 클라이언트 채널 연계나 라우팅 및 로드 밸런싱을 원활하게 하기 위한 방법으로 중간에 API 게이트웨이를 둘 수 있다
동기 방식은 요청하면 바로 응답이 오는 방식이다
여러 서비스 간의 연계를 통해 업무를 처리하는 마이크로서비스 구조에서는 이 같은 상황에서 장애가 연쇄적으로 발생할 수 있다
- 비동기 통신 방식
동기 호출 처럼 응답을 기다리지 않고 다음 일을 처리한다
요청을 보낸 결과가 어떻게 됐는지 응답을 받지 않으므로 동기식처럼 완결성을 보장할 수는 없다
이를 보장하기 위한 메커니즘으로 아파치 카프카, 래빗엠큐, 액티브엠큐 같은 메시지 브로커를 활용한다
이 방식은 메시지 브로커에 의해 중계되기 때문에 서로 통신하는 서비스들이 물리적으로 동일한 시스템에 위치할 필요도 없고 서로 프로세ㅐ스를 공유할 필요도 없으며, 동일한 시간대에 동시에 동작하지 않아도 된다
저장소 분리 패턴
기존에는 애플리케이션 모듈은 분리하되 저장 처리는 모듈별로 격리하지 않고 다른 모듈에서의 호출을 허용하는 구조였다
각 마이크로서비스는 각자의 비즈니스를 처리하기 위한 데이터를 직접 소유해야 한다
자신이 소유한 데이터는 다른 서비스에 직접 노출하지 않고 각자가 공개한 API 를 통해서만 접근 가능
여러 개의 분산된 서비스에 걸쳐 비즈니스 처리를 수행해야 하는 경우 비즈니스 정합성 및 데이터 일관성 보장이 어렵다는 문제가 있다
분산 트랜잭션 처리 패턴
마이크로서비스의 독립적인 분산 트랜잭션 처리를 지원하는 패턴이 사가 패턴이다
각 서비스의 로컬 트랜잭션을 순차적으로 처리하는 패턴
각 로컬 트랜잭션은 자신의 데이터베이스를 업데이트한 다음, 사가 내에 있는 다음 로컬 트랜잭션을 트리거하는 메시지 또는 이벤트를 게시해서 데이터의 일관성을 맞춘다
롤백이 필요하다면?
보상 트랜잭션을 활용한다
어떤 서비스에서 트랜잭션 처리에 실패할 경우 그 서비스의 앞선 다른 서비스에서 처리된 트랜잭션을 되돌리게 하는 트랜잭션이다
데이터 일관성에 대한 생각의 전환 : 결과의 일관성
이전까지는 데이터 일관성이 실시간으로 반드시 맞아야 한다는 생각이 일반적이었다
주문자가 폭주한 경우 주문, 결제, 이메일 처리 같은 순차적인 동시 일관성을 추구하는 것이 아니라
비즈니스 관점에서는 무조건 주문을 일단 많이 받아 놓는 것이 좋을 수 있다
데이터의 일관성이 실시간으로 맞지 않더라도 어느 일정 시점이 됐을 때 일관성을 만족해도 되는 것이 있다
이러한 것을 결과적 일관성 이라고 한다
읽기와 쓰기 분리 : CQRS 패턴
서비스별 데이터 저장소 패러다임에서 서비스의 성능 향상을 위해 서비스 인스턴스를 스케일 아웃해서 여러 개로 실행한 경우
데이터 읽기/수정 작업으로 인한 리소스 교착상태가 발생할 수 있다
이를 위해 명령 조회 책임 분리를 적용한다
일반적인 사용자 서비스에서는 상태 변경을 하는 명령보다는 조회하는 명령이 많이 쓰인다
이렇게 많이 쓰이는 서비스와 상대적으로 적게 쓰이는 서비스를 분리 시키는 것이다
입력, 수정, 삭제 처리를 수행하고 저장소로는 쓰기에 최적화된 관계형 데이터베이스를 사용하고, 업무 규칙을 표현하기 위한 자바 언어를 사용한다
조회 측면에서는 조회 성능이 높은 몽고디비 같은 NoSQL 데이터베이스를 저장소로 사용하고, 간단하게 구현할 수 있는 Node.js 를 사용ㅇ한다
또한, 조회는 많이 일어나기 때문에 스케일 아웃해서 인스턴스를 증가시켜 놓을 수 있다