본문 바로가기
web

[MSA] Spring Cloud를 사용해보자(3)-API Gateway

by 뽀리님 2023. 11. 22.

기존에 있던 모놀리식 아키텍처를 msa 로 구현해보는 과정에서

API Gateway 로 Spring Cloud 를 이용하기로 했다.

 

그전에 MSA와 DevOps, K8S 등의  개념을 알고 가는게 좋을꺼 같아 퍼왔다. 참조하면 좋을꺼같다.

https://happycloud-lee.tistory.com/261?category=8322466

 

일하는 방식 변화 핵심만 빠르게 이해하기: 애자일, 마이크로서비스, 데브옵스, 클라우드

새로운 변화의 물결 내용 순서 마이크로서비스가 최근에 왜 주목 받고 있는지 거시적 관점인 일하는 방식 변화의 측면에서 이해하는 것이 목표입니다. 이를 위해 일하는 방식 변화가 왜 필요한

happycloud-lee.tistory.com

 

서비스 디스커버리(Service Discovery)

서비스 인스턴스의 네트워크 위치를 찾고 로드밸런싱하는 역할하는 놈을 보고 서비스 디스커버리라 한다.

 

내가 구성한 각각의 독립적인 모듈 API 끼리의 통신은 보통 REST API로 통신한다. 

요청을 보내기 위해서는 서비스 인스턴스가 있는 곳의 네트워크 정보를 알아야 한다(IP,PORT주소)

물리적 서버에서 돌아가는 경우라면 미리 설정 파일로 빼서 관리하면 되니 크게 문제는 없다.

 

하지만 클라우드에서 인스턴스는 동적으로 할당되기 때문에 IP주소나 포트 정보가 정해지지 않은 데다가 오토스케일링도 일어나고 중지되고 복구되면서 네트워크 위치가 계속해서 바뀐다. 

따라서  클라이언트나 API 게이트웨이가 호출할 서비스를 찾는 매커니즘 필요하다. 그래서 이게 필요한거임.

 

자세한설명은 2탄참조

https://ssmyefrin.tistory.com/51

 

[MSA] Spring Cloud를 사용해보자(2)-서비스디스커버리

MSA는 각각 흩어진 서비스간의 원격 호출로 구성된다. 원격 호출을 하기위해 각각 서버에 대한 IP와 PORT를 이용한다. 기존 하드웨어 기반의 시스템에서의 서비스 인스턴스는 상대적으로 정적이다

ssmyefrin.tistory.com

 

그리고  나는 이걸 Spring Cloud 를 이용하여 구현해보겠다.

 

 

Spring Cloud

Spring Cloud는 마이크로서비스의 개발, 배포, 운영에 필요한 아키텍처를 쉽게 구성할 수 있도록 지원하는

Spring Boot기반의 프레임워크다. 다시 말해 "MSA구성을 지원하는 Springboot기반 Framework" 다.

Spring Cloud 핵심 component 아래와 같이 요약할 수 있다.

 

 

참조 https://happycloud-lee.tistory.com/207 (뭔말이여 이게..)

 

 

 

그림이 너무 어렵다.

일단 나도 100%완벽하게 이해는 못했으니 넘어가고, 찬찬히 실습해보면서 이해해보려 한다.

 

 

구현할 어플리케이션  목록이다.

 

  • DiscoveryServer : 각 어플리케이션의 정보가 등록되어, 로드 밸런서 역할을 한다.
  • GatewayServer : 모든 요청이 거쳐가는 진입로가 된다.
  • BackendService실제 호출에 대한 응답을 하게 된다.

 

실습환경

- IntelliJ + JDK 17 + SpringBoot 3.1.5 + Gradle
- Spring Cloud
- MacOS

 

 

디스커버리 서버(유레카 서버) 만들기

- 아, 참고로 유레카 서버는 넷플릭스에서 MSA 위해 Spring Cloud 기부한 오픈 소스이다.(ㄱㅅㄱㅅ)

 

1. Project 생성

 

혹은 build.gradle 에 아래 Dependency 추가

implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-server'

 

 

2. application.yml  설정 파일에 추가

유레카 서버로서 동작할 있도록 코드를 작성

spring:
  application:
    name: eureka_server # 애플리케이션 이름
server:
  port: 8761
eureka:
  instance:
    hostname: localhost
  # Eureka Client 설정
  client:
    registerWithEureka: false   # 유레카에 등록할지 여부. 자기 자신을 등록할 필요가 없다.
    fetchRegistry: false  # Eureka 서버로부터 받은 서비스 리스트에 대한 캐싱여부
    serviceUrl:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ # 유레카 서버의 주소 지정
  # Eureka Server 설정
  server:
    waitTimeInMsWhenSyncEmpty: 0 # 유레카 서버가 동기화 대기시간 지정. 0으로 설정되어 있어 즉시 동기화를 시도
    response-cache-update-interval-ms: 5000 # 유레카 서버 응답 캐시 업데이트 간격 지정

management.endpoints.web.exposure.include: "*" # 모든 관리 엔드포인트를 노출하도록 지정

spring.application.name

Eureka 서버에서 서비스를 식별하는 id이다.

 

eureka.client.register-with-eureka

유레카 서버에 자기 자신을 클라이언트로 등록하지 않도록 하는 설정이다.

본 프로젝트는 디스커버리 서버 역할을 하는 유레카 서버이므로 자기 자신을 클라이언트로써 디스커버리 서버에 등록하지 않도록 false로 설정한다.

 

eureka.client.fetch-registry

클라이언트로써 eureka 서버에서 eureka 레지스트리 정보를 가져올지 여부를 설정한다.

설정과 마찬가지로 클라이언트가 아니므로 false 설정한다.

 

 

3. @EnableEurekaServer 로 유레카 서버 명시

유레카 서버로 동작하게끔 메인 애플리케이션에 어노테이션인 @EnableEurekaServer 추가해준다.

@EnableEurekaServer
@SpringBootApplication
public class EurekaApplication {

    public static void main(String[] args) {
        SpringApplication.run(EurekaApplication.class, args);
    }

}

 

 

4. 서버 구동후 http://localhost:8761/ 접속

이렇게 뜬다면  성공

 

 

✔ GatewayServer 만들기

이 서버의 역할은 클라이언트와의 통신, BackendService에 대한 리버스 프록시(Reverse Proxy)-라우팅 을 하게 된다.

 

1.  Project 생성

 

혹은, 아래 의존성추가

implementation 'org.springframework.boot:spring-boot-starter-webflux'
implementation 'org.springframework.cloud:spring-cloud-starter-gateway'
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'

 

게이트웨이스타터를 추가하지 않았더니, 라우팅 자체가 안된다(이걸로 삽질 엄청함ㅠㅠ)

 

 

2. application.yml 에 추가

server:
  port: 8003

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

spring:
  application:
    name: gateway-server2 # 설정한 이름대로 DiscoveryServer 에 서비스가 등록
  cloud:
    gateway:
      # Gateway 라우팅 설정
      routes:
        - id: backend-service # 라우팅할 서비스이름
          uri: lb://BACKEND-SERVICE # Eureka에 등록된 backend-service 리버스 프록시(대문자로되어있을꺼임)
          predicates:
            - Path=/backend/**  # 요청이 들어오면 backend-service 전달되도록 설정
          filters:
            - RewritePath=/backend/(?<segment>.*),/$\{segment} # /backend 로 시작하는 패스를 재작성

 

 

3. @EnableDiscoveryClient 애노테이션을 추가하여 유레카 클라이언트로 동작하게끔 한다.

@EnableDiscoveryClient
@SpringBootApplication
public class ApiGateway2Application {

    public static void main(String[] args) {
        SpringApplication.run(ApiGateway2Application.class, args);
    }

}

 

 

✔ BackendService 생성(실제서비스 생성)

1. Project 생성(생략)

의존성 추가

implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'
implementation 'org.springframework.boot:spring-boot-starter-web'

 

 

2. application.yml 생성

server:
  port: 8002

spring:
  application:
    name: backend-service

eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka
  instance:
    preferIpAddress: true

 

 

3. 구동에 어노테이션 추가

@EnableDiscoveryClient
@SpringBootApplication
public class TodoApiApplication {

    public static void main(String[] args) {
        SpringApplication.run(TodoApiApplication.class, args);
    }

}

 

 

유레카 서버 접속

각각의 클라이언트들이 잘 등록되어있는지 확인

 

 

API 로 확인해보기

- 마이크로서비스에서 직접 날렸을 때

마이크로서비스에서 직접 날렸을때

 

 

 

- 게이트웨이를 통해 날렸을때

 

위와같이 결과가 똑같다는걸 알 수 있음.

 

참조:

https://www.devkuma.com/docs/spring-cloud/quick-guide/#google_vignette

https://velog.io/@rosesua318/Eurekaspring-cloud-discovery-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EC%97%90-%EC%A0%81%EC%9A%A9%ED%95%B4%EB%B3%B4%EA%B8%B0