들어가기 전에
하기 포스팅은 "스프링부트 시작하기(김인우 저)" 책을 공부하며 적은 포스팅입니다. 이번 포스팅에서는 Cloud Config에 대해 알아보돋록 하겠습니다.
스프링 Cloud Config 사용하기
앞서 application.profiles의 설정 파일을 분리하고 프로파일 기능을 사용해 각 환경에 적합한 설정 파일을 손쉽게 적용했습니다. 하지만, 프로파일 기능에는 문제점이 남아있습니다.
설정이 변경될 경우, 프로젝트의 모든 개발자들이 변경된 설정을 적용해야 합니다. Git과 같은 소스 관리 시스템에서 변경된 설정 파일을 받기만 하면 된다고 하더라도, 모든 개발자가 일일이 변경사항을 받는 것은 불편한 일입니다. 또한, 설정이 변경되면 다시 빌드, 배포를 해야 합니다. 즉, 어플리케이션을 재시작이 필요한 것입니다. 테스트 환경에서야 여러번 재시작해도 상관없지만, 실제 운영 환경에서는 꽤 부담스러운 작업입니다. 서버가 이중화 등으로 무중단 서비스가 되고 있는 환경이더라도, 트래픽을 감당하기 위해 여러 서버로 구성한 경우가 많아 해당 서버들에 모두 배포하는 작업이 필요합니다.
이러한 문제점 때문에 Twelve-factor app이라는 방법론에서는 설정을 코드에서 분리하라고 합니다. 이렇게 설정을 코드에서 분리해 관리하는 것으로 스프링 Cloud Config가 있습니다. 스프링 Cloud Config는 설정을 분리해서 설정 정보만 가지는 서버와 이 설정 정보 서버에서 어플리케이션에 필요한 정보를 받아오는 클라이언트로 구성이 됩니다. 따라서, 클라이언트에서는 설정 정보를 가지고 있지 않아 설정의 변경 사항을 모든 개발자 및 서버에 쉽게 반영할 수 있습니다.
스프링 Cloud Config란?
- 스프링 Cloud Config는 Twelve factor 방법론에서 이야기한 코드와 설정의 분리를 위해 설정 파일을 깃 저장소에 저장합니다.
- 설정 서버(Config Server)는 깃 저장소를 보고 있습니다. 즉, 설정 서버는 깃 저장소에 저장된 설정 파일을 확인 및 배포하는 역할을 합니다.
- 마지막으로 각각의 어플리케이션은 설정 서버로부터 자신들에게 필요한 설정 정보를 가져옵니다.
- 깃 관리자에 의해 깃 저장소의 설정 정보가 수정(Push config)되면, 설정 서버는 이를 감지하고 수정된 설정 정보를 받아와 갱신합니다. 따라서, 설정 정보는 중앙에서 관리할 수 있으며 설정이 변경되더라도 사용하는 서버의 숫자에 관계없이 즉각적으로 설정 정보를 변경할 수 있습니다.
YAML 사용하기
스프링 Cloud Config를 사용하기 전에, application.properties를 YAML 형식으로 변경해보도록 하겠습니다. YAML은 사람이 쉽게 읽을 수 있는 데이터 표현 형식입니다.
YAML 플러그인 설치하기
먼저 이클립스에서 YAML 파일을 편집하기 쉽도록 플러그인을 설치해보도록 하겠습니다. 이클립스의 마켓플레이스에서 yml을 검색하여 YEdit을 설치합니다.
YAML로 변경하기
src/main/resources 폴더 밑에 application.yml 파일을 생성하고 아래와 같이 작성합니다.
spring:
datasource:
hikari:
connection-test-query: SELECT 1
allow-pool-suspension: true
jpa:
database: mysql
database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
generate-ddl: true
hibernate:
use-new-id-generator-mappings: false
mybatis:
configuration:
map-underscore-to-camel-case: true
---
spring:
profiles: dev
datasource:
hikari:
driver-class-name: net.sf.log4jdbc.sql.jdbcapi.DriverSpy
jdbc-url: jdbc:log4jdbc:mysql://localhost:3306/example?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC&allowPublicKeyRetrieval=true&useSSL=false
username: 아이디
password: 비밀번호
thymeleaf:
cache: false
web:
resources:
cache:
period: 0
---
spring:
profiles: prod
datasource:
hikari:
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://localhost:3306/example?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC&allowPublicKeyRetrieval=true&useSSL=false
username: 아이디
password: 비밀번호
- bootstrap.yml의 처음부분(---전)은 application.properties에 작성했던 내용입니다.
- ---를 사용해서 하나의 파일 내 여러 개의 구성을 만들 수 있습니다.
application.properties를 삭제했기 때문에, 이와 관련된 코드도 변경해야 합니다. board 어플리케이션에서 직접적으로 application.properties를 사용하던 곳은 DatabaseConfiguration 클래스입니다. DatabaseCofiguration 클래스에서 PropertySource("classpath:/application.properties")를 삭제하면 YAML으로 변경이 완료됩니다.
로그 설정 파일 분리하기
앞에서 logback-spring.xml 파일 내 <springProfile> 속성을 사용해 프로파일에 맞는 로그가 출력되도록 설정했습니다. 로그 설정의 경우, 각 프로파일에 맞게 파일을 분리하는 게 좀 더 편하다고 생각하여 YAML 파일과 다르게 분리해보도록 하겠습니다.
먼저, logback-spring.xml을 복사하여 logback-prod.xml을 만들고, logback-spring.xml의 파일이름을 logback-dev.xml로 변경합니다. 변경이 완료되면 아래와 같이 코드를 작성합니다.
// logback-dev.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true">
<!-- Appenders -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<Pattern>%d %5p [%c] %m%n</Pattern>
</encoder>
</appender>
<appender name="console-infolog" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<Pattern>%d %5p %m%n</Pattern>
</encoder>
</appender>
<logger name="board" level="DEBUG" append-ref="console"/>
<logger name="jdbc.sqlonly" level="INFO" appender-ref="console-infolog"/>
<logger name="jdbc.resultsettable" level="INFO" appender-ref="console-infolog"/>
<!-- 루트 로거 -->
<root level="error">
<appender-ref ref="console"/>
</root>
</configuration>
// logback-prod.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true">
<!-- Appenders -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<Pattern>%d %5p [%c] %m%n</Pattern>
</encoder>
</appender>
<appender name="console-infolog" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<Pattern>%d %5p %m%n</Pattern>
</encoder>
</appender>
<logger name="board" level="ERROR" append-ref="console"/>
<!-- 루트 로거 -->
<root level="error">
<appender-ref ref="console"/>
</root>
</configuration>
마지막으로 application.yml에 아래와 같은 코드를 추가합니다. 로거 설정은 공통이기 때문에 공통 설정 영역에 추가하면 됩니다.
...
logging:
config: classpath:logback-${spring.profiles.active}.xml
...
active profile 지정하기
dev로 프로파일을 적용할지, prod로 프로파일을 적용할지 정하기 위해 프로젝트를 우클릭하여 Run As > Run Configurations...을 선택합니다.
프로파일을 지정한 후 Run 버튼을 클릭해 springboot 어플리케이션을 실행하면 아래와 같이 프로파일이 적용되어 실행됩니다.
비트버킷(BitBucket) 설정하기
스프링 Clound Config를 사용하기 위해 가장 먼저 할 일은 Git 저장소에 설정 파일을 올리는 것입니다. 이를 위해서는 git 저장소를 먼저 준비해야 합니다. 많이 사용되는 Git 저장소로는 Github나 비트버킷이 있습니다.
비트버킷을 사용하기 위해서는 하기 사이트에 접속하여 계정을 생성해야 합니다.
아래 내용은 계정이 있다는 가정하에 진행해보도록 하겠습니다. 먼저, 비트버킷에서 새로운 저장소를 생성합니다. 저장소의 이름은 cloud-config로 설정하겠습니다.
생성한 저장소에 기존에 이클립스에서 만든 설정 파일을 만들어보겠습니다. Add file을 클릭하여 application.yml로 파일명을 정하고 이클립스의 application.yml을 그대로 복사해서 붙여넣습니다.
스프링 Cloud Config Server 생성하기
bitbucket에서 만든 설정 파일을 불러와 배포하는 역할을 하는 Config Server를 생성해보도록 하겠습니다.
Config Server 프로젝트 생성
먼저 새로운 스프링부트 프로젝트를 생성합니다. 아래 캡쳐들처럼 프로젝트를 생성하면 됩니다.
Cloud Server 설정
먼저 CloundConfigApplication 클래스에 @EnableConfigServer 어노테이션을 추가합니다.
package cloudconfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;
@SpringBootApplication
@EnableConfigServer
public class CloudConfigApplication {
public static void main(String[] args) {
SpringApplication.run(CloudConfigApplication.class, args);
}
}
- @EnableConfigServer 어노테이션을 이용하여 Config Server 역할을 수행하게 됩니다.
그 후 application.properties를 application.yml으로 변경하고 아래와 같이 작성합니다.
server:
port: 8888
spring:
cloud:
config:
server:
git:
uri: https://깃 저장소 주소
username: bitbucket 아이디
password: bitbucket 비밀번호
- 실제 프로젝트에서는 아이디, 비밀번호를 사용하기 않고 SSH의 키를 이용합니다. 여기서는 간단히 사용해보기 위해 아이디와 비밀번호를 사용했습니다.
이때 Git 저장소 주소는 아래와 같이 확인할 수 있습니다.
Config Server 확인하기
Config Server를 실행하고 설정 정보를 조회해보도록 하겠습니다. 먼저, cloud-config 프로젝트를 실행한 후, localhost:8888/config-server/dev를 호출해보겠습니다.
만약, 위와 같이 정렬되지 않은 JSON으로 보인다면 chrome에 JSON Formatter를 설치하여 정렬된 JSON 데이터로 확인할 수 있습니다.
1. 먼저 chrome 웹 스토어에 접속합니다.
2. chrome 웹 스토어에서 json을 검색하여 JSON Formatter를 찾고, Chrome에 추가합니다.
위와 같은 과정을 거쳐 JSON Formatter를 추가하면, 아래와 같이 정렬된 데이터를 확인할 수 있습니다.
Config Client 적용하기
이번에는 board 어플리케이션에서 Config Server를 사용하여 config를 구성하도록 Config Client를 적용해보겠습니다. Config Client 역시, Config Server를 구성했던것처럼, 간단하게 설정할 수 있습니다.
build.gradle 수정
먼저 build.gradle에 아래와 같이 의존성을 추가해야 합니다. build.gradle에는 의존성 추가 뿐 아니라 스프링 cloud 버전을 설정해주어야 합니다.
...
ext {
springCloudVersion = '2020.0.0'
}
dependencies {
...
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'org.springframework.cloud:spring-cloud-starter-config'
...
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
}
}
- Actuator는 스프링부트의 하위 프로젝트 중 하나로 HTTP를 이용해 어플리케이션 관리 및 모니터링에 도움이 되는 기능을 제공합니다.
- Clound Config Client 역할을 할 수 있도록, spring-cloud-starter-config 의존성을 추가합니다.
- dependencyManagement에 적은 부분을 바탕으로 board 어플리케이션을 생성할 때, Cloud Config Client 의존성을 선택했다면 자동으로 생성되는 부분입니다. board 프로젝트를 생성할 때에는 cloud config client 의존성을 선택하지 않았기 때문에, 직접 작성해주어야 합니다.
이때, springCloudVersion 변수에 들어갈 값은 springboot 버전에 의해 결정됩니다. 따라서, plugins에 적힌 springboot의 버전을 보고 아래 사진을 통해 호환 가능한 spring-cloud 버전을 확인해야 합니다.
필자의 경우는, springboot 버전이 2.4.1이어서 2020.0.0 버전을 선택했습니다. 필자와 동일한 버전의 springboot을 사용한다면 하기 사이트를 참고하면 좋을 것 같습니다.
spring.io/blog/2020/12/22/spring-cloud-2020-0-0-aka-ilford-is-available
application.yml 변경하기
Config Server를 참조할 수 있도록 아래와 같이 application.yml을 변경합니다. 이때, application.yml을 bootstrap.yml으로 변경해야 서버에서 config을 가져와서 적용할 수 있습니다.
- springboot 버전이 2.4.x 이상이라면, bootstrap.yml으로 설정하면 안됩니다(해당 내용은 아래에서 설명할 예정).
spring:
cloud:
config:
uri: http://localhost:8888
name: cloud-config
결과 확인하기
cloud-config 어플리케이션을 실행시킨 후, board 어플리케이션이 정상적으로 실행되는지 확인합니다.
- Config Server인 cloud-config 어플리케이션은 Git 저장소에서 설정 파일을 읽고 Config Client인 board 어플리케이션은 Config Server가 배포한 설정 정보를 이용해 실행이 되어야 합니다.
필자의 경우는, cloud-config만 테스트했을 때는 이상이 없었으나 board 어플리케이션을 실행하면 아래와 같은 에러가 발생함을 확인할 수 있었습니다. 해당 에러는 config를 제대로 받아오지 못해서 일어난 것입니다. 에러 로그들만 읽으면 마치 다른 부분에서 이슈가 생겼나 생각할 수도 있지만, HikariPool-1 - dataSource or dataSourceClassName or jdbcUrl is required. 로그를 보면 설정이 제대로 들어가지 않았음을 추측할 수 있습니다.
해당 에러를 마주친 원인은, springboot 버전 때문이었습니다. 필자가 사용하는 springboot은 2.4.1 버전이었는데, springboot 2.4부터는 configuration data를 import하는 방식이 기존과 달라졌습니다. 기존에는 application.yml 대신, bootstrap.yml을 통해 config를 import했다면, springboot 2.4부터는 bootstrap이 legacy가 되어버린 것입니다.
- 물론, bootstrap으로 할 수 있는 방법 역시 존재하지만, 앞으로 springboot 버전은 점점 더 나아갈텐데 legacy를 적용해야할 필요성을 느끼지 못해 사용하지 않았습니다.
아래 사진에서와 같이 bootstrap.yml 대신, application.yml에 설정을 넣었습니다.
미완(아래 문제점 해결 필요)
springboot 2.4.1 버전과 spring cloud config 2020.0.0을 이용해서 최대한 제대로 spring cloud config를 사용하려 노력하였으나, 계속해서 오류가 발생하여 여기까지 진행한 후 좀 더 실력을 키워서 확인하려 한다.
작업을 하면서 직면했던 문제들은 아래와 같다.
- bitbucket의 설정값 중 profiles에 관련된 부분이 적용되지 않음(default부분만 적용됨)
- 해당 이슈를 해결하고자, bootstrap.yml을 사용할 수 있는 옵션도 추가하는 등 작업을 했지만, 화면에서 boardIdx값이 0으로 찍히는 이슈 발생(boardIdx 뿐 아니라, 조회수 역시 0으로 찍힘)
- cloud config를 통해 설정값을 가져올 경우, log4jdbc 설정이 제대로 먹지 않는지 콘솔에 쿼리로그가 찍히지 않음
최신 버전을 사용하면 좋은 점도 많겠지만, 아직 springboot에 많이 익숙하지 않은 상태라 오히려 독이 되었던 것 같다. 지금 이 글을 쓰는 시점에는 springboot 2.4.2가 나왔으니 필자가 겪은 문제가 버그라면.. 해결이 되었을지도 모른다. 하지만, 일단 기본기부터 짚고 넘어가야할 것 같아 이 부분은 여기서 홀딩한다.
'FRAMEWORK > Spring' 카테고리의 다른 글
[SpringBoot] springboot에서 테스트 코드 사용하기 (0) | 2021.04.20 |
---|---|
[SpringBoot, PostgreSQL] myBatis ?(물음표) 문자 사용하기 (0) | 2021.02.02 |
[SpringBoot] 게시판 구현하기 20 (스프링 프로파일 적용하기) (0) | 2021.01.11 |
[SpringBoot] 게시판 구현하기 19 (Swagger) (0) | 2021.01.11 |
[SpringBoot] 게시판 구현하기 18 (GCP 이용해 서버 구성하기) (0) | 2021.01.10 |