실무에서는 dev, stage, production 등으로 인프라를 구축하여 환경을 분리하여 사용한다.
예를 들어, dev에서는 dev를 위한 DB를 쓰는식으로 진행한다.
따라서 이를 설정해주는 파일인 application.yml에서 나눠줘야한다.
한 파일에서 모든 환경을 넣어준다면 굉장히 길고 피곤한 설정파일이 완성된다.
이를 해결하기 위해 우리는 각 환경에 맞춰 설정파일을 나눠주려고 한다.
우선은 개발초기이기 때문에
기본
local
dev
production
datasource
key ( 시크릿 키를 담은 파일 )
로 나누도록 한다.
설정파일을 분리하는 방법은
application-{환경}.yml로 post-fix를 통해 환경명을 설정하거나
spring.config.activate.on-profiles 를 통해 명시해줄 수 있다.
application.yml
spring:
profiles:
group:
local: "local, key" // local 환경에서 local과 key를 사용한다.
dev: "dev, datasource, key" // dev 환경에서 dev, datasource, key를 사용한다.
prod: "production, datasource, key" // prod 환경에서 production, datasource, key를 사용한다.
active: local
profile에 대한 전체적인 관리를 group을 통해 해준다.
application-local.yml
spring:
config:
activate:
on-profile: "local"
h2:
console:
enabled: true
settings:
web-allow-others: true
datasource:
url: jdbc:h2:mem:projecti
driver-class-name: org.h2.Driver
username: sa
password:
jpa:
hibernate:
ddl-auto: create-drop
properties:
default_batch_fetch_size: 1000
hibernate:
format_sql: false
show_sql: false
application-dev.yml
spring:
config:
activate:
on-profile: "dev"
jpa:
hibernate:
ddl-auto: update
properties:
default_batch_fetch_size: 1000
hibernate:
format_sql: false
show_sql: false
application-production.yml
spring:
config:
activate:
on-profile: "production"
jpa:
hibernate:
ddl-auto: none
application-datasource.yml
spring:
datasource:
url: jdbc:mysql://${MYSQL_HOST}:${MYSQL_PORT}/${DB_NAME}?useSSL=false&characterEncoding=UTF-8&serverTimezone=Asia/Seoul&allowPublicKeyRetrieval=true&tinyInt1isBit=false
driver-class-name: com.mysql.cj.jdbc.Driver
hikari:
maxLifetime: 580000
maximum-pool-size: 20
password: ${MYSQL_PASSWORD}
username: ${MYSQL_USERNAME}
jpa:
hibernate:
ddl-auto: update
properties:
default_batch_fetch_size: 1000
hibernate:
format_sql: false
show_sql: false
application-key.yml
spring:
config:
activate:
on-profile: "key"
cryptography:
aes:
secret-key:
api-key:
google:
google-calendar-api:
key:
open-ai:
open-ai-api:
key:
cryptography:
aes:
secret-key:
보면 알겠지만 key와 datasource는 공개적으로 보이면안되는 데이터를 담고있다. 이러한 변수들을 어떻게 관리할 수 있을지 살펴보자.
민감한 정보를 관리하는 방법에는 여러가지 방법이 존재합니다.
1. jasypt를 사용한 설정 파일 암호화
키가 노출되었을때 암호화가 무의미하기 때문에 추가적으로 보안을 위해 행할 수 있는 조치라고 생각하였고 암호화한 키를 그대로 퍼블릭 환경에 노출하는 것은 위험하다고 느꼈습니다.
Github Repository - Settings - Secrets and Variables - Actions
secret을 생성하여 application 내용을 집어넣는 경우입니다.
이는 application 파일이 수정될때마다 붙여넣어야하는 번거로움과 파일 변경 추적관리가 안되기 때문에 사용하지 않았습니다.
private repository를 생성하여 설정파일을 보관하고 서브모듈로 연결하여 사용하는 방법입니다.
3-1. private repo생성
applicatione-key와 application-datasource push
3-2 기존 repo에 git submodule add {private repo 주소]
.gitmodules파일이 생성되고 root 저장소에 config 폴더가 생성됩니다.
기존 repo에서 commit push하면
다음과 같이 서브모듈이 생성됩니다.
3-3. build.gradle과 .gitignore
build.gradle에 해당 내용을 추가합니다.
task copyGitSubmodule(type: Copy) {
copy {
from './config'
include '*.yml'
into './src/main/resources'
}
}
./config 폴더안에 있는 yml파일을 src/main.recources에 옮긴다는 의미입니다.
힘들게 옮겼으면 public git repo에 올라가면 안되겠죠?
.gitignore에 해당 내용을 추가합니다.
### Git Submodule
src/main/resources/application-key.yml
src/main/resources/application-datasource.yml
3-4. ci 워크플로우가 있다면 다음 내용을 추가하여 application이 반영되도록 합니다.
- name: Checkout repo
uses: actions/checkout@v3
with:
token: ${{ secrets.SUBMODULE_TOKEN }}
submodules: true
시크릿변수 SUBMODULE_TOKEN에 config(private repo)에 접근가능한 token을 집어넣습니다.
3-5 private repo에 새로운 내용을 반영한다면?
git submodule update --remote
git submodule foreach git pull
git add.
git commit
git push
해당 내용을 반영하여 config head가 최신내용을 바라보도록 합니다.