마이크로 서비스 - (3) 마이크로 서비스 구현 후 회고

임쿠쿠·2022년 6월 19일
1

Microservice

목록 보기
3/3
post-thumbnail

참고)
https://github.com/kimkevin90/node_microservice_second

1. 프로젝트 개요

1) 서비스

(1) 인증

  • 회원가입 / 로그인 / 로그아웃 기능 구현
  • k8s secret을 활용한 JWT_KEY 관리 및 env 적용

(2) 티켓(상품)

  • 상품 조회, 생성, 업데이트 기능 구현
  • 상품 생성, 업데이트 이벤트 publishing
  • 주문 생성, 취소 이벤트 listening

(3) 주문

  • 주문 조회, 생성, 삭제 기능 구현
  • 주문 생성, 취소 이벤트 publishing
  • 결제 기한 만료, 결제 취소, 상품 생성, 상품 업데이트 이벤트 listening

(4) 결제 기한 만료

  • 결제 기한 만료 이벤트 publishing
  • 오더 생성 이벤트 listening
  • 오더 15분 후 만료 이벤트 발생을 위해 bull.js Queue 사용

(5) 결제 서비스

  • 결제 생성 기능 구현
  • 결제 생성 이벤트 publishing
  • 주문 생성, 취소 이벤트 listening

2) Event-Bus

  • node-nats-streaming을 통한 event-bus 구현
  • 각 서비스에 Listener, Publisher 제공을 위해 공통 패키지에서 관리

3) 공통 패키지

  • 에러 처리, 미들웨어(인증), 이벤트 관리

4) 인프라 관리

  • 각 서비스 별 docker image 생성
  • 각 서비스 별 k8s deployment docker 이미지 적용 및 service 생성
  • ingress-controller를 통한 서비스 연결

2. 구현의 어려움

1) 서비스 아키텍처 구성의 어려움

  • 전체 시스템을 각각 어떤 서비스로 분리할지 도메인 별 성격, 크기, 목적을 정의하기란 쉽지않습니다.

2) 데이터 동시성 문제

마이크로서비스의 핵심은, 서비스간 종속성을 분리하는 것입니다. 하지만, 상품 & 주문 서비스 처럼 쉽게 분리할 수 없는 서비스들이 현실에 다양하게 존재합니다.

(1) 문제점

  • 위 도식처럼 상품에 대한 가격, 재고 변화 이벤트가 다량으로 발생 할 시, 해당 이벤트는 비동기로 진행되므로 이를 수신하는 오더 서비스는 최신화된 상품의 정보를 판별할 수 없습니다.

  • 모놀로직 서비스에서는 트랜잭션을 통해 위 문제를 해결했다면, 서비스별 db가 분리된 상태에서 이벤트 정보만으로 이를 극복하기 쉽지않습니다.

(2) 해결방안

  • 결국 비동기 이벤트 발생으로 인한 문제 해결은 이벤트를 emit하는 서비스에서 이벤트에 대한 정확한 명세가 필요합니다.

  • 예를들어 이벤트를 퍼블리싱 시 이벤트에 대한 시퀀스 정보도 같이 전달합니다. 즉 버전 정보를 제공하여 여러 이벤트의 순서를 리스닝 서비스에서 식별할 수 있도록 합니다.

상품과 주문 서비스

  • 필연적인 데이터 중복 발생 : 오더 서비스와 상품 서비스의 종속성을 분리하는 과정에서, 오더서비스에서는 필요한 상품의 일부 프로퍼티를 기록할 필요가 있습니다. 다량의 상품 업데이트에 따른 버전관리를 시행하고, 버전에 따른 상품 정보를 오더 서비스의 상품 스키마에 기록하여 올바른 가격, 재고 등을 업데이트하고 이를 반영한 주문 처리를 진행 할 수 있습니다.

  • 오더서비스는 버전의 순서에 따라 상품 정보를 업데이트하며, 만일 상위 버전의 이벤트가 먼저 수신된다면 이를 이벤트 버스에 롤백 후 일정 시간 뒤 재 수신할 수 있습니다.

주문과 결제 서비스

  • 결제 서비스에서 주문 서비스 이벤트를 리스닝 시, 주문 취소 or 주문 생성 이벤트를 동시에 수신합니다. 수신한 주문 이벤트 버전관리를 통해 현재 동일한 주문의 상태가 진행되야하는지 취소되야하는지 버전의 순서에 따라 결제 진행 여부를 판별 할 수 있습니다.

  • 주문이 생성되면 해당 상품은 더이상 가격변경이 불가 하며, 결제 만료 서비스에서는 15분 대기 후 이를 초과 하면 결제 만료 이벤트를 응답합니다.

3) 추상과 구현의 분리

  • 각 서비스는 동일한 이벤트 버스를 수신 및 발신 합니다. 이로 인해 다량의 중복된 코드가 발생합니다.

  • 공통 패키지에 Event 추상 클래스를 만들고, 각 서비스에 따른 정보를 반영하도록 인터페이스를 구축합니다.

4) 인프라 관리

  • 각 서비스의 api 및 db 서버, event-bus 서버, 라우팅 서버 등 기존에 모놀로직 서비스로 시스템을 구현했을 때 보다 많은 양의 서버를 관리할 필요가 있습니다.

  • 해당 서비스 별 자동화 된 모니터링, 스케일링, 로깅 등을 진행하기 위해 서비스별 deployment & Service를 생성했고, 서비스별 연결은 ingress-contorller를 통해 진행했습니다.

5) 테스트의 어려움

  • 각 서비스는 독립적으로 운영되면서 동시에 이벤트 수신에 따른 다양한 정보가 변경되는데 이러한 과정을 테스트하기가 쉽지않았습니다.

  • jest fake 이벤트를 발생시켜 각 서비스별 테스트를 진행했습니다.

3. 그럼에도 불구하고 마이크로 서비스?

  • 작년에 모 회사에서 면접을 볼때 한 시니어 개발자가 마이크로 서비스에 대해 굉장히 부정적인 의견을 피력했습니다. 그 당시에 저는 단순히 해당 개발자가 레거시에 익숙해서 그런거 아닐까 라는 생각도 들었습니다. 그분에게 https://www.samsungsds.com/kr/insights/msa.html 해당 사이트를 참조하라는 의견을 끝으로 면접은 마무리 됐습니다.

  • 막상 구현해보면서 해당 시니어의 뜻을 조금 더 이해할 수 있었습니다. 마이크로 서비스는 확연히 모놀로직 서비스보다 운영 & 구현의 어려움이 높았습니다. 전체적인 아키텍처도 잘 분리해야 하며, 분리된 서비스 별 동시성 문제는 어떻게 해결할지에 대한 고민도 필요했습니다. 때로는 필연적인 데이터 중복이 발생할 수 밖에 없었습니다. 마지막으로 다수의 서비스 인프라 관리도 모놀로직 보다 어렵습니다.

그럼에도 불구하고 제 짧은 경험으로는 거대한 서비스 & 다수의 사용자가 참여 할 수록 종속성이 분리된 마이크로 서비스는 큰 장점을 가진다는 점입니다.

1) 서비스별 그에 맞는 다양한 언어 & 프레임워크 & DB를 구축할 수 있습니다.
2) 복잡한 로직 트랜잭션 많은 트래픽이 발생하는 서비스만 쉽게 rolling update(스케일링) 할 수 있습니다.
3) 서비스 다운이 발생해도 이벤트 드라이븐을 통해 데이터 손실 최소화 및 재 가동
4) 각 서비스에 보다 집중할 수 있습니다.

profile
Pay it forward

0개의 댓글