Spring Cloud Config란?
각 Application의 설정 파일(property, yum...)을 저장하고 Config Server를 통해 전달해주는 구조
분산 시스템에서 설정 파일을 외부로 분리할 수 있도록 해준다.
그림 처럼 설정 파일의 저장소는 Git이 될수도 있고 서버상의 저장할수도 있다.
실행 중인 Application이 Config Server에서 설정 정보를 받아와 갱신하는 방식이다.
즉 실행 중에 설정값 변경이 필요해지면, 설정 서버만 변경하고 애플리케이션은 갱신하도록 해주기만 하면 된다.
따라서 설정이 바뀔 때마다 빌드와 배포가 필요 없는 구조이다.
구현 예제
Config Server를 구축하고 Client Server에서 설정값을 받아와 DB연결까지 구현해 보았다.
이번 예제에서는 GitHub에 설정 파일을 저장하고 Config Server에서 불러오도록 구현 할 것이다.
1. GitHub에 설정 파일 저장
{application-name}-{profiles}.yml 형식의 파일명으로 파일을 생성한다.
client-local.yml
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/local?serverTimezone=Asia/Seoul&useSSL=false&characterEncoding=utf8&allowPublicKeyRetrieval=true
username: root
password: password
...
client-dev.yml
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/dev?serverTimezone=Asia/Seoul&useSSL=false&characterEncoding=utf8&allowPublicKeyRetrieval=true
username: root
password: password
...
local 환경에서는 local DB로 접속, dev 환경에서는 dev DB로 접속하도록 하겠다.
2. Config Server
프로젝트 생성 시 IntelliJ에서는 Spring Cloud Config를 추가할 수 있다.
Client Server를 생성하는 것이기 때문에 Config Client만 체크해 준다.
build.gradle
plugins {
id 'org.springframework.boot' version '2.5.5'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
repositories {
mavenCentral()
}
ext {
set('springCloudVersion', "2020.0.4")
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
// Spring Cloud Config Server
implementation 'org.springframework.cloud:spring-cloud-config-server'
}
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
}
}
test {
useJUnitPlatform()
}
application.yml
git.uri에는 위에서 설정 파일을 저장한 Repository를 기입한다.
default-label은 기본 branch를 뜻한다.
native.search-locations를 이용하면 서버에 설정 정보를 저장할 수 있다.
포트번호는 8081로 설정하겠다.
server:
port: 8081
spring:
cloud:
config:
server:
git:
uri: https://github.com/devbeekei/spring-cloud-config
default-label: main
# native:
# search-locations: file://${user.home}/외부-설정-파일-경로
예제에서는 public으로 설정했기 때문에 git uri만 적어주면 된다.
해당 Repository가 private라면 rsa private key나 계정으로 사용권한 인증을 해야한다.
인증에 관한 설정은 아래 블로그를 참고하면 되겠다.
SpringBootApplication
@EnableConfigServer 어노테이션을 추가해준다.
@EnableConfigServer는 Config Server로 사용하겠단 뜻이다.
@EnableConfigServer
@SpringBootApplication
public class SpringCloudConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCloudConfigServerApplication.class, args);
}
}
이렇게 간편하게 Config Server에 설정이 끝났다.
3. Client Server
Config Server와 마찬가지로 프로젝트 생성 시 Spring Cloud Config의 Config Client를 체크한다.
build.gradle
plugins {
id 'org.springframework.boot' version '2.5.5'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
repositories {
mavenCentral()
}
ext {
set('springCloudVersion', "2020.0.4")
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
// Spring Cloud Config Client
implementation 'org.springframework.cloud:spring-cloud-starter-config'
// actuator
implementation 'org.springframework.boot:spring-boot-starter-actuator'
// JDBC
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
// MySQL
runtimeOnly 'mysql:mysql-connector-java'
}
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
}
}
test {
useJUnitPlatform()
}
actuator은 Spring Boot에서 제공해주는 어플리케이션을 모니터링하고 관리하는 기능을 지원해준다.
Spring Cloud Config에서는 업데이트 된 설정 정보를 갱신해주는 역활을 한다.
application.yml
Spring Boot 2.4 버전 이후에는 Spring Cloud Config Client에 관한 설정이 변경되었다.
bootstrap.yml 파일은 Application이 구동될 때 application.yml보다 먼저 로드되어, Config Server에서 정의된 설정값들을 읽기 위해 사용했었다.
하지만 2.4 버전 이후에는 bootstrap.yml을 사용하지 않고 application.yml에 Config Client를 설정해준다.
server:
port: 8082
spring:
config:
import: "optional:configserver:http://localhost:8081/"
cloud:
config:
name: client
profile: local
management:
endpoints:
web:
exposure:
include: refresh
이렇게 설정하게 되면 Config Server에서 설정된 GitHub에 client-local.yml을 읽어오게된다.
management는 actuator endpoints를설정한다.(설정 정보 갱신을 위한)
이제 Client Server를 구동해보겠다.
설정 파일을 정상적으로 받아와 Database에 연결된 것을 확인할 수 있다.
profile을 dev로 변경해서 구동하면 마찬가지로 dev Database에 연결된다.
만약 설정 파일에 변경이 있다면?
만약 GitHub에 저장된 client-local에 업데이트가 되었다면
Client Server에서 얻는 설정 정보는 업데이트가 되지 않은 정보를 얻어오게 된다.
Test API를 하나 만들어서 확인해 보겠다.
@RestController
public class ConfigTestController {
@GetMapping("/config")
public String string(@Value("${spring.datasource.url}") String dbUrl) {
return dbUrl;
}
}
http://localhost:8082/config 접속 시 아래와 같은 문구가 출력된다.
client-local.yml에 기입했던 정보와 일치한다.
client-local.yml을 수정하고 다시 http://localhost:8082/config에 접속해보면
변경되기 전에 설정 정보가 출력된다.
이처럼 설정 파일은 변경되었지만 얻어오는 설정 정보는 달라지지 않은것은 확인 할 수 있다.
이럴땐 위에서 설정했던 actuator endpoints에 POST로 요청 후 다시 http://localhost:8082/config에 접속해보면
$ curl -X POST "http://localhost:8082/actuator/refresh"
변경된 내용이 정상적으로 출력된다.
GitHub Repository에 설정 파일이 업데이트 될때마다 각 서버에 갱신 요청이 귀찮다면
웹 훅이나 GitHub Action을 사용해 자동으로 Client Server에 설정 정보를 갱신하도록 구축해도 좋을것 같다.
설정값 암호화 하기
Config Server의 설정값을 대칭키로 암호화 할 수 있다.
Config Server application.yml 파일에 암호화 할 대칭키를 추가한다.
- spring.cloud.config.server.encrypt.enable : false로 설정하지 않으면 http://localhost:8081/client/local로 접속 시 암호화 되지 않은 정보가 노출되게 된다.
- encrypt.key : 암호화 대칭키
server:
port: 8081
spring:
cloud:
config:
server:
git:
uri: https://github.com/devbeekei/spring-cloud-config
default-label: main
encrypt:
enabled: false
encrypt:
key: devbeekei-encrypt-key
Config Server를 재시작 하고 /encrypt 엔드포인트에 POST로 암호화 할 정보를 호출하면 암호화 된 문자열을 받을 수 있다.
이것을 복사 하여 설정파일에 저장한다.암호화 된 문자열 앞에 {cipher}를 추가해 암호화된 문자라는것을 알려줘야 한다.
Client Server에도 동일하게 대칭키를 설정해주고 재시작하게 되면 정상적으로 Database가 연결된다.
GitHub에서 전체 코드를 확인하실 수 있습니다.
'Spring' 카테고리의 다른 글
Spring Boot Validation 어노테이션 정리 (0) | 2021.11.10 |
---|---|
Spring Security + OAuth2.0 소셜 인증 예제(Google, Naver, Kakao) (6) | 2021.10.21 |
Spring Boot + log4j2 (0) | 2021.10.07 |
Spring Boot + Flyway를 이용한 데이터베이스 마이그레이션 (0) | 2021.09.16 |
Querydsl 설명 및 예제 (0) | 2021.09.15 |