[MSA] 마이크로서비스 코딩 공작소 - 1장

서정범·2023년 11월 17일
0

MSA

목록 보기
2/10

해당 챕터에서는 마이크로서비스 아키텍처가 무엇이고, 왜 필요한가에 대해서 다룹니다.

먼저, 마이크로 서비스의 필요성에 대해 알아보기 전에 알아둬야 될 것이 있습니다.

  • 관심사 분리가 무엇이고 왜 필요한가?
  • 모놀리식의 한계가 무엇인가?

이 두 가지를 먼저 짚고갈 것입니다.

먼저, 관심사 분리에 대해서 짚고 갑시다.

관심사 분리

관심사 분리(Separation of Concerns, SoC)는 컴퓨터 과학과 엔지니어링의 핵심 원칙 중 하나입니다.

❗️이 원칙의 핵심은 프로그램이나 시스템을 구성하는 다양한 요소들이 각자의 관심사(concern)에만 집중하도록 설계하는 것입니다.

말 그대로 각자의 역할에 집중할 수 있는 환경을 제공해줘서 서로 엮이게 되는 정도를 낮춰주는 방식입니다.

이렇게 할 경우 장점은 다음과 같습니다.

  1. 유지보수의 용이성
  2. 재사용성의 향상
  3. 가독성 및 이해도 향상
  4. 테스트 용이성
  5. 변경의 용이성

제가 현재까지 느낀 것은 "관심사 분리"는 선택 사항이 아니며 여기서 선택해야될 것은 "어느 정도"까지 분리를 할 것인가에 집중해야 합니다.

이것은 객체의 특성 및 재사용성, 이것의 라이프사이클이 어떻게 되는가 등등을 고려하여 분리를 해야할 것이고, 최종적으로는 개발 환경에 따라 결정이 될 것입니다.

따라서, 정답이 있는 것은 아니라고 판단하고 있습니다. 다만, 틀린 것 같다 정도는 있을 뿐!

모놀리식의 한계

여기서 제가 말하는 모놀리식은 MVC 방식으로 Model, View, Control을 하나의 서버에서 맡아서 진행하는 방식입니다.

기본적으로 하나의 서버에서 필요한 객체들을 가지고 있기 때문에 접근성이 용이합니다.

또한, 하나의 서버에서 전체 코드의 흐름를 관리 하기 때문에 코드가 복잡하지 않는 경우에는 문제를 파악하기도 쉬울 것입니다.

규모가 크지 않다면 개발 난이도도 어렵지 않고, 비용적인 측면에서도 절약적일 것입니다.

그리고 이것은 제가 프로젝트를 하면서 느낀 것인데 관련 Reference가 MVC 방식이 훨씬 많기 때문에 이러한 부분도 무시 못하겠더라고요..

하지만, 점점 더 많은 사람들이 인터넷을 사용하게 되면서 전제 사용자는 많아지고 있고 네트워크 트래픽 또한 늘어나고 있습니다.

코드의 양이 많아지고 네트워크의 트래픽이 점점 늘어날 수록 복잡해지면서 문제가 많이 발생할 가능성이 커집니다.

여기서 모놀리식의 가장 큰 문제점이 나온다고 생각하는데, 하나의 문제가 발생했을 때 그것이 전체 문제로 확산될 가능성이 크다는 것입니다. 즉, 서비스 다운으로 이어질 가능성이 높습니다.

또한, MVC 방식으로 협업 프로젝트를 진행해 보았다면 알 수 있겠지만 Collision이 일어날 가능성이 높고 서로에 대해서 조심해야 되고 불필요하게 알아야 되는 것들이 늘어나면서 작업의 효율이 떨어질 수 있을 것입니다.

배포와 확장에 관한 부분도 빠질 수 없는 내용인데 간단하게 정리하자면 필요한 부분만 새로 배포하려고 하거나 확장하려고 할 경우에 결국 전체 서비스를 다운했다가 시작하게 되는 문제가 발생합니다. 이것으로 인해 빠르게 변화되는 트랜잭션에 서비스가 따라가기 힘들게 됩니다.

요즘은 시장의 변화 속도도 매우 빠르고 트랜드도 빠르게 변화합니다. 이것에 맞춰서 애플리케이션이 따라가줘야 될텐데, MVC 방식에서는 그것이 또 쉽지 않다고 하는 것 같습니다.

그렇다고 모놀리식 방식이 안 좋다고 하는 것이 아닙니다. MSA는 이러한 부분에서는 효과적이지만 비용 및 관리적인 측면에서 Cost가 매우 높다고 합니다. 그리고,, 난이도도 높은 것 같고요....

한계점을 간단하게 정리하자면 이와 같습니다.

  • 코드의 복잡성: 관리 힘듬
  • 작은 문제 -> 큰 문제로 확산: Big ball of mud application
  • 협업의 문제
  • 배포와 확장성

MSA

❗️ 마이크로서비스는 작고 느슨하게 결합된 분산 서비스입니다.

다음과 같은 특징을 가지고 있습니다.

  • 작은 컴포넌트로 분해
  • 독립적 배포
  • 서비스 소비자와 공급자간에 경량의 통신 프로토콜 사용
  • 각 서버에서 여러 언어를 사용가능하게 되면서 개발자 Infra 확장
  • 더 작고 명확한 책임 영역으로 나눠서 팀 조직 가능

MSA를 잘 표현한다고 생각하는 문구가 하나 있습니다.

작고(small), 단순하고(simple), 분리된(decoupled) 서비스 = 확장 가능하고(scalable), 회복적이며(resilient) 유연한(flexible) 애플리케이션

도중에 간단하게 전체 흐름을 보여주는 내용이 나옵니다.

간단하게 정리해두자면,

  1. 요청의 신뢰성을 보장받기 위해 "키클록 서버"에 인증
  2. 게이트웨이와 통신
  3. 서비스 서버에서 처리 후 DB 갱신
  4. 카프카 토픽 발행 후 다른 서비스의 Redis 캐시 업데이트
  5. 이 요청(span) 추적 로그 관리
  6. 애플리케이션 지표 노출

어차피 뒤에서 언급될 내용이니깐 여기까지만 정리해 두겠습니다.

아키텍처적인 관점에서의 전체적인 흐름은 이와 같았고, 그렇다면 하나의 요청이 서버에서 처리되는 것은 어떤 흐름일까요?

스프링 부트에서 REST 마이크로서비스 작업을 간단히 보자면 다음과 같습니다.

  1. 마이크로서비스에 대한 HTTP 요청
  2. 경로(route) 매핑
  3. 매개변수 분해
  4. JSON -> 자바 객체 매핑
  5. 비지니스 로직 수행
  6. 자바 -> JSON 객체 매핑

좀 더 구체적으로 정리를 해두고 싶어서, 이 부분은 조금 더 정리하겠습니다.

  1. 마이크로서비스에 대한 HTTP 요청: 클라이언트(브라우저, 모바일 앱, 다른 서비스 등)로부터 HTTP 요청이 마이크로서비스에 도착합니다.

  2. Dispatcher Servlet: 모든 요청은 스프링의 DispatcherServlet에 의해 처음으로 처리됩니다. DispatcherServlet은 웹 애플리케이션의 중앙 집중식 진입점으로 작동합니다.

  3. Handler Mapping: DispatcherServlet은 HandlerMapping을 통해 요청 URL을 적절한 컨트롤러 메서드에 매핑합니다. RequestMappingHandlerMapping은 @RequestMapping 어노테이션이 달린 메서드를 찾아 요청과 매핑합니다.

cf) RequestMappingHandlerMapping은 AbstractHandlerMethodMapping을 상속하고 있어서 해당 클래스의 inner 클래스인 MappingRegistry가 가지고 있는 HashMap 변수인 registry RequestInfo랑 MappingResgistration(RequestInfo, HandlerMethod)를 매핑시켜 놓는다.

  1. 메서드 실행: 매핑된 컨트롤러 메서드의 매개변수는 요청으로부터 추출되어 전달됩니다. 스프링은 @RequestParam, @PathVariable, @RequestBody 등의 어노테이션을 사용하여 요청 데이터를 메서드 매개변수에 바인딩합니다. (이때 리플랙션을 사용합니다.)

리플렉션은 런타임에 클래스의 메타데이터를 검사하거나 수정할 수 있는 Java의 기능입니다.

  1. Handler Adapter: DispatcherServlet은 HandlerAdapter를 사용하여 실제 컨트롤러 메서드를 실행합니다.

  2. JSON -> 자바 객체 매핑: 요청 본문(Body)에 JSON 데이터가 포함된 경우, 이를 자바 객체로 변환하기 위해 HttpMessageConverter가 사용됩니다. 예를 들어, @RequestBody 어노테이션을 사용하는 경우입니다.

  3. 비즈니스 로직 실행: 컨트롤러에서 비즈니스 로직을 실행합니다.

  4. 자바 -> JSON 객체 매핑: 응답을 반환할 때, 자바 객체는 JSON 형식으로 클라이언트에게 전송됩니다. 이 때도 HttpMessageConverter가 사용됩니다.

이 부분은 책에는 없는 내용이지만 알고있던 내용과 엮어서 정리해 뒀습니다.

클라우드 컴퓨팅

❗️ 클라우드 컴퓨팅은 유연하고 안전하면서 사용하기 쉬운 환경을 제공하고자 인터넷을 통해 컴퓨팅과 가상화된 IT 서비스(데이터베이스, 네트워킹, 소프트웨어, 서버, 분석 등)를 제공하는 것이다.

  • 온프레미스
  • IaaS(Infrastructure as a Service)
  • CaaS(Container as a Service)
  • Paas(Platform as a Service)
  • Faas(Function as a Service)
  • SaaS(Software as a Service)

마이크로서비스를 작성할 때 서비스를 배포할 수 있는 형태는 다음과 같습니다.

  • 물리 서버
  • 가상 머신 이미지
  • 가상 컨테이너

클라우드 기반 마이크로서비스의 장점은 탄력성(elasticity) 개념에 기반을 둡니다.

그리고 서버 탄력성은 애플리케이션이 더 회복적(resilient)일 수 있음을 의미합니다.

도커 컨테이너를 사용해서 배포하는 Caas 위주로 책에서 다루고 있고, 실제로 이 방식이 가장 유연하다고 생각합니다.

Caas 클라우드 공급자의 일반적인 특징은 다음과 같습니다.

  • 간소화된 인프라스트럭처 관리
  • 대규모 수평 확장성
  • 지리적 분산을 이용한 높은 중복성(redundancy)

고려해야할 점

마이크로서비스를 작성하고 구축할 떄 고려해야 할 몇가지 지침이 있습니다.

  1. 적정 규모: 서비스가 한 가지 책임 영역에 집중되도록 하려면 어떻게 해야 하는가?

  2. 위치 투명성: 서비스 클라이언트에 영향을 주지 않고 서비스 인스턴스를 추가하고 삭제하려면 물리적 위치를 어떻게 관리해야 하는가?

  3. 회복성: 서비스에 문제가 있을 때 서비스 클라이언트가 빠르게 실패하려면 어떻게 해야 하는가?

  4. 반복성: 새로운 서비스가 시작할 때마다 항상 기존과 동일한 코드와 구성을 갖게 하려면 어떻게 해야 하는가?

  5. 확장성: 서비스 간 종속성을 최소화하면서 애플리케이션을 신속히 확장하려면 어떻게 해야 하는가?

이것을들 위한 개발 패턴들이 있습니다.

  • 핵심 개발 패턴
  • 라우팅 패턴
  • 클라이언트 탄력성 패턴
  • 보안 패턴
  • 로깅 및 추적 패턴
  • 애플리케이션 지표 패턴
  • 빌드 및 배포 패턴

이 뒤에도 이것들에 대한 간단한 설명들이 나오지만 뒤에서 단원별로 다뤄주기 때문에 생략하도록 하겠습니다.

여기서, 하나만 추가적으로 정리하고 가겠습니다.

마이크로서비스 로깅과 추적 패턴

마이크로서비스에서 로깅은 매우 중요한 역할을 합니다.

여러 서버가 분산되어 있기 때문에 하나의 요청이 어느 서버로 통해서 작업을 수행하는지 추적하기 위해선 로깅이 잘 되어 있어야 합니다.

하나의 요청을 span이라고 하는데, 이 요청을 추적하기 위해서 요청에 상관관계 ID(correlation ID)를 붙여줍니다. (Tracking ID)

이것을 통해서 로그를 수집할 수 있고, 마이크로서비스를 추적하여 트랜잭션과 관련된 모든 서비스 간 클라이언트 트랜잭션 흐름을 시각화하고 성능을 확인할 수 있다.

중요한 것은, 그렇다면 어느 정도까지 로깅을 해야 하는 것이 중요합니다.

일반적으로 생각해서 모든 트랜잭션에서 발생하는 로그들을 수집하는 것은 스토리지와 네트워크 트래픽에 부담이 될 수 있습니다.

그래서 다음과 같은 사항들을 고려해볼 수 있습니다.

  1. 로그 레벨 관리
  2. 로그 저장 전략
  3. 효율적인 로그 저장
  4. 로깅 솔류션 사용
  5. 샘플링
  6. 로깅 인프라 분산

로그 레벨에는 위험도 순으로 Trace -> Debug -> Info -> Warn -> Error 순으로 확인할 수 있는데 자세하게 확인하고 싶다면 Debug를 사용하고 데이터(지표)가 있다면 Info를 사용하는 것으로 알고있다만, 일반적인 경우고 실제로는 변동이 많을 것이라고 생각됩니다.

profile
개발정리블로그

0개의 댓글