이번 챕터에서는 각 서비스의 설정 파일들을 관리하는 부분에 대해서 살펴보겠습니다.
1. 설정 파일 관리의 필요성
현재 MSA의 각 마이크로 서비스들의 설정 파일들은 서버 내부에 존재합니다.
이렇게 설정 파일들이 각 마이크로 서비스 내부에 존재할 때는 생산성, 운영 측면에서의 문제가 발생합니다.
여러 상황을 가정해봅시다.
1. 서버 성능 테스트를 위해 설정 파일에서 어떤 설정의 수치를 조절해가며 테스트하는 상황
이 상황에서 서버 내부에 설정 파일들이 존재하면 어떻게 될까요?
수치를 하나씩 조절할 때마다 애플리케이션을 새로 빌드 및 배포해야하는 과정이 필요합니다.
단지 설정 값을 조절하는 작업 하나 때문에 애플리케이션을 다시 빌드하고, 다시 배포해야 하는 것입니다.
2. 여러 설정 파일에 공통으로 있는 어떤 설정 값을 변경해야 하는 상황
이 상황에서 서버 내부에 설정 파일들이 존재하면 어떻게 될까요?
설정 값을 변경하기 위해 설정 파일이 있는 애플리케이션에 직접 접속해서 수정하거나,
CD 파이프라인에서 설정 파일을 관리한다면 애플리케이션 별 CD 파이프라인을 다 접속해서 변경해야 할 것입니다.
이러한 상황 외에도 각 서버 내부에 설정 파일이 존재할 때 불편한 상황들이 많을 것입니다.
그런데 만약 서버 내부가 아닌 서버 외부의 한 곳에서 설정 파일을 관리할 수 있다면 어떨까요?
서버 내부에 존재하여 불편한 상황들을 해결할 수 있을 것입니다.
이렇게 서버 외부에서 설정 파일을 한번에 관리해주는 라이브러리가 바로 Spring Cloud Config입니다.
2. 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. (중략) The default implementation of the server storage backend uses git, so it easily supports labelled versions of configuration environments as well as being accessible to a wide range of tooling for managing the content.
이를 요약해보면 다음과 같습니다.
- 분산 시스템에서 외부화된 구성을 위한 서버 및 클라이언트 제공
- Config Server 사용 시, 모든 환경에서 애플리케이션의 외부 속성을 관리할 수 있는 중앙 집중식 장소 생성
- 저장소의 기본 구현은 git을 사용하므로 버전 관리 및 쉽게 접근 가능
공식 문서에 나와 있듯이, Config Server & Client가 존재하고 중앙 집중 저장소는 git을 통해 관리되는 원리입니다.
그림으로 Spring Cloud Config를 나타내보면 다음과 같습니다.
Spring Cloud Config 환경을 구성하게 되면,
설정 파일들을 하나의 서버에서 관리할 수 있고 설정 파일이 변경되어도 서버의 재빌드 & 재배포 없이 운영할 수 있습니다.
이제 이러한 Spring Cloud Config를 프로젝트에 적용해보겠습니다.
3. Spring Cloud Config 적용해보기
3-1. Spring Cloud Config Server 생성
설정 파일들을 관리할 중앙 서버인 Spring Cloud Config Server를 생성해봅시다.
3-1-1. Build.gradle 의존성 추가
ext {
set('springCloudVersion', "2023.0.0")
}
dependencies {
implementation 'org.springframework.cloud:spring-cloud-config-server'
implementation 'org.springframework.boot:spring-boot-starter-actuator'
}
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
}
}
유틸 기능을 사용하기 위한 spring-actuator 의존성과 함께 spring-cloud-config-server 의존성을 추가해줬습니다.
actuator는 설정 정보를 갱신할 때 사용됩니다.
3-1-2. Github Repository에 설정 파일 추가
설정 파일을 관리할 Public Github Repository를 생성하는 과정은 생략하고, 생성된 Github Repository에 설정 파일을 추가해봅시다.
이때 주의할 점은, 설정 파일 이름을 Spring Cloud Config Server의 요청 Endpoint에 맞춰서 생성해야 합니다.
- /{application }/{profile}[/{label}]
- {application}-{profile}.yml
- /{label}/{application}-{profile}.yml
- /{application}-{profile}.properties
- /{label}/{application}-{profile}.properties
저는 2번째 형식에 맞춰서 다음과 같이 형식을 설정했습니다.
3-1-3. Spring Cloud Config Server application.yml 설정 파일 추가
Spring Cloud Config Server의 application.yml 설정 파일을 다음과 같이 생성합니다.
server:
port: 8888
spring:
application:
name: config-server
cloud:
config:
server:
git:
uri: https://github.com/sh111-coder/sh-board-config
search-paths: config-file/**
default-label: main
management:
endpoints:
web:
exposure:
include: "*"
endpoint:
shutdown:
enabled: true
- spring.cloud.config.server.git.uri : 설정 파일이 위치한 Github Repository 주소(.git이 제외한 주소를 적어줍니다.)
- spring.cloud.config.server.git.default-label : 설정한 Github Repository에서 설정 파일이 위치하는 브랜치
- spring.cloud.config.server.git.search-path : Github Repository 안의 설정 파일들이 위치한 경로
- management 하위 : actuator 설정
만약 Github Repository 최상단에 파일들이 위치하지 않고 Repository 안에 설정 파일 경로가 존재한다면,
spring.cloud.config.server.git.search-path 옵션을 통해 경로를 지정하면 됩니다.
3-1-4. Spring Cloud Config Server활성화
@SpringBootApplication
@EnableConfigServer
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
}
실행 Application에 @EnableConfigServer 어노테이션을 선언하여 Spring Cloud Config Server를 활성화시켜 줍니다.
3-1-5. Spring Cloud Config Server 요청 테스트
Spring Cloud Config Server의 애플리케이션을 실행하고,
이전에 설정 파일의 이름을 만든 형식으로 요청을 보내봅시다.
- /{application }/{profile}[/{label}]
- {application}-{profile}.yml
- /{label}/{application}-{profile}.yml
- /{application}-{profile}.properties
- /{label}/{application}-{profile}.properties
저는 1, 2번째 형식을 선택해서 파일을 생성했으므로, 'http://localhost:8888/discovery-server/local' 형식으로 요청을 보냈습니다.
요청 결과로 해당 파일의 내용이 JSON으로 잘 출력되는 것을 확인할 수 있습니다.
3-2. Spring Cloud Config Client 설정
이제 위의 요청에 대한 응답(설정 파일 정보)을 받아서 설정 정보를 저장하는 Spring Cloud Config Client를 설정해봅시다.
3-2-1. Build.gradle에 Spring Cloud Config Client 의존성 추가
Spring Cloud Config Server에서 설정 파일을 받아서 사용하는 각 마이크로 서비스들의 Build.gradle에 의존성을 추가합니다.
저는 api-gateway, member-service, board-service, discovery-service에 의존성을 추가해줬습니다.
implementation 'org.springframework.cloud:spring-cloud-config-client'
3-2-2. application.yml 설정
Client의 application.yml 설정입니다.
Github Repository에 옮겼던 application.yml 설정들을 Client 단에서는 제거해줍니다.
이때 주의해야할 점은, server.port 설정과 service.application.name 부분은 client 단에서 설정해야 합니다.
왜냐하면 포트는 Config Server에서 응답을 받기 전에 결정이 되고,
Discovery-Server(Service Registry)에서 서비스들을 등록해야 하기 때문입니다.
server:
port: 8021
spring:
application:
name: board-service
profiles:
active: local
config:
import: optional:configserver:http://localhost:8888
- spring.application.name & spring.profiles.active : 설정 파일 이름대로 설정해줍니다.
- 위의 예시 : board-service-local.yml 시 적용
- spring.config.import : optional:configserver:{config-server의 URL}
- optional : optional prefix를 설정하지 않으면 config server에 연결할 수 없는 경우 애플리케이션이 종료됩니다.
이렇게 각 마이크로 서비스(Config Clinet)의 설정을 마무리합니다.
4. Spring Cloud Config 추가 설정하기
기본적은 Spring Cloud Config 설정은 끝났습니다.
이번에는 다음 추가설정을 진행해보겠습니다.
- 설정 파일이 위치한 Public Repository -> Private으로 변경 & SSL로 연결
4-1. Repository Private으로 변경 & SSL로 연결 설정
현재 설정 파일들을 불러오는 Repository가 Public으로 열려 있는 상태인데 이는 보안상 위험합니다.
따라서, Repository를 Prviate으로 변경하고 Private Repository에서 설정 파일을 가져오도록 하겠습니다.
(Private으로 변경 과정은 생략하도록 하겠습니다.)
순서는 다음과 같습니다.
- Config Server에서 공개키(비대칭키) 생성
- 공개키를 Github Repository의 Deploy keys에 저장
- Config Server의 application.yml에서 연결 정보 추가
4-1-1. Config Server에서 공개키 (비대칭키) 생성
현재 저는 따로 서버를 배포하지 않고 로컬에서 구현을 진행하기 때문에 로컬에서 공개키를 생성하도록 하겠습니다.
(Local은 mac 환경입니다.)
ssh-keygen -m PEM -t ecdsa -b 256
위의 명령어를 통해 공개키를 생성합니다.
명령어를 입력하면 저장되는 파일 위치가 나오고,
공개키의 암호인 passphrase를 설정하는 단계가 나오는데, 저는 더 나은 보안을 위해 설정해줬습니다.
설정했다면, 이후 application.yml에 추가해줘야하므로 기억해줍니다.
암호화 알고리즘으로는 'ecdsa'를 사용했습니다.
일반적으로 널리 사용하는 rsa 알고리즘을 사용하면 Github에서 보안 취약점 때문에 SHA-1 Error가 발생한다고 합니다.
cat {key 저장 경로}/id_ecdsa.pub
cat 명령어로 생성된 key를 확인할 수 있습니다.
이제 해당 key를 Github Repository의 deploy keys에 저장해줍시다.
4-1-2. 공개키를 Github Repository의 Deploy keys에 저장
위와 같이 설정 파일이 위치한 Github Repository의 Settings -> Deploy keys에서 Add deploy key로 저장할 수 있습니다.
4-1-3. Config Server의 application.yml에서 설정 정보 추가
spring:
cloud:
config:
server:
git:
uri: git@github.com:sh111-coder/sh-board-config.git
search-paths: config-file/**
default-label: main
ignoreLocalSshSettings: true
hostKey: hostKey # hostKey를 base64 인코딩한 값
hostKeyAlgorithm: # ecdsa-sha2-nistp256 // 비대칭키 생성 알고리즘
privateKey: |
-----BEGIN EC PRIVATE KEY-----
... 생략
-----END EC PRIVATE KEY-----
passphrase: passphrase # 비대칭키 생성 시 입력한 값
- spring.cloud.config.server.git.uri : git@github.com:{github nickname}/{private repo git 주소} 형식으로 작성합니다.
- spring.cloud.config.server.git.ignoreLocalSshSettings : true로 설정하면 로컬 SSH 설정을 무시하고 자체적으로 구성된 Git 설정 연결을 사용합니다. ssh로 private github repo을 연결하기 위해 true로 설정해줍니다.
- spring.cloud.config.server.git.hostKey : ssh 호스트 키를 지정해줍니다. (이후 설명)
- spring.cloud.config.server.git.hostKeyAlgorithm : 공개키를 생성할 때 사용한 알고리즘을 지정해줍니다.
- spring.cloud.config.server.git.privateKey : 생성한 공개키를 입력합니다.
- 주의할 점은 설정 파일에 privateKey를 입력 시 '|'를 처음에 입력해야 합니다.
- spring.cloud.config.server.git.passphrase : 공개키 생성 시 입력했던 passphrase를 입력합니다.
※ hostKey 조회
hostKey는 Config Server에서 ssh-keyscan으로 알 수 있습니다.
ssh-keyscan -t ecdsa github.com
출력 형식은 다음과 같습니다.
{host} {key type} {hostKey}
위와 같이 출력됩니다.
※ privateKey 조회
privateKey는 다음과 같이 조회할 수 있습니다.
cat {key 저장 경로}/id_ecdsa
이렇게 application.yml에 설정을 추가한 후에 실행해보면, Private Repository로 변경해도 설정이 잘 적용되는 것을 볼 수 있습니다.
※ Spring Cloud Config 설정 파일 내용 갱신하기
앞서 Spring Cloud Config를 사용해야하는 이유 중에 설정 파일 내용이 변경되었을 경우에 불편한 점이 있었습니다.
Spring Cloud Config는 이러한 상황을 다음과 같은 3가지 구현을 통해 해결할 수 있습니다.
- 변경한 설정을 갱신할 Spring Cloud Config Client(각 마이크로 서비스)들의 actuator API 호출하기
- spring-cloud-bus를 통해 변경을 감지하여 갱신 이벤트 전파하기
- Watcher를 통해 지속적으로 변경 여부를 질의하여 갱신하기
현재 구현 상황에서 가장 심플하게 구현할 수 있는 방법은 actuator API를 호출하는 1번 방법입니다.
사실 모든 application.yml에서 다음과 같은 설정으로 acutator API를 활성화했었습니다.
management:
endpoints:
web:
exposure:
include: "*"
따라서, {각 서비스 URL}/actuator/refresh를 보내면 간단하게 설정 갱신이 이루어집니다.
이 방법은 결국 수동으로 요청을 보내서 갱신해야하기 때문에 서버가 여러 대라면 상당히 불편할 것입니다.
그래서 2번, 3번 방법으로 갱신하는 것이 좋다고 생각하지만 추후에 시간이 된다면 구현해보겠습니다.
관련 구현 부분은 아래의 블로그에서 잘 설명해주고 있습니다.
https://mangkyu.tistory.com/254
🎯 Github Repository 링크 (전체 코드)
https://github.com/sh111-coder/sh-board-msa
📘 Monolithic to MSA 전체 목차
[MSA] 개인 프로젝트 Monolithic to MSA 전환기 - (1) MSA란?
[MSA] 개인 프로젝트 Monolithic to MSA 전환기 - (2) 멀티 모듈 구성하기
[MSA] 개인 프로젝트 Monolithic to MSA 전환기 - (3) Service Discovery 패턴 적용하기(feat. Spring Cloud Eureka)
[MSA] 개인 프로젝트 Monolithic to MSA 전환기 - (4) API Gateway 구현(feat. Spring Cloud Gateway)
[MSA] 개인 프로젝트 Monolithic to MSA 전환기 - (5) 서비스 간 통신하기(feat.Spring Cloud OpenFeign)
[MSA] 개인 프로젝트 Monolithic to MSA 전환기 - (6) 각 서비스의 설정 파일 관리하기(feat. Spring Cloud Config)
[MSA] 개인 프로젝트 Monolithic to MSA 전환기 - (7) 서비스 장애 대응 Circuit Breaker 구현(feat. Resilience4J)
[MSA] 개인 프로젝트 Monolithic to MSA 전환기 - (10) MSA 전환 후 비교 및 회고 + 마무리
Reference
https://mangkyu.tistory.com/253