[E-commerce App] Configuration Service & Spring Boot Actuator
지금까지 user-service
, order-service
, catalog-service
를 개발했다.
지금까지는 구성정보 파일을 application.yml
파일을 사용했었는데, 문제는 구성 정보가 변경되면 서비스 자체를 다시 빌드한 뒤에 배포해야 한다는 것이다.
이러한 점을 개선하기 위해 어플리케이션 내부에 구성파일을 가지고 있는 것이 아니라 외부에 있는 시스템을 통해 구성파일 정보를 관리할 수 있는 기능에 대해 살펴보자.
1. Spring Cloud Config
- 분산 시스템에서 서버, 클라이언트 구성에 필요한 설정 정보(
application.yml
)를 “외부” 시스템에서 관리 - 하나의 중앙화 된 저장소에서 구성요소 관리 가능
- 구성 정보가 변경되었을 때, 각 서비스를 다시 빌드하지 않고 바로 적용 가능
- 애플리케이션 배포 파이프라인을 통해 DEV(개발) - UAT(테스트) - PROD(배포) 환경에 맞는 구성 정보 사용
1) Local Git Repository
ecommerce.yml
파일 생성
로컬에 git-local-repo
라는 디렉토리를 생성한 후, 그 안에 ecommerce.yml
파일을 생성한다.
token:
expiration_time: 86400000
secret: user_token
gateway:
ip: 192.168.2.1 # 본인 IP
🚨 주의
아래에서 local git repository에 등록할건데,
그 이후에ecommerce.yml
파일의 내용이 변경되었다면, add - commit 을 해주어야 변경사항이 적용된다!
(push는 아직 진행하지 않는다.)
Local Git Repository에 등록
터미널을 켜서 이전에 생성한 git-local-repo
디렉토리로 이동한다.
$ git init
$ git add ecommerce.yml
$ git commit -m "update an application yaml file"
이렇게 하면, 아직 remote repository에는 올라가지 않고, local repository에만 올린 것이다.
아직까지는 local에서만 관리한다.
(push
이후에 remote repository에 올라간다.)
🌟 참고) 우선순위
지금까지 만든 네 개의 서비스(user-service
,order-service
,catalog-service
,gateway-service
) 모두application.yml
파일을 가지고 있다.
우선순위는 아래와 같다.
application.yml
application-name.yml
- 예)user-service.yml
,order-service.yml
application-name-<profile>.yml
- 예)user-service-dev.yml
,order-service-prod.yml
2) config-service
프로젝트 생성
- Spring Boot: 2.7.4
- dependencies
-Config Server
application.yml
server:
port: 8888
spring:
application:
name: config-service
cloud:
config:
server:
git:
uri: file:///Users/minju/study/msa/git-local-repo
spring.cloud.config.server.git.uri
를 보면,
파일 프로토콜(file://
) 을 이용해 불러오고자하는 파일의 경로를 지정할 수 있다.
파일의 경로는, 터미널에서 git-local-repo
디렉토리로 이동 한 뒤, pwd
커맨드로 알아낼 수 있다.
서버 기동
서버를 기동한 뒤, http://127.0.0.1:8888/ecommerce/default
로 접속하면, 아래와 같이 ecommerce.yml
파일에 작성한 설정정보를 확인할 수 있다.
이때 엔드포인트를 살펴보면, /ecommerce/default
인데,
/ecommerce
는 git-local-repo
안에 있는 설정파일(ecommerce.yml
)의 이름이고,
/default
는 설정파일의 profiles로, 아무것도 설정하지 않았으니 /default
이고, 만약 ecommerce-dev.yml
로 설정했다면 /dev
가 되는 것이다.
3) user-service
pom.xml
pom.xml
에 아래 dependency를 추가한다.
<!-- spring-cloud-config 설정 -->
<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>
bootstrap.yml
추가
bootstrap.yml
은 외부에 있는 Spring Cloud Config에 대한 정보를 등록해주는 역할로,
application.yml
보다 우선순위가 높다.
spring:
cloud:
config:
uri: http://127.0.0.1:8888
name: ecommerce
이 때, spring.cloud.config.uri
에 있는 :8888
은 config-service
의 port 번호이다.
spring.cloud.config.name
에는 읽어오고자하는 yaml 파일의 이름을 적는다.
(나는 git-local-repo
디렉토리 안에 ecommerce.yml
정보를 가져올 것이기 때문에 ecommerce
를 입력했다.)
사실 bootstrap.yml
에 적은 내용을 application.yml
에 적어도 상관 없다.
하지만 지금 하고자 하는 작업의 핵심은,
원래 가지고 있던 application.yml
의 특정 부분을 떼어서, 별도의 공용서버 역할을 하는 Spring Cloud Config(=config-service
)를 이용해보는 것이다.
기존 application.yml
변경
ecommerce.yml
에 토큰 정보를 넣어놨으니 config-service
로 부터 해당 정보를 가져올 것이기 때문에, application.yml
에서는 제거하자.
# token:
# expiration_time: 86400000 # 60 * 60 * 24 * 1000 # 하루짜리 토큰
# secret: user_token
UserController.java
config-service
로부터 구성 정보를 잘 가져왔는지 테스트하기 위해 health-check
코드를 변경해보자.
@RestController
@RequestMapping("/")
public class UserController {
...
@GetMapping("/health-check")
public String status() {
return String.format("It's Working in User Service"
+ ", port(local.server.port) = " + env.getProperty("local.server.port")
+ ", port(server.port) = " + env.getProperty("server.port")
+ ", token secret = " + env.getProperty("token.secret")
+ ", token expiration time = " + env.getProperty("token.expiration_time")
);
}
}
서버 기동
service-discovery
(=유레카 서버), gateway-service
, config-service
를 먼저 기동한 뒤,
user-service
를 기동한다.
서버를 기동하게 되면 위 사진처럼 나오는데, 여기서 세 가지를 확인할 수 있다.
- Configuration Server(=
config-service
)의 위치정보가 잘 표시되어있다. - 읽어온 구성정보 파일의 이름이
ecommerce
이고 profiles는default
이다. - 읽어오고자하는 bootstrap의 위치 정보는
git-local-repo
에 있는ecommerce.yml
로부터 가져온 것이다.
이제 health-check
에 접속해보자.
🚨 주의
만약,ecommerce.yml
의 내용이 변경되었다면,user-service
도 재기동 해주어야한다.
하지만 Configuration Server의 구성 정보를 변경할 때마다 마이크로 서비스를 재기동 해야하는 것은 번거로운 일이다..🤔
2. Changed configuration values
변경된 configuration 정보를 가져올 수 있는 방법에는 어떤 것들이 있을까?
- (마이크로 서비스) 서버 재기동
- Actuator refresh
- Spring cloud bus 사용 🌟
서버 재기동은 이전에 말했듯이 번거로운 방법이다.
이번 글에서는 Actuator에 대해 먼저 알아보고, 다음 글에서 Spring cloud bus에 대해 알아보자.
Spring Boot Actuator
- Application 상태, 모니터링
- Metric 수집을 위한 Http End point 제공
아래 사이트에서 다양한 End point를 확인할 수 있다.
https://docs.spring.io/spring-boot/docs/current/reference/html/actuator.html
user-service
Actuator 기능을 사용하기 위해서는 각각의 마이크로 서비스에 Actuator dependecy를 추가해주어야 한다.
pom.xml
<!-- Spring Boot Actuator -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
application.yml
management:
endpoints:
web:
exposure:
include: refresh, health, beans
refresh, health, beans 세 가지 actuator를 등록한 것이다.
이 때 refresh가 가장 중요한 설정인데, Configuration Server에 있는 값을 현재 마이크로 서비스에 refresh 하겠다는 의미이다.
WebSecurity.java
@Configuration
@EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {
/**
* 권한 부여
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
// 🌟 Actuator 에 대해서는 인증을 거치지 않고 바로 사용
http.authorizeRequests()
.antMatchers("/actuator/**")
.permitAll();
...
}
...
}
gateway-service
application.yml
Actuator 대해서는 인증 필터를 거지치 않기로 했으니, gateway-service
의 application.yml
에 아래 코드를 추가한다.
- id: user-service
uri: lb://USER-SERVICE
predicates:
- Path=/user-service/actuator/**
- Method=GET,POST
filters:
- RemoveRequestHeader=Cookie
- RewritePath=/user-service/(?<segment>.*), /$\{segment}
테스트
gateway-service
와 user-service
를 재기동 한 뒤, user-service
의 application.yml
에서 등록한 refresh, health, beans에 요청해보자.
health와 beans는 GET
요청이고, refresh는 POST
요청이다.
health
beans
refresh
refresh를 테스트 하기 위해서, 먼저 ecommerce.yml
의 값을 바꿔보자.
기존
기존에는 token.secret
이 user_token
이었다.
변경
token.secret
을 user_token_changed
로 변경한다.
(ecommerce.yml
코드 변경 후에 add -> commit 까지 진행해야 한다!)
refresh 요청
서버를 재기동하지 않고 /actuator/refresh
요청만으로 아래 사진처럼 변경사항이 적용된 것을 확인할 수 있다.
💛 개인 공부 기록용 블로그입니다. 👻