본문 바로가기
🔬아키텍처/- Spring Cloud for MSA

Spring Cloud Gateway 를 이용해 API Gateway를 구성하고 실습해보자 (Eureka Server 를 이용해 Load Balancing 하기)

by Wonit 2021. 4. 24.

해당 글은 Spring Cloud Netflix Eureka에 의존하는 글입니다. 실습 환경을 따라하시려면 Eureka 글을 확인하시길 바랍니다.

목차

  • Gateway Pattern
  • Gateway가 도입되면?
  • Spring Cloud Gateway
    • Gateway가 동작하는 방법
    • Gateway 내부 구조
  • 실습하기
    • 프로젝트 생성하기
    • 의존성
    • 마이크로서비스 연결하기
    • Eureka Server 에서 각각 인스턴스 정보를 받아 로드밸런싱 하기

Gateway Pattern

 

Microservice 에서 Gateway 패턴은 가장 흔하고 유용하게 쓰는 패턴 중 하나이다.

 

Spring Cloud와 별개로 Gateway 패턴에 대한 자세한 사항은 MSA의 핵심 구성 요소 - API Gateway 패턴 에서 확인할 수 있습니다.

 

지난 시간 우리는 Eureka로 Discovery Pattern 을 구현하였다.

 

지난 시간에서 구현한 Eureka Server-Client 의 구조를 잠시 살펴보자.

 

 

각각의 마이크로 서비스들의 포트는 8812, 51728, 9271 은 Eureka Server에 Registration 되어있는 상태로 각각의 서비스들의 포트가 바뀌어도 Eureka Dashboard에서 확인할 수 있는 구조였었다.

 

하지만 이런 상황이라면 어떨까?

 

User-Service(8812 포트) 에서 login 을 수행하고 Order-Service(51728 포트) 에서 상품을 주문 한 뒤, Delivery-Service(9271) 포트에서 배달 주문을 수행한다.

문제 상황은 해당 주문과 배달을 위해서 각각의 마이크로서비스들은 다른 서비들의 포트를 모두 알고있어야 한다. 만약 포트가 바뀐다면 포트가 바뀐 서버를 제외한 모든 서버는 바뀐 포트의 번호를 수정하고 다시 발드 -> 배포를 해야하는 상황이다.

 

이런 상황에서 우리는 합리적인 생각을 할 수 있다.

 

만약 모든 마이크로서비스의 포트가 동일하다면? 혹은 모든 마이크로서비스를 호출할 때 포트를 신경쓰지 않아도 된다면?

 

이런 상황에서 Spring Gateway가 등장하는 것이다.

 

Gateway가 도입되면?

 

위의 상황에서 Gateway를 도입한다면 다음과 같은 구조가 될 것이다.

 

User 서버가 Order 서버에 어떠한 요청을 보내야 하는 상황이라고 가정해보자!

 

  1. User 서버는 Order 서버에 보내야할 요청을 Gateway로 전달
  2. Gateway는 Eureka Server로 Order 서버의 정보 discovery
  3. Gateway가 Order Server로 연결

 

이렇게 된다면 다음과 같은 장점들이 존재한다.

 

  • 각각의 마이크로서비스들은 서로의 포트 번호를 몰라도 된다.
  • Front 에서는 요청을 Gateway로만 보내면 되기 때문에 Gateway 포트만 알면 모든 요청을 수행할 수 있다.
  • 모든 요청은 Gateway 를 거치기 때문에 로그를 쉽게 다룰 수 있다.
  • Gateway 가 요청의 진입점이므로 통합 인증을 수행할 수 있다.

이러한 장점을 최초에는 Spring Cloud Netflix Zuul을 이용했다.

 

하지만 Netflix Zuul은 Servlet MVC와 Tomcat을 이용하는 이유 때문에 현재 프로그래밍의 흐름인 비동기 통신 로직에 취약했었다.

 

그에 대한 대안을 찾고자 Spring Cloud는 Tomcat을 대신하여 Netty 서버를 내장한 Web Flux 기반 Gateway 인 Spring Cloud Gateway를 만들게 되었다.

 

Spring Cloud Gateway

Spring Cloud Gateway 공식 문서

 

Spring Cloud Gateway는 Spring WebFlux 위에서 API Gateway를 구축하기 위한 라이브러리이다.

 

Spring Cloud Gateway aims to provide a simple, yet effective way to route to APIs and provide cross cutting concerns to them such as: security, monitoring/metrics, and resiliency.

Spring Cloud Gateway (SCG)는 간편하면서도 효과적인 API 라우팅 방법을 제공하고, 보안 모니터링, 메트릭, 복원력 문제를 해결하는 것을 목표로 한다.

 

Spring Cloud Gateway 에서 사용하는 3가지 명칭

 

우선 Spring Cloud Gateway 에서 사용하는 3가지 용어에 대해서 먼저 알아볼 필요가 있다.

 

  1. Route
  2. Predicate
  3. Filter

 

Route (라우트)

목적지의 URI와 Predicates라는 조건들의 목록 그리고 필터들을 이용하여 어떤 곳으로 Routing 할 것인지를 명시하는 역할을 한다.


우리는 Routes 에 대한 속성과 정보를 입력하여 Spring Cloud가 어떤 방식으로 동작하라고 지시할 수 있게 된다.

 

Predicate

쉽게 조건이라고 생각하면 된다.


아래에서 우리는 Predicates 를 작성할 것인데, 다음과 같이 작성한다.

 

predicated: -Path=/user/**

 

그럼 user 라는 모든 경로를 지정한 것이다.

 

Filter

들어오는 요청과 응답, Request, Response을 특정 필터를 타게 함으로 우리가 원하는 방식으로 요청을 보내거나 헤더를 조작할 수 있고, 해당 필터를 이용해서 로그 파일을 작성하게 할 수 도 있다.

 

Spring Cloud Gateway의 구조

 

Spring Cloud Gateway는 다음과 같은 구조로 구성되어 있다.

 

  1. Client 는 Spring Cloud Gateway 에 요청을 보낸다.
  2. Gateway Handler Mapping 에서 해당 요청에 대한 Route와 Predicates가 일치한다고 판단하면 해당 요청은 Gateway Web handler로 보내진다.
  3. handler 에서 Filter Chain 을 이용해서 사전 필터 혹은 사후 필터로 나누어 동작한다.
  4. 필터링이 된 후 실제 마이크로서비스에게 전달된다.

 

Spring Cloud Gateway 내부 들여다보기

 

Spring Cloud Gateway는 크게 3가지의 파트가 존재한다.

 

  • Gateway Handler Mapping
    • Gateway 가 CLient로 부터 어떤 요청이 왔는지 확인하고 Mapping 하는 작업을 수행한다.
  • Predicate
    • Handler Mapping 시에 필요한 Uri 정보나, Path 정보를 확인하는 주체가 된다.
  • Filter
    • Handler Mapping이 된 후 들어온 요청에 대한 필터 작업을 수행할 수 있다.
    • 2개의 필터로 크게 나뉘며 사전(Pre Filter)와 사후(Post Filter)로 나눌 수 있다.
      • Pre Filter
        • 특정 작업이 일어나기 전에 지정
      • Post Filter
        • 특정 작업이 끝난 후에 지정
    • 이런 필터에 대한 정보는 yml 설정 파일에도 정의할 수 있고 java code 에서 정의할 수도 있다

 

프로젝트 생성하기

 

지난 시간에 만들었던 폴더 아래에 gateway-service 라는 Spring 프로젝트를 만들어주자.

 

의존성 추가하기

 

우리가 지난 시간에 했던 프로젝트에 Gateway를 도입해보자.

 

우선 Gateway를 Eureka Client로 동작시키게 하기 위해서 Eurke Client 의존성과 Spring Cloud Gateway 의존성을 함께 추가시켜보자.

 

마이크로서비스 연결하기

해당 실습 내용은 Spring Cloud Netflix Eureka에 의존하는 내용 입니다. 실습 환경을 따라하시려면 Eureka 글을 확인하시길 바랍니다.

 

앞서 계속 이야기했지만 Gateway도 하나의 서비스이다.

 

그러므로 Eureka Server에 등록시켜서 Dashboard 에서 한 번에 관리할 수 있게 Eureka Discover Client로 등록시켜주자.

 

역시 application.properties 파일을 application.yml 파일로 변경시켜 설정 정보들을 추가해주자.

 

server:
  port: 8000

eureka:
  client:
    fetch-registry: true
    register-with-eureka: true
    service-url:
      defaultZone: http://localhost:8761/eureka

spring:
  application:
    name: gateway-service

 

그리고 Eureka와 다른 Microservice 들을 실행시켜서 Eureka Dashboard에 잘 나오는지 먼저 확인해보자.

 

  1. Eureka Server 실행
  2. User Microservice 실행
  3. Order Microservice 실행
  4. Gateway service 실행

 

그럼 다음과 같이 Eureka Dashboard 에 잘 등록된 것을 확인할 수 있다.

 

이제 각각의 서비스를 Gateway로 연결해보자.

 

서비스를 Gateway 로 연결하기 위해서는 먼저 Gateway Service 에 application.yml을 수정해야 한다.

 

yml에 들어가야 할 설정 정보는 다음과 같다.

 

  • spring.cloud.gateway.routes : 라우팅에 대한 정보
  • spring.cloud.gateway.routes.id : 해당 라우팅이 어떠한 이름으로 라우팅 될 것인지에 대한 이름
  • spring.cloud.gateway.routes.uri : 현재 라우팅을 어디에 포워딩 시킬 것인지를 명시해준다.
  • spring.cloud.gateway.routes.predicates : 조건식으로 특정 요청이 predicates에 맞는 조건으로 들어오면 해당 route를 수행할 것을 지정한다.
server:
  port: 8000

eureka:
  client:
    fetch-registry: true # 유레카 클라이언트 활성화
    register-with-eureka: true # 유레카 클라이언트 활성화
    service-url:
      defaultZone: http://localhost:8761/eureka # 유레카 클라이언트로 등록

spring:
  application:
    name: gateway-service

  cloud:
    gateway:
      routes:
        - id: user-service
          uri: http://localhost:64412 # 포워딩할 주소, http://localhost:8000/user 로 들어오면 http://localhost:64412 로 포워딩
          predicates:
            - Path=/user/** # 해당 gateway 서버의 /user/**로 들어오는 요은 user-service로 인식하겠다는 조건

        - id: order-service
          uri: http://localhost:54412 # 포워딩 할 주소, http://localhost:8000/order 로 들어오면 http://localhost:54412 로 포워딩
          predicates:
            - Path=/order/** # 해당 gateway 서버의 /order/**로 들어오는 요은 order-service로 인식하겠다는 조건

 

위의 상태가 된다면 이제 Gateway 로 들어오는 모든 요청이 predicates에 충족한다면 우리가 지정한 uri로 포워딩을 하는 상태가 된다.

 

하지만 주의해야 할 것이 있다.

 

포워딩을 할 때, http://localhost:8000/order/info 로 요청이 들어왔다고 가정하면 다음과 같이 포워딩이 된다.

 

하지만 우리의 서비스 인스턴스의 Request Mapping 은 다음과 같이 되어있다.

// 생략
public class UserServiceApplication {
    // 생략
    @GetMapping("/info")
    public String info(@Value("${server.port}") String port) {
        return "User 서비스의 기본 동작 Port: {" + port + "}";
    }
}

// 생략
public class OrderServiceApplication {
    // 생략
    @GetMapping("/info")
    public String info(@Value("${server.port}") String port) {
        return "Order 서비스의 기본 동작 Port: {" + port + "}";
    }
}

만약 이 상태에서 그대로 요청을 보낸다면 404 Error 를 맞게 된다.

 

당연히 우리의 마이크로서비스들은 localhost/info 로만 Request Mapping이 되어있는데 Gateway는 localhost/user/info 로 요청을 보내주니..

 

지금은 아주 간단하게 UserService와 OrderService의 RequestMapping 정보를 바꿔주도록 하자.

 

다음에 가서 RewitePath를 이용하여 RequestMapping 정보를 바꾸지 않고 경로를 바꿔주는 방법을 배워볼 것이다.

// 생략
public class UserServiceApplication {
    // 생략
    @GetMapping("/user/info")
    public String info(@Value("${server.port}") String port) {
        return "User 서비스의 기본 동작 Port: {" + port + "}";
    }
}

// 생략
public class OrderServiceApplication {
    // 생략
    @GetMapping("/order/info")
    public String info(@Value("${server.port}") String port) {
        return "Order 서비스의 기본 동작 Port: {" + port + "}";
    }
}

 

그리고 다시 요청을 보내면 각각 잘 동작하는 것을 볼 수 있다.

 

Eureka Server 에서 각각 인스턴스 정보를 받아 로드밸런싱 하기

지금 조금 아쉬운 부분이 있다.


Eureka Server 에서 각각 인스턴스 정보를 관리하는데, Gateway 에서는 직접 포트와 서버의 주소를 명시해주고 있기 때문이다.

 

아예 Eureka Server로 부터 정보를 받아서 로드밸런싱을 수행해보자.

 

그러기 위해서는 Eureka Dashboard에 있는 Application Name 을 알아야 한다.

 

 

각각 ORDER-SERVICE와 USER_SERVICE 로 되어 있으니 application.yml 에서 포워딩할 주소를 다음과 같이 변경하자.

 

spring:
  # 생략
  cloud:
    gateway:
      routes:
        - id: user-service
          uri: lb://USER-SERVICE
          predicates:
            - Path=/user/**
        - id: order-service
          uri: lb://ORDER-SERVICE
          predicates:
            - Path=/order/**

그럼 User-Service 가 Scale-Out 되더라도, 즉 포트가 여러 나뉜다거나 혹은 uri 정보가 변경되더라도 Gateway 에서는 그걸 알 필요가 없게 되는 것이다.

이게 바로 Service Discovery 의 매력이다.

 

그리고 다시 동작시켜보면 잘 작동하는 것을 볼 수 있다.

 

이렇게 오늘은 아주 간단하게 Spring Cloud Gateway 를 만져보고 각각 마이크로서비스들과 연결하여 동작시켜 보았다.

 

아직 Gateway 의 아주 기본적인 내용만 다뤘으므로 다음 시간 부터 Filter를 이용해 Request와 Response를 조작하는 방법에 대해서 알아보자.

댓글3