타인이 만든 프로그램을 유지보수 및 업그레이드 하면서 궁금한 점이 생겼다. 그것은 바로 Config-Server 라는 이름의 프로그램이었다.
처음에는 이 또한 프로그램의 일부로, 어떤 로직을 수행하는 것인줄 알았다. 하지만 막상 코드를 열어보니 안에는 아무 내용이 없었고, 그저 설정 파일과 디폴트로 만들어지는 메인 클래스만이 존재할 뿐이었다.
그래서 이게 뭘까... 도대체 왜 만든 걸까...? 하고 찾아보다 Spring-Cloud-Config 라는 개념을 처음으로 알았고, 앞으로 유지보수를 위해서도 알아야 할 내용이라는 생각이 들어 공부를 시작했다.
Spring Cloud Config
어플리케이션에는 당연히 application.properties, application.yml, bootstrap.properties 와 같은설정 파일들이 존재한다.
어떤 사업을 위해 프로그램을 제작한다고 생각해보자, 해당 프로그램에는 포탈과 코어 두 개의 프로그램이 필요하다. 그런데 두 프로그램은 몇 가지 설정파일 내 변수를 공유해야 한다.
같은 변수를 서로 다른 어플리케이션에 기재해놓고 사용하거나, DB에 테이블을 만들어 System.setProperty로 관리하는 방법도 물론 존재한다. 하지만 그보다 편한 방법은 바로 Spring Cloud Config를 사용하는 것이다.
Spring Cloud Config는 설정 파일을 외부로 분리해준다. 설정 파일을 관리하는 Config 서버를 별도로 두고, 어플리케이션들을 해당 서버에 연동시켜 Config 서버에서 제공하는 설정 파일을 사용하는 방식이다.
실습 준비
Spring Cloud Config 서버의 실습을 위해 몇 가지 수행해야 할 작업들이 있다.
가장 먼저 Spring Cloud Config는 Git 혹은 파일 시스템에 설정 파일을 저장한다.
*공식 홈페이지에서는 Redis 및 RDBMS에서도 지원한다고는 하는데, 아무리 시도해봐도 제대로 동작하지 않았다.
오늘의 실습에서는 GIT을 이용할 것이기 때문에 설정 파일들을 저장할 Repository를 생성해야 한다.
이를 위해 개인 gitlab에 public으로 config_test 라는 이름의 리포지토리를 생성했다.
그 후에는 Spring Cloud Bus가 사용할 메시지 큐를 설치해야 한다.
Kafka와 RabbitMQ가 사용 가능하다고 하니, 나는 RabbitMQ를 설치했다.
실습
먼저 서버로 사용될 어플리케이션을 생성한다.
디펜던시는 간단하게 아래와 같이 구성하면 된다.
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-monitor</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency>
</dependencies>
- actuator : 스프링 어플리케이션의 상태 관리
- config-server : 설정 서버
- monitor : webhook 사용으로 설정 파일 내용 변경 감지
- stream-rabbit : 어플리케이션 간 메시지 송수신
application.properties에 git 정보와 rabbitmq 정보를 입력한다.
*리포지토리가 public이기 때문에 아이디와 패스워드를 설정하지 않았다. 만약 Private이라면 설정해줘야 한다.
그리고 메인 클래스에 @EnableConfigServer 어노테이션을 추가한다.
@EnableConfigServer
@SpringBootApplication
public class ConfigserverApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigserverApplication.class, args);
}
}
다른 모든 것은 자동화가 될 예정이므로, 서버쪽에는 더 이상 수정할 포인트가 없다.
이제 해당 config server와 연결될 클라이언트 어플리케이션들을 만들어보자.
어플리케이션들의 디펜던시는 다음과 같이 구성했다.
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
</dependencies>
그 후에는 application.properties에 다음의 설정들을 작성했다.
굳이 spring.application.name을 지정한 이유는 spring cloud config가 다음과 같은 방식으로 어플리케이션별 설정 파일을 관리하기 때문이다.
/{application}/{profile}[/{label}]
/{application}-{profile}.yml
/{label}/{application}-{profile}.yml
/{application}-{profile}.properties
/{label}/{application}-{profile}.properties
나는 여기서 application name만을 이용하였다. 하지만 - 덕분에 이름은 testserver-2 이지만 ConfigServer에서는 /testserver/2로 관리된다.
다음으로 테스트용으로 사용할 간단한 컨트롤러와 서비스를 작성해준다.
package com.example.demo.service;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Service;
@Service
@RefreshScope
public class TestService {
@Value("${test_str}") private String testStr;
public String getStr() { return testStr; }
}
*config server에서 가져오는 변수를 사용하는 클래스는 @RefreshScope 어노테이션이 붙어야 한다.
package com.example.demo.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import com.example.demo.service.TestService;
@RestController
public class TestController {
@Autowired TestService service;
@GetMapping("/test")
public String getConfigInfo() {
return service.getStr();
}
}
이제 github에서 위의 설정대로 testserver-1.properties 라는 파일을 만들어, 내부에 test_str=12345 라는 테스트용 설정을 작성하여 확인해보자.
프로젝트 설정 파일에 존재하지 않는 test_str이라는 변수가 불러와진 것으로 보아, 잘 동작함을 확인할 수 있다.
그런데 여기서 한 가지 더 신경을 써야할 점이 있다.
이 상태에서 github에 저장된 설정을 수정하면 어떻게 될까?
Github의 내용을 12345에서 123456으로 수정해보았다.
수정 후 commit을 하면 config 서버에서 확인되는 값은 변경이 된다.
*{configserver}:{port}/testserver/2 에서 확인이 가능하다.
하지만 어플리케이션에서 받아와 사용하는 정보는 아직도 12345로 변경이 되지 않았다.
이러한 점은 직접 http://{config_server}:{port}/actuator/busrefresh 에 POST 전송을 통해 수동으로 갱신을 해줄 수 있지만, 굳이 그러고 싶진 않았다.
그래서 찾아보니 github에서는 webhook을 별도로 걸어주어야만 했다.
Config Server의 포트인 8081 서버의 포트를 열어준 후 내 공인 ip 주소를 확인하자.
*포트 포워딩은 마인크래프트 포트포워딩이라고 검색하면 아주 상세하게 작성된 글들이 많다.
그 후 생성한 리포지토리 > Settings > WebHooks 내부에 다음의 설정을 추가한다.
이제 만들었던 프로젝트들을 다시 구동시켜보면 github의 내용이 바뀌면 서버와 어플리케이션의 설정 사항 모두 잘 변경되는 것을 확인할 수 있다.
'실습 > 리눅스 서버 + 스프링 부트' 카테고리의 다른 글
Redis In Java_메시지 큐 (0) | 2022.03.20 |
---|---|
Redis In Java_자료구조 (0) | 2022.03.20 |
Spring Boot 캐시 사용 (0) | 2022.03.11 |
httpd (아파치 웹 서버) (0) | 2022.03.06 |
Filter, Interceptor, AOP (0) | 2022.02.01 |