Spring Config Server에서 Config 파일들을 어떻게 쉽게 갱신할까?

Lord·2024년 11월 24일

Problem Solving Skills

목록 보기
4/17
post-thumbnail

고민의 시작 : Config 파일 갱신의 어려움

Spring Cloud를 사용하면서 Config Server를 구성하게 되었고, 각 서비들의 주요 설정 파일인 application.yaml파일들을 config-repo에서 한꺼번에 관리하게 되었다. 그러나 중간에 하나의 파일이 변경되면 변경된 사항을 config server에서는 알지 못하므로 서버를 재시작하여 다시 변경된 설정을 불러오도록 하였다. 여기서 나의 고민은 시작이 되었다.

정말 이렇게 서버를 재시작해야 변경된 설정들을 갱신할 수 있을까?

결론을 미리 이야기 하자면 절대 그렇지 않다. 각 서비스들의 설정 파일인 application.yaml에 대한 변경사항을 github repo에 푸시를 하여 올리면, 자동으로 config server가 매핑하는 config 파일에 대한 변경사항이 반영되는 방법이 존재하였다. 그 방법을 찾을 때까지의 고민 과정을 이 포스팅을 통해 공유하여 조금이라도 나와 같은 고민을 하는 개발자들에게 도움이 되고자 한다.

첫 번째 시도 : Actuator 엔드포인트 활용하기

Actuator란 무엇인가?

Spring Boot Actuator는 스프링 부트에서 제공하는 기능으로 애플리케이션의 관리 및 모니터링을 위한 다양한 기능을 제공한다. 이를 통해 시스템 상태, 메트릭, 보안 점검, 환경 변수 등을 쉽게 확인할 수 있으며, 운영 환경에서 시스템을 효율적으로 관리할 수 있도록 지원한다. 엔드포인트에는 여러가지가 있고 예를 들면 아래 표와 같은 이름과 기능을 제공한다.

엔드포인트기능
/actuator/health애플리케이션의 상태를 확인하는 기능을 제공한다. 기본적으로 UP 또는 DOWN 상태를 반환하며, 구성에 따라 세부 정보를 포함한다.
/actuator/info애플리케이션에 대한 일반적인 정보를 제공한다. application.properties 또는 YAML 파일에 정의된 커스텀 정보도 포함할 수 있다.
/actuator/env애플리케이션의 환경 변수와 프로퍼티 정보를 조회할 수 있다.
/actuator/loggers애플리케이션에서 사용 중인 로거의 상태와 로깅 레벨을 조회하거나 변경할 수 있는 기능을 제공한다.
/actuator/metrics애플리케이션의 메트릭 정보를 제공한다. 메모리 사용량, GC, CPU 사용률 등 다양한 메트릭 데이터를 확인할 수 있다.
/actuator/beans애플리케이션 컨텍스트에 로드된 모든 스프링 빈의 목록과 의존성을 확인할 수 있다.
/actuator/refreshSpring Cloud Config 환경에서 사용되며, 설정 정보를 새로고침할 수 있도록 지원한다. 이 엔드포인트를 통해 Config Server로부터 최신 구성을 로드할 수 있다.

특히, 여기서 주목해 봐야 할 엔드포인트는 설정 변경과 관련한 기능을 가지고 있는 /actuator/refresh 이다.

Actuator의 Refresh 엔드포인트 사용하기

Spring Cloud Config Server와 함께 Actuator의 /refresh 엔드포인트를 사용하면 애플리케이션 실행 도중에 Config 파일의 변경 사항을 반영할 수 있다. 아래와 같이 config server의 application.yaml 파일에 actuator에 대한 설정을 추가하였다.

management:
  endpoints:
    web:
      exposure:
        include: refresh

그리고 아래와 같이 엔드포인트를 호출하자, 200 ok가 뜨면서 변경된 설정 정보의 key를 응답으로 보내주엇다.

# Actuator refresh 엔드포인트 호출 예시
curl -X POST http://localhost:8080/actuator/refresh

refresh 결과

위의 응답은 실제로 jwt.accessExpiration의 값을 변경하고 나서 받은 결과이다.

의문 발생 : 비효율성

위의 방법을 찾고 나서 이러한 생각이 들었다.

설정 정보가 변경된 서비스가 여러 개 존재한다면, 내가 그 모든 서비스들에게 /actuator/refresh 요청을 보내줘야하는데 너무 비효율적인 것 아닐까?

시스템 규모가 커지면서 수십 개의 서비스에 수동으로 /refresh 요청을 보내는 것은 비효율적이고 오류 발생 가능성도 높다는 생각이 들었다. 반복적으로 갱신 작업을 수동으로 수행하는 과정에서 이미 refresh한 서비스에 또 요청을 날리고 다른 서비스들에는 요청을 날리지 않는 것과 같은 실수의 가능성도 커질 것이라 예상하였고, 따라서 이를 한번에 해줄 수 있는 다른 방법이 필요하다고 생각하였다.

두 번째 시도: Spring Cloud Bus (with RabbitMQ)

spring cloud bus

Spring Cloud Bus란 무엇인가?

Spring Cloud Bus는 분산 시스템 환경에서 설정 변경이나 이벤트를 각 서비스에 전파하는 역할을 한다. 메시지 브로커를 통해 설정 변경 이벤트를 전체 서비스에 브로드캐스트할 수 있게 해주는 도구로, 이를 통해 중앙에서 변경된 사항을 모든 서비스에 빠르게 전달할 수 있다. 대표적인 메시지 브로커로는 KafkaRabbitMQ가 있는데 나는 RabbitMQ를 선택하였다. 이에 대한 이유는 뒤에 다른 목차에서 설명하려고 한다.

Spring Cloud Bus 활용하기

Spring Cloud Bus를 사용하면 설정 파일을 변경한 후 config server에 한 번의 /actuator/busrefresh 요청만 보내면, 변경 사항이 모든 연결된 서비스에 자동으로 전파된다. 이를 통해 설정 변경의 복잡성을 줄이고, 단일 명령으로 전체 시스템에 반영할 수 있는 장점을 가질 수 있다. 먼저 모든 서비스에 cloud bus에 대한 의존성을 아래와 같이 추가해주어야 한다. 또한 각 서비스 마다 같은 RabbitMQ에 연결시켜 주어야 한다.

// Spring Cloud Bus 의존성 추가 (Gradle 예시)
dependencies {
    implementation 'org.springframework.cloud:spring-cloud-starter-bus-amqp'
    implementation 'org.springframework.cloud:spring-cloud-starter-config'
}

물론, 모든 서비스들의 설정에는 refresh, busrefresh 엔드포인트가 추가되어야 한다.

management:
  endpoints:
    web:
      exposure:
        include: refresh, busrefresh

그리고 아래와 같이 Config Server에서 busrefresh의 엔드포인트를 호출 해주면 이에 대한 요청이 RabbitMQ에 존재하는 각 서비스들의 큐에 올라가게 된다. 그러면 이러한 큐를 구독하고 있는 서비스들에게 메시지가 전달되는데, 이때의 메시지가 /refresh의 엔드포인트 호출인 것이다. (위에서 이야기한 의존성을 추가하게 되면 그 서비스는 자동으로 cloud bus에 대한 큐를 생성하고 구독하게 된다.)

# Spring Cloud Bus refresh 엔드포인트 호출
curl -X POST http://localhost:8080/actuator/busrefresh

응답에는 204 No Content가 뜨지만 아래와 같이 RabbitMQ에는 큐에 메시지가 발행이 되고, 설정이 변경된 서비스에는 메시지가 전달이 되어서 설정이 갱신되는 것을 확인할 수 있었다.

rabbitMQ 메시지 전송 확인

서비스 로그

위의 로그에서 jwt.accessExpiration 키가 refreshed 되었다는 메시지를 확인할 수 있다.

Spring Cloud Bus의 전체 과정

  1. Config Server에서 /actuator/busrefresh를 POST 요청한다.
  2. 연결된 RabbitMQ에 /actuator/refresh 메시지를 모든 서비스의 큐에 발행한다.
  3. 큐를 구독하고 있는 각 서비스들은 메시지를 전달 받고 /actuator/refresh를 실행한다.
  4. 변경된 설정이 반영된다.

쉽게 이해하면 위와 같은 플로우라고 생각하면 된다.

RabbitMQ와 Kafka : 무엇을 선택해야 할까?

RabbitMQKafka 모두 Spring Cloud Bus와 연동이 가능하지만, 프로젝트의 특성에 따라 선택이 달라질 수 있다. RabbitMQ는 빠른 설정과 쉬운 관리가 장점이고, Kafka는 대용량 트래픽 처리와 높은 내구성을 제공한다. 대규모 분산 시스템에서는 Kafka가 더 적합한 선택일 수 있다. 만약 자신의 서비스에서 Kafka를 이미 사용중이라면 Kafka를 사용하는 것이 올바른 방법이라고 생각한다. 굳이 RabbitMQ를 새로운 서버로 도입하여 Cloud Bus만을 위해서 사용하는 것은 불필요하다고 본다. 그러나 Kafka를 사용중이지 않는다면 RabbitMQ를 사용하는 것이 올바른 판단이라고 생각한다. Busrefresh하는 과정은 대규모의 메시지 처리를 요구하는 과정이 아니기 때문에 비교적 설정과 관리가 쉬운 RabbitMQ를 선택하는 것을 추천한다. 그러나 항상 이야기 하듯, 요구사항에 따라 적절한 메시지 브로커를 선택하는 것이 중요하다. 신뢰성과 처리량이 중요한 경우 Kafka를, 설정의 간편함이 중요한 경우 RabbitMQ를 사용하는 것이 좋다. RabbitMQKafka에 대한 자세한 비교와 나에 대한 생각은 아래 포스팅을 참고하길 바란다..!

Kafka 데이터 스트리밍의 핵심 기술

추가 고민 : 자동화 도입하기

Bus Refresh를 자동화 할 수는 없을까?

지금까지 설정 변경후, 갱신에 대한 고민의 시작으로, 두 가지의 방안을 거쳐 Bus Refresh라는 방법을 채택한 과정을 살펴보았다. 그러나 여기서 하나의 의문점이 또 들었다.

설정을 변경하고 나면 내가 직접 Bus Refresh를 보내는 것이 아닌, GitHub에서 Bus Refresh 요청을 보내줄 수 있지 않을까?

이전에 우리 팀의 디스코드에 Webhook을 이용하여 PR, ISSUE에 대해서 자동으로 메시지를 보내줄 수 있는 기능을 추가한 적 있었는데, 가만히 떠올려보니 config-repo에 푸시가 올라온다면 내 서버로 /actuator/busrefresh 요청을 보내줄 수 있지 않을까?라는 생각이 들었다.

결국 Spring Cloud Config Monitor라는 라이브러리를 사용하여서 Github Webhook을 이용한 자동화를 구성하였다. 이번 포스팅이 너무 길어질 것 같아서 관련된 내용은 다음 포스팅에 자세하게 다루어 볼 예정이다.

고민의 결과 : 변화와 개선

단일 Bus Refresh로 전체 시스템 갱신

더 이상 모든 서비스 인스턴스에 개별적으로 refresh 요청을 보낼 필요가 없어졌다. 이제는 busrefresh 한 번으로 모든 서비스가 변경된 설정을 실시간으로 반영하게 되었다. 이를 통해 관리가 매우 효율적이게 되었으며, 오류 발생 가능성도 크게 줄어들었다.

GitHub Webhook과의 연동을 통한 자동화

GitHub Webhook을 사용해 설정 파일에 변경이 발생할 때 자동으로 /actuator/busrefresh 요청을 보내도록 구성함으로써, 설정 변경 후의 수동 작업이 필요 없어졌다. 또한, 이러한 자동화를 통해 설정 변경 과정에서 발생할 수 있는 문제들을 미리 방지할 수 있었다.

고민 마무리 : 배운 점과 결론

이번 고민의 마무리는 자동화까지 이루고 나서야 끝이 났다. 자동화를 하고 나서의 의문점과 추가 고민들은 자동화에 대한 포스팅에서 다룰 예정이다. 이번 고민을 통해 Spring Cloud Config Server가 설정을 갱신하는 더 나은 방법들을 학습하였고 Spring Cloud Bus를 이용하여 전체 서비스에 걸쳐 변경 사항을 쉽게 반영할 수 있는 구조를 구축하게 되었다. 또한, 자동화 도구와의 연계를 통해 유지보수성을 크게 향상시킬 수 있었고, 분산 시스템 환경에서 설정의 일관성을 유지하며 서비스의 가용성을 높이는 경험을 쌓을 수 있었다.

profile
다재다능한 Backend 개발자에 도전하는 개발자

0개의 댓글