Spring 기반 프로젝트에서 MSA를 구현하기 위해서 Spring Cloud에 대해 알 필요가 있다.
Spring Cloud : 개발자가 분산 시스템에서 일부 공통 패턴(예: 구성 관리, 서비스 검색, 회로 차단기, 지능형 라우팅, 마이크로 프록시, 제어 버스, 일회성 토큰, 글로벌 잠금, 리더십 선택, 분산)을 신속하게 구축할 수 있는 도구를 제공한다. 세션, 클러스터 상태)
쉽게 말해 분산 시스템에서 필요한 다양한 기능들을 제공하고 있으며, MSA를 구성할 때 유용하게 사용되는 서브 프로젝트이다.
위의 그림을 살펴보면, MSA를 구축하기 위해서 API 게이트웨이가 요청을 처리하고 그 요청을 처리할 서비스에게 다시 그 요청을 위임해준다. 이와 같은 프로세스가 구축되기 위해서는 호출할 서비스를 찾는 매커니즘이 필요한데, 이것을 Service Discovery라고 부른다. Service Discovery는 즉, 외부에서 마이크로 서비스의 위치를 찾아는 역할을 하게 된다.
해당 포스팅의 실습은 Spring Cloud Netflix의 Eureka를 활용할 것이다.
Spring Cloud Netflix: Spring 환경 및 기타 Spring 프로그래밍 모델 관용구에 대한 자동 구성 및 바인딩을 통해 Spring Boot 앱에 대한 Netflix OSS 통합을 제공한다. Eureka 인스턴스를 등록할 수 있으며, Eureka 서버를 생성할 수 있다.
실습하기
Eureka Server
1) 프로젝트 생성
화면에는 자바 17로 되어 있지만, 저는 실제로 실습할 때 11을 사용했고, 스프링 버전은 2.7.17을 사용했습니다.
의존성으로 Eureka Server를 추가합니다. 지금 생성하고 있는 Eureka Server는 API 게이트웨이와 각 서비스의 위치를 저장하는 역할을 합니다.
2) application.yml 작성
server:
port: 8761
spring:
application:
name: discovery-service
eureka:
client:
register-with-eureka: false
fetch-registry: false
application.properties 파일을 application.yml 으로 수정 후, 해당 내용을 작성합니다. 어떤 것을 사용해도 아무 문제는 없으나 구조를 파악하기도 쉽고 코드가 줄어들기 때문에 해당 포스팅의 실습은 yml으로 진행합니다.
- server.port : Eureka 서비스의 포트를 8761로 지정합니다. 이 포트로 Eureka 대시보드에 접속할 수 있습니다.
- spring.application.name: 애플리케이션의 이름을 discovery-service로 지정합니다. 이 이름은 Eureka 서버에 등록될 때 사용됩니다.
- eureka.client.register-with-eureka: 현재 애플리케이션을 Eureka 서버에 등록하지 않도록 설정합니다. 이 속성을 false로 설정하면 이 애플리케이션은 Eureka 서버에 자신을 등록하지 않습니다.
- eureka.client.fetch-registry: 현재 애플리케이션은 Eureka 서버로부터 등록 정보를 가져오지 않도록 설정합니다. 이 속성을 false로 설정하면 이 애플리케이션은 Eureka 서버로부터 등록된 서비스 목록을 받아오지 않습니다.
false로 설정한 이유는 실습을 진행하는 현재 애플리케이션은 Eureka Client가 아닌 Server이기 때문입니다.
3) @EnableEurekaServer
package com.example.eurekaserver;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
메인 메소드가 있는 EurekaServerApplication 클래스에서 @EnableEurekaServer 애노테이션을 추가합니다.
@EnableEurekaServer 는 Spring Cloud(Spring Cloud Netflix 프로젝트)에서 제공하는 애노테이션으로, Spring Boot 애플리케이션을 Eureka 서버로 설정할 수 있습니다. 쉽게 말해 이 애플리케이션은 서비스 디스커버리에 사용되는 Eureka 서버로 동작하게 됩니다.
4) 실행해보기
yml에서 설정한 port로 접속해보십니다. 해당 프로젝트는 8761로 설정했기 때문에 localhost:8761로 접속합니다.
그림과 같이 Eureka 대시보드가 보인다면 간단한 Eureka Server 구현완료입니다!
Eureka Clinet(API GateWay)
Server를 생성했으니 다음으로 Client를 생성할 차례입니다. API Gateway와 Service들을 차례로 구현해봅시다.
1) 프로젝트 생성
해당 프로젝트도 마찬가지로 그림은 자바 17로 되어있지만, 실제로 실습할 때 11을 사용했고, 스프링 버전은 2.7.17을 사용했습니다.
의존성을 그림과 같이 추가해주세요.
- Eureka Discovery Client(필수)
- Gateway(필수)
- Lombok(선택)
2) application.yml 작성
server:
port: 9000
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defalutZone: http://localhost:8761/eureka
spring:
application:
name: apigateway-service
cloud:
gateway:
routes:
- id: user-service
uri: lb://USER-SERVICE
predicates:
- Path=/users/**
- server.port: API Gateway 서버의 포트를 9000으로 등록합니다. 클라이어트는 이 포트를 통해 API Gateway에 요청할 수 있습니다.
- eureka.client.register-with-eureka: 현재 애플리케이션을 Eureka 서버에 등록하도록 설정합니다.
- eureka.client.fetch-register: Eureka 서버로부터 등록된 서비스 목록을 가져오도록 설정 합니다. API Gateway는 Eureka서버로부터 등록된 서비스들의 목록을 얻을 수 있습니다. 30초마다 Eureka Client 가 유레카 레지스트리 변경 사항 여부 재확인합니다.
- spring.application.name: 애플리케이션 이름을 apigateway-service로 지정합니다. 이 이름은 Eureka 서버에 등록될 때 사용됩니다.
- spring.cloud.gateway.routes: 이 설정은 현재 애플리케이션이 API Gateway일 경우 설정하는 부분입니다.
- id: 라우트의 고유 식별자로 사용됩니다.
- uri: 해당 라우트로 들어온 요청을 USER-SERVICE라는 Eureka에 등록된 서비스로 라우팅합니다.
- predicates: 해당 라우트를 적용할 조건을 지정합니다.
- 정리하자면, /users/** 경로로 api gatway 애플리케이션에 요청할 경우, Eureka에서 USER-SERVICE를 찾은 다음 해당 애플리케이션으로 라우팅 됩니다. 로드 밸런서(lb)가 사용되며, 만약 USER-SERVICE라는 이름의 애플리케이션이 2개 이상 등록된 경우, 라운드 로빈 방식으로 동작하여 요청이 각각의 서비스에 균등하게 분배됩니다.
3) 실행하기
Eureka Server에 접속하여 API Gateway가 등록이 되어있는지 확인 해봅니다.
Eureka Client(Service)
자 이제 마지막으로 Service를 담당할 애플리케이션을 구현해봅니다.
1) 프로젝트 생성
이름을 user-service로 지었지만, 현재 포스팅에서는 user에 관련된 서비스를 처리하지 않습니다. 개인적으로 차후에 기능 추가를 위해서 프로젝트 이름만 저렇게 명시한 것을 미리 말씀드립니다...
의존성을 추가합니다.
- Spring Web(필수)
- Eureka Discovery Client(필수)
- Lombok(선택)
2) application.yml 작성
server:
port: 9002
spring:
application:
name: user-service
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://127.0.0.1:8761/eureka
- server.port: 현재 애플리케이션의 포트를 9002로 설정합니다.
- spring.application.name: 애플리케이션 이름을 user-service로 지정합니다. 이 이름은 Eureka Server에 등록될 때 사용됩니다.
- eureka.client
- register-with-eureka: 현재 애플리케이션을 Eureka 서버에 등록하도록 설정합니다.
- fetch-registry: Eureka서버로부터 등록된 서비스 목록을 가져오도록 설정합니다.
- service-url.defaultZone: Eureka 서버의 주소를 설정합니다.
3) @EnableDiscoveryClient
package com.example.userservice;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class UserServiceApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceApplication.class, args);
}
}
main 메소드가 위치한 UserServiceApplication 클래스에 @EnableDiscoveryClient 애노테이션을 작성합니다.
@EnableDiscoveryClient 애노테이션을 작성함으로써 서비스 디스커버리 클라이언트로 사용하도록 설정합니다.
@EnableDiscoveryClient vs @EnableEurekaClient
@EnableDiscoveryClient: Eureka뿐만 아니라 Consul, Zookeeper 등과 같은 다양한 서비스 디스커버리 구현체와 연동될 수 있다.
@EnableEurekaClient: 주로 Eureka 서비스 디스커버리와의 통합을 목적으로 하는 경우에 사용된다.
최신 Spring Cloud에서는 해당 애노테이션을 명시하지 않아도 된다. 보여주기 위한 용도로 사용할 수 있겠다. 실제로 해당 애노테이션을 지워도 Eureka Server에 등록된다.
4) Test 용 Controller 작성
package com.example.userservice.user;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/users")
@RestController
public class UserController {
@GetMapping
public String test() {
return "Hello!";
}
}
API Gateway에 요청을 해도 해당 애플리케이션으로 라우팅될 수 있는지 확인하기 위한 용도의 Controller를 간단하게 만들어 보았습니다.
5) 실행 후 Test
USER-SERVICE가 등록되어 있음을 확인할 수 있습니다. 이제 테스트를 진행해보겠습니다.
포트번호가 9002로 등록되어 있는 user service에 요청한 것이 아닌, 포트 번호가 9000번인 API Gateway에 요청을 했음 해도 불구하고 API Gateway가 Eureka를 통해 서비스 디스커버리를 수행하고, 등록된 서비스들 중에서 user-service를 찾아 라우팅했습니다. API Gateway가 서비스 디스커버리를 통해 실제로 요청을 처리할 서비스를 찾아서 전달한 것입니다.
postman이 없다면 웹 브라우저에서도 테스트가 가능합니다.
부록: Actuator활용해서 설정된 Route 확인하기
Spring Actuator: Spring Framework의 일부로서, Spring Boot 프로젝트에 쉽게 통합할 수 있는 모니터링 및 관리 기능을 제공한다. Actuator는 애플리케이션의 운영 환경에서 상태를 확인하고 모니터링하며, 런타임 중에 유용한 정보를 노출하는데 사용된다.
1) 의존성 추가
implementation 'org.springframework.boot:spring-boot-starter-actuator'
API Gateway 애플리케이션에서 Spring Actuator을 추가해줍니다.
2) Application.yml 작성
management:
endpoints:
web:
exposure:
include: gateway
endpoint:
gateway:
enabled: true
다음 설정을 추가해줍니다.
- management.endpoints.web.exposure.include: Actuator의 웹 엔드포인트 중에서 노출할 엔드포인트를 설정합니다. 여기서는 gateway 엔드포인트를 노출하도록 설정되어 있습니다.
- management.endpoint.gateway.enabled: gateway 엔드포인트를 활성화하도록 설정합니다. 즉, Actuator의 gateway 엔드포인트가 사용 가능하게 됩니다.
* 테스트 환경에서만 사용하도록 합시다.
3) 실행하기
localhost:9000/actuator/gateway/routes
위의 그림처럼 설정된 route를 확인할 수 있습니다.
저는 order service도 따로 프로젝트를 만들어 두어서 등록되어 있네요.
해당 내용은 공식문서에 설명되어 있습니다!
https://docs.spring.io/spring-cloud-gateway/reference/spring-cloud-gateway/actuator-api.html
'DevOps' 카테고리의 다른 글
[k8s] 쿠버네티스 란? (1) | 2023.12.03 |
---|---|
[MSA] Spring Boot 프로젝트에서 MSA 실습하기(Kafka) - 2 (2) | 2023.11.26 |
[아키텍처] 모놀리식 아키텍처 VS 마이크로 서비스 아키텍처(MSA) (0) | 2023.11.15 |
[GitActions] Error: Gradle script '/home/runner/work/' is not executable. (0) | 2023.06.01 |
netlify sass error (0) | 2022.04.18 |