클라이밍 앱 '클밋'을 제작하며 고민/개발 내용을 기록한 글입니다.
스프링 프로젝트를 진행할 때 보통 환경별 분리된 DB를 사용하거나 스키마 또는 테스트용으로 로컬db를 사용하기도 한다.
이전에는 로컬테스트로 localhost:3306 썼다가 잘되면 다시 엔드포인트를 적었다.만약 연관관계나 db컬럼 수정시에는 다시 또 로컬로 테스트를 하기 위해 엔드포인트를 지우고 localhost를 적는다.
이번 프로젝트에서는 편하게 하기 위해 스프링 프로파일
을 적용했다.
프로필? 프로파일? 프로파일이라고 더 많이 부르는 것 같다. 프로파일은 애플리케이션 구성의 일부를 분리, 특정 환경에서만 사용하도록 하는 방법을 제공한다고한다. 일반적으로는 spring.profiles.active
속성으로 프로필을 지정할 수 있다.
spring:
profiles:
active: "dev,hsqldb"
또는 명령줄을 통해 지정이 가능하다.
$ java -jar -Dspring.profiles.active=production demo-0.0.1-SNAPSHOT.jar
yml파일은 아래와 같이 분리가 가능하다.
server:
port: 8080
---
spring:
profiles: development
datasource:
url: "개발용 db"
server:
port: 8081
---
spring:
profiles: production
datasource:
url: "배포용 db"
server:
port: 8082
공식문서에서는 위로 예시를 들었다. 하지만 시큐리티, jwt 설정등이 추가되어 한 환경에 50~60줄이 넘기때문에 파일 별로 나누는 방식을 선택했다.
나누는 방법은 application-{환경이름}.yml
파일 이름을 지으면 된다.
application.yml에는 profile과 공통 설정을 적었다.
(지정한 프로파일이 우선적용이니 겹치는 내용이 있다면 조심하자)
spring:
profiles:
active: ${profile}
mvc:
pathmatch:
matching-strategy: ant_path_matcher
servlet:
multipart:
max-request-size: 200MB
max-file-size : 200MB
springdoc:
swagger-ui:
path: /
operations-sorter: alpha
display-request-duration: true
dev와 prod에는 db, s3, jwt, security등 환경별로 다른 설정값들을 넣어주었다.
로컬의 경우 선택하는 것은 간단한다.
profiles안에 원하는 프로파일을 넣어주면 된다.
또는 직접 빌드 후 원하는 프로파일로 실행시켜도 된다.
$ java -jar -Dspring.profiles.active=dev demo-0.0.1-SNAPSHOT.jar
지금처럼 로컬에서는 인텔리제이로 몇 번의 딸깍
과 한 줄 입력으로 지정이 간단한다.
하지만 배포시에는 환경을 어떻게 지정해줄까?
이전에는 단순히 프로파일 구분없이 ENTRYPOINT를 통해 실행시켜주었지만 이제는 dev와 prod를 나누어야한다.
ENTRYPOINT ["java", "-jar", "-Dspring.profiles.active=dev"]
ENTRYPOINT ["java", "-jar", "-Dspring.profiles.active=prod"]
처음에는 이 두 줄을 if문으로 나눌까했다. 그러기 위해서는 profile이 어떤것인지 받는 인자가 있어야하고 또 그렇다면 굳이 if로 나눌 필요없이 active= 에 넣어줄 수 있지 않을까?라는 생각을 했고 관련 내용을 찾아보았다.
+ ARG GO_VERSION=1.21
+ FROM golang:${GO_VERSION}-alpine AS base
공식문서에는 Go언어의 버전을 지정하는 예제를 사용해 설명해주고있다.
도커 이미지 빌드시 ARG키워드는 FROM문으로 삽입되고, 만약 GO_VERSION에 대한 빌드 인자가 없으면 기본값인 1.21로 설정된다고한다.
인자값을 주기 위해서는 --build-arg="값"
으로 넣어주고 ${변수}
로 넣으면 된다.
위 방법을 적용한 도커파일이다.
FROM openjdk:17-oracle
ARG JAR_FILE=build/libs/*.jar //빌드된 JAR 파일의 위치를 지정
COPY ${JAR_FILE} app.jar // JAR파일을 Docker 이미지 안으로 복사
ENTRYPOINT ["java", "-jar", "-Dspring.profiles.active=${SPRING_PROFILE}", "/app.jar"] // 컨테이너 시작시 명령어 지정
ENTRYPOINT는 컨테이너가 시작하면 환경 변수인 SPRING_PROFILE을 받아 프로파일을 설정 후 실행하도록 정의했다.
그럼 도커이미지 빌드시 도커파일에 매개변수는 어떻게 주입을 해야할지 알아보자.
빌드하는 과정을 브랜치 기준으로 if문을 통해 나눴다. github.ref_name은 공식문서를 참고했고 트리거가 된 브랜치명을 담고 있다.
왜 브랜치 기준으로 환경을 나누나요?
빌드시에 --build-arg="값"
인자값을 지정해 도커파일 내부로 값을 전달해주었다.
# Docker 이미지 빌드 (Main Branch)
- name: Docker 이미지 빌드 (Main Branch)
if: github.ref_name == main
run: docker build --build-arg SPRING_PROFILE=prod -t gourderased/spring-project:latest .
# Docker 이미지 빌드 (Release Branch)
- name: Docker 이미지 빌드 (Release Branch)
if: github.ref_name == release'
run: docker build --build-arg SPRING_PROFILE=dev -t gourderased/spring-project:latest .
이후 컨테이너가 실행되면 위 도커파일에 prod 또는 dev가 전달되어 배포시에도 프로파일 설정이 가능해졌다.
설정 자체는 크게 어렵지 않았지만 도커에 대한 이해가 더욱 필요함을 느꼈다. 이번 글을 정리하면서 도커를 실행하는 것과 빌드할 때가 각각 언제인지 명확하지 않았는데 확실히 알게 되었다. 깃허브 액션의 경우에는 지원해주는 기능이 생각보다 많았다.
다음번에는 가능하다면 슬랙과 연동해 action이 수행하는동안 기다리지 않고 알림을 통해 성공 여부를 확인하는 스크립트를 작성하고 싶다.