본문 바로가기
🔬아키텍처/- Spring Cloud for MSA

[Spring Cloud Config] Application의 설정 정보 (application.yml) 를 중앙에서 관리하기 (by native repository)

by Wonit 2021. 4. 27.

해당 글은 Spring Cloud Netflix EurekaSpring Cloud Gateway 의 Built-in Route로 Predicates와 Filter 조작하기)에 의존하는 글입니다. 실습 환경을 따라하시려면 Eureka와 Gateway 글에 나온 실습을 따라하시길 권고드립니다.

목차

  • Gartner 에서 정의된 MSA 표준 구성 요소인 Config Store Server
    • 자주 변경되는 설정 정보는 설정 정보만 담고 있는 서버에서 관리하고 이를 필요로 하는 서버에게 각각 뿌려주는 방식
  • 우리가 구성한 서비스 구조
    • 각각의 application.yml
    • 서비스 구조의 문제점
  • Spring Cloud Config 란?
  • 실습
    • Spring Cloud Config 프로젝트 생성하기
      • @EnableConfigServer
      • profiles.active.native
    • User-Service 에서 Config 서버로 받은 데이터 사용하기
      • bootstrap.yml
    • 테스트하기

Microservice Architecture 에서 Config Store Server가 갖는 의미

Config Store Server 는 말 그대로 설정 정보 저장 서버이다.

 

Gartner 그룹에서 정의한 MSA 인프라 필수 구성 요소에 의하면 Config Store Server 는 Service Mesh 영역에 위치하는 필수 요소라고 정의되어진다.

 

 

Service Mesh 에서 Config Store 서버는 자주 변경되는 설정 정보는 설정 정보만 담고 있는 서버에서 관리하고 이를 필요로 하는 서버에게 각각 뿌려주는 방식으로 이용된다.

 

이러한 Config Store 서버가 있음으로 Application의 외부 속성을 중앙 집중식 관리가 가능해진다.

 

중앙 집중식 관리가 없으면 어떻게 될까?

 

중앙 집중식 관리가 없다는 것은 아주 일반적인 우리의 application 의 구조일 것이다.

 

우리가 구성한 서비스의 구조

지난 시간 동안 우리는 서비스를 다음과 같이 구성했었다.

  • Eureka Server
  • Gateway Service
  • User Service
  • Order Service

Some Server는 구성하지 않았지만 다양한 서비스가 있다는 전제를 위하여 추가하였습니다.

 

각각의 서버는 외부 설정 정보를 자신의 서버에서 관리하고 있는데, 여기서 말 하는 외부 설정 정보는 application.yml에 들어가는 정보들을 이야기한다.

 

예를 들어서 Gateway 서버와 User, Order 서버에 각각 토큰 복호를 위한 Secret Key와 DB 설정 정보들이 다음과 같이 존재한다고 가정해보자.

 

User & Order-Service Application.yml

token:
  jwt:
    secret: my_secret_key

spring:
  datasource:
    driver-class-name: org.h2.Driver
    url: jdbc:h2:mem:users
    username: sa
    password: my_password

 

User 와 Order 에는 JWT 토큰을 encoding, decoding 을 위한 비밀 키 정보와 Database 설정 정보들이 들어있다.

 

Gateway Services Application.yml

token:
  jwt:
    secret: my_secret_key

 

Gateway 에는 JWT 토큰을 encoding, decoding 을 위한 비밀 키 정보만 존재한다.

 

위의 서비스 구조의 문제점

위의 서비스에서는 어떤 문제점들이 존재할까?

 

각각의 서버는 외부 설정 정보를 자신의 애플리케이션 내에서 관리하게 된다.

 

만약 이런 상황이라면 어떨까?

 

애플리케이션 스펙이 변경되어 JWT 관련된 공통 설정 정보를 변경해야 한다. 하지만 서버가 100대가 존재한다면?

 

그럼 다른 생각을 할 여지 없이 100대의 서버에 공통 설정 정보인 JWT 정보를 변경하는 일을 수동으로 100번 작업해야 한다.

 

단순한 정보 하나가 변경된 이유 때문에 잘 돌아가는 100개의 서버를 끄고 다시 테스트 -> 빌드 -> 배포 과정을 거쳐야 한다.

 

CI CD 파이프라인이 잘 구축되었다고 하더라도 이는 큰 부담일 수 밖에 없다.

 

그럼 이런 생각을 할 수 있지 않을까?

 

마이크로서비스들의 외부 설정 정보들을 하나의 서버에서 관리하고 각각의 서버가 이를 가져가는 구조는 어떨까?

 

이런 이유 덕에 Config Store Server 가 탄생하게 되었고, Spring Cloud 에서는 Spring Cloud Config 를 제공하게 되었다.

 

Spring Cloud Config

Spring Cloud Config 공식 문서를 봐보자.

Spring Cloud Config provides server-side and client-side support for externalized configuration in a distributed system. With the Config Server, you have a central place to manage external properties for applications across all environments.

Spring Cloud Config 는 분산 시스템 (마이크로 서비스)에서 외부 설정 정보 (application.yml 등등)을 위한 서버와 클라이언트를 제공합니다.

 

Spring Cloud Config 에서는 서버와 클라이언트를 제공한다고 하는데, 서버가 바로 설정 정보를 모아놓고 저장하여 뿌려주는 주체가 되고 클라이언트는 서버에서 뿌려주는 설정 정보를 받아 이용하는 측으로 생각할 수 있다.

 

또한 Spring Cloud Config 는 단지 Spring Application 에서만 돌아가는게 아니라 다른 언어로 작성된 Applicaiton 에서도 돌아간다고 한다.

 

Config File 관리

Spring Cloud Config 에서는 설정 정보들을 다양한 방식으로 관리할 수 있다.

 

  • FTP
  • native file system
  • Local Git Stage
  • Git Hosting Service (Github, Gitlab)

 

하지만 기본적으로 Spring Cloud Config는 Git을 이용하는 것을 권장하고 있고 default 가 git 연결으로 한다.

 

그래서 만약 native file 의 정보를 가지고 오고싶다면 spring boot profile 정보를 native로 바꿔줘야 한다.

 

아래에서 자세히 실습해보자.

 

Architecture with Config server

Config 서버가 우리의 아키텍처로 들어온다면 다음과 같을 것이다.

 

  1. 각각의 설정 정보들을 빼서 하나의 github repo 로 업로드한다.
  2. Config 서버와 remote 서버(github repository) 을 연결한다.
  3. Remote 서버와 local git stage를 서로 연결하고 push 한다.
  4. 각각의 Application 서버는 설정 정보를 Config 서버에게 요청한다.

실습

우리의 실습은 Github 에서 가져오는 것이 아니라 Native File System 에서 yml 파일들을 불러오는 것이다.

 

Github 에서 설정 정보를 가져오는 것은 다음 시간에 해볼 것이다.

 

우선 각각의 서비스들을 기동시켜주자.

 

  1. Eureka Server 가동
  2. Gateway Service 가동
  3. User Service 가동
  4. Order Service 가동

Config-Service

이제 집중형 설정 관리 서버인 Config Service 프로젝트를 생성해보자.

 

프로젝트 생성과 의존성 추가

 

프로젝트 생성 초기에 IDE 에서 선택할 수 있지만 만약 그렇지 못한 경우라면 아래와 같이 gradle 에 직접 명시해줄 수 있다.

 

build.gradle

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

dependencies {
    implementation 'org.springframework.cloud:spring-cloud-config-server'
}

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

해당 실습은 IDE 에서 의존성을 선택하지 않고 직접 의존성을 추가하였습니다.

 

Spring Cloud의 2020 버전과 cloud config 의존성을 추가해준다.

 

ConfigServiceApplication.java

@SpringBootApplication
@EnableConfigServer
public class ConfigServiceApplication {

    public static void main(String[] args) {
        SpringApplication.run(ConfigServiceApplication.class, args);
    }

}

 

그리고 Application 에서 @EnableConfigServer 를 이용해서 Config 서버로 동작하게 지정해준다.

 

Config Server 에서 사용할 yml 파일 저장소

 

본인의 PC 아무 곳에 configuration 파일들을 저장할 디렉토리를 하나 만들고 message.yml 파일을 생성하자.

 

그리고 해당 디렉토리에 존재하는 파일의 이름에 따라서 Config 서버의 url이 결정되는데, 이는 다른 서비스가 가져와야할 name이 되고 이를 통해서 식별할 수 있게 된다.

 

message.yml 안에 들어갈 내용은 아래에서 더 알아보도록 하고 일단 해당 파일의 디렉토리의 위치만 파악한다.

 

이제 Config Server는 해당 yml 파일을 다른 마이크로서비스들에게 나눠줄 것이다.

 

Config Server의 Application.yml 수정

server:
  port: 8888

spring:
  application:
    name: config-service

  profiles:
    active: native

  cloud:
    config:
      server:
        native:
          search-locations: file://${user.home}/외부-설정-파일-경로

기본적으로 Config 서버는 8888 포트에서 동작하게 하고 config 파일들을 어디서 불러올지 지정해준다.

 

우리는 native file system 에서 불러올 것이기 때문에 active를 native 로 지정해주자.

 

그리고

 

spring.cloud.config.server.native.search-locations: file://${user.home}/파일 경로

를 이용해서 불러오도록 할 것인데, 각자 생성한 폴더의 경로를 지정해주자.


${user.home} 는 단축어로 홈 디렉토리의 위치를 뜻한다.

 

만약 native 파일이 아니라 git 파일아라면 다음 시간에 더 자세히 이야기하겠지만 git.uri 로 지정해야 한다.

 

여기서 주의해야할 점이 Spring Boot 의 default profiles를 native로 변경해줘야 한다. 만약 profile와 active에 대해서 모른다면 Spring Boot 의 Profile과 설정 정보 에서 확인할 수 있다.

native file directory 에서 message.yml 작성하기

 

아까 생성한 로컬 파일 디렉토리의 message.yml 을 채워보자.

 

message:
  owner: config-service's native folder
  content: :) 안녕하세요 각각의 마이크로서비스에서 사용될 데이터입니다. :)

spring:
  datasource:
    url: dbUrl
    driver: com.h2.Driver
    username: admin
    password: password1234

jwt:
  token:
    key: my_secret_key

 

그리고 api tester나 웹 브라우저로 localhost:8888/message/native 로 접속해보자.


다음 시간에 더 자세히 이야기하겠지만 config server 의 url 정보는 다음과 같이 구성된다.

  • https://config-server.com/${name 정보}/${profile 정보}
    • application.yml : https://config-server.com/message/native

그리고 요청을 보낸 결과를 다시 보면

 

 

해당 url로 요청을 보내면 위와 같은 yml 데이터를 받아올 수 있게 된다.

 

이제 각각의 마이크로서비스에서 이를 사용할 수 있도록 연결시켜보자.

 

User & Order Service

이제 각각의 마이크로서비스에서 독립적인 설정 파일에 더불어 Config 서버가 내려주는 설정 파일들을 이용해야 한다.

 

설정 정보를 추가하기 위해서는 bootstrap 을 이용하는데, 2021년도 기준으로 spring cloud 에서는 bootstrap 을 자동으로 읽어오는 기능이 빠져있다.

 

그러므로 spring-cloud-starter-bootstrap 의존성을 추가해줘야 한다.

 

참고로 bootstrap.yml 은 application.yml 보다 먼저 로드하기 때문에 config 서버의 정보를 consuming 하는 서버에서 설정 정보가 누락되어 application 이 실행되지 않는 것을 방지한다.

 

만약 그렇지 않으면 spring.cloud.bootstrap.enbled=true 로 설정해야하는데, 우리는 외부 설정 파일을 불러오는 곳을 분리하기 위해서 bootstrap 의존성을 추가하고 bootstrap.yml 파일에서 config 관련 정보들을 다루도록하자.

 

Config 의존성 추가 및 bootstrap 의존성 추가

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

bootstrap.yml 생성

 

앞서 이야기한 것 처럼 bootstrap.yml 에서 설정들을 관리하기로 했었다.


application.yml 파일과 동일한 depth 에서 bootstrap.yml 파일을 추가하고 다음과 같이 명시해주자.


config 서버에서 data를 fetching 하기 위해서 config server의 uri 를 명시해준다.

 

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

spring.cloud.config.uri 밑에 spring.cloud.config.name 으로 어떤 파일을 불러올지 명시할 수 있는데, 직접 명시하지 않으면 application.yml 파일을 불러온다.

 

위와 같이 작성하거나 혹은

 

spring:
  cloud:
    config:
      uri: http://localhost:8888
      name: message
      profile: native

 

로 직접 name과 profile 을 지정해서 불러올 수도 있다.

 

현재로서는 어떠한 방법도 상관 없으니 편한 방법대로 작성하자

 

테스트를 위한 Request Mapping End Point 작성

UserServiceApplication.class 에 Config 서버의 configuration 정보를 잘 불러오는지 테스트를 위한 End Point 를 추가한다.

 

역시 Eureka 서버가 기동되었다고 가정하고 테스트하겠다.

 

@SpringBootApplication
@EnableDiscoveryClient
@RestController
public class UserServiceApplication {

    public static void main(String[] args) {
        SpringApplication.run(UserServiceApplication.class, args);
    }

    @GetMapping("/config")
    public String string(@Value("${message.owner}") String messageOwner,
                         @Value("${message.content}") String messageContent) {
        return "Configuration File's Message Owner: " + messageOwner + "\n"
                + "Configuration File's Message Content: " + messageContent;
    }

}

 

위의 코드에 대해서 잠시 이야기를 하자면 @Value 어노테이션을 통해서 Environment 객체에 존재하는 값을 가져올 수 있는데, 우리가 bootstrap.yml 을 이용해서 application.yml 파일보다 먼저 로드하였기 때문에 설정 파일에 있는 message.owner 과 content를 불러올 수 있게 되는 것이다.

 

테스트

 

우리의 설정 정보를 config 서버에서 잘 불러왔는지 gateway를 통해서 user-service 에 접근해보자.

 

만약 gateway 서비스가 없는 경우라면 그냥 user-service url을 호출해도 무방하다.

 

그럼 위와 같이 잘 작동하는 것을 볼 수 있다.

 

댓글0