
프로젝트 개발 후 각 서비스를 docker 이미지로 빌드한 뒤 dockerhub에 push 해보려고 하는데, 생각해보니 관련 설정 파일들도 이제 git에 함께 관리해줘야 했습니다.
하지만, 내부 민감 정보들이 있어 해당 정보들을 우선 암호화 방식으로 처리할까 했었지만, 서비스들 중 공통으로 설정되는 정보들을 한 곳에서 관리해주는 것이 좋으며, 이러한 정보들은 배포 이후에 서버를 내릴 필요 없이 git에서 관리가 가능하기 위해서 Config Server 도입을 결정했습니다.
이를 구현하기 위해서는 Spring Cloud에서 제공하는 Spring Cloud Config에 대해서 알아봐야 합니다.
Spring Cloud Config는 분산 시스템에서 외부화된 설정 정보를 서버 및 클라이언트에게 제공하는 시스템입니다. 즉, 외부에서 모든 서비스의 환경에 대한 공통된 정보들을 중앙에서 관리해주는 서버입니다.

서비스가 실행중일 때에는 메모리 상에 설정 정보들이 로드되어 있기 때문에 문제 없지만, 서비스 시작 시점에 Git 서버 또는 Config Server에 장애가 있다면 다른 서비스에 전파될 수 있습니다. 또한, Config Server에 의해서 장애 단일 지점이 될 수 있으므로 Config Server를 위한 별도의 운영이 추가로 필요하게 될 수 있습니다.
설정 정보도 적용되는 우선순위가 있습니다. 우선순위에 알맞게 작성해줘야 올바른 설정이 Microservice에 적용됩니다.
application.yml 파일의 네이밍에 따라서 우선순위 설정이 가능합니다.
application.yml > 애플리케이션명.yml > 애플리케이션명-profile.yml
위처럼 우선순위가 선정되며, 특정 profile 설정 또는 애플리케이션명 설정으로 제한하고 싶은경우, config server 에서 설정해주면 해당 프로필에 맞는 설정이 적용됩니다.
ex) 애플리케이션명.yml -> user.yml(회원 서버 관련 설정)
Spring Initializr를 사용하여 Config Server 프로젝트를 생성합니다.
implementation 'org.springframework.cloud:spring-cloud-config-server'
Spring Cloud Config Server 의존성을 추가해줍니다.
server:
port: 8888
spring:
application:
name: config-server
cloud:
config:
server:
git:
uri: file:///C:\project\bootakhae-config-repo
config 서버관련 설정 정보를 입력해줍니다.
@SpringBootApplication
@EnableConfigServer
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
}
@EnableConfigServer 어노테이션을 정의하여 Config Server를 활성화 해줍니다.
웹 브라우저에 다음과 같이 URL을 검색하여 설정 정보를 검색합니다.
http://localhost:8888/bootakhae/default
이는 application명칭/profile 형식으로 설정 정보 조회가 가능합니다.

위와 같이 configuration 메타 데이터 조회가 가능합니다.
이제 위 설정을 하나의 마이크로 서비스에 적용해보도록 하겠습니다. 우선, jwt 관련 설정이므로 User-Service에 Config Client를 설정해보도록 하겠습니다.
implementation 'org.springframework.cloud:spring-cloud-starter-config'
implementation 'org.springframework.cloud:spring-cloud-starter-bootstrap'
bootstrap 관련 의존성을 추가한 이유는 Config-Server에서 관리하는 설정 정보가 해당 서버의 application.yml 파일을 읽기 전에 로딩돼야 합니다. 따라서 Config Client 서버 실행과 동시에 bootstrap을 사용하여 설정 정보를 로딩해주기 위해서 사용합니다.
spring:
cloud:
config:
uri: http://localhost:8888
name: bootakhae
Config Client 서버 실행과 동시에 해당 정보를 통해서 설정 정보를 불러와서 로딩해줍니다.
그 다음으로 기존에 Config Client에 추가되어 있었던 설정 정보를 지워준 다음 서버를 실행해보도록 하겠습니다.

위처럼 콘솔을 통해서 Config 서버의 설정 정보를 가져온 것을 알 수 있습니다.
다음과 같이 메서드를 정의해줍니다.
/**
* 헬스 체크
*/
@GetMapping("health-check")
public ResponseEntity<String> healthCheck(){
String body = String.format("port(local.server.port) :" + env.getProperty("local.server.port")
+", port(server.port) :" + env.getProperty("server.port")
+", with token secret :" + env.getProperty("token.secret")
+", with token access time : " + env.getProperty("token.access-expired-time")
+", with token refresh time : " + env.getProperty("token.refresh-expired-time"));
return ResponseEntity.ok(body);
}
postman을 사용하여 테스트한 결과 다음과 같은 결과가 출력된 것을 알 수 있습니다.

Config Server를 도입하는 이유 중 하나가 배포 중 설정 정보를 변경하기 위함입니다. 하지만, 현재 구현한 부분까지는 서버를 재시작해야 적용이 됩니다.
이를 해결하기 위해서 Actuator를 활용한 api 통신을 통해 설정 정보를 업데이트 해줄 수 있습니다.
Actuator 사용을 위해서 의존성을 추가해줍니다.
implementation 'org.springframework.boot:spring-boot-starter-actuator'
그 다음으로 application.yml 파일에 actuator를 통해 사용할 엔드포인트들을 추가해줍니다.
management:
endpoints:
web:
exposure:
include: refresh
그 다음으로 기존에 설정했던 값들을 수정해준 뒤 git 커밋을 해줍니다.

이 때, 서버를 재실행 하지 않는 이상은 postman으로 실제 값을 조회하더라도 변경되지 않은 것을 확인할 수 있습니다.

이 때, user-service 서버에서 actuator의 refresh를 호출하게 되면 값이 변경됩니다.

Config Server에서 관리하는 설정 중 변경된 항목들만 반환해줍니다.

값이 정상적으로 변경되는 것을 확인할 수 있습니다.
결과적으로, actuator의 refresh를 사용하면 서버를 재실행 하지 않아도 설정 수정을 반영할 수 있게 됩니다.
token:
secret: BQBIp8JxTfqZhpmySSvz29ZE/8gODZCu4PBhOkyFKiIAq8hGZ8f8a9vllqqUJl2362noOfcjE2eNs//SuoSBtP8peNCKxZGsZ3QUpmMKw7fJARiFUN67EcZ02O+XhDdlpOROevsYjfGvfSHeQ4PqaADvYquFvIeUvGwBC3/vQzVZYL8R+JdOxpq3W/mMQ7j6rIK70MkUj7gt90TiJ18eJ45U+J+T4VWtC0+kHEIEFSRs8wTpPicAl5CLlZmJ+GI7R9ZERDsAKwyQ/XacfrHAp3QdLyvkVQ7qp4MrP8X6fwpOZFleerNdRx0oXATpqtN5eYGfG8BVVpgSQoq7ZMP85pm1NQNDlOLjmYdHuOAuESwg+G30ucb5ipynr+GmHK5Nwg4A78BnNvhVAqGqdwT/xVfCkkbCtHd4FYhynrMdhddiEVbzUQMg/PsN/B0AadXV6xwm+Yxgz31qn2wwlW7vMy9tKKVxiso7hQK1qI18r60FTaOB7cWzsYBBCN3KaD0R3tM=
access-expired-time: 300 #1800
refresh-expired-time: 86400 #86400
token:
secret: AQBIp8JxTfqZhpmySSvz29ZE/8gODZCu4PBhOkyFKiIAq8hGZ8f8a9vllqqUJl2362noOfcjE2eNs//SuoSBtP8peNCKxZGsZ3QUpmMKw7fJARiFUN67EcZ02O+XhDdlpOROevsYjfGvfSHeQ4PqaADvYquFvIeUvGwBC3/vQzVZYL8R+JdOxpq3W/mMQ7j6rIK70MkUj7gt90TiJ18eJ45U+J+T4VWtC0+kHEIEFSRs8wTpPicAl5CLlZmJ+GI7R9ZERDsAKwyQ/XacfrHAp3QdLyvkVQ7qp4MrP8X6fwpOZFleerNdRx0oXATpqtN5eYGfG8BVVpgSQoq7ZMP85pm1NQNDlOLjmYdHuOAuESwg+G30ucb5ipynr+GmHK5Nwg4A78BnNvhVAqGqdwT/xVfCkkbCtHd4FYhynrMdhddiEVbzUQMg/PsN/B0AadXV6xwm+Yxgz31qn2wwlW7vMy9tKKVxiso7hQK1qI18r60FTaOB7cWzsYBBCN3KaD0R3tM=
access-expired-time: 1800 #1800
refresh-expired-time: 86400 #86400
spring:
cloud:
config:
uri: http://localhost:8888
name: bootakhae
profile: dev
위처럼 설정한 뒤 postman을 사용하여 API 테스트한 결과, 다음과 같은 결과가 출력됐습니다.

dev profile로 설정했던 값이 저장된 것을 확인했습니다. 위 bootstrap.yml에 설정한 profile을 주석처리한 뒤 출력해보겠습니다.

bootstrap에서 profile 설정을 해줘야 해당 profile이 우선순위를 갖게 됩니다. 설정 정보가 적용되는 과정은 다음과 같습니다.
local application.yml 이 최초 설정 -> bootakhae.yml 이 설정 정보 덮어씌움 -> bootakhae-dev.yml이 설정 정보 덮어씌움(profile 설정한 경우)
위와 같이 어떤 파일에 있는 설정 정보가 반영되는지 이해하고 있어야 합니다.