[Spring Cloud] Microservice(MSA) 구축 : (4) Spring Cloud Config Server 구축
이전 포스팅에서는 여러개의 Microservice를 만들고, Spring Cloud Gateway와 Registry Server를 구축하여보았었다.
이번 포스팅에서는 Spring Configuration Server를 구축하여, Microservice들의 설정들을 중앙에서 효율적으로 관리할 수 있도록 만들어 보려고 한다.
Spring Cloud MSA 구조를 다시 보면..
Spring Cloud는 분산 시스템과 MSA를 쉽게 개발하고 구축하기 위한 Spring 기반의 프레임워크 모음이다.
Spring Cloud는 다양한 분산 시스템 문제에 대한 해결책을 제공하며, 여러 서비스로 구성된 복잡한 시스템을 쉽게 관리하고 확장할 수 있도록 지원한다
Spring Cloud API Gateway
마이크로서비스 아키텍처에서 Gateway로서 클라이언트의 단일 진입점 역할을 하는 서버 역할을 한다.
인증, 권한 부여, 로드 밸런싱, 라우팅, 트래픽 제어 등의 기능을 제공한다.
클라이언트는 API Gateway를 통해 여러 서비스에 대한 요청을 보내고, API Gateway는 이를 해당 서비스로 라우팅해준다.
Spring Cloud Config Server
설정 관리를 중앙에서 효과적으로 관리할 수 있게 해주는 서버 역할을 한다.
각 마이크로서비스는 Config Server를 통해 자체적인 설정을 동적으로 가져올 수 있으며 설정의 중앙 집중 관리로 유지보수 및 변경 관리를 용이하게 한다.
Spring Service Registry (Naming Server)
MSA에서 서비스 인스턴스의 등록과 검색을 담당하는 서버 역할을 한다.
서비스 인스턴스의 등록과 해제를 관리하며 서비스 디스커버리를 통해 클라이언트는 특정 서비스 인스턴스를 동적으로 찾을 수 있.
대표적인 예로는 Netflix Eureka
Configuation Server의 필요성
각각의 마이크로서비스들에는 application.yml과 같이 설정 파일들이 존재할 것이다.
물론 각각의 서비스에서 사용되는 설정들도 있겠지만, MSA들 간에 공유되어야 하는 설정값들도 있을 것이다.
예를 들면 인증과 인가를 구현할 때, API Gateway에서 JWT를 복호화하는 Secret과, User Service에서 JWT를 암호화할 때 사용되는 Secret은 같은 값이어야 할 것이다.
만약 해당 Secret이 한 군데에서 바뀐다면 모든 곳에서 바꾸어 주어야 할 것이고, Microservice들이 늘어난다면 해당 설정값들을 일관되게 관리해주기 어려울 수 있다. 위 그림과 같이 token.secret값의 일관성이 깨지게 된다면, Authentication 시 JWT 발급 방식과, Authorization에서 사용되는 JWT 복호화의 방식이 달라지게 될 것이다.
따라서 Configuration Server를 구축하여, 이런 공통된 설정값들을 다른 마이크로서비스에서 일관되게 사용할 수 있도록 관리해주고, 동적으로 설정값을 가져와 사용할 수 있도록 하려고 한다.
Spring Cloud Config Server 구축
따라서 프로젝트를 다음과 같이 구성해 볼 것이다.
1. Spring Cloud Config Server를 생성하고, 분산 시스템에서 서버 클라이언트 구성에 필요한 설정 정보(application.yml)을 관리
2. 따라서 중앙화 된 저장소에서 구성요소를 관리할 수 있도록 한다.
3. 설정 파일의 Profile을 지정할 수 있도록 작성하고, Native Storage나, Git과 같은 Remote Storage에서 설정 파일을 저장하고, Configuration Server는 해당 Storage의 설정 파일을 다른 MSA들에게 공급해 주도록 한다.
ConfigService 작성
1. 의존성 추가하기
▶ pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
1. Configuration Server로 사용하기 위해서 Spring-Cloud-Config-Server를 의존성으로 추가해준다.
2. Spring Boot Actuator는 Spring Boot 애플리케이션의 운영 환경에서 감시 및 관리를 위한 기능을 제공하는 모듈이다. 이 모듈을 사용하면 애플리케이션의 상태, 메모리 사용량 등과 관련된 여러 정보에 접근하고, 애플리케이션을 조작하는 데 도움이 되는 다양한 엔드포인트를 제공할 수 있다. 서버의 상태에 접근할 수 있도록 의존성으로 추가해준다.
2. EnableConfigServer
▶ ConfigServiceApplication.java
@SpringBootApplication
@EnableConfigServer
public class ConfigServiceApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServiceApplication.class, args);
}
}
@EnableConfigServer 어노테이션을 추가하여 ConfigServer로서 작동하도록 해준다.
3. 설정 작성
▶ application.yml
server:
port: 8887
spring:
application:
name: config-service
cloud:
config:
server:
git:
uri: https://github.com/Jaehwi-So/MSA_Study.git
# if private repository
# username: [your username]
# password: [your password]
Spring Cloud Config 설정
- spring.cloud.config.server: Spring Cloud Config Server 관련 설정을 정의한다.
- git: Git 저장소를 사용하여 Config 구성 소스를 가져온다.
해당 Repository에는 ecommerce.yml, ecommerce-dev.yml, ecommerce-prod.yml 세 개의 설정 파일을 만들어 두었다. Spring Cloud Config Server는 해당 Git의 설정파일들을 가져와서 다른 MSA들에게 주겠다고 설정한 것이다.
다음과 같이 Config Server의 /[설정파일 name]/[설정파일 profile] 엔드포인트로 접근해보면 해당 설정파일 이름과 프로파일에 해당하는 yml의 정보를 가져오는 것을 확인할 수 있다.
▶ application.yml
server:
port: 8887
spring:
application:
name: config-service
profiles:
active: native
cloud:
config:
server:
native:
search-locations: file://${home}/Documents/GitHub/MSA_Native_repo
git:
uri: https://github.com/Jaehwi-So/MSA_Study.git
다음과 같이 Spring Cloud Config Server가 설정파일 구성요소를 가져오는 방식을 로컬 파일 시스템에서 구성 소스를 찾도록 할 수 있다.
profiles.active : native 방식으로 명시하면, Git이 아닌, Native 방식으로 구성요소를 찾도록 한다. (지정하지 않으면 디폴트로 Git에서 구성요소를 찾는다.)
cloud.config.server.native : /Documents/Github/MSA_Native_repo에 있는 설정 파일의 구성 소스의 경로를 지정해주었다.
User Microservice와 API gateway에서 Config Server 연결하기
마이크로서비스와 API Gateway에서 Config Server와 연결하기 위해서 공통으로 수행해야 하는 작업들이다.
1. 의존성 추가하기
▶ pom.xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
spring-cloud-starter-config: Configuration Server에서 정보를 가져올 수 있도록 의존성을 추가한다.
spring-cloud-starter-bootstrap: application.yml과 분리되어 먼저 메모리에서 실행되는 설정파일인 bootstrap.yml을 사용할 수 있도록 하기 위해 추가한다.
spring-boot-starter-actuator : Actuator를 통해서 설정 정보가 변경되면 Refresh 할 수 있도록 의존성을 추가한다.
2. bootstrap.yml에 Config Server 연결 정보 작성하기
bootstrap.yml 파일은 Spring Boot 애플리케이션이 시작될 때 가장 먼저 로딩되는 설정 파일이다.
이 파일은 application.yml 또는 application.properties 파일보다 먼저 로드되어야 하는 설정들을 담고 있다.
주로 외부 구성 소스(예: Spring Cloud Config Server, 환경 변수, 외부 프로퍼티 파일)를 설정하거나 애플리케이션이 부팅될 때 필요한 기본 설정을 구성하는 데 사용된다
▶ bootstrap.yml
spring:
cloud:
config:
uri: http://127.0.0.1:8887
name: ecommerce
profiles:
active: dev
해당 파일에는 Spring Cloud Config Server의 UR를 지정하고, 가져올 설정 파일의 name과 profiles를 지정한다.
다음처럼 설정을 지정하면 Configuration Server에서 위에서 보았던 하기 이미지와 같은 설정 구성 정보를 가져오게 될 것이다.
3. 기존 사용하던 토큰 설정내용 삭제
▶ application.yml
eureka:
client:
fetch-registry: true
register-with-eureka: true
service-url:
defaultZone: http://127.0.0.1:8761/eureka
instance:
instance-id: ${spring.application.name}:${spring.application.instance_id:${random.value}}
#token:
# expiration_time: 86400000 #1일
# secret: mySecretSecc123
#...
이제 Centralized된 Config Server를 사용하여 토큰의 Secret을 관리할 것이므로 해당 설정 파일은 더 이상 Microservice의 yml에서 관리하지 않을 것이다.
테스트
@GetMapping("/health-check")
public String status(){
return String.format("It's Working in user service " +
", port " + env.getProperty("local.server.port") +
", server.port" + env.getProperty("server.port") +
", token.secret" + env.getProperty("token.secret") +
", token.expiration_time " + env.getProperty("token.expiration_time"));
}
다음과 같이 token.secret을 출력해주는 GET API를 작성해주었다.
Configuration 서버의 설정 구성정보를 가져와서 MSA에서 사용하고 있는 것을 확인할 수 있다.
설정 파일의 Refresh
만약 Git의 ecommerce.yml 파일의 구성 요소가 바뀌었다고 생각해보자. 그렇다면 Configuration Server에서 관리하고 있는 설정 파일의 내용도 달라져 있을 것이다. 그렇지만 Configuration Server와 이미 연결되어서 시작된 MSA들이 업데이트 된 설정파일을 반영되게 하려면 어떤 방법들이 있을까?
대략적으로 아래의 방법들이 있다.
- Microservice 재기동 : 가장 심플한 방법이지만 현실성이 매우 떨어짐
- Actuator Refresh : Actuator 엔드포인드 명령을 통해서 새로고침
- Spring cloud bus : 분산 시스템의 노드를 경량 메시지 브로커와 연결, 상태 및 구성에 대한 변경 사항을 노드들에게 전달
우선 Actuator Refresh 방식에 대해서 살펴보려고 한다.
Actuator Refresh
▶ application.yml
# ...
#token:
# expiration_time: 86400000 #1일
# secret: mySecretSecc123
management:
endpoints:
web:
exposure:
include: refresh
yml 파일에서의 다음과 같은 설정은 /actuator/refresh의 엔드포인트를 허용해서 사용 가능하게 만들어 주겠다는 의미이다.
/actuator/refresh 엔드포인트는 Spring Boot 애플리케이션에서 사용되는 Actuator 모듈의 하나로, 동적으로 구성 속성을 다시 로드하도록 하는 역할을 한다. 이 엔드포인트를 호출하면 애플리케이션의 설정을 새로고침할 수 있다.
여러 마이크로서비스가 하나의 설정 서버에서 구성 정보를 가져오고 있을 때, 이 엔드포인트를 사용하여 동적으로 변경된 구성 정보를 적용할 수 있으므로 애플리케이션을 중단시키지 않고도 설정을 업데이트할 수 있는 기능으로 사용될 수 있다.
다음처럼 Configuration Server의 token.secret이 mySecret2인데, User-service는 변경되기 이전의 mySecret을 사용하고 있는 경우 동기화가 필요하다.
이 때 actuator의 refresh를 통해서, 동적으로 구성 요소들을 로드할 수 있고, 이 때 Configuration Server의 내용을 다시 가져오게 된다. 따라서 Actuator를 이용한 동기화가 가능하다.
다음처럼 User-service에서 actuator 요청을 날리게 되면 업데이트 된 프로퍼티의 정보들이 응답으로 나온다. 해당 응답이 업데이트된 구성 요소들의 내용을 나타내준다.
한계점
Actuator Refresh만으로는 한계가 명확하다. 왜냐하면 Config Server의 구성 내용이 변할 때 마다 Microservice들을 일일이 Actuator refresh해주어야 한다는 점인데, Microservice들의 구성원들과 인스턴스들이 매우 많고 복잡한 경우 수동으로 해줄 수 없다는 점이다. 그래서 나온 것이 Spring Bus를 이용하여 Message 브로커를 활용하는 방법이 있는데, 해당 내용은 다음 포스팅에서 살펴 볼 예정이다.