1.마이크로서비스 설계 원칙
2.마이크로서비스 데이터 설계
3.마이크로서비스 통신 설계
4.마이크로서비스 외부 API 통합 패턴

1. 마이크로서비스 설계 원칙

1. 비즈니스 도메인을 중심으로 모델링

도메인 기반 설계를 통해 더욱 안정된 서비스를 구축하고 다양한 사용자 인터페이스에 대해 보다 쉽게 각 서비스들을 재결합 할 수 있다.

2. 자동화 문화

많은 수의 마이크로서비스의 복잡성을 관리하려면 자동화가 필수적이다.

  • CI/CD
  • 빌드 파이프라인 단계에서의 SIT(System Integration Testing) & UAT(User Acceptance Testing) 테스트
  • 프로비저닝, OS 구성, 서비스 이미지 생성 등

3. 구현 세부 정보 숨기기

서비스가 독립적으로 변경되고 진화할 수 있도록 각 서비스의 구현 세부 정보를 숨겨야 한다. 필요한 정보는 DB에 직접 접근하는 것이 아닌 각 서비스 API를 통해서 얻어야 한다.

4. 모든 것을 분권화하기

각 서비스는 자체 결정을 내릴 수 있어야 하며 그 안에서 완전한 자율성을 가질 수 있어야 한다.

  • Service Ochestration
    : 중앙 집중식 접근 방식

  • Service Choreography
    : 느슨하게 결합된 접근 방식

5. 독립적으로 배포

서비스 구성 요소를 변경하는 경우 다른 구성 요소를 변경하지 않고 프로덕션으로 릴리스할 수 있어야 한다.

6. 소비자 우선

당신의 서비스가 호출되기 위해 존재한다는 생각을 가지고 있어야 한다. 따라서 소비자 관점에서 디자인을 하고 API Docs 도구를 사용하여 API를 문서화하는 것은 필수적이다.

7. 오류 격리

기본적으로 마이크로서비스는 신뢰할 수 없다는 것을 전제로 설계해야 한다. 계단식 오류를 방지하기 위해 각 오류를 격리함으로써 시스템 복원력을 높이는 것이 중요하다.

  • Timeout
    : 모든 프로세스간 호출에 대한 시간 제한과 기본 초과 시간을 설정한다.
  • Bulkhead
    : 원격 호출을 서로 격리하고 원격 서비스 호출을 자체 스레드 풀로 분리한다.
  • Circuit breaker
    : 느리게 실행되고 성능이 저하된 시스템 호출을 종료해 자원 고갈을 방지한다.

일부 서비스 호출의 실패가 전체 서비스 자원을 고갈시켜서는 안된다.
Hystrix: 대표적인 오류 격리 서비스
1. 지정한 오류율을 초과한 경우 발생
2. 회로의 상태를 CLOSE -> OPEN 변경
3. 회로가 열린 상태에서 모든 요청에 대해 fallback method(대체함수) 실행
4. 지정 시간이 지난 후 HALF OPEN(하나의 요청만 원래 method로 실행) 수행
5. HALP OPEN이 성공하면 회로 상태 CLOSE, 실패하면 다시 OPEN

8. 높은 수준의 모니터링

시스템에서 무슨일이 일어나고 있는지 알기 위해서 충분한 입력과 로그가 생성되어야 한다. 또한 보다 높은 수준의 모니터링을 위해서 상태 체크, 중앙 집중식 로그 및 통계 집계, 상관 관계 ID를 사용한 분산 추적 등이 가능해야 한다.

2. 마이크로서비스 데이터 설계

서비스당 데이터베이스

이점

각 서비스 간 느슨한 결합으로 의존성을 낮추고 서비스 특성에 맞는 데이터베이스를 자유롭게 선택 가능하다.

문제점 및 해결방법

  1. 여러 데이터베이스를 통해 조인해야 하는 쿼리
  • 이벤트 소싱
  • API 구성
  • CQRS(명령/조회 책임 분리)
  1. 여러 데이터베이스에 걸친 트랜잭션
  • 사가현 패턴

1-1. 이벤트 소싱 (Event Sourcing)

이벤트 소싱이란, 이벤트를 위주로 비즈니스 로직을 구현하고, 애그리거트를 event store에 저장하는 것을 의미한다.

애그리거트: DDD에 기반한 설계에서 하나의 도메인에서 필요한 객체들을 하나의 군집으로 묶은 것

데이터를 가진 서비스가 직접 데이터를 조작하게 하지 않고, 서비스는 이벤트를 발생시키고 해당 이벤트는 event store에 저장만 한다.
그리고 이벤트 컨슈머가 해당 이벤트를 처리하도록 한다.

이벤트 소싱은 클라우드 환경에서의 메시지 중심의 분산 시스템에 적합하다.

이벤트 소싱은 쓰기에 적합하지만 읽기에 매우 비효율적인 구조를 가진다. 따라서 CQRS(Command Query Responsibility Segregation)를 적용하면 읽기 성능을 높일 수 있다.

이벤트 소싱 + CQRS

  1. Command가 발생하면 Command API에 요청 전달
  2. Command를 처리하는 서비스는 event store에 API 처리 결과를 저장
  3. 이벤트가 처리된 후 이벤트 브로커로 결과 전송
  4. 이벤트는 queries API로 전달되면 view store에 저장된다.
  5. 이후 client로부터 query가 요청되면 view store에 동기화된 데이터를 조회하게 된다.

이벤트 소싱 단점

  1. 높은 학습 곡선
  2. 어플리케이션의 복잡도 증가
  3. 애그리커트의 이력 보존이 주요 목표인만큼 데이터 삭제의 어려움

1-2. API 구성

API 컴포지션 패턴은 데이터를 소유하고 있는 각 서비스를 호출한 다음 결과를 조합하여 쿼리 작업을 구현하는 것을 의미한다.

장점

높은 학습 곡선 없이 MSA에서 데이터를 쿼리하는 가장 편리한 방법

단점

경우에 따라 대규모 데이터의 비효율적인 조인이 발생할 수 있다.

1-3. CQRS 패턴

CQRS의 주요 목표는 문제를 분리하는 것이다.

  • 생성, 수정 및 삭제 작업은 명령 모듈에 의해 구현된다.
  • 조회 작업은 쿼리 모듈에 의해 구현된다.
  • 명령 모듈에서 생성된 이벤트는 쿼리 모듈이 전달받고 항상 동기화된 상태를 유지한다.

이점

  1. MSA에서 쿼리를 효율적으로 구현할 수 있음
  2. 이벤트 소싱 기반 응용 프로그램에서 쿼리를 가능하게 함
  3. 서비스의 명령 및 쿼리 측면을 별도의 코드와 스키마로 분리함

단점

  1. 더 복잡한 아키텍처
    : 개발자가 쿼리쪽 서비스를 따로 또 작성해야 하고 Application에서 다양한 유형의 DB를 사용할 수 있으므로 개발자와 DevOps 모두 복잡성이 증가
  2. 복제 지연 발생
    이벤트가 명력측에서 게시되는 시점과 쿼리측에서 처리되는 시점 사이에 업데이트 지연이 발생

2-1. 사가 패턴(Saga Pattern)

사가 패턴을 사용하면 분산 트랜잭션을 사용하지 않고도 MSA에서 데이터 일관성을 유지할 수 있다.

트랜잭션의 관리 주체가 DBMS가 아닌 서비스이다. 따라서 사가 패턴은 보상 트랜잭션 처리도 해줘야 한다.

  • 보상 트랜잭션: 사가의 n번째 트랜잭션이 실패했을 때, 이전 n-1 번째 트랜잭션부터 수행된 모든 로컬 트랜잭션을 역순으로 롤백하는 것

Choreography 방식


각 서비스 간 이벤트를 주고 받는 Event Pub/Sub 방식이다. 메시지 큐를 통해 비동기 방식으로 전달하는 것도 가능하다.
1. 메인이 되는 서비스에서 관련 서비스의 로컬 트랜잭션을 호출한다.
2. 트랜잭션이 실패하면 취소된 서비스로부터 보상 이벤트를 발생시켜 보상 트랜잭션을 처리한다.

장점

  1. 단순성
    : 비즈니스 개체가 생성, 수정, 삭제되면 서비스는 이벤트를 게시만 하면 된다.
  2. 느슨한 결합
    : 이벤트를 통해 처리되므로 각 서비스가 누군지 알 필요가 없다.

단점

  1. 각 서비스는 자신에게 영향을 미치는 모든 이벤트를 항상 구독하고 있어야 한다.
  2. 개별 트랜잭션들이 공통된 공유ID를 정의해야하고, 이벤트 추적, 디버깅 어려움, 새로운 스텝 추가시 복잡도 증가

Orchestration 방식


Orchestrator를 중심으로 한 Invoke/Reply 방식이다.
1. 트랜잭션에 참가하는 각 서비스들의 로컬 트랜잭션은 Orchestrator에 의해 호출되고 상태값이 설정된다.
2. 참가하는 모든 트랜잭션이 처기가 된다면 메인 서비스의 상태가 변경된다.
3. 참가하는 서비스 중 하나라도 트랜잭션 실패하면 Orchestrator는 보상 트랜잭션을 수행한다.

장점

  1. 서비스의 복잡도가 감소한다.
  2. 느슨한 결합
    : 서비스는 Orchestrator가 호출하는 API를 구현하므로 이벤트에 대해 알 필요가 없다.
  3. 관심사의 분리를 통해 비즈니스 논리를 단순화 한다.

단점

  1. Orchestrator에서 너무 많은 비즈니스 로직을 중앙 집중화할 위험이 존재한다.

3. 마이크로서비스 통신 설계

마이크로서비스에서 각 서비스들을 호출하기 위한 여러가지 통신 방법이과 고려해야 될 사항들이 있다.

RPC 선택시 고려 사항

API에 대한 RPC 메커니즘을 선택하기 전에 서비스와 클라이언트 간의 상호 작용 스타일을 고려하는 것이 좋다.

  1. 상호 작용이 일대일인지 일대다인지 여부
  2. 상호 작용이 동기식인지 비동기식인지 여부
  • One-way notifications: 클라이언트가 서비스에 요청을 보내지만 응답을 기대하지 않는 방식

메시지 형식

1. 텍스트 형식

JSON or XML은 가장 많이 사용되는 텍스트 기반 형식이다.

👍장점

  • 사람이 읽을 수 있다.

👎단점

  • 메시지가 장황하다.
  • 텍스트 구문 분석과 관련된 오버헤드가 존재한다.

2. 바이너리 형식

Thrift, Protocol Buffers(Protobuf) 및 Avro는 가장 많이 사용되는 바이너리 형식이다.

👍장점

  • 메타 데이터가 최소한이므로 페이로드가 작다.
  • 텍스트 기반 구문 분석보다 비교적 빠르다.

👎단점

  • 사람이 읽을 수 없다.

동기 호출 패턴

호출 패턴으로는 REST / GraphQL / gRPC 와 같은 동기식 통신 메커니즘 또는 AMQP / STOMP 등 비동기식 메시지 기반 통신 메커니즘이 있다.

1. REST

HTTP URI를 통해 자원을 명시하고 HTTP 메서드를 통해 해당 자원에 대한 CRUD 작업을 처리하는 것

  1. REST API 정의
    가장 인기있는 REST 인터페이스 정의 언어는 OpenAPI 입니다.

  2. 단일 요청으로 여러 리소스를 가져와야 하는 과제
    이러한 확장성 부족으로 인해 발생할 수 있는 언더패치나 오버패치를 해결하기 위해 페이스북의 GraphQL이나 넷플릭스의 Falcor 같은 대체 API 기술이 점점 인기를 얻고 있다.

  3. HTTP 동사에 대한 매핑 작업의 과제
    REST API 디자인 문제는 수행하려는 작업을 HTTP 메서드에 매핑하면서 발생한다. PUT을 사용해서 주문을 취소, 수정 등의 업데이트 작업을 해야한다면 다음과 같이 해결할 수 있을 것이다.

  • 주문 취소: /orders/{orderId}/cancel
  • 주문 수정: /orders/{orderId}/revise
    이렇게 하위 리소스를 정의하여 처리할 수 있겠지만 이는 RESTful하지 않다. 이런 문제의 결과로 gRPC와 같은 대안이 인기를 얻고 있다.

2. gRPC

gRPC는 구글이 개발한 고성능의 오픈소스 RPC 프레임워크로 MSA 구조안에서 여러 언어로 작성된 서비스들 간의 통신을 지원한다.

HTTP/2를 사용하며 메시지 형식은 바이너리 타입의 protobuf를 사용하기 때문에 상대적으로 적은 페이로드를 사용하고 효율적이다.

  1. .proto 파일 내부에 서비스를 정의한다.
  2. protocol buffer compiler를 이용하여 서버와 클라이언트 코드를 작성한다.
  3. 서버 인터페이스를 구현하고 gRPC 서버를 생성한다.
  4. 클라이언트 어플리케이션을 만들고 RPC 호출을 한다.

👍장점

  • 풍부한 업데이트 작업이 있는 API를 비교적 간단히 디자인할 수 있다.
  • 메시지 형식이 바이너리라 작고 효율적이다.
  • 양방향 스트리밍을 통해 RPC와 메시징을 모두 사용할 수 있다.
  • 다양한 언어로 작성된 클라이언트들과 통신이 가능하다.

👎단점

  • REST/JSON 기반 API보다 더 많은 작업을 수행해야 한다.

3. GraphQL

GraphQL은 클라이언트에 데이터를 표시하는 방법을 쿼리로 정의함으로써 단일 요청을 사용하여 여러 리소스를 가져오는 문제를 해결한다.

GraphQL 서버는 클라이언트에 스키마(요청할 수 있는 데이터 모델)를 제공한다.

👍장점

  • 클라이언트는 서버에서 필요한 것을 정확하게 지정하여 언더패칭, 오버패칭을 줄이고 유연한 확장성을 가질 수 있다.

👎단점

  • 기본 제공 캐싱 지원이 없음
  • REST보다 복잡하다.

비동기 호출 패턴

1. One-way notifications

서비스는 채널을 구독하고 메시지를 처리한다. 응답이 다시 전송되지 않는다.

2. Publish / Subscribe

클라이언트는 여러 구독자가 읽을 수 있는 채널에 메시지를 게시하고 필요에 따라 여러 서비스가 구독하여 메시지를 처리한다.

3. Publish / Async Response

Pub/Sub 방식과 Request/Response 방식을 결합하여 더 높은 수준의 상호 작용을 형성한다.

4. 마이크로서비스 외부 API 통합 패턴

서비스의 외부 API를 설계하는 것은 클라이언트의 다양성으로 인해 더욱 복잡해졌다. 일반적으로 클라이언트는 서로 다른 데이터 요구 사항을 갖기 때문이다.

1. 직접 통신

클라이언트가 서비스를 직접 호출하는 방식으로 API를 디자인할 수 있다.

단점
1. 클라이언트는 보다 상세한 데이터를 위해 여러번의 API 요청을 해야하며 성능 저하를 유발한다.
2. 클라이언트가 각 서비스의 API에 대해 모두 알아야 하므로 캡슐화가 부족하고 클라이언트-서버 간의 강한 의존성을 갖는다.

2. API 게이트웨이 패턴

기본적으로 API 게이트웨이는 외부에서 서비스에 대한 진입점 역할을 하는 서비스이다.
구성 요소로는 요청 라우팅, 프로토콜 변환, API 구성 및 인증, 모니터링 및 속도 제한, 캐싱, 회로 차단 등 다양한 역할을 수행할 수 있다.

1. 요청 라우팅

요청 라우팅은 API 게이트웨이의 가장 중요한 기능 중 하나이다.
API 게이트웨이는 라우팅 맵을 참조하여 요청을 적절한 서비스로 라우팅한다.

2. API Composition 패턴

API 게이트웨이는 API Composition을 사용하여 API 작업을 수행할 수 있다. 하나의 요청으로 필요한 데이터 호출 및 조합하여 원하는 결과를 받을 수 있다.

3. 프로토콜 변환

MSA 각 서비스는 REST 및 gRPC를 포함하여 내부적으로 다양한 프로토콜을 사용할 수 있지만 외부에는 REST API를 제공해야 할 수도 있다. 이런 경우 외부와 내부 API간에 프로토콜 변환 작업이 필요하다.

4. BFF

단일 진입점을 갖는 서비스의 문제점은 클라이언트마다 다른 요구사항을 가진다는 것이다. 따라서 클라이언트에 따른 API 게이트웨이 구성으로 적절한 요청을 처리할 수 있다.

5. cross-cutting 문제 구현

cross-cutting 문제의 예는 아래와 같다.

  • 인증
  • 권한부여
  • 속도제한
  • 캐싱
  • 메트릭 수집
  • 로깅 요청

6. 기성품 API 게이트웨이

API 게이트웨이 기능을 구현한 여러 서비스가 이미 존재한다.

  • AWS API 게이트웨이: 백엔드에서 API Composition을 구현해야 한다.
  • Kong: NGINX 서버를 기반으로 하며, HTTP 메서드, 헤더 및 경로를 기반으로 유연한 라우팅 규칙을 정의할 수 있다.

7. API 게이트웨이 개발

웹 프레임워크를 사용하여 각 서비스에 대한 요청을 프록시하는 자체 API 게이트웨이를 빌드할 수 있다.

  • Netflix Zuul
    : Zuul은 라우팅, 속도 제한, 인증, cross-cutting 등을 구현하는 Netflix 프레임워크이다. Zuul은 서블릿 필터와 유사한 재사용 가능한 요청 인터셉터 필터 개념을 사용한다.

  • Spring Cloud Gateway
    : Spring Boot, Spring Webflux를 기반으로 하는 API 게이트웨이 프레임워크이다.

장점

  • 각 서비스의 내부 구조를 캡슐화 한다.
  • 클라이언트별 API를 제공하여 요청 횟수를 줄인다.
  • 클라이언트 코드를 단순화한다.

단점

  • 개발, 배포 등 또다른 관리 포인트가 생기는 것이다.
  • 서비스 노출을 위해 API 게이트웨이를 업데이트해야 하고 그에 따라 API 게이트웨이가 개발 병목 지점이 될 수 있다.

참고사이트1 - Microservice Design
참고사이트2 - Microservice Data Design
참고사이트3 - Microservice Communication Design
참고사이트4 - Microservice External Api Design

profile
Java 백엔드 개발자입니다. 제가 생각하는 개발자로서 가져야하는 업무적인 기본 소양과 현업에서 가지는 고민들을 같이 공유하고 소통하려고 합니다.

0개의 댓글