Post

마이크로서비스(MSA)의 필요성

MSA의 출현 배경

  • 잘개 쪼개져 있다, 기존 방식으로 해결할 수 없는 비즈니스의 압박으로 마이크로 단위로 쪼갬
  • 왜 모뮬리식 아키텍쳐를 버리고 마이크로 서비스 아키텍처로 전환했는지?
    • 비즈니스 환경의 변화
      • VUCA : 한치앞도 예측할 수 없는 시대, 계획을 세우고 1년 뒤 제품 서비스시작하면 이미 시장은 변해있음
      • Time To Market : 아이디어 떠올랐을때 코드 구현 후 시장에 출품할때까지의 시간, 과거엔 배포를 1년에 1번씩 진행했으나 현재는 배포 빈도가 기하급수적으로 늘어남 , 과거 개발은 무결성이 중요하나 현재는 빨리 내놓고 고객의 피드백을 받아 고객이 원하는 것을 구현하는 것 => 속도가 중요해짐
      • Digital Transformation : 모든 기업은 소프트웨어 기업이 되어버림, 기능에 SW가 필수요소가 됨 (뱅킹앱 오더앱 등) SW 경쟁력 = 기업의 경쟁력
      • 요구사항에 비해서 개발 속도가 느려짐 => MSA의 도입
      • 안정적이지만 느린 시스템이 아닌 안정적이면서 빠른 것을 목표

모놀리식 아키텍처(Monolithic) 란 ?

  • 모든 기능이 하나의 어플리케이션에서 통합되어있는 구조
  • UI / 비즈니스 로직 / 데이터 접근 등 하나의 큰 Application 안에 구성됨
  • 공유 데이터베이스 : 모든 모듈이 하나의 거대한 통합 DB를 바라보게 됨
    • 데이터 정합성 맞추기 쉬움 등 중앙관리 / 통제 수월함
    • Debug , 테스트 용이
    • 기능이 복잡해지고 사용자가 많아질 수록 전체를 파악하기 힘듦
  • The Big Ball of Mud (거대한 진흙 덩어리) : 성공한 모놀리식 시스템
    • 수년간 잦은 요구사항의 변경, 긴급 패치 , 개발자 교체 등 => 아키텍쳐 규칙이 서서히 무너짐
    • 작은 기능 하나를 고치기 위해 전체 프로젝트의 히스토리를 파악해야 하는 문제 (A를 수정했으나 영향도로 인해 C D 에서 오류가 발생하는 등)
    • 생산성 하락 .. 이직.. 퇴사..

부분 장애의 전파

  • 사용하지 않는 기능에서 메모리 누수 발생할 경우 ? : 같은 서버에 있는 Heap Memory 다 잡아먹어서 로그인 등 다른 서비스도 같이 문제 발생
    • A 서비스가 응답하지 않을 경우 , 연계되는 모든 서비스가 무너짐
    • 모놀로식 -> 전파 속도가 함수 호출로 연결되어 있기 때문에 장애 전파를 막을 벽이 없음
    • 전부 살거나 전부 죽거나 : 게시판 때문에 쇼핑몰을 전부 닫아야 하는 등

모놀리식 = 영향도 크고 배포하기가 힘듦 배포 단위를 쪼개야 함

부분 장애를 전체 장애로 키우지 않는 것 => MSA

비효율적인 확장성

  • 각 서비스마다 Traffic이 다름
  • Cloud => 성능마다 비용 측정하여 결제
    • 모놀리식 : C 기능 처리를 위해서 필요하지 않은 A B 서비스 복제됨 (비용이 N배, 리소스 낭비) ![[Pasted image 20260126102015.png]]

효율적인 확장 : 필요한 서비스에서만 자원을 확장하는 것

MSA의 특성과 문제점

  • 기능들이 서로 다른방에 있음, 서로 네트워크로 대화
  • 얼마나 작게 구성해야 Micro한 것인지?
    • 다른팀에게 허락을 받지 않아도 되는 크기
    • 서비스 단위
    • 효율적인 리소스 사용 , 유연한 확장 (필요한 놈만 배포)

![[Pasted image 20260126104103.png]]

Database per Service

  • 모든 서비스는 각자의 DB를 가져야 한다
  • 최적의 DB 저장소 고를 수 있는 자유
  • 가장 큰 문제
    • JOIN 불가능
      • 주문 DB에는 회원 ID만 있고 이름이 없음
    • Transaction
  • 해결책 : 통신
    • 동기 API 통신 (REST) : 강한 결합의 문제 발생
    • 비동기 : 주문 발생 시 Message Queue에 던져놓음, 다른곳에서 그걸 받아서 처리
      • Kafka, RebittMQ 구현 쉽지않음 but 비동기 EVENT 통신이 필수적임
  • 격리의 중요성
    • 모든 것은 반드시 고장난다
    • 네트워크 끊김, DB Timeout 등 피할 수 없음
    • but 한 녀석의 실패가 전체로 번지게 해서는 안된다 (도미노를 끊어야함)
    • 서킷 브레이커(Circuit Breaker Pattern)
      • Fail Fast (빨리 실패하기) : 되는거 먼저 빠르게 살리자
      • 안전성 확보를 위한 방어 운전 시스템

![[Pasted image 20260126104502.png]]

배포 독립성 (Deployment Independence)

  • 인프라 자동화 : 코드 커밋되면 자동 빌드 - 배포 - 테스트 (CI/CD)
  • MSA => 쪼개진 조각들을 자동으로 관리할 수 있는 플랫폼 역량이 받쳐주어야 함
  1. 데이터를 섞지 않는다.
  2. 기다리지 않는다 (Aysnc X)
  3. 같이 죽지 않는다 (장애 격외 생존)
  4. 손으로 하지 않는다 (자동화 배포)

MSA 핵심 아키텍처 패턴

  1. 분산된 데이터
  2. 복잡한 통신
  3. 장애 전파

이를 해결하기 위해 아키텍처 패턴 구성이 필요함

The Landscape of MSA Patterns

MSA는 수 많은 패턴들의 집합체임

  1. 분해 (어떻게 쪼갤 것인가)
  2. 통신 (어떻게 서로 통신할 것인가)
  3. 데이터 (JOIN 어떻게 할지 등)
  4. 관측 (흩어진 로그를 묶어서 볼지)
  5. 회복 (죽지 않는 프로그램을 구성)

![[Pasted image 20260126105741.png]]

분해 패턴 (Decomposition Patterns) - Strangler Fig

  • 기능 하나하나를 야금야금 새 시스템으로 옮겨간다
  • 핵심 가치 : 리스크 최소화 (Rollback 빠름) ![[Pasted image 20260126110102.png]]

분해 패턴 (Decomposition Patterns) - Bulkhead

  • 암초에 부딪혀서 구멍이 나더라도 배가 침몰하지 않게 하기위한 원리
  • 하나의 서비스가 과부하로 쓰러질 때, 여파가 시스템 전체로 퍼지는 것을 막아줌
  • 핵심 : 자원 격리
  • 보통은 ThreadPool Java에서 하나로 할당함 / 해당 패턴에선 서비스별 Thread 부여

Integration Patterns (통합 패턴) - API Gateway

  • 하나의 Gateway로 모든 Client 처리
  • MSA는 서비스가 수십개로 쪼개짐
  • 손님은 호텔 주방 구조를 알 필요가 없음
  • 모든 클라이언트는 모바일이든 웹이든 API Gateway를 바라보게 됨
  • 적절한 서비스를 라우팅 해줌
  • 공통 기능 처리 : 인증, 인가, 로깅 등
  • 주의점 : API Gateway가 새로운 모놀리식이 될 수 있음
  • Gateway Team이 병목되어서 개발 속도가 느려질 수 있음
  • Gateway에선 비즈니스 로직을 넣지 말고 보안에 충실해야 함

Integration Patterns (통합 패턴) - API Gateway + BFF

  • 하나의 Gateway로 모든 Client 처리하지 않음
  • Client별로 전용 Gateway 생성 (Backend For Frontend)
  • 모바일용 BFF , Web용 BFF가 따로 있음
  • BFF 패턴은 조직의 구조와도 연결이 됨 (FE에서 직접 관리하는 경우 등)

Data Management Patterns (데이터관리 패턴) - DB per Service

  • 서비스 별 DB 구성
  • 데이터의 성격에 맞는 저장소 선택할 수 있는 자유성이 존재
  • JOIN 문제 발생
  • 데이터 정합성

Data Management Patterns (데이터관리 패턴) - SAGA

  • Transaction을 여러개의 local Transaction으로 쪼개서 순차적으로 실행함
  • 시스템은 모든 상태를 일치시켜야 함
  • 보상 Transcation : 주문 했다고 돈을 냈더라도 커피가 바로 주어지지 않음 (이름 불릴때까지 기다림 or 재료가 떨어지면 환불하거나 다른 메뉴 선택 등 처리)

Data Management Patterns (데이터관리 패턴) - CQRS

  • 보여주는 것과 쓰는 것은 서로 달라야 한다
  • 주문내역, 상품정보, 회원정보가 합쳐진 화면은 어떻게 보여줄지?
  • CQRS는 쓰기모드 / 읽기모드를 분리함
  • 데이터가 변경되면 이벤트를 발행함, 조회에 최적화된 형태로 뭉쳐서 저장
  • 사용자가 주문 목록을 요청하면 복잡한 계산 없이 읽기 DB에서 꺼내기만 하면 됨
  • 문제 : 읽기 / 쓰기 DB 설계에 시차가 존재 => 시스템의 구조 복잡해짐
    • Traffic 복잡한 일부 서비스에서만 적용해야 함

Data Management Patterns (데이터관리 패턴) - Event Sourcing

  • 상태를 저장하지 않고 상태를 변경시킨 EVENT (사건)을 저장
  • 내 돈 왜 만원임? 어제 5만원 있었는데 ..
  • 기존 DB 에선 LOG 뒤져야 함
  • 행동, 사건들을 저장해서 역추적
  • 장점
    • 완벽한 감사로그 (데이터 변화 추적 가능)
      • 은행 , 회계, 보험시스템 등
    • 타임머신 생김
      • 버그 발생 시 과거 상태 재현해서 디버깅 가능
  • 주의 : 쓰는쪽은 이벤트를 쌓고 읽기쪽은 현재상태를 미리 만들어 두어야 함 (CQRS)

Observability Patterns (관측성 패턴) - Distributed Tracing

  • 요청에 여권을 담 : Tracing ID
  • 요청 처음 들어올 때 고유한 ID를 발급함, 서비스 A가 B를 호출할 때 고유한 id, B -> C에서도 해당 ID를 같이 들고감
  • 시각화도구 사용 시 id 하나로 전체 여정을 그래프로 볼 수 있게됨 (추적 가능)
  • A B 는 비교적 빨랐는데 C에서 오래걸렸구나, E에서 더 오래걸렸구나 라고 병목지점 확인가능 : 가시성 (Obserbility)을 확보한다
  • 개발단계부터 요구-추적 전략을 세워야 함

Observability Patterns (관측성 패턴) - Monitoring & Logging

  • 대시보드 플랫폼 Grappa 등 연결해서 사용가능
  • 측정 x -> 관리 x
  • 중앙집중형 로깅 / 모니터링 체계 구축 필요

Resiliency Patterns (회복성 패턴) - Circuit Breaker

  • 과전류 흐르게되면 전류 끊겨서 가전제품 타는걸 막아줌
  • 서비스 A 호출 => B가 응답이 없거나 에러를 뱉으면 A는 기다림
  • A 기다리는 쓰레드 죽으면 Circuit Breaker 개입
    • Close (Circuit Breaker 닫혀있는 상태)
    • Open (열리면서 차단기가 올라감 , 흘러가는게 막힘)
    • Half Open (회복이 되었는지 확인하는 상태, 요청 한번만 보내봄)
  • 미리 준비된 poll back을 보여줌 => 고객은 오류난건지 모름

Resiliency Patterns (회복성 패턴) - Retry

  • 분산 시스템에서는 네트워크 잠깐 끊길 수 있음
  • 실패하면 Retry 한다
  • 함정 : 잘못쓰게되면 시스템 파괴 가능
  • 전략
    • 언제 재시도 할 것인가?
      • 404, 400은 100번 보내도 실패함 / 500 or Time에서 Retry
    • 재시도 시 Traffic 과부화 막기 위해 Backoff Time 걸어놓음
      • 실패하면 1초 / 2초 / 4초.. 간격을 늘려가며 서버가 숨 쉴 틈을 주어야 함
    • 멱등성
      • 결재 요청 보냈을 때 Timeout나면 첫번째요청이 성공했다면 고객은 두번 빠져나갈 수 있음 : Retry 쓰려면 서버에서 같은 요청이 두 번 와도 한번만 처리하는 멱등성 처리가 되어있어야 함
This post is licensed under CC BY 4.0 by the author.

© gyowoo. Some rights reserved.