Spring Cloud Config 란?
- Spring Boot로 만든 REST 기반의 어플리케이션 구성정보 관리 오픈소스
Spring Cloud Config 개발 배경
- 개발자는 어플리케이션 안의 상수(Constants)를 YAML, JSON, XML 등과 같은 파일로 구성정보를 분리한다.
- 하지만 구성정보가 변경되면 어플리케이션은 재배포되어야한다.
- 이러한 방식은 어플리케이션 수가 적은 상황에서는 문제가 없겠지만, 수십 개의 마이크로서비스가 실행되는 클라우드 기반의 어플리케이션에서는 통하지 않는다.
Spring Cloud Config 특징
- 아래 4가지 클라우드 환경 구성관리 시스템 원칙을 따른다.
- 분리(segregate): 실제 물리적인 서비스의 배포와 서비스 구성정보를 완전히 분리해야한다. 어플리케이션의 구성정보를 서비스 인스턴스와 함께 배포화면 안되고, 서비스에 환경변수로 전달하거나 중앙 저장소에서 읽어 와 구성정보를 전달해야한다.
- 추상화(abstract): 서비스 인터페이스 뒷단에 있는 구성 데이터의 접근 방식을 추상화해야한다. 즉, 서비스 저장소에 직접 액서스하는 코드를 작성하기보다 어플리케이션이 REST 기반의 JSON 서비스를 사용해 구성 데이터를 조회하게 만들어야한다.
- 중앙 집중화(centralize): 클라우드 기반의 어플리케이션에는 수백 개의 서비스가 존재할 수 있어, 구성정보를 보관하는 저장소 개수를 최소한으로 줄이는 것이 중요하다. 어플리케이션 구성정보를 가능한 소수 저장소에 집중한다.
- 견고성(harden): 어플리케이션 구성정보를 배포된 서비스와 완전히 분리하고 중앙 집중화하므로 어떤 솔루션을 사용하더도 고가용성과 다중성을 구현할 수 있어야한다.
- Server-Client 아키텍처를 가진다.
- Spring Cloud Config server는 파일시스템, Git, Redis, Posgresql 등과 같은 저장소 내의 구성 데이터를 읽어온 후, 구성정보에 접근할 수 있도록 REST API를 제공
- Spring Cloud Config client는 기동될 때, Spring Cloud Config server로부터 조회하고, 해당 구성정보를 이용하여 서비스 기동
- 저장소의 구성 데이터가 업데이트 되면, Spring Cloud Config Server에는 바로 반영되지만, Spring Cloud Config client에는 바로 반영되지 않는다. 이럴 때는 Spring Boot Actuator의 /actuator/refresh 엔드포인트를 호출하여, 어플리케이션의 구성정보를 다시 읽어오도록 할 수 있다.
- 대칭키, 비대칭키 암호화 방식을 이용한 구성정보 암호화 설정을 할 수 있다.
- Spring Cloud Config는 application, profile, label 정보를 이용하여 특정 구성정보를 load한다.
Spring Cloud Config 구현
사전준비
- Postgres에 구성정보 테이블 생성 및 구성정보 저장
CREATE TABLE spring_cloud.tbl_config_property (
id serial primary key,
created_at timestamp,
application character varying(50) COLLATE pg_catalog."default",
profile character varying(50) COLLATE pg_catalog."default",
label character varying(50) COLLATE pg_catalog."default",
key character varying(50) COLLATE pg_catalog."default",
value character varying(500) COLLATE pg_catalog."default"
);
INSERT INTO spring_cloud.tbl_config_property(created_at, application, profile, label, key, value) VALUES(NOW(), 'spring-cloud-config-client', 'dev', '0.0.1', 'message', 'hello spring');
INSERT INTO spring_cloud.tbl_config_property(created_at, application, profile, label, key, value) VALUES(NOW(), 'spring-cloud-config-client', 'dev', 'latest', 'message', 'hello cloud');
INSERT INTO spring_cloud.tbl_config_property(created_at, application, profile, label, key, value) VALUES(NOW(), 'spring-cloud-config-client', 'prd', 'latest', 'message', 'hello config');
Spring Cloud Config server 구현
ext {
set('springCloudVersion', "Hoxton.SR4")
}
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
}
}
dependencies {
// spring cloud config server 의존성
implementation 'org.springframework.cloud:spring-cloud-config-server'
// postgresql 의존성
implementation 'org.springframework.boot:spring-boot-starter-data-jdbc'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
compile 'org.postgresql:postgresql'
}
- application.yml 설정 (resources 폴더 아래에 위치)
server:
port: 8081
spring:
application:
name: "spring-cloud-config-server"
profiles:
active: jdbc
cloud:
config:
server:
jdbc:
sql: SELECT key, value FROM spring_cloud.tbl_config_property WHERE APPLICATION=? and PROFILE=? and LABEL=?
order: 0
jpa:
database: POSTGRESQL
show-sql: true
datasource:
hikari:
connection-timeout: 5000
maximum-pool-size: 10
platform: postgres
url: jdbc:postgresql://{postgresql ip주소}:5432/postgres
username: postgres
password: root123!
driver-class-name: org.postgresql.Driver
validationQuery: "SELECT 1"
@SpringBootApplication
@EnableConfigServer //spring cloud config server 서비스를 위한 어노테이션
public class SpringCloudConfigApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCloudConfigApplication.class, args);
}
}
- 실행 후 구성정보 조회 api 호출
- db 내용을 변경한 후에, api 재호출하면 변경된 구성정보가 나타난다.
Spring Cloud Config Client 구현
ext {
set('springCloudVersion', "Hoxton.SR4")
}
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
}
}
dependencies {
// spring cloud config 의존성
implementation('org.springframework.cloud:spring-cloud-starter-config')
// spring actuator 의존성
implementation('org.springframework.boot:spring-boot-starter-actuator')
}
- bootstrap.yml 설정 (resources 폴더 아래에 위치)
- bootstrap.yml은 application.yml보다 먼저 참조되기 때문에 여기에다가 환경 설정 내용을 작성하는 것이다.
server:
port: 8082
spring:
application:
name: spring-cloud-config-client
cloud:
config:
profile: dev # 서비스가 실행될 profile
label: 0.0.1 # 서비스가 실행될 label
uri: http://localhost:8081 # spring cloud config server 위치
- application.yml 설정 (resources 폴더 아래에 위치)
# /actuator/refresh를 위해 추가
management:
endpoints:
web:
exposure:
include: "refresh"
@RefreshScope // refresh로 데이터 로드하기 위한 어노테이션
@Component
@Getter
@Setter
public class MessageProperty {
@Value("${message}")
private String message;
}
@RestController
public class MessageController {
private final MessageProperty serviceConfig;
public MessageController(MessageProperty serviceConfig) {
this.serviceConfig = serviceConfig;
}
@GetMapping("/message")
String message() {
return serviceConfig.getMessage();
}
}
구성정보 암호화
Spring Cloud Config 사용시 고려사항
- @RefreshScope를 통해서 동적으로 로드 되는 구성정보는 사용자가 정의한 구성정보만 해당된다. 즉, Spring Data를 통한 데이터베이스 설정 등은 /actuator/refresh, @RefreshScope를 이용하여 동적 로드 할 수 없다.
- Spring Cloud Config를 사용하려면, 구성정보를 제공하는 서버가 실행되어야하므로 추가적인 서버 resource or 단독 서버/VM 필요
- Spring Cloud Config server가 monolithic한 장애지점이 될 수 있다.
- DB에 저장되어 있는 구성정보가 변경되면, 클라이언트의 /actuator/refresh api를 1회 호출해야 구성정보가 업데이트 된다. 클라이언트가 많다면, 클라이언트 수만큼 api를 호출해야하는 문제가 있다.
- 유레카와 같은 서비스 디스커버리 기법을 이용하여 해당 문제를 해결한다고 한다.
별첨
여러 구성정보 관리 오픈소스
### 참고자료
- 스프링 마이크로서비스 코딩 공작소: https://books.google.co.kr/books?id=VACDDwAAQBAJ&printsec=frontcover&hl=ko#v=onepage&q&f=false
- 스프링 - Spring Cloud Config 예제: https://yaboong.github.io/spring-cloud/2018/11/25/spring-cloud-config/