[책 정리] 마이크로서비스 개발 - 02

seul·2023년 5월 5일

반년만에 읽어보는 마이크로 서비스 개발
읽어야지, 읽어야지 하면서 순위만 밀려왔던 숙제 같은 책을 읽어본다.

도메인 주도 설계로 시작하는 마이크로 서비스 개발
w. 한정헌, 유해식, 최은정, 이주영

2. MSA의 이해

🫵 2.1 리액티브 선언: 현대 애플리케이션이 갖춰야 할 바람직한 속성들

현대의 애플리케이션은 도처에 존재해서 사람들은 기기에 포함된 애플리케이션이 요청에 즉각 응답하고 항상 가동되길 기대한다.
2014년 요나스 보네르 등이 선언한 리액티브 선언문에는 4가지 특성을 강조한다.

< 리액티브 선언문의 4가지 특성 >
1. 응답성 (Responsive): 사용자에게 신뢰성 있는 응답을 빠르고 적절하게 제공하는 것
2. 탄력성 (Resilient): 장애가 발생하거나 부분적으로 고장 나더라도 시스템 전체가 고장나지 않고 빠르게 복구하는 능력
3. 유연성 (Elastic): 사용량에 변화가 있더라도 균일한 응답성을 제공하는 것
4. 메시지 기반 (Message Driven): 비동기 메시지 전달을 통해 위치 투명성, 느슨한 결합, 논블로킹 통신을 지향하는 것을 의미

✌️ 2.2 강 결합에서 느슨한 결합의 아키텍처로의 변화

내용 별 거 없음
예전에는 검증된 유명 제품을 사용해서 품질이 보장된다고 생각했는데,
요즘에는 클라우드 환경 내에서 다양한 기능을 지원하는 게 더 중요하다고 생각 중~

🤟 2.3 마이크로서비스의 외부 아키텍처와 내부 아키텍처

  • 외부 아키텍처: 하드웨어 인프라, 애플리케이션을 운영 및 구동하기 위한 플랫폼, 애플리케이션 영역에 있는 구성요소와 그것들의 관계를 정의하는 것
  • 내부 아키텍처: 비즈니스 애플리케이션 (마이크로서비스의 내부 구조)

👉 2.4 MSA 구성 요소 및 MSA 패턴

  • 인프라 구성 요소: 마이크로서비스를 지탱하는 하부구조 인프라를 구축하는 데 필요한 구성 요소
  • 플랫폼 패턴: 인프라 위에서 마이크로서비스의 운영과 관리를 지원하는 플랫폼 차원의 패턴
  • 애플리케이션 패턴: 마이크로서비스 애플리케이션을 구성하는 데 필요한 패턴

2.4.1 인프라 구성요소

옛날에는 오랜 시간 동안 힘들게 구축했던 인프라를 AWS, 구글, 마이크로소프트, IBM 등이 IaaS, Paas 서비스를 통해 제공한다

마이크로서비스는 어떤 장비에도 구동될 수 있지만,
베어메탈 장비(어떤 소프트웨어도 담지 않은 하드웨어 서버 제품군 자체)로 구동한다면 각 마이크로서비스마다 장비를 구축해야 한다.
-> 그럼 당연히 가상 인프라 환경 검토하지

👶 VM과 컨테이너
가상 머신 제품과 컨테이너 제품 중 하나를 선택해야 한다.

  • 가상 머신: 하이퍼바이저(hypervisor) 소프트웨어를 이용해서 하나의 시스템에서 여러 개의 운영체제를 사용하는 기술이다. 게스트 OS를 사용하기 때문에 운영체제 패치 설치, 라이브러리 설치 등 때문에 지속적으로 오버헤드가 발생할 수 있다. (ex. AWS EC2, 애저(Azure) VM)
  • 컨테이너: 작은 서비스를 패키지 하고 배포하기에 적합 (ex. Docker)

도커의 특징

  • 이식성: 어떠한 호스트 커널이나 플랫폼 버전에 상관없이 도커만 실행할 수 있으면 사용 가능, 동일하게 동작
  • 신속성: 크기가 작고 바겨워서 빠르게 배포 가능, 문제 발생 시 수정 필요 없이 새로 가동
  • 재사용성: 동일한 환경을 재사용해서 설정 가능 (개발, 테스트, 스테이징, 프로덕트 환경을 동일한 환경으로 구축)

👶 컨테이너 오케스트레이션
컨테이너를 관리하기 위한 기술
컨테이너가 많아지면 컨테이너의 자동 배치 및 복제, 장애 복구, 확장 및 축소, 컨테이너 간 통신, 로드 밸런싱 등의 컨테이너 관리를 위한 기능이 필요해진다.
(ex. 도커 스윔, 아파치 메소스, 쿠버네티스)

👶 그 밖의 다양한 클라우드 인프라 서비스
AWS, Azure, GCP 등은 기존의 물리 서버, 네트워크, 스토리지, DB를 대체할 다양한 가상 서버, 가상 네트워크 및 가상 스토리지, 가상 DB 등을 제공하고 있다.

2.4.2 마이크로 서비스 운영과 관리를 위한 플랫폼 패턴

👧 개발 지원 환경: 데브옵스 인프라 구성
수동 빌드/배포 하면 시간이 진짜 많이 소요된다. 자동화가 절실하다...
자동화된 빌드나 배포 작업을 CI/CD라고 한다.

CI(Continuous Integration): 지속적인 통합

  • 오랜 시간이 걸리는 빌드를 매일 자동화해서 수행한다면 개발 생산성이나 소스코드 품질이 높아진다는 경험에서 출발했다.
  • 자동으로 통합 및 테스트하고 그 결과를 리포트로 기록하는 활동

CD(Continuous Delivery, Continuous Deployment): 지속적 제공, 지속적인 배포

  • 지속적 제공: 빌드된 소스코드의 실행 파일을 실행 환경에 반영하기 전 단계까지 진행하는 방식 (배포 수동)
  • 지속적 배포: 소스코드 저장소에서 빌드한 소스코드의 실행 파일을 실행 환경까지 자동으로 배포하는 방식

👧 빌드/배포 파이프라인 설계
빌드/배포 되는 과정 동안 수행해야 할 태스크가 정의된 것

자동화 될 요소들

  • 형상관리 레포지토리에서 소스코드를 가져와 빌드해서 실행 파일을 만드는 작업
  • 실행 파일을 실행 환경에 배포하는 작업
  • 이런 작업들을 통제하고 연결해서 전 작업이 성공하면 다음 작업이 자동으로 수행되게 하는 연계 자동화 작업

매우 힘들어서 일부만 자동화 할 수 있다.
이건... 알아서 적용하기.

👧 마이크로서비스 생태계와 운영 관리 요소의 탄생
넷플릭스가 큰 거 했다.

아마존이 2006년에 IaaS 서비스인 EC2를 발표했고, 넷플릭스가 그즈음에 스트리밍 데이터베이스의 스토리지가 손실되는 대규모 서비스 장애를 겪는다.
그래서 모노리스 시스템에서 마이크로서비스 기반의 시스템으로 전환한다.

그랬더니 여러 문제점이 발생하고 이 해결법으로
넷플릭스OSS를 공개한다.
마이크로서비스 간의 라우팅과 로드 밸런싱을 위한 줄(Zuul)과 리본(Ribbon), 모니터링을 위한 히스트릭스(Hystrix), 서비스 등록을 위한 유레카(Eureka)...

이후 2013년에 도커가 등장하고,
이쯤에 스프링에서 스프링 부트를 발표하고,
최근에는 구글의 쿠버네티스가 등장했다.

👧 경험으로 획득한 지혜: 마이크로 서비스 관리/운영 패턴
넷플릭스의 오픈소스로 인해 마이크로서비스 관리와 운영을 지원하는 전형적인 패턴이 생겼다.
(ex. API 게이트웨이, 서비스 디스커버리, 모니터링, 트레이싱)

👧 스프링 클라우드: 스프링 부트 + 넷플릭스 OSS
1. 모든 마이크로서비스(스프링 클라우드 서비스를 포함한)는 인프라에 종속되지 않도록 데이터베이스, 파일 등에 저장된 환경 설정 정보를 형상관리 시스템에 연계된 'Config 서비스'에서 가져와 설정 정보를 주입한 후 클라우드 인프라의 개별 인스턴스로 로딩된다.
2. 로딩과 동시에 '서비스 레지스트리'에 자신의 서비스명과 클라우드 인프라부터 할당받은 물리 주소를 매핑해서 등록한다.
3. 클라이언트가 'API 게이트웨이'를 통해 마이크로서비스에 접근하고, 이때 API 게이트웨이는 적절한 라우팅 및 부하 관리를 위한 로드 밸런싱을 수행한다.
4. 또한 API 게이트웨이에서 클라이언트가 각 서비스에 접근하기 위한 주소를 알기 위해 '서비스 레지스트리' 검색을 통해 서비스의 위치를 가져온다.
5. 동시에 API 게이트웨이는 클라이언트가 각 서비스에 접근할 수 있는 권한이 있는지 '권한 서비스'와 연계해 인증/인가 처리를 수행한다.
6. 이러한 모든 마이크로서비스 간의 호출 흐름은 '모니터링 서비스'와 '추적 서비스'에 의해 모니터링되고 추적된다.

👧 다양한 서비스의 등록 및 탐색을 위한 서비스 레지스트리, 서비스 디스커버리 패턴
여러 개의 백엔드 마이크로서비스를 어떻게 호출할지, 인스턴스가 여러 개로 복제됐다면 어떻게 부하를 적절히 분산할까?
-> 서비스 디스커버리 패턴을 보세요!

너무 길어서 이건 책 보거라...

다 적기 힘들다. 밑에서부터는 중요한 것만 적겠다.

👧 외부 구성 저장소 패턴
데이터베이스 연결 정보, 파일 스토리지 정보가 애플리케이션에 포함되면 변경 시 반드시 재배포 해야 한다. 그럼 서비스 중단을... 해야 하는데!

그래서 외부 구성 저장소 패턴이 나왔댄다.
클라우드에서 운영되는 애플리케이션은 특정한 배포 환경에 종속된 정보를 두면 안 된다는 원칙이다.

분리해야 할 환경 정보?
데이터베이스 연결 정보, 배포 시 변경해야 할 호스트명, 백엔드 서비스의 연결을 위한 리소스 정보, 서비스가 기동되는 개발 서버, 테스트, 운영 서버의 IP 주소와 포트 정보 등

스프링 클라우드 컨피그를 이용하여 애플리케이션에서 분리하고 컨피그 서비스를 통해 런타임 시 주입되게 한다.


👧 인증/인가 패턴
원래는 세션에 사용자의 로그인 정보와 권한 정보를 관리했는데 마이크로서비스를 이용하면 세션 데이터가 손실될 수 있다.

그래서 클라이언트 토큰으로
1. 사용자가 서버에 로그인 하면
2. 서버에서 인증 확인 후 토큰을 생성하고
3. 서버에서 사용자 정보를 담은 토큰 전송하고
4. 사용자가 토큰으로 리소스 접근 요청하고
5. 서버가 토큰을 검토해서 인가 허용하고
6. 서버가 리소스 접근을 허용한다.

이 방법은 API 게이트웨이를 사용해서 인증 서비스로 연결되도록 할 수 있다.


👧 장애 및 실패 처리를 위한 서킷 브레이커 패턴
A라는 서비스가 B라는 서비스를 호출해서 자신의 서비스를 제공하는데,
B에서 장애가 발생하면 동기 요청의 성격상 A가 계속 기다려서 사용자는 A에서 장애가 발생한 것처럼 생각한다.

서킷 브레이커 패턴은
B 서비스 호출에 대한 연속 실패 횟수가 임곗값을 초과하면 회로 차단기가 작동해서 이후에 서비스를 호출하려는 모든 시도를 즉시 실패하게 만든다.
그리고 폴백(fallback) 메서드를 지정해 두면 장애가 발생했을 때 장애가 발생했을 대 폴백 메서드가 자연스럽게 처리를 진행한다.


👧 모니터링과 추적 패턴
그럼 장애를 어떻게 감지하나요...?

스프링 클라우드에서는 히스트릭스라는 라이브러리를 제공한다.
히스트릭스 대시보드로 요청을 실시간으로 볼 수 있다.

분산은 책으로 보삼!


👧 중앙화된 로그 집계 패턴
서비스에서 발생한 이벤트 스트림 형태의 로그를 수집하고 살펴볼 도구가 필요하다.
대표적으로 사용하는 것이 ELK 스택이다.

  • 엘라스틱서치 Elasticsearch: 분석 엔진, 원하는 방법으로 검색을 수행 및 결합
  • 로그스태시 Logstash: 로그 집합기, 다양한 소스에서 동시에 데이터를 수집해 변환한 뒤 특정 보관소로 데이터를 보냄
  • 키바나 Kibana: 시각화

예를 들자면,
각 서비스에 로그스태시가 설치되어 각 로그를 수집해서 레디스 저장소에 보낸다.
또 다른 서비스에서는 엘라스틱서치와 키바나로 로그 중앙 관리 저장소와 대시보드 서비스를 각각 구축한다.
로그가 중앙 레디스에 쌓이면 레디스에서 중앙 관리 저장소에 로그를 보내고,
로그 저장소에 엘라스틱서치 엔진이 로그를 인덱승해서 키바나로 보여준다.
(레디스 왜 두나요? 로그 스트림이 몰리면 성능 문제 생겨용)


서비스 메시 패턴과 이스티오는 처음 들어서 조금 더 공부해야 할 듯... 하다.


2.4.3 애플리케이션 패턴

👧 프론트엔드
업무 기능 하나가 변경되어 재배포해야 할 때,
백엔드: 수정 후 하나의 서비스로 독립적 배포
프론트엔드: 덩어리이기 때문에 변경되지 않은 다른 기능도 함께 빌드 및 배포 됨

해결 방안이 밑에...


👧 UI 컴포지트 패턴 또는 마이크로 프론트엔드
기능별로 분리하고 이를 조합하기 위한 프레임(frame) 형태의 부모 창을 통해 각 프론트엔드를 조합해서 동작하게 한다.


👧 마이크로서비스 통신 패턴

  • 동기 통신 방식
    • 클라이언트에서 서버 측에 존재하는 마이크로서비스 REST API를 호출할 때 사용되는 기본 통신 방법
    • 요청(request)하면 바로 응답(response)이 오는 방식
    • 마이크로서비스인 상황에서 만약 A 서비스에서 B 서비스를 호출했는데 B에서 응답이 없다면? 반응이 오지 않아서 계속 기다리게 되고 의존 관계가 높아진다
  • 비동기 통신 방식
    • 메시지를 보낸 다음에 응답을 기다리지 않고 다음 일을 처리한다
    • 보낸 결과과 어떻게 됐는지 응답을 받지 않으므로 동기식처럼 완결성을 보장할 수 없음
    • 매커니즘: 메시지 브로커 (ex. 아파치 카프카, 래빗엠큐, 액티브엠큐)

메시지 브로커
메시지를 보내는 생산자(producer)와 메시지를 가져다가 처리하는 소비자(consumer)가 서로 직접 접속하지 않고 메시지 브로커에 연결된다.

  • 물리적으로 동일한 시스템에 위치할 필요 없음
  • 프로세스 공유할 필요 없음
  • 동일한 시간대에 동시에 동작하지 않아도 된다


    👧 저장소 분리 패턴
    국내 엔터프라이즈 애플리케이션 내부를 보면 모든 비즈니스 로직이 데이터베이스의 SQL 처리에 몰려있는 경우가 대부분 => 데이터 중심 애플리케이션

저장소 분리 패턴은,
각 마이크로서비스는 각자의 비즈니스 처리를 위한 데이터를 직접 소유한다.

  • 데이터를 다른 서비스에 직접 노출하지 않음

  • API를 통해서만 접근

  • 저장소를 자율적으로 선택할 수 있음

  • 변경의 파급 효과가 적음


    👧 분산 트랜잭션 처리 패턴
    비즈니스 정합성과 데이터 일관성을 어떻게 보장할 것인가?

  • 사가(Saga) 패턴
    - 분산된 서비스를 하나의 트랜잭션으로 묶지 않고 각 로컬 트랜잭션과 보상 트랜잭션을 이용
    - 로컬 트랜잭션이 자신의 데이터베이스를 업데이트 하고, 다음 로컬 트랜잭션을 트리거하는 메시지 또는 이벤트를 게시하여 데이터의 일관성을 맞춤
    - 트랜잭션 실패 시 보상 트랜잭션을 통해 이전 로컬 트랜잭션이 작성한 변경 사항을 취소


    👧 데이터 일관성에 대한 생각의 전환: 결과적 일관성
    예전에는 실시간으로 일관성을 지켜야 하는 경우가 많았는데,
    모든 비즈니스 처리가 반드시 실시간성을 요구하는 것이 아니다

결과적 일관성: 데이터의 일관성이 실시간으로 맞지 않더라도 어느 일정 시점이 됐을 때 일관성을 만족해도 되는 것


👧 읽기와 쓰기 분리: CQRS 패턴
CQRS: Command Query Responsibility Segregation, 명령 조회 책임 분리

  • 도입 원인: 서비스 인스턴스를 스케일 아웃 해서 여러 개로 실행한 경우 데이터 읽기/수정 작업으로 인한 리소스 교착 상태가 발생할 수 있다

  • 방식: 저장소에 읽기 모델과 쓰기 모델을 분리하는 방식으로 변화시킨다

  • 명령 측면 마이크로서비스
    • 입력, 수정, 삭제 처리 수행
    • 쓰기에 최적화된 관계형 데이터베이스 수행
    • 업무 규칙을 표현하기 좋은 자바 언어
  • 조회 측면 마이크로서비스
    • 조회만 수행
    • 조회 성능이 높은 NoSQL 데이터베이스 저장소 사용
    • 조회를 간단하게 구현하는 Node.js
    • 사용량이 많아 스케일 아웃 해서 인스턴스 증가

일관성이 깨지게 되니까 이벤트 주도 아키텍처 도입
명령 서비스는 저장소에 데이터를 쓰면 이벤트를 발생시켜 메시지 브로커에 전달
조회 서비스는 메시지 브로커의 이벤트를 구독하고 있다가 이벤트 데이터를 가져와 최신 상태로 동기화

관련한 API 통신에 도입되는 CQRS는 실제로 도움이 될 것으로 보인다.
책을 참고하자 ^^

profile
자존감은 일상의 성실함으로부터 온다

0개의 댓글