최근에 사이드 프로젝트를 하나 진행하고 있다.
배포도 생각중이라 DB 정보를 어떻게 관리해야 하나 고민을 하던 와중에 우연치 않게aws parameter store
에 대해서 듣게 되었다.가격도 공짜에 사이드 프로젝트에 적용하기에 너무 괜찮은 옵션이라 바로 사용해보기로 했다.
우선
aws parameter store
검색을 해보니 대부분spring boot 2.X
버전에 대해서만 게시글들이 많았고, 3.X 버전에 대해서는 게시글이 적었다.그래서 나도 누군가에게 도움이 되었으면 하는 바람으로 spring boot 3.2에
aws parameter store
를 적용한 후기에 관해서 기록을 남겨본다.
우선 참고한 블로그는 여러가지가 많지만 제일 큰 도움을 받은 블로그는 해당 블로그들이다.
Spring Cloud AWS 3.1.0 Reference Docs 2.1. Bill of Materials 항목을 보면 maven이랑 gradle에 의존성을 추가하는 방법에 대해서 나와있다.
gradle에 의존성 추가하는 방법
dependencies {
implementation platform("io.awspring.cloud:spring-cloud-aws-dependencies:${springCloudAwsVersion}")
// Replace the following with the starter dependencies of specific modules you wish to use
implementation 'io.awspring.cloud:spring-cloud-aws-starter-sqs'
}
이걸 parameter store 모듈을 사용하면서, spring boot 3.2 버전에 맞추도록 코드를 수정하면 다음과 같다.
//AWS Parameter Store
implementation platform("io.awspring.cloud:spring-cloud-aws-dependencies:3.1.0")
implementation 'io.awspring.cloud:spring-cloud-aws-starter-parameter-store'
※ spring-cloud-aws Github을 보면 spring boot 3.2.x 버전은 spring cloud aws 3.1.x 버전을 쓰라고 나와있다.
(※ 저는 yaml
파일을 더 선호해서 properties
파일 대신에 yaml
을 사용했습니다.)
spring.config.import
를 추가할 때 기존 2.X 버전이랑 많이 달라져서 고생을 조금 했습니다.
위에 참고한 블로그 글이랑 Loading External Configuration을 살펴보면
spring.config.import
을 추가할 때Key
를 같이 추가해줘야 합니다.
#properties
spring.config.import=aws-parameterstore:/config/spring
#yaml
spring:
config:
import: aws-parameterstore:/config/spring
그리고 저는 이렇게 세팅해서 사용하고 있습니다.
config:
type: aws-parameterstore:/config/example/
spring:
config:
activate:
on-profile: prod
import: ${config.type}
datasource:
url: ${jdbc.url}
driver-class-name: com.mysql.cj.jdbc.Driver
username: ${jdbc.username}
password: ${jdbc.password}
여기에 약간 특이한 점은 /config/example
가 아니라 /config/example/
를 사용하고 있다는 것인데 해당 내용에 관해서는 아래에서 설명하겠습니다.
2.X 버전에서 사용했던 것처럼 Key
를 추가하지 않은 상태로 import
를 하게 되면 에러가 발생합니다.
# 2.X 버전에서 하던 것 처럼 사용해봄
# yaml
spring:
config:
import: 'aws-parameterstore:'
aws:
paramstore:
enabled: true
prefix: /config
profile-separator: _
name: spring
Could not import properties from AWS Parameter Store: No Parameter Store keys provided in
spring.config.import=aws-parameterstore:
configuration.
03:41:54.228 [main] ERROR org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter --
***************************
APPLICATION FAILED TO START
***************************
Description:
Could not import properties from AWS Parameter Store: No Parameter Store keys provided in `spring.config.import=aws-parameterstore:` configuration.
Action:
Consider providing keys, for example `spring.config.import=aws-parameterstore:/config/app`
aws 사이트에 접속 후 검색 창에 Systems Manager를 치면 서비스가 뜬다.
왼쪽을 보면 사용 가능한 서비스들이 있는데 거기서 파라미터 스토어를 찾아서 클릭
파라미터 생성을 클릭 후 이름과 필요한 내용을 기입 후 파라미터를 생성하면 된다.
그리고 이름
과 값
을 제일 신경써서 작성하도록 하자.
이름
으로는 아까 yaml
에서 import
할 때 사용한 key
와 변수명
을 조합한 값을 사용한다.
key : /config/example/
변수명 (${ }
안의 값) : jdbc.url
특징으로는 값 변경은 가능하지만, 이름 변경은 불가능하다.
EC2에서 parameter store를 사용하기 위해서는 EC2에게 parameter store에 접근할 수 있도록 권한을 부여해야 한다.
EC2에 IAM을 설정해주기 전에 EC2가 parameter store를 볼 수 있도록 IAM 역할을 하나 새롭게 생성하자.
IAM 서비스에 들어가, 역할을 새롭게 하나 생성한다.
AWS 서비스
선택, 사용 사례는 EC2
선택 AmazonSSMReadOnlyAccess
정책이다.ec2-readOnly-parameter-store
IAM을 생성했으므로 EC2에 IAM 역할을 부여하자.
EC2 서비스에 들어간 후, 인스턴스 탭을 누른다.
parameter store를 사용할 EC2에 대고 마우스 우클릭
을 누르면 여러 옵션이 뜨는데
거기서 보안
-> IAM 역할수정
을 연달아서 클릭
위에서 생성한 IAM 역할을 부여하자.
실행시켜 보면 해당 문구가 뜨면서 parameter store에서 parameter들을 읽어 온다는 것을 알 수 있다.
2024-01-01T16:30:40.258Z INFO 60395 --- [ main] .a.c.a.c.p.ParameterStorePropertySources : Loading property from AWS Parameter Store with name: /config/example/, optional: false
해당 블로그 글에서도 있는 내용인데 ParameterStorePropertySource
가 getParameters(GetParametersByPathRequest paramsRequest)
를 실행하면서 가져올 parameter의 이름을 추출하게 된다.
//전체 코드
private void getParameters(GetParametersByPathRequest paramsRequest) {
GetParametersByPathResponse paramsResult = ((SsmClient)this.source).getParametersByPath(paramsRequest);
Iterator var3 = paramsResult.parameters().iterator();
while(var3.hasNext()) {
Parameter parameter = (Parameter)var3.next();
String key = parameter.name().replace(this.parameterPath, "").replace('/', '.').replaceAll("_(\\d)_", "[$1]");
LOG.debug("Populating property retrieved from AWS Parameter Store: " + key);
String propertyKey = this.prefix != null ? this.prefix + key : key;
this.properties.put(propertyKey, parameter.value());
}
if (paramsResult.nextToken() != null) {
this.getParameters((GetParametersByPathRequest)paramsRequest.toBuilder().nextToken(paramsResult.nextToken()).build());
}
}
이때 메서드를 진행하면서 this.parameterPath
을 ""
로 그리고 /
값을 .
로 변경시키는데 이 부분 때문에 조금 귀찮은 일이 발생한다.
//주의해서 볼 부분
String key = parameter.name().replace(this.parameterPath, "").replace('/', '.').replaceAll("_(\\d)_", "[$1]");
여기서 this.parameterPath
는 우리가 import
할 때 추가한 key
값이다.
/config/example/
<<< 이 부분
ParameterStorePropertySource
클래스가 parameter store에서 parameter들을 다 가져오고, 이름을 key
로 추출한 다음, 해당 하는 property
에 key, value 값을 넣어준다.
우리가 aws parameter store에 등록한 parameter 이름은/config/example/jdbc.url
이므로 이걸로 이름이 key
로 변하는 예시를 들어보면
/config/example/jdbc.url
에서 /config/example/
을 없애면 jdbc.url
가 된다./
가 없으므로 jdbc.url
를 key
값으로 해서 해당하는 value를 property
에 넣는다.jdbc.url
가 있으므로 정상적으로 값이 주입된다.우리가 만약에 import
할 때 /config/example
를 key
값으로 썼으면 어떻게 됐을까?
/config/example/jdbc.url
에서 /config/example
을 없애면 /jdbc.url
가 된다./
가 있으므로 .
로 변환하면, .jdbc.url
를 key
값으로 해서 해당하는 value를 property
에 넣는다.jdbc.url
인데, 주입된 key
는 .jdbc.url
이므로 정상적으로 값 주입이 되지 않아서 값을 읽어오지 못한다./config/examplejdbc.url
로 parameter를 만들던가, 사용하는 변수명으로 ${jdbc.url}
대신에 ${.jdbc.url}
를 사용해야한다.개인적으로
${jdbc.url}
가 조금 더 가독성이 좋은 것 같아서import
할 때key
값을/config/example/
로 사용했다.