본문 바로가기

Programming/Spring Boot

[Spring Boot] Spring Cloud Config를 이용해서 설정값 별도 관리하기

반응형

안녕하세요. 남산돈가스입니다.

오늘은 Spring Cloud Config를 이용하여 각 서비스의 정적 설정정보들을(properties)를 별도로 분리하여 관리하는 법에 대해서 알아보려고 합니다.

일반적으로 Spring 어플리케이션을 개발할 때, 설정파일을 .properties 나 .yml 로 관리하고 있습니다. 추가적으로 이 파일들을 각 profile별로 분리하여 각 시스템 환경에 따라서 적용되도록 관리하고 있는데, 이런 정적 파일로 관리되는 것들의 단점은 설정값에 변경사항이 발생한 경우, 빌드/배포를 다시 해야한다는 점입니다. 또한 혹시 모를 보안적인 이슈로 인하여 DB접속정보라던지 중요 Key값에 대한 정보가 담긴 설정파일이 유출 될 경우 심각한 보안사고를 초래할 수 있습니다.

이러한 문제들을 해결해 줄 수 있는 것이 Spring Cloud Config 입니다. 방식은 이렇습니다.

  1. 제공 할 설정파일들을 관리하는 git repository를 생성합니다. (당연히 Private Repository) 
  2. Spring Cloud Config 의존성을 가진 config 서버를 생성합니다.
  3. config 서버는 위 생성한 git에 연결하여 설정파일들을 읽어옵니다.
  4. 설정정보들을 수신할 어플리케이션들은 config 서버에서 본인들의 설정들을 받아와서 사용합니다.
  5. 중간에 설정이 바뀌는 일이 발생하면 config 서버만 변경해주고 어플리케이션들은 설정을 갱신하면 재배포 없이 변경 된 설정을 이용할 수 있습니다.

예제를 보면서 확인해보겠습니다.

1. Private Repository 생성

github에 접속하여 private repository를 생성해보겠습니다. 아무래도 중요 민감 정보들을 관리해야하니 Private으로 생성합니다.

위와 같이 repository를 생성한 뒤, yml파일을 하나 생성합니다. 저의 경우는 zuul-server-dev라고 명명했는데, 명명규칙은 다음과 같습니다. 

{application-name}-{profile}.yml 또는 {application-name}-{profile}.properties

이제 이 repo와 config 서버를 연동해보겠습니다.

2. Spring Cloud Config 프로젝트 생성

        <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-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>

maven 환경의 spring boot 프로젝트와 함께 위와 같은 의존성을 추가하여 프로젝트를 생성합니다.

@SpringBootApplication
@EnableConfigServer
public class ConfigApplication {

    public static void main(String[] args) {
        SpringApplication.run(ConfigApplication.class, args);
    }

}

 

그리고 위와 같이 EnableConfigServer라는 어노테이션을 추가하여 이 서비스가 config server이라는 설정을 부여합니다.

server:
  port: 8888

spring:
  cloud:
    config:
      server:
        git:
          uri: mygit:KimSungShin/spring-cloud-config.git
          ignore-local-ssh-settings: true
          private-key: |
            -----BEGIN RSA PRIVATE KEY-----
            {YOUR-KEY-CONTENT}
            -----END RSA PRIVATE KEY-----
          strict-host-key-checking: false

설정 파일은 위처럼 기본 포트는 대체로 8888번 포트를 쓰기 때문에 설정하였고, uri 부분에 github에 생성한 private repository의 ssh url을 입력합니다.

public repository였다면 여기까지만 설정하셔도 서버 기동 시 정상작동을 확인하실 수 있지만, 저희는 private 저장소를 생성했기 때문에 서버에서 위 저장소를 clone하기 위해선 ssh key가 필요합니다. 

2. Git 연동

1번에서와 같이 private repository의 연동을 위해선 ssh 연동이 필요합니다. ssh 연동을 하기 위해 로컬환경의 ssh key를 생성하여 github에 등록하여 연동해보겠습니다.

우선 config server를 기동하시는 로컬환경으로 가셔서 ssh key를 생성해야합니다. 저 같은 경우는 제 로컬PC환경에서 진행을 하므로 mac의 터미널로 접속하여 .ssh 폴더에 key를 생성하겠습니다.

위 스크린샷과 같이 Linux 및 Unix 환경이시라면 .ssh 폴더로 이동하셔서 명령어를 입력합니다.

$ ssh-keygen -m PEM -t rsa -b 4096 -C "your email address" -f config-server.id_rsa

결과로 뒤 config-server.id_rsa 와 config-server.id_rsa.pub 두 개의 파일이 생성됩니다. .pub은 공개 키 파일, 다른 건 비밀 키 파일입니다. 둘 중 공개키 파일을 github에 등록하여 ssh 접속이 가능하도록 하겠습니다. 아까 생성한 private repos의 setting > deploy keys 메뉴로 이동하여 위 처럼 .pub(공개 키 파일)의 plain text를 복사하여 붙여넣고 등록합니다.

이렇게 public key를 등록했으면 이제 서버 설정을 통해서 private repos를 clone할 수 있게 합니다. 다시 서버 설정으로 돌아와서,

private-key 데이터 영역에 |(bar)를 넣고 개행한 뒤 아까 설정한 .id_rsa (비밀 키)의 plain text를 복사하여 붙여넣기 합니다. 이렇게 하면 서버 설정이 완료됩니다.

3. Config Server 실행

이제 설정을 마쳤으니 서버를 실행해봅니다.

대충 실행이 잘 된 것 같으니 실제로 어떻게 환경설정들을 불러오는 지 알아보겠습니다.

아까 1번에서 말씀드린 것처럼 파일 명명규칙이 {application-name}-{profile} 이라고 말씀드렸는데요. 호출 역시 비슷한 구조입니다.

http://localhost:8888/{application-nam}/{profile}을 GET요청하면 결과를 확인할 수 있습니다.

아까 제가 github에 업로드한 파일은 zuul-server-dev.yml이었습니다.

http://localhost:8888/zuul-server/dev 를 요청해보니 위와 같이 결과가 내려온 것을 확인했고, propertySources 데이터를 보면 제가 yml에 입력한 내용이 json 형식으로 변환되어 응답 된 것을 확인할 수 있습니다.

4. Spring Cloud Config Client

이제 config server의 설정을 마무리하였으니, 이 설정들을 가져와서 사용할 client 설정을 해보겠습니다.

client 설정엔 두 가지 의존성이 필요합니다.

<dependency>
	<groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-config</artifactId>
</dependency>

actuator 가 필요한 이유는 config server의 설정 정보가 업데이트 되었을 경우, client의 서버의 재기동/배포 없이 변경사항을 refresh하여 가져올 수 있도록 하기 위해서입니다.

client 서버의 설정파일은 기존 설정파일을 application으로 쓰고계시다면, bootstrap.yml 또는 properties로 생성하여 여기에 config client 설정을 입력합니다. 그 이유는 spring boot 환경은 실행 시 우선적으로 bootstrap 파일을 application보다 먼저 읽어드리기 때문입니다.

 

bootstrap 설정파일에 config server의 url을 입력하고 실행 환경을 dev로 설정했습니다. 이 어플리케이션의 name은 application.yml에 zuul-server 로 설정되어있습니다. 이렇게 설정하면 서비스가 실행 될 때, 자동으로 http://localhost:8888/zuul-server/dev 로 요청하여 설정파일을 가져오게 됩니다.

spring:
  profiles:
    active: dev
  cloud:
    config:
      uri: http://localhost:8888
      
management:
  endpoints:
    web:
      exposure:
        include: refresh

management 영역은 spring actuator api를 이용하여 저장소의 설정들이 변경되었을 때, 재기동 없이 변경사항을 적용하기 위한 설정입니다.

그럼 테스트해보겠습니다.

5. 테스트

config client 프로젝트에 test용 컨트롤러를 생성하고 config server의 프로퍼티를 불러와 리턴하는 rest api를 만들겠습니다.

@RestController
@RefreshScope
public class ConfigClientController {
    @Value("${zuul.routes.ipatioc.path}")
    private String path;

    @GetMapping("/test")
    public String test() {
        return path;
    }
}

여기서 RefreshScope 라는 어노테이션을 주목하시면 됩니다. 그 이외에는 일반적으로 아시는 Rest 컨트롤러의 코드들이고 이 어노테이션은 actuator를 이용하여 refresh라는 api가 요청오면 spring이 다시 로드하게 되는 영역이라는 것을 어노테이션으로 선언해놓은 것 입니다. 

그러므로 위에 코드에서 config server에서 불러 올 "zuul.routes.ipatioc.path" 이 프로퍼티의 값을 github 저장소에서 변경한 뒤 actuator/refresh api로 변경사항을 갱신하면 /test의 결과값이 변경 된 프로퍼티 값으로 변경되어 나오는 것이죠.

먼저 변경 전 프로퍼티를 호출해 보면,

위 1번에서 업로드 한 zuul.routes.ipatioc.path 값인 /ipatioc/** 이 리턴됩니다.

제가 이 값을 /ipatio-c/test 로 변경해보았습니다.

이렇게 변경한 뒤, 이제 actuator/refresh api를 호출합니다.

정상적으로 200OK와 함께 변경 된 내용들을 확인할 수 있습니다. 그렇다면 이제 이 프로퍼티가 정말로 변경되었는 지 확인하기 위해서 위에서 호출한 test api를 다시 요청해보겠습니다.

변경사항이 재기동/배포 없이 적용되어 있는 것을 확인하실 수 있습니다.

결론

이렇게 spring cloud config 를 사용하면 중요 민감정보 뿐만 아니라 서비스 별 / profile 별로 관리해야하는 환경설정 파일들을 중앙에서 한 곳에서 관리할 수 있는 이점과 재배포 없이 반영할 수 있는 이점을 얻을 수 있습니다. 프로그램 중단과 같이 민감한 작업들을 쉽게 관리할 수 있어 정말 편리한 서비스인 것 같습니다.

감사합니다.