[MSA] MSA 전환 프로젝트 - 5. Spring Cloud Bus
이전 포스트에서 Spring Cloud Config를 설정해 봤다.
그럼 이제 Spring Cloud config 설정도 끝났겠다 외부의 설정파일을 마음대로 수정하면 처음에 말했던 대로 기동중인 애플리케이션의 설정 정보들이 실시간으로 바뀌게 될까?
애석하게도 그렇지가 않다.
바뀐 설정 정보가 적용되기 위해서는
- 서비스 재기동
- Actuator Refresh
- Spring Cloud Bus
이렇게 세 가지 방법이 필요한데 이 중에 서비스를 재기동 하는 방법은 애플리케이션을 끄고 설정을 변경하는 것과 다르지 않기 때문에 아래 두 가지 방법을 사용해야 한다.
두가지 방법 모두 실습을 해볼 예정이기 때문에 우선은 Actuator Refresh를 활용하는 방법에 대해 먼저 알아보겠다.
1. Actuator Refresh
1-1. Dependency 추가
Spring 프로젝트에서 Actuator를 사용하기 위해 Spring Cloud Config Client로 설정한 서비스에 Actuator 의존성을 추가해준다.
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-actuator'
}
1-2. appication.yml
management:
endpoints:
web:
exposure:
include: refresh, health, beans
application.yml 파일에서는 이렇게 Actuator의 기능을 실행할 수 있는 Endpoint들을 설정할 수 있다.
Endpoint에는 많은 종류 있지만 그 중에 설정 정보를 다시 가져오는 refresh, 현재 애플리케이션이 기동중인지 확인할 수 있는 health, 애플리케이션에 등록된 빈들을 확인할 수 있는 beans 총 3개의 엔드포인트만 등록하도록 하겠다
1-3. Actuator 작동
Spring Cloud Config Client로 설정한 서비스 중 user-service 애플리케이션을 이용헤서 Actuator가 잘 작동하는지 확인해보겠다.
user-service 애플리케이션을 기동시키고 1-2에서 등록했던 Actuator의 Endpoint 중 하나인 health의 응답을 받아보았다.
"status": "UP"을 반환하는 것으로 보아 정상적으로 작동되고 있음을 알 수 있다.
@GetMapping("/health-check")
@Timed(value = "users.status", longTask = true)
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"));
}
user-service의 컨트롤러에는 health-check라는 Endpoint로 연결되는 메서드가 있는데 말 그대로 서버의 상태를 출력하는 메서드다.
그 중 설정 파일에 등록된 환경 변수 값을 읽어와 secret key와 expiration time을 출력하는 기능도 가지고 있기 때문에 이 메서드를 활용해 보겠다.
health-check를 호출하면 secret의 값을 잘 읽어오는 걸 볼 수 있다.
이제 Spring Cloud Config 저장소에 있는 application.yml 혹은 user-service.yml 파일의 내용을 수정해 준 후 한 번 확인해보겠다.
그리고 다시 health-check를 호출해 보면
역시나 전혀 변하지 않은 것을 확인해 볼 수 있다.
이 때 변경사항을 적용시키기 위해 필요한 것이 Actuator Refresh다.
앞서 1-2에서 등록한 Actuator의 Endpoint 중 refresh를 호출하면 이렇게 200 OK 스테이터스와 함께 token.secret 이라는 값이 반환되는데 아마 token.secret의 값에 변경이 있었고 이것을 반영했다는 뜻으로 이해된다.
이제 다시 health-check를 호출해보겠다.
보이는 것처럼 secret의 값이 잘 변경된 것을 확인할 수 있다.
이처럼 Actuator Refresh를 활용하면 서비스를 재실행, 빌드, 배포하는 번거로운 과정을 거치지 않더라도 설정 파일의 변경사항을 적용할 수 있다.
그러나 이런 Acutator Refresh를 호출하는 방법은 해당 애플리케이션에만 적용될 뿐 다른 기동중인 서비스에는 아무런 영향을 끼치지 못한다.
그렇기 때문에 적용이 필요한 애플리케이션마다 Actuator Refresh를 호출해주는 과정이 필요한데
지금처럼 서비스의 개수가 많지 않은 상황에서는 필요한 애플리케이션을 전부 돌아다니며 refresh를 호출할 수 있겠지만
수백개의 서비스가 존재할 경우 각각의 애플리케이션마다 refresh를 수동으로 호출해줘야 한다는 건 상상만 해도 벌써 피곤한 일이다.
이를 해결하기 위한 방법이 바로 Spring Cloud Bus다.
2. Spring Cloud Bus
Spring Cloud Bus란?
Spring Cloud Bus는 분산 시스템 환경, 마이크로서비스 환경에서 각각의 노드나 서비스를 연결시켜주는 경량 메시지 브로커이다.
Spring Cloud Bus를 이용한다면 Configuration 들을 Broadcast 하게 변경 사항을 적용시킬 수 있다.
Spring Cloud Bus는 SpringBoot Application 에 부착되어 설정 정보를 지속적으로 반영할 수 있게 한다.
더 쉽게 이해하기 위해서 아래 설정 정보 적용 과정을 함께 따라가보자.
- 개발자는 변경된 configuration file을 저장소에 저장한다.
- Spring Cloud Bus가 Message Broker로 변경된 설정 정보에 대한 Message를 발행한다.
- Message Broker는 설정 정보를 저장한다.
- 설정 정보가 변경되었음을 Spring Cloud Config Server에게 알려준다.
- Message Broker가 해당 메시지를 Subscribing 하고 있는 Application들에게 Broadcasting 한다.
- 각각의 Application은 Spring Cloud Bus가 받은 설정 정보를 반영한다
일단 Spring Cloud Bus가 어떤 역할을 하는 라이브러리인지는 대충 알 수 있을 것이다.
쉽게 이야기해서 설정파일의 변경 사항을 메시지 브로커를 통해서 등록된 Client들에게 뿌려주고 Client들은 일괄적으로 앞서 보았던 Actuator Refresh를 하게 만드는 것이다.
이를 위해서 메시지 브로커를 구현해주어야 하는데 크게 2가지로 나눠볼 수 있다.
- AMQP
- 메시지 지향 미들웨어를 위한 개방형 표준 응용 계층 프로토콜
- 메시지 지향, 큐잉, 라우팅, 신롸성, 보안
- Erlang, RabbitMQ에서 사용
- Kafka
- Apache Software Foundation이 Scalar 언어로 개발한 오픈 소스 메시지 브로커 프로젝트
- 분산형 스트리밍 플랫폼
- 대용량의 데이터를 처리 가능한 메시징 시스템
이 중에서 RabbitMQ를 통해 Spring Cloud Bus를 구현할 생각이고 Kafka는 이후에 마이크로 서비스간 통신을 할 때 활용할 생각이기 때문에 이 둘의 차이점만 가볍게 정리만 하고 넘어가 보겠다.
- RabbitMQ ( 적은 수의 데이터 안전 )
- 메시지 브로커
- 초당 20+ 메시지를 소비자에게 전달
- 메시지 전달 보장, 시스템 간 메시지 전달
- 브로커, 소비자 중심
- Kafka ( 대용량 )
- 초당 100k+ 이상의 이벤트 처리
- pub/sub, Topic에 메시지 전달
- Ack를 기다리지 않고 전달 가능
- 생산자 중심
2-1. Dependency 추가
Dependency를 추가하기에 앞서 RabbitMQ를 사용하기 위해서는 RabbitMQ를 설치해야 한다.
다만 설치를 위해서 Windows와 mac os가 거쳐야하는 과정이 다르고 그다지 복잡하지 않기에 따로 설명하지는 않고 설치가 되었다 가정한 상태로 진행하겠다.
dependencies {
implementation 'org.springframework.cloud:spring-cloud-starter-bus-amqp'
}
앞서 이전 포스트에서 Spring Cloud Config Client로 등록했던 서비스들 전부 해당 Dependency를 추가해준다.
앞으로 나올 모든 적용사항 모두 공통적으로 적용해주면 된다.
2-2. application.yml
spring:
rabbitmq:
host: 127.0.0.1
port: 5672
username: guest
password: guest
RabbitMQ를 적용하기위한 설정이다.
port, username, password 모두 따로 설정하지 않았다면 위와같은 기본값을 가질 것이다.
추가로 Actuator의 Endpoint로 busrefresh를 추가해준다.
management:
endpoints:
web:
exposure:
include: refresh, health, beans, busrefresh
2-3. Spring Cloud Bus 테스트
현재 user-service의 health-check를 호출해보았다.
이전에 적용했던 변경사항이 그대로 유지되어 있다.
그럼 이제 다시 application.yml과 user-service.yml 파일을 수정해보겠다.
현재 필자의 시스템에서는 application.yml파일은 apigateway-service에서 user-service.yml은 user-service에서 각각 불러오고 있다.
이런 상황에서 만약 user-service에서만 Actuator Refresh를 호출한다면 user-service와 apigateway-service에 등록된 Secret Key 값이 달라지게 될 것이고 그러면 apigateway에서 진행되는 인증 절차도 제대로 처리되지 않을 것이다.
이를 방지하기 위해서는 user-service와 apigateway-service 두 곳에서 Actuator Refresh를 호출해야 했으나
이제는 Spring Cloud Bus를 적용했기 때문에 둘 중 한 곳에서 아까 등록한 Actuator의 Endpoint인 busrefresh를 호출해주면 Client에 등록된 서비스 중 변경사항이 적용될 서비스들 전부가 refresh 되게 된다.
busrefresh를 호출하면 위처럼 204 No Content 스테이터스와 함께 빈 Body가 반환된다.
이렇게 뜨면 정상적으로 적용이 됐다는 뜻이다.
이제 다시 user-service의 health-check를 호출해보면
Secret의 변경사항이 잘 적용된 것을 확인할 수 있다.
apigateway-service는 Secret Key를 확인하는 메서드를 따로 작성하지 않아 확인할 수는 없지만 새로 발급받은 토큰이 정상적으로 작동하는 것으로 보아 apigateway-service의 Secret Key 변경사항도 잘 적용되었다는 것을 짐작해 볼 수 있다.
생각보다 분량이 많아 2개의 포스트에 걸쳐서 Spring Cloud Config를 적용하고 이를 MSA환경에서 효과적으로 활용하기 위한 Spring Cloud Bus 라이브러리까지 알아보았다.
MSA를 적용해보면서 느끼는 건 확실히 적용하고 난 후에는 관리가 효율적인 것은 맞지만 초기에 들여야하는 리소스가 Monolithic 방식에 비해 많다는 것이다.
그리고 마이크로 서비스 간 통신, 데이터 동기화, 모니터링 등 아직도 적용해야할 것이 많은 만큼 이번 MSA 시리즈는 좀 길어질 예정이다.
다음 포스트에서는 마이크로 서비스 간 통신에 대해서 알아보려고 한다.