[TIL] Spring Cloud Config

김건우·2024년 8월 3일

[TIL]

목록 보기
7/25

Spring Cloud Config

  • Spring Cloud Config는 분산 시스템 환경에서 중앙 집중식 구성 관리를 제공하는 프레임워크입니다.
  • 애플리케이션의 설정을 중앙에서 관리하고, 변경 사항을 실시간으로 반영할 수 있습니다.
  • Git, 파일 시스템, JDBC 등 다양한 저장소를 지원합니다.

주요 기능

  • 중앙 집중식 구성 관리: 모든 마이크로서비스의 설정을 중앙에서 관리합니다.
  • 환경별 구성: 개발, 테스트, 운영 등 환경별로 구성을 분리하여 관리할 수 있습니다.
  • 실시간 구성 변경: 설정 변경 시 애플리케이션을 재시작하지 않고도 실시간으로 반영할 수 있습니다.

환경별 구성 관리

환경별 설정 파일

  • Config 서버는 환경별로 다른 설정 파일을 제공할 수 있습니다.
  • 예를 들어, application-dev.yml, application-prod.yml 파일을 Git 저장소에 저장하여 환경별 설정을 관리합니다.

프로필 사용

Spring Boot 애플리케이션에서 프로필을 사용하여 환경을 구분할 수 있습니다:
spring:
  profiles:
    active: dev

실시간 구성 변경

❗ 실시간 구성 변경을 반영하는 방법에는 여러 가지가 있습니다.
Spring Cloud Bus를 사용하는 방법, 수동으로 /actuator/refresh 엔드포인트를 호출하는 방법, Spring Boot DevTools를 사용하는 방법, 그리고 Git 저장소를 사용하는 방법이 있습니다. 각 방법은 상황에 따라 적절히 선택하여 사용할 수 있습니다. Spring Cloud Bus는 메시징 시스템을 통해 실시간으로 설정 변경 사항을 전파하는 데 매우 유용하며, Git 저장소를 사용하면 설정 파일의 버전 관리를 쉽게 할 수 있습니다. Spring Boot DevTools는 주로 개발 환경에서 유용하게 사용됩니다.

Spring Cloud Bus

  • Spring Cloud Bus를 사용하면 설정 변경 사항을 실시간으로 클라이언트 애플리케이션에 반영할 수 있습니다. 이를 위해서는 메시징 시스템(RabbitMQ 또는 Kafka 등)을 사용하여 변경 사항을 전파해야 합니다.

수동 구성 갱신

  • /actuator/refresh 엔드포인트 사용

    • Spring Cloud Bus를 사용하지 않는 경우, 클라이언트 애플리케이션에서 수동으로 설정을 갱신할 수 있습니다. 이를 위해 Spring Actuator의 /actuator/refresh 엔드포인트를 사용할 수 있습니다.
  • 설정 갱신 절차

    1. Config 서버에서 설정 파일을 변경합니다.

    2. 클라이언트 애플리케이션의 /actuator/refresh 엔드포인트를 POST 요청으로 호출하여 변경된 설정을 반영합니다.

      이 방법은 간단하지만, 각 클라이언트 애플리케이션에서 수동으로 엔드포인트를 호출해야 합니다.

  • Spring Boot DevTools 사용

    • Spring Boot DevTools를 사용하면 개발 환경에서 파일 변경을 자동으로 감지하고 애플리케이션을 재시작할 수 있습니다. 이는 classpath 내의 파일 변경도 포함됩니다.

Git 저장소 사용

  • Spring Cloud Config 서버가 Git 저장소에서 설정 파일을 읽어오도록 설정할 수 있습니다. 이는 설정 파일의 변경 사항을 쉽게 반영하고, 여러 서비스 간에 일관된 구성을 유지하는 데 유용합니다.

실습

Config 서버를 생성하고, port와 간단한 message를 동적으로 변경해보고 실제 갱신되는 모습을 확인한다. 수동으로 /actuator/refresh 엔드포인트 사용해서 변경을 확인.

ConfigServer

	implementation 'org.springframework.boot:spring-boot-starter-actuator'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	implementation 'org.springframework.cloud:spring-cloud-config-server'
	implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'

다음과 같은 dependency를 적용한다. config-server 라도 eureka를 통해 통신해야 하기에 관련 설정도 추가해준다.

또한 @EnableConfigServer 도 꼭 붙여줘야 한다!

다음 계층 형태로 yml 파일을 작성한다.

application.yml

server:
  port: 18080

spring:
  profiles:
    active: native
  application:
    name: config-server
  cloud:
    config:
      server:
        native:
          search-locations: classpath:/config-repo  # 리소스 폴더의 디렉토리 경로

eureka:
  client:
    service-url:
      defaultZone: http://localhost:19090/eureka/

product-service.yml

server:
  port: 19093

message: "product-service message"

product-service-local.yml

server:
  port: 19083

message: "product-service-local message"

Product-service

	implementation 'org.springframework.boot:spring-boot-starter-actuator'
	implementation 'org.springframework.cloud:spring-cloud-starter-config'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'

서버와 달리 config 를 dependency에 추가한다.

application.yml

server:
  port: 0  # 임시 포트, 이후 Config 서버 설정으로 덮어씌움

spring:
  profiles:
    active: local
  application:
    name: product-service
  config:
    import: "configserver:"
  cloud:
    config:
      discovery:
        enabled: true
        service-id: config-server

management:
  endpoints:
    web:
      exposure:
        include: refresh

eureka:
  client:
    service-url:
      defaultZone: http://localhost:19090/eureka/

message: "default message"

product-service 서버에서는 profile 설정을 local로 설정했고,
config.import를 통해 configserver 에서 설정을 가져옴을 설정했고,
cloud.config.discovery.enabled: true: 를 통해 eureka 서버로 부터 가져올 수 있게끔 설정했다.

또한
actuator 설정으로 refresh 엔드포인트를 노출시켰다.

ProductController.java

/**
 * @RefreshScope 애노테이션은 Spring 애플리케이션의 빈이 설정 변경을 반영할 수 있도록 하는 역할을 합니다.
 * 기본적으로 Spring 애플리케이션의 빈은 애플리케이션이 시작될 때 초기화되고, 설정 값이 변경되더라도 해당 빈은 갱신되지 않습니다.
 * 이 애노테이션을 사용하면 /actuator/refresh 엔드포인트를 호출하여 설정 변경 사항을 동적으로 반영할 수 있습니다.
 */
@RefreshScope
@RestController
@RequestMapping("/product")
public class ProductController {

    @Value("${server.port}")
    private String serverPort;

    @Value("${message}")
    private String message;

    @GetMapping
    public String getProduct() {
        return "Product detail from PORT : " + serverPort + " and message : " + this.message ;
    }
}

또한 중요하게 봐야할 것은 @RefreshScope 이다. 해당 어노테이션을 붙여놔야지 @Value 어노테이션으로 yml에 있는 정보가 바뀐다면 동적으로 반영할 수 있다.


서버를 모두 띄우고 테스트 해본다.

먼저
localhost:19083/product 로 접근해 보면, product 서버의 포트인 0 번과 default 메시지가 아니라 config 서버에서 지정해준 19083 포트에 product-service-local message 가 뜨는 것을 확인할 수 있다.

profiles:active: local 설정을 없앤다면 product-service message가 뜬다.

다음은 자동 업데이트를 테스트하기 위해 config-server의 product-service-local.yml 의 내용을 변경하고, 서버를 재시작 한다.

단지 서버를 재시작 한다고 해서 자동으로 변경되지 않고,

해당 엔드포인트로 POST 메서드를 통해 요청해주면 message 의 값이 변경되었다는 의미를 200 상태코드와 함께 반환해준다.

변경된 사항이 없으면 빈 결과값을 내뱉는다.

여튼 다시 localhost:19083/product 엔드포인트로 접근해보면, product-service 서버를 다시 재시작 하지 않고도, yml 파일의 값 변경을 확인하고 적용하는 모습을 보여준다.

로그에서도 확인할 수 있는데, product-service.yml 이 2번 뜨는 이유는 아직 정확히는 모르겠다..


마무리

MSA 에서는 Config 설정 또한 하나의 서버를 통해 공통적인 관리를 해주는 것이 신기하기도 복잡하기도 했다.

생각해보면 계층형 아키텍처에서는 서버가 하나이기에 크게 고민할 필요 없었던 문제지만, MSA 환경에서는 여러 서버에서의 공통적인 Config 설정 또한 중요한 포인트이다.

실시간으로 구성을 변경하는 방법도 다양했는데, 특히 Git 저장소도 활용할 수 있으니 정말 환경이 잘 구성되어 있다고 생각한다. 정말 실시간이 중요하고, 개발 환경에 메시징 시스템을 사용하고 있는 경우라면 Spring Cloud Bus + 메시징 시스템을 사용하는 편이 좋아 보인다.
acutator를 많이 사용해보진 못했는데, refresh 엔드포인트의 역할을 하나 알게되었다. 이처럼 수동 갱신은 간편하지만, 각 client 애플리케이션에서 수동으로 호출해줘야 한다는 번거로움이 존재하기에 도입한다면 고려해볼 만한 문제인 것 같다.

앞에 내용들은 많이 들어본 내용이였는데, config 쪽은 생소해서 재밌기도하고 어렵기도 했다.

profile
공부 정리용

0개의 댓글