![[Spring] Code Deploy 사용 시에 환경변수가 적용되지 않는 문제](https://img1.daumcdn.net/thumb/R750x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcBOTfg%2FbtsI3aZycca%2FuCVyUKBgy4hlKMJfbkWtrK%2Fimg.jpg)
1. 프로젝트 환경
- Java 17
- SpringBoot 3.3.2
- Code Deploy기반 CI/CD
2. Code Deploy는 .bashrc 파일을 사용하지 못한다.
CI/CD 파이프라인 구축을 위해 Code Deploy를 사용하였고, 빌드된 파일을 AWS EC2에서 실행하기 위해 .bashrc 파일에 환경변수 값을 등록하였다. EC2의 .bashrc에 환경변수를 등록하였으므로 빌드된 파일을 실행시켰을 때, 문제가 없을 것이라 생각했습니다.
Github으로 코드를 push 하면 사진과 같이 문제없이 EC2로 jar 파일이 배포됩니다.
AWS EC2의 .bashrc에 prod 환경에서 필요한 환경 변수 값을 저장하였고, 스프링 애플리케이션 실행 시에 사용하기를 기대하였습니다.
그러나… 스프링 애플리케이션 실행 로그를 확인하면 환경변수 값을 전달받지 못하고 있습니다.
Caused by: java.lang.IllegalArgumentException
: Could not resolve placeholder 'JWT_SECRET_KEY' in value "${JWT_SECRET_KEY}"
3. Code Deploy + AWS Parameter Store으로 해결하기.
AWS Systems Manager Parameter Store - AWS Systems Manager
AWS Systems Manager Parameter Store AWS Systems Manager의 기능인 Parameter Store는 구성 데이터 관리 및 암호 관리를 위한 안전한 계층적 스토리지를 제공합니다. 암호, 데이터베이스 문자열, Amazon Machine Image(AMI
docs.aws.amazon.com
Parameter Store는 구성 데이터 관리 및 암호 관리를 위한 안전한 계층적 스토리지를 제공합니다. 암호, 데이터베이스 문자열, Amazon Machine Image(AMI) ID, 라이선스 코드와 같은 데이터를 파라미터 값으로 저장할 수 있습니다. 값을 일반 텍스트 또는
암호화된 데이터로 저장할 수 있습니다.
Parameter Store에 직접 환경변수 값을 등록하여, EC2에서 빌드 파일 실행 시에 사용하도록 할 수 있습니다.
3-1. AWS Parameter Store에 환경변수 등록하기
파라미터 생성을 클릭합니다.
Parameter Store는 이름을 작성할 때 규칙이 있습니다.
- /{prefix}/{name}{profile-separator}{profile}/{환경변수 이름}
- prefix : 파라미터의 접두사
- ex) config
- default : config
- name : 애플리케이션을 식별할 이름이다.
- ex) todobuddy
- ex) application
- profile-separator : 하나의 애플리케이션을 여러 환경에 배포할 수 있도록 하기 위해 사용된다.
- ex) todobuddy 애플리케이션을 dev, prod 두 개의 환경을 배포할 때, 사용하는 환경 변수 값이 다를 수 있다.
- todobuddy_dev, todobuddy_prod처럼 _으로 애플리케이션 환경을 분리할 수 있다.
- default : _
- proflie : 애플리케이션 배포 환경을 작성한다.
- ex) prod
- prefix : 파라미터의 접두사
환경변수 이름을 작성하였다면, 해당 환경변수의 값을 등록합니다.
그럼 아래 사진과 같이 정상적으로 등록된다. 애플리케이션이 빌드된 파일을 실행할 때, Parameter Store에 등록한 환경변수를 사용하게 됩니다.
주의 : 사진에는 JWT_EXPIRATION_TIME으로 등록하였으나, JWT.EXPIRATION.TIME으로 수정하였습니다.
4. IAM 역할 등록
4-1. IAM 사용자 권한 추가
Parameter Store에 등록된 Key(환경변수) 값을 가져오기 위해서는 IAM 사용자에 권한을 추가해야 합니다.
- AmazonSSMFullAccess
- AmazonSSMReadOnlyAccess
AWS Parameter Store에 접근하기 위하여 액세스키를 발급받아 줍니다.
4-2. EC2에 역할(Role) 부여
AWS EC2가 Parameter Store에 등록된 Key에 접근하기 위해 역할(Role)을 부여해야 합니다.
- 기존의 EC2에 할당하고 있었던 code-deploy-role 역할에 새로운 정책 두 가지를 추가합니다.
- AmazonSSMFullAccess
- AmazonSSMReadOnlyAccess
2. EC2에 역할을 할당합니다.
5. AWS Client에 액세스키 등록
AWS EC2에 IAM 정보를 등록한다.
$ vim ~/.aws/credentials
[default]
aws_access_key_id={생성된 access_key_id}
aws_secret_access_key={생성된 secret_access_key}
$ vim ~/.aws/config
[default]
region={본인 region}
output=json
6. 스프링부트 설정
6-1. build.gradle에 의존성 추가
dependencies {
//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'
}
6-2. application.yml 설정
스프링 애플리케이션이 Parameter Store에 등록된 Key에 접근하기 위한 정보를 추가합니다.
- aws.paramstore.enabled 는 AwsParamStoreBootstrapConfiguration 를 활성화시키는 용도입니다.
- spring.config.import 는 외부 시스템(AWS Parameter Store)에서 값을 가져오기 위해 사용합니다. /config/todobuddy_local 경로에 환경변수를 등록하였으므로, import 값으로 aws-parameterstore:/config/todobuddy_local/ 를 지정하였습니다.
spring:
config:
import: 'aws-parameterstore:/config/todobuddy_local/'
aws:
paramstore:
enabled: true
prefix: /config
profile-separator: _
name: todobuddy
최종적인 application.yml 코드는 다음과 같습니다. AWS Parameter Store에 등록한 JWT.EXPIRATION.TIME 값을 사용할 수 있습니다.
spring:
config:
import: 'aws-parameterstore:/config/todobuddy_local/'
activate:
on-profile: local
datasource:
url: jdbc:mysql://localhost:3309/todo-buddy-backend-db?serverTimezone=UTC&characterEncoding=UTF-8
username: dev
password: 1234
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
hibernate:
ddl-auto: create-drop
properties:
hibernate:
format_sql: true
dialect: org.hibernate.dialect.MySQLDialect
mail:
host: smtp.gmail.com
port: 587
username: "test"
password: "test"
properties:
mail:
smtp:
auth: true
timeout: 5000
starttls:
enable: true
data:
redis:
host: localhost
port: 6399
jwt:
secret-key: "test"
expiration-time: ${JWT.EXPIRATION.TIME}
issuer: "test"
aws:
paramstore:
enabled: true
prefix: /config
profile-separator: _
name: todobuddy
6-3. 테스트 해보기
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello(@Value("${jwt.expiration-time}") long time) {
return time + "";
}
}
Parameter Store에 등록한 3600 값이 정상적으로 반환되는 것을 확인할 수 있습니다.
EC2 환경에서도 정상적으로 애플리케이션이 실행됩니다!