Spring Cloud Config
포스트
취소

Spring Cloud Config

Spring Cloud Config

정의

  • 클라이언트-서버 구조를 통해 여러 마이크로서비스의 외부 설정 정보를 관리할 수 있도록 해주는 기술
  • 환경설정 정보를 반환하는 API 서버의 역할을 한다고 보면 된다.
  • 해당 기술을 사용한 환경설정 정보용 서버를 Config Server라고 부른다.
  • Config Server로부터 환경설정 정보를 받는 쪽은 Config Client라고 부른다.

등장 배경

마이크로서비스라는 것은 서로 다른 개별의 애플리케이션이다.
그런데 마이크로서비스의 종류가 한,두가지면 상관이 없겠지만,
만약 서비스의 종류가 100개가 넘는 대형 프로젝트라면
환경설정 정보가 변동된다면 100개가 넘는 서비스마다 값을 바꿔줘야 할 것이다.

이런 상황에서 등장한 것이 Spring Cloud Config다.
Git이나 SVN, 파일 시스템 등 외부 저장소에 환경설정 정보를 저장하고,
해당 저장소에서 properties나 yaml같은 환경설정 파일을 읽어서,
그 값을 각 마이크로서비스에 전달해준다.

특징

  • 중앙 집중식 설정 관리
    • 설정 파일을 하나의 장소에 모아 관리한다.
    • Git, SVN, 파일 시스템 등이 해당한다.
  • 버전 관리
    • Git이나 SVN같은 형상 관리 시스템을 통해 설정 변경 이력 관리 가능.
  • 동적 설정 반영
    • 설정 변경 시 실시간으로 클라이언트에 반영할 수 있다.
    • Webhook이나 메시지 브로커를 통해 반영한다.
    • Spring Cloud Bus라는 기술을 함께 사용해야 한다.
  • 환경별 설정 지원
    • application-{profile}.yml 형식으로 다양한 환경에 따른 설정이 가능하다.
    • dev, test, prod 등으로 구분한다.
  • 암호화/복호화 지원
    • 비밀번호나 API 키같은 민감한 정보를 안전하게 저장할 수 있다.
    • 물론 저장소 자체의 보안도 신경을 써야 한다.
    • 외부 저장소에 저장하는 것은 편리함의 성격이 강하다.
      • 보안의 성격도 있긴하지만 편리함이 더 크다.

장점

  • 중앙화된 관리
    • 하나의 서버에서 모든 설정을 관리할 수 있다.
  • 환경별 분리
    • dev/test/prod 환경에 맞는 설정 파일을 분리해서 관리할 수 있다.
  • 유연한 저장소 연동
    • Git, SVN, File System 등 다양한 저장소를 지원한다.
  • 자동화 및 지속적 배포 연계
    • CI/CD 파이프라인과 통합할 수 있다.
  • 실시간 설정 반영
    • Spring Cloud Bus와 연계 시 재시작 없이 설정을 반영할 수 있다.

단점

  • 의존성 증가
    • Config Server에 문제가 생기면 전체 시스템에 영향이 간다.
  • 초기 설정 복잡
    • 서버/클라이언트 구성 및 Git 연동 등 설정이 복잡할 수 있다.
  • 보안 문제
    • 민감 정보가 Git에 저장될 경우 유출될 가능성이 존재한다.
    • 그래서 암호화가 필수다.
  • 성능 이슈
    • 대규모 시스템에서는 설정 파일 로딩 시 병목이 발생할 수도 있다.
  • 실시간 반영 조건 복잡
    • Spring Cloud Bus, RabbitMQ, Kafka 등 추가 구성이 필요하다.

저장소 활용하기

저장소 종류별로 환경설정 파일을 작성하는 방법을 알아보자.

Local Git Repository

로컬 리포지토리에서는 깃 저장소의 주소만 설정하면 된다.

spring:
  application:
    name: config-service
  cloud:
    config:
      server:
        git:
          uri: file:///C:/git/cloud_config
          default-label: master

Remote Git Repository

리모트 리포지토리에서는 저장소의 주소를 입력하면 된다.
public repository일 때는 이대로 사용하면 되고,
private repository일 때는 username과 password를 함께 명시하면 된다.

spring:
  application:
    name: config-service
  cloud:
    config:
      server:
        git:
          uri: 리포지토리_주소
          default-label: master

Native File Repository

spring:
  application:
    name: config-service
  cloud:
    config:
      server:
        native:
          search-locations: file:///C:/git/cloud_config

관련 속성 설명

  • spring.cloud.config.server.git.uri
    • 저장소의 주소
  • spring.cloud.config.server.git.username
    • 사용자명
  • spring.cloud.config.server.git.password
    • 비밀번호
  • spring.cloud.config.server.git.default-label
    • 브랜치명
    • 별도로 설정하지 않으면 main으로 되어 있다.
    • 만약에 NoSuchLabelException 터지면 여기가 문제다.
  • spring.cloud.config.server.native.search-locations
    • 폴더 경로
    • 끝에 s가 붙은 것을 알 수 있지만 여러 개를 지정할 수 있다.

위 속성들말고도 다양한 속성들이 있다.

파일명 짓기

환경설정 파일명도 규칙에 맞게 지어야 한다.
{애플리케이션명}-{프로파일}.yaml의 구조로 작성하면 된다.

만약 애플리케이션명이 ecommerce고 프로파일이 prod라면,
파일명은 ecommerce-prod.yaml이 된다.

참고로 첫번째 대시를 기준으로 읽기 때문에
프로파일에도 대시가 들어가도 된다.
대략 ecommerce라는 앱의 운영 베타버전이면
ecommerce-prod-beta.yaml이 될 것이다.

Spring Cloud Config 연동 (Config Server)

build.gradle

Config Server용 프로젝트를 생성한 뒤 build.gradle에 아래의 의존성을 추가해주자.

implementation 'org.springframework.cloud:spring-cloud-config-server'

그리고 앞선 프로젝트들처럼 아래의 내용도 함께 추가해주자.

ext {
  set('springCloudVersion', "2024.0.1")
}

dependencyManagement {
  imports {
      mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
  }
}

Config Server 역할 부여

이제 @SpringBootApplication 애노테이션이 있는 클래스로 이동해서
@EnableConfigServer 애노테이션을 추가함으로써
해당 프로젝트가 Config Server로 동작하게 하자.

Spring Cloud Bus 연동 (GateWay Service, MicroService)

build.gradle

Config Server랑 연동할 서비스의 build.gradle에 아래의 의존성을 추가해주자.

implementation 'org.springframework.cloud:spring-cloud-starter-config'

Config Server 연동 (스프링 부트 2.4 미만)

스프링 부트 2.4 미만 버전에는 bootstrap.yml이라는 파일을 통해서
Config Server와 연동했었다. (정확하게는 bootstrap-{profile}.yml)

그래서 아래와 같은 파일을 만들게 되면 Config Server와 연동이 가능했다.

spring:
  cloud:
    config:
      uri: http://localhost:8888

그리고 스프링 부트 2.4에서는 의존성도 하나 더 추가해줘야 한다.

implementation 'org.springframework.cloud:spring-cloud-starter-bootstrap'

의존성을 추가하는 게 아니라 환경설정 파일에
spring.cloud.bootstrap.enabled=true를 설정해도 된다.

Config Server 연동 (스프링 부트 2.4 이상)

스프링 부트 2.4부터는
application.yml 파일과 bootstrap.yml 파일이
하나로 합쳐졌다.

스프링 부트 2.4부터는 application.yaml 파일에 아래와 같이 설정하면 된다.

spring:
  config:
    import: optional:configserver:http://127.0.0.1:8888/

참고로 optional prefix가 없으면 Config Server에 연결할 수 없을 때
애플리케이션이 종료되어 버리니 주의하자.

설정 파일 우선 순위

  1. 프로젝트의 application.yaml
  2. 저장소의 application.yaml
  3. 프로젝트의 application-{profile}.yaml
  4. 저장소의 {application name}/{application name}-{profile}

참고로 순서대로 읽다가 동일한 키 값이 있으면 덮어씌워지니 주의하자.

공통된 설정 사용

바로 직전의 설정 파일의 우선 순위에 보면,
2번째 순위에 저장소의 application.yaml가 해당하는 것을 볼 수 있다.

그래서 만약 user-serviceorder-service라는
마이크로서비스가 있다고 가정한다면,
application.yaml 파일 하나를 통해 공통된 설정을 공유할 수 있다.

만약 application.yaml 파일이 없다면
4번째 순위에 해당하는 파일명이 존재해야 한다.

설정 정보 주입하기

값을 실제로 사용하려면 값을 미리 저장할 클래스 및 필드를 정의해야 한다.
아래는 예시용 클래스다.

@Setter
@Getter
@RefreshScope
public class SampleConfig {
    @Value("token.secret")
    private String secret;
}

@Value 애노테이션을 통해 설정 정보에 있는 값을 읽어 들인다.
그리고 @RefreshScope가 있으면 Config Server에서 반환하는
설정 정보의 값이 바뀌었을 때 애플리케이션 서버를 재실행한다.

만약 생성자를 사용하고 싶다면 @PostConstructor 애노테이션을 사용해야 한다.
그렇지 않으면 생성자가 먼저 만들어지고,
그 다음에 @Value를 실행하기 때문에 원하지 않는 동작이 발생할 수 있다.

그리고 @PostConstructor를 사용하게 되면,
@RefreshScope가 있어도 서버가 재실행 됬을 때 생성자를 재실행하지는 않는다.
그럴 때는 아래의 코드를 추가해주자.

@EventListener(RefreshScopeRefreshedEvent.class)
public void onRefresh(RefreshScopeRefreshedEvent event) {
  // 재실행 관련 내용
}

출처

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.

MSA와 보안

Spring Cloud Bus