마이크로서비스와 같은 분산형 아키텍처에서는 보안과 로깅, 여러 서비스 호출에 걸친 사용자 추적처럼 중요한 작업들을 해야 할 시점이 옵니다.
이러한 기능들을 각 서비스에 직접 구축하게 될 경우 다음과 같은 결과를 초래할 수 있습니다.
이 문제를 해결하기 위해서 다음과 같은 것을 고려해야 합니다.
이러한 서비스를 게이트웨이(gateway)라고 합니다.
서비스 클라이언트(웹 서버와 같은)는 더 이상 마이크로서비스를 직접 호출하지 않으며, 모든 호출은 단일 정책 시행 지점(PEP, Policy Enforcement Point) 역할을 하는 서비스 게이트웨이를 경유한 다음 최종 목적지로 라우팅됩니다.
여기서 설명할 내용은 다음과 같습니다.
먼저, 게이트웨이에 대해서 간단하게 정리를 하고 들어갈까 합니다.
마이크로서비스 아키텍처에서 게이트웨이 서비스는 클라이언트와 백엔드 서비스 간의 중간 계층으로 작동하여, 여러 마이크로서비스로의 라우팅, 크로스-커팅(cross-cutting) 관심사의 처리 등을 담당합니다.
마이크로서비스 아키텍처의 복잡성을 관리하고, 클라이언트와 서비스 간의 상호작용을 단순화하는 데 중요한 역할을 합니다.
특징
장점
단점
서비스 게이트웨이는 애플리케이션 내 마이크로서비스를 호출하기 위해 유입되는 모든 트래픽의 게이트키퍼(gatekeeper) 역할을 합니다.
서비스 게이트웨이가 있으면 서비스 클라이언트는 각 서비스 URL을 직접 호출하지 않고 서비스 게이트웨이에 호출을 보냅니다. 또한 서비스 게이트웨이는 클라이언트와 개별 서비스 호출 사이에 있기 때문에 서비스를 호출하는 중앙 정책 시행 지점(PEP) 역할도 합니다.
서비스 게이트웨이에서 구현할 수 있는 횡단 관심사 예는 다음과 같습니다.
중요한 것은 서비스 게이트웨이가 단일 장애 지점이나 잠재적 병목 지점이 될 수 있기 때문에 다음 사항을 염두에 둬야 합니다.
스프링 클라우드 게이트웨이는 스프링 프레임워크 5, 프로젝트 리액터(Projext Reactor), 스프링 부트 2.0을 기반으로 한 API 게이트웨이 구현체입니다.
해당 게이트웨이는 논블로킹 방식인데, 주요 스레드를 차단하지 않는 방식으로 동작한다는 의미입니다.
따라서 이러한 스레드는 언제나 요청을 받아 백그라운드에서 비동기식으로 처리하고 처리가 완료되면 응답을 반환합니다.
다음과 같은 기능들을 제공합니다.
여기서 비동기 프로그래밍 모델에 대해서 설명하기 위해서
전통적인 멀티 스레딩
,단일 스레드 비동기 모델
,스레드 풀
,리액티브 프로그래밍
등과 같은 개념들이 설명되어야 하는데 필자가 아직 정확히 이해하지 못한 내용들이라 일단 넘어가도록 하겠습니다.
스프링 클라우드 게이트웨이는 본래 리버스 프록시입니다.
리버스 프록시(reverse proxy)는 자원에 도달하려는 클라이언트와 자원 사이에 위치한 중개 서버입니다.
클라이언트는 어떤 서버와 통신하고 있는지 알지 못합니다. 리버스 프록시는 클라이언트 요청을 캡처한 후 클라이언트를 대신하여 원격 자원을 호출합니다.
스프링 클라우드 게이트웨이에서는 상위 경로에 매핑하는 방법이 다음과 같이 있습니다.
게이트웨이에 대한 모든 경로 매핑은 yml 파일
에서 경로를 정의해서 수행합니다.
하지만 스프링 클라우드 게이트웨이는 다음 코드처럼 gateway-server 구성 파일에 구성 정보를 추가해서 서비스 ID를 기반으로 요청을 자동으로 라우팅할 수 있습니다.
spring:
cloud:
gateway:
discovery.locator: -------- 서비스 디스커버리에 등록된 서비스를 기반으로 게이트웨이가 경로를 생성하도록 설정한다.
enabled: true
lowerCaseServiceId: true
해당 코드처럼 스프링 클라우드 게이트웨이는 호출되는 서비스의 유레카 서비스 ID를 자동으로 사용하여 하위 서비스 인스턴스와 매핑합니다.
예를 들어서 다음과 같은 URL이 있다고 해봅시다.
http://localhost:8072/organization-service/v1/organization/blabla
여기서 lcoalhost:8072는 게이트웨이의 서버를 의미할 것입니다.
게이트웨이 서버는 바로 뒤에 있는 organization-service
를 확인하고 이것을 이용하여 호출해야 되는 서버를 확인할 것입니다.
스프링 클라우드 게이트웨이는 유레카 서비스 ID로 생성된 자동화된 경로에만 의존하지 않고 명시적으로 경로 매핑을 정의할 수 있어 코드를 더욱 세분화할 수 있습니다.
다음은 수동으로 정의한 코드입니다.
spring:
cloud:
gateway:
discovery.locator:
enabled: true
lowerCaseServiceId: true
routes:
- id: organization-service -------- 이 선택적(optional) ID는 임의의 경로에 대한 ID입니다.
- uri: lb://organization-service -------- 이 경로의 대상 URI를 설정합니다.
predicates: -------- 경로(path)는 load() 메서드로 설정되지만, 여러 옵션 중 하나입니다.
- Path=/organization/**
filters: -------- 응답을 보내기 전이나 후에 요청 또는 응답을 수정하고자 스프링 web.filters 들을 필터링합니다.
- RewritePath=/organization/(?<path>.*), /$\{path} ------- 매개 변수 및 교체 순서로 경로 정규식을 받아 요청 경로를 /organization/**에서 /**으로 변경한다.
- predicates: 라우팅 설정에서 특정 조건을 기반으로 요청을 필터링 하기 위한 구성 요소.
수동과 자동을 사용할 때 알아두어야 되는 것이 있습니다.
게이트웨이가 유레카 서비스 ID를 기반으로 자동화된 경로 매핑
을 사용하여 서비스를 노출할 때, 실행 중인 서비스 인스턴스가 없다면 게이트웨이는 서비스 경로를 아예 노출하지 않습니다.
그러나 수동으로 경로
를 서비스 디스커버리 ID에 매핑하면 유레카에 등록된 인스턴스가 없더라도 게이트웨이는 여전히 경로를 표시합니다.
이때 서비스에 대한 경로를 호출하면 HTTP 500 에러가 반환됩니다.
이러한 동작들은 취약점이 될 수 있으니 알아둡시다.
이후에 동적으로 라우팅 구성을 재로딩하기 위한 방법으로 스프링 액추에이터(Spring Actuator)를 사용하는 방식이 설명되고 있는데 어렵지 않으므로 넘어가겠습니다.
게이트웨이로 모든 요청을 프록시(proxy)할 수 있기 때문에 서비스 호출을 단순화할 수 있습니다.
이것은 사용자 정의 로직을 작성하여 동작시킬 수도 있는데, 보안, 로깅, 추적 등과 같은 횡단 관심사 처리를 하기에 적합합니다.
이러한 방식으로 스프링 클라우드 게이트웨이 Predicate과 Filter Factories는 스프링 관점(aspect) 클래스와 유사하게 사용할 수 있습니다.
이들을 사용하면 다양한 동작을 일치시키거나 가로챌 수 있어 원래 코드 작성자 모르게 호출 동작을 변경하거나 꾸밀 수 있습니다.
이것은 각 개별 서비스 서버에서 동작하는 것이 아닌 모든 서비스에 대한 공통 관심사를 구현할 수 있게 해줍니다.
서술자(predicate)를 사용하면 요청을 처리하기 전에 요청이 조건 집합을 충족하는지 확인할 수 있습니다.
동작을 살펴봅시다.
요청된 경로가 액세스하려는 특정 경로의 구성과 일치 여부를 확인
하는 역할을 합니다.게이트웨이 서술자는 요청을 실행하거나 처리하기 전에 요청이 조건 집합을 충족하는지 확인하는 객체입니다.
경로마다 논리 AND
로 결합할 수 있는 여러 Predicate Factories를 설정할 수 있습니다.
책에 설명되어 있는 서술자는 다음과 같습니다.
게이트웨이 Filter Factoires를 사용하면 코드에 정책 시행 지점(PEP)
을 삽입하여 모든 서비스 호출에 대해 일관된 방식으로 작업을 수행할 수 있습니다.
즉, 이러한 필터로 수신 및 발신하는 HTTP 요청과 응답을 수정할 수 있습니다.
스프링 클라우드 게이트웨이 내에서 필터를 사용하여 사용자 정의 로직을 만들 수 있습니다.
필터를 사용하여 각 서비스 요청이 통과하는 비지니스 로직 체인을 구현할 수 있습니다. 스프링 클라우드 게이트웨이는 다음 두 가지 종류의 필터를 지원합니다.
실제 요청이 목적지로 전송되기 전에
사전 필터가 호출됩니다. 대상 서비스 이후
호출되고 응답은 클라이언트로 다시 전송됩니다. 단계를 살펴봅시다.
이후에는 스프링 클라우드 게이트웨이를 이용하여 필터를 구현하는 코드를 설명하고 있습니다.
특히, TreadLocal
을 사용하여 Tracking ID
를 부여하는 부분을 설명하는데 TreadLocal을 이용하여 상관관계 ID를 요청에 부여해 놓고 그것을 헤더로 전달한다면 요청을 Tracking 할 수 있게 됩니다.
코드에 대한 내용은 생략하도록 하겠습니다.