마이크로서비스를 알아보자

ISAAC LEE·2022년 9월 4일
0

마이크로 서비스

  1. 마이크로 서비스와 모놀리스의 비교
    • 모놀리스
      • 하나의 단위로 개발되는 일체형 애플리케이션
      • 스케일 아웃시 모놀리스 애플리케이션 전체가 확장된다.
    • 마이크로서비스
      • 여러개의 애플리케이션으로 구성됨
      • 특정 기능만 확장 가능
  1. 마이크로 서비스 흐름
    1. 모든 서비스는 환경 설정 정보를 config 서비스에서 가져와 설정 정보를 주입한다.
    2. 서비스가 로딩될 때 서비스 레지스트리에 서비스명과 물리 주소를 매핑한다.
    3. 클라이언트에서 API Gateway 를 통해 접근했을 때 로드 밸런싱을 수행하여 부하 관리를 진행한다.
    4. API Gateway 에서 특정 서비스에 접근하기 위해 서비스 레지스트리 검색을 통해 서비스의 위치를 가르킨다.
    5. API Gateway 는 특정 서비스에 접근할 수 있는 권한이 있는지 확인하여 권한 서비스에서 인증/인가 처리를 수행한다.
    6. 이러한 흐름은 모니터링되어야 한다.
  1. 마이크로 서비스 패턴

    • 서비스 디스커버리 / 레지스트리 패턴

      • 클라이언트에서 여러개의 서비스를 호출하기 위해 최적 경로를 찾아주는 라우팅 기능 및 적절한 부하 분산을 하는 로드 밸런싱 기능을 가지고 있다.
      • 각 서비스 인스턴스가 로딩될 때 자신의 서비스 이름과 할당된 주소를 레지스트리 서비스에 등록한다.
      • 클라이언트가 해당 서비스를 호출했을 때 레지스트리에서 해당 서비스의 이름과 주소를 검색한 후 호출한다.
    • API Gateway 패턴

      • 다수의 클라이언트에서 여러 개의 서버 서비스를 호출하면 매우 복잡한 호출 관계가 생기는데 이것을 관리하기 위해 단일 진입점을 만들어 놓는다.
      • 레지스트리 서비스와 연계되어 동적 라우팅 및 로드 밸런싱을 수행
      • 권한 서비스와 연계되어 인증/인가 수행
      • 로깅 서비스와 연계되어 API 호출자의 정보 및 요청/응답 데이터 로깅
      • 모니터링 서비스와 연계되어 장애가 일어난 서비스 격리(서킷 브레이커 패턴)
    • BFF(Backend For Frontend) 패턴

      • API Gateway 와 같이 진입점을 하나로 두지 않고 frontend 유형에 따라 각각 두는 패턴
      • 각 플랫폼에 대한 처리만 수행하는 BFF 를 두고 이후에 통합적인 Gateway 를 거치면서 공통적인 기능 (인증/인가, 로깅) 등의 처리를 하는 구조로 구성할 수 있다.
    • 외부 구성 저장소(config) 패턴

      • 애플리케이션 구성 설정에 관련된 패턴
      • 마이크로 서비스가 사용하는 설정 정보를 쉽고 일관되고 변경 가능하다.
      • 클라우드에서 운영되는 애플리케이션은 배포되는 환경이 자주 바뀌기 때문에 코드에서 사용되는 환경 설정 정보는 코드와 완전히 분리되어야 한다.
      • 분리되어야 할 정보로는 DB 연결 정보, 서비스들의 연결을 위한 리소스 정보 등이 있다.
    • 인증/인가 패턴

      • 인증/인가 방식
        1. 세션
          • 공유 저장소에 세션을 저장하고 인증/인가를 판단한다. 이때 공유 저장소로는 Redis, Memcached 를 사용한다.
        2. 토큰
          • 클라이언트에 저장되고 서버로 요청을 보낼 때 마다 전송하여 인증/인가 처리한다.
      • 인증/인가 흐름
        1. 리소스에 대한 접근 요청이 오면 API Gateway 에서 인증 서비스에 전달
        2. 인증 서비스에서는 인증된 사용자가 보낸 것인지(인증), 해당 리소스에 대한 권한이 있는지(인가) 확인한 후 토큰 발급
        3. 토큰을 가지고 리소스에 대한 접근 요청
        4. 리소스 응답
    • 서킷 브레이커 패턴

      • 여러 서비스로 구성된 시스템에서 특정 서비스에서 장애가 발생한 경우 다른 서비스에서 영향을 받을 수 있다. 이때 장애가 발생한 서비스를 격리할 수 있는 방법 중 하나이다.
      • 서비스 상태를 실시간으로 모니터링 할 수 있어야 한다.
      • 특정 서비스에 대한 요청의 실패 횟수가 임곗값을 초과하면 서킷 브레이커가 작동하여 해당 서비스를 호출하려는 요청을 모두 실패하게 만든다.
      • fallback 메소드를 지정하면 장애가 발생했을 때 해당 메소드가 작동한다.
    • 모니터링과 추적 패턴

      • 서킷 브레이커 패턴을 가능하게 하려면 각 서비스의 장애를 실시간으로 알 수 있어야 한다.
      • 모니터링 하다가 특정 서비스에 문제가 발생했을 때 적절한 조치를 취할 수 있다.
      • API 호출 빈도나 응답 시간등을 확인하여 이를 개선할 수 있다.
    • 중앙화된 로그 패턴

      • 로그는 이벤트 스트림으로 처리해야 한다. 로그는 시작과 끝이 고정된 것이 아닌 서비스가 실행되는 동안 계속 흐르는 흐름이다.
      • 각 서비스는 스트림의 전달이나 저장에 관여하지 않는다. 로그를 전달 및 저장하는 기능을 서비스에서 구현하면 유연성이 떨어지기 때문이다.
      • 스트림의 형태로 로그를 수집하고 시각화할 수 있는 도구가 필요한데 제일 많이 사용되는 기술이 오픈소스인 ELK(ElasticSearch, LogStash, Kibana) 스택이다.
        1. ElasticSearch: 분석 엔진
        2. Logstash: 로그 수집기
        3. Kibana: 시각화 도구
      • ELK 스택을 사용하면 각 서비스의 로그를 집계하여 중앙에서 관리할 수 있고 특정 로그를 검색하거나 분석할 수 있다.
      • 로그 수집 흐름
        1. 각 서비스의 Logstash 들이 중앙 Redis Queue 에 로그를 집어넣는다.
        2. Queue 에서 ElasticSearch 로 로그를 보낸다.
        3. Kibana 로 시각화한다.
    • 서비스 메시 패턴

      • 서비스 탐색, 서킷 브레이커, 로드 밸런서 등을 비즈니스 로직과 분리하여 네트워크 인프라 계층에서 수행하게 하여 폴리글랏 하게 서비스를 운영할 수 있게함
  1. 마이크로 서비스의 통신

    • 동기 통신

      • REST API 를 호출할 때 사용하는 기본적인 통신 방법
    • 비동기 통신

      • 동기 통신 처럼 응답을 기다리지 않는다. 요청을 보내 놓고 다른 일을 처리한다.
      • 카프카와 같은 메세지 브로커를 사용한다.
      • 메시지를 보내는 생산자(producer), 메시지를 가져가서 처리하는 소비자(consumer) 가 존재한다.

      4.1. 비동기 방식의 이벤트 기반 아키텍처(Event Driven Architecture)

    • 분산 시스템에서 발신자가 이벤트를 생성 및 발행(publish) 하고 해당 이벤트가 필요한 수신자에게 전송하면 해당 이벤트를 구독(subscribe) 하고 있던 수신자가 이벤트를 처리하는 방식의 아키텍처이다.

  1. 저장소의 분리

    • 모노리스에서 요청이 증가하여 마이크로 서비스로 분리한 경우 서비스는 한가하고 통합 데이터베이스만 여전히 바쁜 상황이 있을 수 있다.
    • 각 서비스가 각자의 비즈니스를 처리하기 위해 각자의 저장소를 소유하는 것이다.
    • 서비스가 소유하고 있는 저장소는 각 서비스에서 공개한 API 를 통해서만 접근할 수 있다.
    • 또한 저장소가 각자의 서비스에 격리되기 때문에 저장소를 자율적으로 선택할 수 있다.
    • 여러 서비스에 걸쳐 비즈니스를 처리해야 하는 로직의 경우 데이터의 일관성을 어떻게 보장해야 하는지에 대한 문제가 있다.

    5.1. 분산 트랜잭션 패턴

    • 여러 개의 서비스를 하나의 트랜잭션으로 묶는 방법

    • 2-phase-commit 과 같은 방법이 있는데 서비스에 Lock 이 걸려 성능 문제가 발생한다.

      5.1.1 Saga 패턴

      • 각 서비스의 트랜잭션을 순차적으로 처리하는 패턴
      • 분산된 서비스를 하나의 트랜잭션으로 묶지 않고 각 트랜잭션과 보상 트랜잭션을 실행하면서 데이터의 정합성과 일관성을 유지한다.
      • 보상 트랜잭션은 어떤 서비스에서 트랜잭션 처리에 실패한 경우 그 서비스보다 먼저 처리됐던 서비스의 트랜잭션을 되돌리게 하는 방법이다.
  2. 읽기와 쓰기 분리

    • CQRS(Command Query Responsibility Segregation) 명령 조회 책임 분리
    • 데이터를 저장하는 요청과 조회하는 요청을 나눠서 시스템의 부하를 줄이는 등의 이점을 가짐
  3. MSA 내부 아키텍처

    1. 관심사의 분리
      • 시스템의 각 영역이 처리하는 관심사가 분리되어 관리되어야 한다.
      • 시스템을 쉽게 이해하고 변경하기 쉽게 만들어준다.
      • 데이터베이스의 본질은 데이터의 저장 처리이다. SQL 로 모든 것을 해결하려고 하지 말자.
    2. 헥사고날 아키텍처와 클린 아키텍처
      1. 레이어드 아키텍처
        1. 프레젠테이션
          • 화면 표현 및 전환
        2. 비즈니스 로직
          • 비즈니스 흐름 제어
        3. 데이터 엑세스
          • 데이터의 처리
        • 위와 같이 3계층으로 구분하며 레이어 간의 응집성을 높이고 의존도를 낮추기 위해 몇 가지의 규칙이 존재한다.
          1. 상위 계층이 하위 계층을 호출하는 단방향성을 유지한다.
          2. 상위 계층은 하위의 여러 계층을 알 필요없이 바로 밑에 존재하는 계층만 활용한다.
          3. 상위 계층이 하위 계층에 영향을 받지 않게 구성한다.
          4. 하위 계층은 자신을 사용하는 상위 계층을 알지 못하게 해야 한다.
          5. 계층 간 호출은 인터페이스를 통해 호출하는 것이 바람직하다. (구현 클래스에 직접 의존하지 않음으로 약결합을 유지함.)
      2. 헥사고날 아키텍처
        • 포트 앤드 어댑터 아키텍처라고도 한다. 비즈니스 로직을 처리하는 내부 영역과 인터페이스를 처리하는 외부 영역으로 나눈다.
        • 내부 영역에서는 외부와 연계되는 포트를 가지고 있다.
        • 외부 영역은 외부에서 들어오는 요청을 처리하는 인바운드 어댑터와 비지니스 로직에 의해 호출되어 외부와 연계되는 아웃바운드 어댑터가 존재한다.
        • 포트는 내부 영역이 외부의 어댑터에 전혀 의존하지 않게 한다.
          • 포트는 인/아웃바운드로 구분되며 인바운드는 내부 영역의 사용을 위해 외부에서 호출된 포트이고 아웃바운드는 내부 영역에서 외부를 호출하는 방법을 정의한다.
        • 인바운드 어댑터로는 컨트롤러, 커맨드 핸들러, 메시지 구독 핸들러 등이 포함될 수 있다.
        • 아웃바운드 어댑터로는 DAO, 이벤트 발행자, 외부 서비스를 호출하는 라이브러리 등이 포함된다.
      3. 클린 아키텍처
        • 헥사고날 아키텍처와 유사
        • 안에서 바깥쪽으로 엔티티, 유스케이스, ETC 로 구분된다.
          1. 엔티티
            • 비즈니스 업무 규칙, 해당 도메인의 업무를 규정하는 핵심 업무 규칙, 핵심 업무 규칙은 데이터와 결합이 되어 있기 때문에 객체로 쉽게 만들 수 있다.
            • 엔티티는 데이터베이스 혹은 다른 것에 의존해서는 안되고 유스케이스 객체를 통해서만 조작되어야 한다.
          2. 유스케이스
            • 애플리케이션 업무 규칙, 엔티티 내부의 핵심 업무 규칙을 호출하며 시스템이 사용되는 흐름을 담는다.
          3. ETC
            • 입/출력 장치, 데이터베이스, 프레임워크, 통신 라이브러리 등으로 구성된다.
      • 위 3가지 아키텍처의 지향하는 원칙은 다음과 같다.
        1. 관심사에 따라 응집성을 높이고 관심사가 다른 영역에서는 의존도를 낮춘다.
        2. 비즈니스 로직 영역을 다른 기술 기반 영역으로부터 분리한다.
        3. 기술 중심의 외부 영역과 업무 규칙이 정의된 내부 영역으로 구분한다.
        4. 외부 영역은 언제든지 교체 및 확장이 가능하며 이것이 내부 영역에 영향을 줘서는 안된다.
        5. 인터페이스 혹은 추상 클래스를 지원하는 언어의 경우 외부 영역의 구현 클래스가 내부 영역의 추상 인터페이스에 의존하게 하는 DIP 원칙을 사용한다.
        6. 인터페이스는 내부 영역에 존재하여야 하며 외부 영역의 어댑터가 이를 구현한다.
      • 내부 영역에서는 도메인이 존재하고 서비스가 이를 감싼다. 도메인에서는 비즈니스 규칙을 구현하고 서비스에서는 도메인을 호출하여 업무를 처리하는 절차를 기술하고 외부 영역과의 연계를 위해 서비스 인터페이스를 가진다.
      • 내부 영역에서는 서비르 인터페이스 제외한 다른 또 하나의 인터페이스가 존재하는데, 리포지토리 인터페이스이다. 비즈니스를 처리하기 위한 기본적인 데이터베이스 처리 사항을 추상화하여 정의한다. 그렇게 하면 외부 영역의 데이터베이스 어댑터는 해당 인터페이스를 각 데이터베이스 처리 기술에 맞게 구현된다.
      • 외부 영역에서는 데이터베이스 어댑터, 인/아웃 바운드 어댑터 등이 존재한다. REST API 컨트롤러 어댑터, 메시지 컨슈머 어댑터, 다른 서비스를 호출하는 fetch 어댑터가 존재하고 메시지 발행 어댑터, fetch 어댑터 등은 DIP 원칙에 따라 외부에서 내부로 의존하도록 설계한다.
profile
안녕하세요. 개발하면서 배웠던 것을 블로그에 작성하고 있습니다. 잘못된 정보의 지적을 환영합니다.

0개의 댓글