[DEV] 기록

MSA를 도입할 때 고려해야 하는 사항

꾸준함. 2023. 3. 16. 02:16

개요

많은 빅테크 회사들이 애플리케이션을 구축할 때 마이크로서비스 아키텍처를 도입하지만 MSA 패턴이 장점만 갖는 것이 아니기 때문에 반드시 모든 서비스가 마이크로서비스 애플리케이션일 필요는 없습니다.

따라서 이번 게시글에서는 MSA를 도입할 때 고려해야 하는 사항 및 사용하지 말아야 하는 환경 혹은 케이스에 대해 간략하게 정리해 보겠습니다.

 

MSA에 중요한 역할

MSA를 도입할 때 중요한 역할은 아래와 같이 세 가지가 있으며 이 세 팀의 관점을 통합해야 성공적으로 개발 및 운영을 할 수 있습니다.

  • 아키텍트
  • 개발자
  • DevOps 엔지니어

 

아키텍트

아키텍트는 애플리케이션의 각 부분이 잘 상호작용할 수 있도록 개발자가 작성할 코드에 대한 스캐폴드를 제공하며 세 가지 핵심 업무에 집중해야 합니다.

  • 비즈니스 문제 분해
  • 서비스 세분화
  • 서비스 인터페이스 정의

 

1. 비즈니스 분해

 

프로젝트가 너무 커질 경우 관리가 안되므로 관리 가능한 파트로 분해하는 것이 효율적입니다.

이에 따라 아키텍트는 비즈니스 문제를 각 활동 영역을 대표하는 부분으로 분해하고 각 파트와 연관된 비즈니스 규칙 및 데이터 로직을 해당 파트 내 은닉하는 역할을 수행합니다.

비즈니스 문제를 식별하고 마이크로서비스 후보로 분해할 때는 아래와 같은 지침을 사용할 수 있습니다.

  • 비즈니스 문제를 기술하는 데 사용된 명사에 주목
  • 동사에 주목
  • 데이터 응집성 발굴

 

비즈니스 문제를 기술하는 데 사용된 명사에 주목

  • 문제를 기술하는데 동일한 명사가 반복해서 사용될 경우 마이크로서비스 후보로 분해하기 좋은 징후
  • 자주 등장하는 단어이기 때문에 핵심 비즈니스 영역일 확률이 높음

 

동사에 주목

  • 동사는 행동을 강조하기 때문에 비즈니스 문제의 윤곽을 자연스럽게 드러냄
  • 조회, 등록, 업데이트, 삭제 같은 단어는 핵심 동사 (CRUD)
  • ex) 트랜잭션 X는 A와 B로부터 정보를 조회한 뒤 조건에 맞을 경우 C에 등록한다
    • 이와 같은 케이스는 여러 서비스가 동작 중임을 암시하므로 마이크로서비스로 나눠야 함

 

데이터 응집성 발굴

  • 비즈니스 문제를 분해할 때 서로 연관성이 높은 데이터를 찾아야 함
  • 연동하는 과정에서 여태까지 논의가 되지 않았던 데이터를 읽고 업데이트해야 하는 상황이 놓이면 또 다른 마이크로서비스로 나눠야 한다고 볼 수 있음
    • 마이크로서비스는 완전히 본인만의 데이터를 가져야 함

 

 

2. 서비스 세분화

 

비즈니스 문제 영역을 마이크로서비스로 나눌 때 적절한 수준의 세분화가 필요합니다.

문제는 "적절함"이 어느정도 수준인지 모르는 경우가 다반사라는 것인데 올바른 세분화 수준에 대한 정답을 구하기 위해서는 아래의 개념을 알 필요가 있습니다.

  • 마이크로서비스는 광범위하게 시작하고 더 작은 서비스로 리팩토링
  • 서비스 간 연동하는 방식에 집중
  • 기능이 추가됨에 따라 서비스 책임 지속 변화

 

마이크로서비스는 광범위하게 시작하고 더 작은 서비스로 리팩토링

  • 시작부터 모든 것을 마이크로서비스로 만들어 버릴 경우 과하게 세분화가 될 확률이 높음
  • 일단은 적당히 크게 구현을 진행한 이후 적절하게 세분화하면서 리팩토링하는 것을 추천

 

서비스 간 연동하는 방식에 집중

  • 앞서 언급했드시 큰 것을 작게 리팩토링하는 것이 쉬움
  • 따라서 서비스 간 연동하는 방식에 집중하면 비즈니스 문제 영역에 대한 큰 단위의 인터페이스를 구현하는데 도움이 됨

 

기능이 추가됨에 따라 서비스 책임 지속 변화

  • 새로운 기능이 추가될 때 대개 마이크로서비스가 해당 책임을 맡음
  • 마이크로서비스는 단일 서비스로 시작해서 여러 서비스로 분화되며 성장
    • 기존 서비스는 새로운 서비스들을 오케스트레이션하고 애플리케이션의 다른 부분 기능을 캡슐화하는 역할

 

"적절함"을 판별하기 위한 개념을 숙지했으니 이제 세분화가 올바르게 되지 못한 마이크로서비스의 특성에 대해 파악해야 합니다.

 

세분화 수준이 부족할 경우 아래와 같은 특성을 지닙니다.

  • 비즈니스 로직의 일반적 흐름이 복잡하고 지나치게 많은 제약을 지님
  • 다수 테이블에 걸쳐 데이터를 관리 혹은 서비스 데이터베이스 외부에 있는 테이블에 접근 필요
  • 테스트가 너무 많아 수백 개의 유닛 테스트와 통합 테스트 

 

반대로 세분화가 과하게 되었을 경우 아래와 같은 특성을 지닙니다.

  • 애플리케이션에 수십개의 마이크로서비스가 존재하고 각 서비스가 하나의 DB 테이블과 통신
  • 마이크로서비스가 지나치게 상호 의존적
  • 마이크로서비스가 단순 CRUD 서비스 집합체

 

이처럼 처음부터 완벽하게 설계하기가 힘들기 때문에 처음부터 세분화하기보다는 큼직하게 나누어서 시작한 이후 리팩토링을 하는 것이 좋고 Waterfall 방법론처럼 처음부터 완벽한 설계를 하기보다는 Agile 방법론을 선택해 진화적 사고 프로세스를 거쳐 개발하는 것이 좋습니다.

 

 

3. 서비스 인터페이스 설계

 

MSA 패턴을 적용하면 수많은 마이크로서비스 간 연동이 필요하므로 많은 개발자가 쉽게 이해하고 소비할 수 있는 서비스 인터페이스 설계가 필요합니다.

즉, 개발자가 한 두개의 마이크로서비스를 보고 모든 서비스가 어떻게 동작하는지 규칙을 습득하는 것이 이상적입니다.

이를 달성하기 위해서는 아래의 지침을 사용하는 것이 좋습니다.

  • REST 철학 수용
  • URI 사용하여 의도 전달
  • Request, Response에 JSON 사용
  • HTTP 상태 코드를 통해 결과 전달

 

REST 철학 수용

  • 표준 HTTP 동사 사용 (GET, PUT, POST, DELETE)
  • 서비스 호출 프로토콜로 HTTP/HTTPS 수용

 

URI 사용하여 의도 전달

  • 서비스의 엔드포인트로 사용되는 URI를 통해 어떤 동작을 수행하는지 예상 가능해야 함

 

Request, Response에 JSON 사용

  • XML SOAP보다는 JSON이 훨씬 가볍고 JSON으로 변환해 주는 다양한 라이브러리 존재
  • ex) Jackson 라이브러리

 

HTTP 상태 코드를 통해 결과 전달

  • 서비스 호출의 결과를 표준 응답 코드를 통해 파악 가능
  • 상태 코드를 익히고 모든 서비스에 일관되게 사용하는 것이 중요

 

개발자

개발자는 마이크로서비스가 작다고 해서 좋은 설계 원칙을 포기하는 것이 아니라 서비스 내 각 계층마다 책임이 분리된 계층화된 서비스를 구축하는 것에 집중해야 합니다.

개발자의 역할은 자명하므로 부연 설명을 생략하겠습니다.

 

DevOps 엔지니어

DevOps에게 마이크로서비스 설계는 운영 환경 내 서비스 관리와 연관되어 있습니다.

서비스가 멈춤 없이 지속적으로 동작하게 만드는 것이 중요하므로 dev보다는 Ops 쪽이 좀 더 우선순위가 있으며 아래의 원칙을 적용해야 합니다.

  • 마이크로서비스는 하나의 소프트웨어 산출물로 시작 및 종료할 수 있는 서비스의 여러 인스턴스를 독립적으로 배포 가능
  • 서비스 인스턴스 배포 시 필요한 설정 정보를 외부에서 읽어 오거나 환경 변수로 전달받되 사람의 개입이 없어야 함
  • 클라이언트는 서비스의 정확한 위치를 알 수 없어야 하며 대신 애플리케이션이 마이크로서비스의 물리적 위치를 모르더라도 인스턴스 위치를 찾을 수 있어야 함
    • Spring Cloud의 경우 서비스 디스커버리 에이전트와 마이크로 서비스 클라이언트 간 통신 필요
    • 마이크로서비스를 EurekaClient로 등록 필요
  • 마이크로서비스 내 OOM(Out of Memory)와 같은 에러가 발생할 수 있으므로 각 마이크로서비스의 heath check가 필요
    • Spring Cloud의 경우 Spring Actuator와 연동할 경우 마이크로서비스 상태 확인 가능
    • 상태가 정상이 아닐 경우 디스커버리 에이전트는 해당 인스턴스를 우회해서 라우팅
    • 해당 인스터스 내리고 신규 인스턴스 띄우는 작업 수행 필요 (사람 개입 없이)

 

아이러니하게도 위 네 가지 원칙은 마이크로서비스 개발 과정에서 존재할 수 있는 역설을 드러냅니다.

마이크로서비스는 기본적으로 분산되어 있고 자체 컨테이너에서 독립적으로 실행되므로 애플리케이션 내 관리 포인트가 많아집니다.

관리 포인트가 많아진다는 것은 장애 발생 확률이 올라간다는 뜻이므로 정교한 조정 기술을 필요로 합니다.

DevOps 엔지니어는 운영상 요구사항인 지속 가능한 서비스를 충족시키기 위해 위 네 가지 원칙을 마이크로서비스를 빌드하고 운영 환경에 배포할 때마다 아래와 같은 프로세스를 거칠 수 있도록 설계해야 합니다. 

  • 서비스 조립 (service assembly)
  • 서비스 부트스트래핑 (service bootstrapping)
  • 서비스 등록 및 디스커버리 (service registration/discovery)
  • 서비스 모니터링 (service monitoring)

 

서비스 조립

  • DevOps 관점에서 MSA의 핵심 개념은 트래픽 증가와 같은 애플리케이션의 환경 변화에 대응하여 마이크로서비스의 많은 인스턴스를 신속히 배포할 수 있다는 것 (auto-scaling)
  • 이를 위해서는 모든 의존성을 포함한 단일 산출물로 패키징 되고 설치될 수 있도록 executable jar 파일 생성 필요
    • 의존성을 포함하지 않는 경우 설정 정보가 불일치할 가능성이 있어 장애 발생 확률이 높아짐

 

서비스 부트스트래핑

  • 마이크로서비스가 처음 시작되고 애플리케이션 설정 정보를 load할 때 일어나는 과정
  • 사람 개입 없이 모든 환경에서 빠르게 시작하고 배포할 수 있도록 런타임 코드에서 애플리케이션 코드와 환경 구성 코드를 분리하는 방법
  • ex) Spring Cloud Config에서 설정 정보를 읽어옴

 

서비스 등록 및 디스커버리

  • 클라우드 환경에서 인스턴스가 죽었다 다시 뜨면 ip 및 port가 변경됨
  • 따라서 마이크로서비스는 Eureka Server와 같은 제삼자 에이전트에 자신을 등록하고 클라이언트는 해당 에이전트와 통신하여 서비스 위치를 찾음

 

서비스 모니터링

  • 일관된 상태 확인 인터페이스를 만들어 클라우드 기반 모니터링 도구를 사용할 경우 문제 적절히 대응 가능
  • 디스커버리 에이전트에서 문제 있는 서비스 인스턴스 발견 시 해당 인스턴스를 종료시키거나 신규 인스턴스를 시작하는 등 정상화 조치 취할 수 있음

 

정리하자면 MSA의 성공을 위해서는 아키텍트, 개발자, DevOps 엔지니어의 협업이 필요하며 각자의 역할이 다르기 때문에 세 팀의 관점을 적절히 통합할 필요가 있습니다.

 

MSA를 도입하지 않아야 하는 케이스

앞서 살펴봤듯이 MSA를 도입할 때 무조건 장점만 있는 것이 아니고 많은 제약 사항이 발생합니다.

따라서 모든 애플리케이션에 MSA 패턴을 반드시 도입할 필요는 없으며 아래의 케이스인 경우에는 오히려 monolithic이 더 잘 어울립니다.

  • 마이크로서비스로 분리했을 때 과하게 복잡해지는 경우
  • 가용 예산이 부족한 경우
  • 사용자가 적은 소규모 애플리케이션인 경우
  • 애플리케이션이 여러 datasource에 걸쳐 복잡한 데이터를 집계하고 변환해야 하는 경우

 

마이크로서비스로 분리했을 때 과하게 복잡해지는 경우

  • monolithic 애플리케이션일 때는 존재하지 않던 복잡성이 마이크로서비스로 분산되고 세분화됨에 따라 발생할 수 있음
  • 마이크로서비스 애플리케이션이 성공하기 위해서는 자동화와 모니터링과 같은 운영 작업에 투자가 불가피

 

가용 예산이 부족한 경우

  • 대규모 마이크로서비스 기반 애플리케이션의 경우 prod 환경에서만 구축/관리해야 하는 서버나 컨테이너가 두 자리 많게는 세 자리일 수 있음
  • 이를 관리하고 모니터링하는 데는 많은 비용이 필요

 

사용자가 적은 소규모 애플리케이션인 경우

  • 마이크로서비스는 재사용성을 추구하며 고도의 회복성/확장성이 필요한 대규모 애플리케이션에 적합
  • 소규모 애플리케이션인 경우 오히려 분산 시스템을 구축했을 때 발생하는 복잡성이나 비용이 그 가치보다 많이 발생할 수 있음 (손익분기점 넘기기 어려움)

 

애플리케이션이 여러 datasource에 걸쳐 복잡한 데이터를 집계하고 변환해야 하는 경우

  • 마이크로서비스는 적은 수의 테이블을 추상화하며 DB에 단순한 쿼리 생성, 추가, 실행 등 '운영성' 작업을 수행하는 메커니즘으로 동작
  • 따라서 애플리케이션이 다양한 DB에 걸쳐 복잡한 데이터를 집계하기 위해 JOIN 하고 변환해야 하는 경우 분산된 특성으로 인해 작업 수행이 어려워짐
  • 이럴 경우 과도한 책임을 떠안고 성능이 안 나오는 문제 발생

 

참고

스프링 마이크로서비스 코딩 공작소 개정 2판 3장

반응형