보라코딩
MSA 추가 공부 (Saga 패턴, 인증/인가 등) 본문
Two-Phase Commit
Two-Phase Commit (2PC) 프로토콜은 분산 시스템에서 트랜잭션의 원자성을 보장하기 위해 사용되는 기법
이 프로토콜은 트랜잭션이 모든 관련된 참여자(Participant)들에 의해 성공적으로 수행되거나, 그렇지 않으면 전혀 수행되지 않도록 합니다.
2PC는 주로 데이터베이스 시스템에서 사용되며, 특히 분산 데이터베이스 환경에서 중요합니다.
Two-Phase Commit 프로토콜
2PC 프로토콜은 두 단계로 이루어집니다: 준비 단계(Prepare Phase)와 커밋 단계(Commit Phase).
1. 준비 단계 (Prepare Phase)
- 코디네이터(Coordinator)는 트랜잭션을 시작하고, 모든 참여자에게 트랜잭션 준비 여부를 묻는 메시지(prepare request)를 보냅니다.
- 참여자(Participants)는 트랜잭션을 수행할 준비가 되었는지 확인합니다. 준비가 되었으면, "준비 완료(Ready)" 메시지를 코디네이터에게 보내고, 아니라면 "거부(Abort)" 메시지를 보냅니다.
- 각 참여자는 로컬 로그에 준비 상태를 기록하여, 장애가 발생하더라도 이후의 조치를 취할 수 있도록 합니다.
2. 커밋 단계 (Commit Phase)
- 모든 참여자로부터 "준비 완료(Ready)" 응답을 받은 경우, 코디네이터는 트랜잭션을 커밋합니다. 코디네이터는 모든 참여자에게 "커밋(Commit)" 메시지를 보내고, 참여자들은 트랜잭션을 실제로 커밋합니다.
- 하나라도 "거부(Abort)" 응답을 받은 경우, 코디네이터는 트랜잭션을 중단합니다. 코디네이터는 모든 참여자에게 "중단(Abort)" 메시지를 보내고, 참여자들은 트랜잭션을 롤백합니다.
- 참여자들은 커밋 또는 롤백 상태를 로컬 로그에 기록하여, 완료 상태를 보장합니다.
장단점
장점
- 원자성 보장: 모든 참여자가 트랜잭션을 성공적으로 완료하거나, 그렇지 않으면 롤백하여 일관성을 유지합니다.
- 일관성 유지: 분산된 환경에서 데이터의 일관성을 보장할 수 있습니다.
단점
- 오버헤드: 준비 단계와 커밋 단계에서 각각 메시지 통신이 필요하여, 네트워크 오버헤드가 발생합니다.
- 차단: 참여자가 응답하지 않거나 장애가 발생할 경우, 코디네이터와 다른 참여자들은 대기 상태에 빠질 수 있습니다.
- 복잡성: 장애 복구 및 로그 관리의 복잡성이 증가합니다.
예시
- 코디네이터 서비스는 트랜잭션을 시작하고, 참여자 A와 B에게 트랜잭션 준비를 요청합니다.
- 참여자 A와 참여자 B는 로컬 로그에 준비 상태를 기록하고, 준비가 완료되었음을 코디네이터에게 알립니다.
- 코디네이터는 두 참여자로부터 모두 준비 완료 메시지를 받으면, 트랜잭션 커밋을 결정하고, 커밋 메시지를 보냅니다.
- 참여자 A와 B는 트랜잭션을 커밋하고, 그 결과를 로컬 로그에 기록합니다.
- 만약 참여자 중 하나라도 준비가 되지 않았다고 알리면, 코디네이터는 트랜잭션 중단을 결정하고, 중단 메시지를 보냅니다. 참여자들은 트랜잭션을 롤백합니다.
2PC 프로토콜은 MSA(Microservices Architecture) 환경에서 데이터베이스 일관성을 유지하기 위해 사용될 수 있지만, 그 오버헤드와 복잡성 때문에 일부 경우에는 다른 트랜잭션 관리 기법(SAGA 패턴 등)을 사용하는 것이 더 나을 수 있습니다.
SAGA 패턴
SAGA 패턴은 분산 시스템에서 데이터 일관성을 유지하기 위한 트랜잭션 관리 기법
특히 긴 시간에 걸쳐 수행되는 트랜잭션을 관리하는 데 유용
Choreography based SAGA pattern과 Orchestration based SAGA pattern
1. Choreography based SAGA pattern
각 서비스가 독립적으로 자신이 수행할 작업을 알고 있으며, 다른 서비스와 협력하여 트랜잭션을 완성합니다.
각 서비스는 자신이 할 일을 완료한 후, 다음 서비스를 호출하여 그 서비스가 할 일을 수행하도록 합니다.
특징
- 분산 제어: 중앙 통제자가 없이 각 서비스가 자신의 작업을 알고 수행합니다.
- 유연성: 서비스 간 결합도가 낮아, 서비스의 독립성이 높습니다.
- 확장성: 새로운 서비스나 작업을 추가하는 것이 상대적으로 쉽습니다.
예시
- 주문 서비스: 주문을 생성하고, 결제 서비스를 호출합니다.
- 결제 서비스: 결제를 처리하고, 재고 서비스를 호출합니다.
- 재고 서비스: 재고를 확인하고, 주문 완료 메시지를 보냅니다.
장단점
- 장점: 단일 장애 지점(Single Point of Failure)이 없고, 확장성과 유연성이 뛰어납니다.
- 단점: 서비스 간의 상호작용이 복잡해질 수 있으며, 전체 트랜잭션 상태를 파악하기 어려울 수 있습니다.
2. Orchestration based SAGA pattern
Orchestration based SAGA 패턴에서는 중앙 조정자가 존재하여, 전체 트랜잭션을 조정하고 관리합니다. 중앙 조정자는 각 서비스에 작업을 지시하고, 각 단계의 완료 여부를 추적합니다.
특징
- 중앙 제어: 중앙 조정자가 전체 트랜잭션을 관리하고 조정합니다.
- 가시성: 중앙 조정자가 트랜잭션 상태를 명확히 추적할 수 있습니다.
- 복잡성: 중앙 조정자가 추가되어 시스템 복잡도가 증가할 수 있습니다.
예시
- 조정자 서비스: 주문을 생성하고, 결제 서비스에 결제 요청을 보냅니다.
- 결제 서비스: 결제를 처리하고, 완료되면 조정자 서비스에 알립니다.
- 조정자 서비스: 재고 서비스에 재고 확인 요청을 보냅니다.
- 재고 서비스: 재고를 확인하고, 완료되면 조정자 서비스에 알립니다.
- 조정자 서비스: 주문 완료를 알리는 메시지를 보냅니다.
장단점
- 장점: 트랜잭션 상태를 명확히 추적할 수 있어 관리가 용이합니다.
- 단점: 중앙 조정자가 단일 장애 지점이 될 수 있으며, 시스템 복잡도가 증가할 수 있습니다.
비교
- Choreography: 서비스 간의 결합도가 낮고 확장성이 좋지만, 트랜잭션 상태 추적이 어렵습니다.
- Orchestration: 트랜잭션 상태 추적이 용이하고 관리가 쉬우나, 중앙 조정자가 단일 장애 지점이 될 수 있습니다.
Outbox 패턴
Outbox 패턴 설명
Outbox 패턴은 데이터베이스와 메시지 브로커 간의 트랜잭션을 관리할 때 유용합니다.
데이터베이스와 메시지 브로커 간의 트랜잭션의 일관성 보장!
- 데이터베이스 트랜잭션: 트랜잭션 내에서 애플리케이션의 주요 데이터를 저장하면서, 동시에 보내야 할 메시지를 Outbox 테이블에 저장합니다. 이 작업은 하나의 원자적 트랜잭션으로 처리됩니다.
- Outbox 테이블: Outbox 테이블은 메시지를 큐에 보내기 전 임시로 저장하는 곳입니다. 이 테이블은 애플리케이션의 데이터베이스 내에 존재합니다.
- 스케줄러 또는 이벤트 프로세서: 백그라운드 프로세스(스케줄러 또는 워커)가 주기적으로 Outbox 테이블을 확인하고, 메시지를 Kafka와 같은 메시지 브로커로 전송합니다. 전송이 완료되면 Outbox 테이블에서 해당 메시지를 삭제하거나 상태를 업데이트합니다.
이렇게 하면 데이터베이스 트랜잭션이 성공적으로 커밋된 후에만 메시지가 Kafka로 전송되기 때문에 데이터 일관성을 유지할 수 있습니다. 또한, 메시지 전송 중 오류가 발생하더라도 나중에 다시 시도할 수 있습니다.
인증 / 인가
MSA에서의 인증 및 인가 구현
1. API Gateway 사용
- API Gateway는 모든 클라이언트 요청을 받아 각 마이크로서비스로 라우팅하는 중간 지점입니다.
- 인증과 인가를 중앙에서 관리하여, 각 서비스가 개별적으로 이를 처리할 필요가 없습니다.
- API Gateway는 JWT를 검증하고, 인가 규칙을 적용하여 요청을 필터링합니다.
2. 독립적인 인증 서비스
- Identity Provider (IdP) 또는 Authentication Service는 사용자 인증을 담당하며, JWT를 발급합니다.
- 모든 마이크로서비스는 IdP가 발급한 JWT를 사용하여 사용자를 인증합니다.
- 인가 로직은 각 서비스 내부에서 처리되거나 중앙 인가 서비스에서 처리됩니다.
예시 시나리오
- 사용자 로그인
- 사용자가 로그인 페이지에서 자격 증명을 입력합니다.
- IdP는 자격 증명을 확인하고, JWT를 발급합니다.
- JWT 발급
- JWT에는 사용자 ID, 역할, 만료 시간 등의 정보가 포함됩니다.
- JWT는 서명되어 있어 변조를 방지합니다.
- 클라이언트 요청
- 클라이언트 애플리케이션은 JWT를 포함하여 각 마이크로서비스에 요청을 보냅니다.
- JWT 검증 및 인가
- API Gateway 또는 각 마이크로서비스는 JWT를 검증합니다.
- JWT의 역할 또는 속성 정보를 바탕으로 인가 규칙을 적용하여 접근을 제어합니다.
결론
MSA에서의 인증과 인가는 분산된 서비스 환경에서도 일관된 보안 정책을 유지할 수 있도록 설계해야 합니다. 중앙 집중화된 인증 서버(IdP)를 통해 인증을 관리하고, JWT와 같은 토큰 기반 접근 방식을 사용하여 각 서비스가 독립적으로 인가를 수행하는 것이 일반적입니다. 이를 통해 확장성과 보안성을 모두 확보할 수 있습니다.
필터와 인터셉터
요청(request) 및 응답(response)을 가로채고 처리하기 위해 필터(Filter)와 인터셉터(Interceptor)를 사용
둘 다 요청이 컨트롤러에 도달하기 전에 또는 응답이 클라이언트에게 반환되기 전에 특정 작업을 수행
Spring 필터 (Filter)
역할
- Servlet 필터는 서블릿 컨테이너 수준에서 작동하며, 모든 요청 및 응답을 가로챌 수 있습니다.
- 주로 보안, 로깅, 인증, 인코딩 설정, CORS 처리 등의 작업에 사용됩니다.
동작 방식
- 클라이언트 요청이 들어오면 필터 체인이 먼저 실행됩니다.
- 각 필터는 요청을 처리하고 다음 필터 또는 최종적으로 서블릿(또는 스프링 디스패처 서블릿)으로 요청을 전달합니다.
- 응답이 클라이언트로 돌아올 때도 필터 체인을 따라 역순으로 처리됩니다.
구현 방법
- javax.servlet.Filter 인터페이스를 구현하여 필터 클래스를 작성합니다.
- doFilter 메서드에서 요청 및 응답을 처리합니다.
- 필터는 @WebFilter 어노테이션을 사용하거나 Spring의 FilterRegistrationBean을 통해 등록할 수 있습니다.
Spring 인터셉터 (Interceptor)
역할
- Spring 인터셉터는 Spring MVC 수준에서 작동하며, DispatcherServlet에 의해 처리되는 요청을 가로챕니다.
- 주로 인증, 권한 부여, 로깅, 세션 검증 등의 작업에 사용됩니다.
동작 방식
- 핸들러 매핑 과정에서 인터셉터가 먼저 실행됩니다.
- 각 인터셉터는 핸들러(컨트롤러)가 실행되기 전(preHandle), 핸들러 실행 후(postHandle), 그리고 응답이 클라이언트로 반환되기 전(afterCompletion)에 특정 작업을 수행할 수 있습니다.
구현 방법
- HandlerInterceptor 인터페이스를 구현하여 인터셉터 클래스를 작성합니다.
- preHandle, postHandle, afterCompletion 메서드를 오버라이드하여 필요한 작업을 수행합니다.
- 인터셉터는 WebMvcConfigurer의 addInterceptors 메서드를 통해 등록할 수 있습니다.
요약
- 적용 레벨:
- 필터는 서블릿 컨테이너 레벨에서 작동하여 모든 서블릿 요청에 대해 작동합니다.
- 인터셉터는 Spring MVC 레벨에서 작동하여 DispatcherServlet을 통해 처리되는 요청에만 작동합니다.
- 주요 목적:
- 필터는 주로 서블릿과 관련된 작업(보안, 인코딩, 로깅 등)에 사용됩니다.
- 인터셉터는 Spring MVC 요청 전후에 특정 로직을 삽입하는 데 사용됩니다.
- 구현 및 등록 방법:
- 필터는 javax.servlet.Filter 인터페이스를 구현하고, 서블릿 컨테이너 또는 Spring Boot에서 필터로 등록합니다.
- 인터셉터는 HandlerInterceptor 인터페이스를 구현하고, Spring MVC 설정에서 등록합니다.
필터와 인터셉터를 적절히 사용하면, 요청 및 응답 처리를 세밀하게 제어할 수 있어 보안, 성능, 로깅, 모니터링 등 다양한 요구사항을 충족시킬 수 있습니다.
Eureka
Gateway
Spring Cloud
Config
1. Spring Cloud
Spring Cloud는 분산 시스템을 쉽게 구축하고 운영할 수 있도록 다양한 기능을 제공하는 프로젝트입니다. 이 프로젝트는 여러 하위 프로젝트로 구성되어 있으며, 그 중 일부는 다음과 같습니다:
주요 하위 프로젝트
- Spring Cloud Netflix: Netflix OSS(오픈 소스 소프트웨어) 스택과 통합된 다양한 기능을 제공합니다. 예를 들어, Eureka (서비스 디스커버리), Ribbon (클라이언트 사이드 로드 밸런서), Hystrix (서킷 브레이커) 등이 있습니다.
- Spring Cloud Config: 분산 시스템에서의 설정 관리를 위한 중앙 구성 서버를 제공합니다.
- Spring Cloud Gateway: API Gateway의 기능을 제공합니다.
- Spring Cloud Sleuth: 분산된 서비스 간의 트랜잭션 추적을 위한 분산 추적 도구입니다.
- Spring Cloud Bus: 마이크로서비스 간의 이벤트 버스를 통해 설정 변경 등을 실시간으로 전달합니다.
2. Gateway
Spring Cloud Gateway는 마이크로서비스 아키텍처에서 API Gateway의 역할을 수행하는 도구입니다.
기능
- 요청 라우팅: 클라이언트 요청을 적절한 마이크로서비스로 라우팅합니다.
- 필터링: 요청이나 응답을 수정하거나, 인증 및 인가와 같은 추가 기능을 수행할 수 있는 필터를 적용합니다.
- 로드 밸런싱: 다양한 마이크로서비스 인스턴스 간의 로드 밸런싱을 제공합니다.
3. Eureka
Eureka는 Netflix가 개발한 서비스 디스커버리 도구로, Spring Cloud Netflix 프로젝트의 일환으로 제공됩니다.
기능
- 서비스 레지스트리: 각 마이크로서비스는 Eureka 서버에 자신을 등록합니다. 이를 통해 다른 서비스들이 해당 서비스의 위치를 알 수 있습니다.
- 서비스 디스커버리: 서비스가 서로를 찾고 통신할 수 있도록 도와줍니다. 클라이언트는 Eureka 서버를 통해 필요한 서비스의 위치를 동적으로 검색합니다.
- 상태 확인: Eureka는 등록된 서비스의 상태를 주기적으로 확인하고, 실패한 서비스는 레지스트리에서 제거합니다.
4. Spring Cloud Config
Spring Cloud Config는 분산 시스템에서 설정을 중앙에서 관리하기 위한 도구입니다. 이를 통해 여러 마이크로서비스의 설정을 일관되게 유지할 수 있습니다.
기능
- 중앙 구성 서버: 모든 마이크로서비스의 설정 파일을 중앙에서 관리합니다. 설정 파일은 Git, SVN 등의 버전 관리 시스템에 저장될 수 있습니다.
- 설정 변경 반영: 설정이 변경되면 각 마이크로서비스는 자동으로 변경된 설정을 가져올 수 있습니다.
- 프로파일 기반 구성: 서로 다른 환경(예: 개발, 테스트, 프로덕션)에 따라 다른 설정을 적용할 수 있습니다.
결론
- Eureka: 서비스 디스커버리와 등록을 통해 동적으로 서비스 간 통신을 가능하게 합니다.
- Gateway: 클라이언트 요청을 적절한 서비스로 라우팅하고, 다양한 필터링 기능을 제공합니다.
- Spring Cloud: 다양한 하위 프로젝트를 통해 분산 시스템을 쉽게 구축하고 운영할 수 있도록 도와줍니다.
- Config: 분산 시스템의 설정을 중앙에서 관리하여 일관성을 유지하고, 설정 변경을 실시간으로 반영합니다.