gradlew을 이용하여 ec2에 spring boot 서버 배포하기 (feat. jasypt)

hwibaski·2023년 8월 17일
0

Java

목록 보기
3/7

목적

spring boot 프로젝트를 EC2에 배포하면서 알게된 내용을 요약 정리하는 글입니다.

Jasypt를 이용한 암호화

application.yml에는 데이터베이스 커넥션을 위한 DB정보들이 담깁니다.

실제 운영서버에서는 어떻게든 이 DB 정보들을 외부로 노출시키지 않아야합니다.

그러기 위해서 Jasypt를 이용해서 application.yml의 주요 DB 정보들을 암호화시켰습니다.

레포지토리를 private으로 바꾸는 방법도 있었지만 단편적인 방법이란 생각과 함께 인적 실수로 인해 레포지토리를 public으로 돌렸을 경우를 대비할 수 없기에 해당 방법은 사용하지 않았습니다.


EC2 배포

EC2에 버전에 맞는 Java와 Git이 설치되어 있다는 가정하에 기록합니다.

git을 이용해 프로젝트 레포지토리를 클론합니다.

./gradlew build를 이용해 빌드를 하려고 했는데 아래의 이미지와 같이 빌드가 되지 않았습니다.

image

일단 해당 명령어가 테스트를 수행하는지도 처음 알게 됐습니다.

테스트를 수행한다면 DB 연결을 시도할테고 해당 DB에 접근하는 DB 정보가 암호화되어 있고 그 정보를 복호화할 수 없으니 해당 테스트가 실패하는 것으로 확인했습니다.

따라서 Jasypt를 이용해서 암호화한 DB 정보를 복호화할 key를 명령어들에 입력해서 해당 문제를 해결했습니다.

두 개의 단계에서 복호화할 수 있는 key가 필요했습니다.


  1. ./gradlew build 로 프로젝트 빌드 시
$ ./gradlew build -Pjasypt.encryptor.password='jasypt_password'
  1. java -jar 로 서버 구동 시
$ java -jar ./api/build/libs/api-0.0.1-SNAPSHOT.jar --jasypt.encryptor.password='jasypt_password'

추가적으로 jasypt에 해당 시스템 변수를 사용하는 모듈의 build.gradle 파일에 다음 설정을 추가해야 합니다.

test {
    useJUnitPlatform()
    systemProperty 'jasypt.encryptor.password', findProperty("jasypt.encryptor.password")
}

여기까지만 봐도 해당 문제는 해결이 됐습니다.
하지만 gradle build시 어떤 task들이 같이 실행되고 여러가지 해결 방법이 있는지 공부하기 위해서 추가적인 자료 조사를 실시했습니다.

gradle build 시 실행되는 task들

아래의 명령어를 실행해보면 그래들 빌드 시 어떠한 태스크를 수행하는지 확인할 수 있었습니다.

--console=plain 옵션이 실행되는 태스크를 콘솔에 프린트해줍니다.

$ ./gradlew build -Pjasypt.encryptor.password='jasypt_password' --console=plain  

:api:test 태스크가 수행되는 과정에서 DB 커넥션이 발생하는 것을 확인했습니다.

> Task :compileJava NO-SOURCE
> Task :processResources NO-SOURCE
> Task :classes UP-TO-DATE
> Task :jar UP-TO-DATE
> Task :assemble UP-TO-DATE
> Task :compileTestJava NO-SOURCE
> Task :processTestResources NO-SOURCE
> Task :testClasses UP-TO-DATE
> Task :test NO-SOURCE
> Task :check UP-TO-DATE
> Task :build UP-TO-DATE
> Task :core:compileJava UP-TO-DATE
> Task :api:compileJava UP-TO-DATE
> Task :api:processResources UP-TO-DATE
> Task :api:classes UP-TO-DATE
> Task :core:processResources UP-TO-DATE
> Task :core:classes UP-TO-DATE
> Task :core:jar UP-TO-DATE
> Task :api:bootJarMainClassName UP-TO-DATE
> Task :api:bootJar UP-TO-DATE
> Task :api:jar UP-TO-DATE
> Task :api:assemble UP-TO-DATE
> Task :api:compileTestJava UP-TO-DATE
> Task :api:processTestResources NO-SOURCE
> Task :api:testClasses UP-TO-DATE
> Task :api:test

2023-07-25 23:56:37.538  INFO 43162 --- [ionShutdownHook] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2023-07-25 23:56:37.540  INFO 43162 --- [ionShutdownHook] .SchemaDropperImpl$DelayedDropActionImpl : HHH000477: Starting delayed evictData of schema as part of SessionFactory shut-down'
2023-07-25 23:56:37.542 DEBUG 43162 --- [ionShutdownHook] org.hibernate.SQL                        : 
    
    alter table expense_transaction 
       drop 
       foreign key FKhtc5v7xro83h7vt6i0qx3cbir
2023-07-25 23:56:37.587 DEBUG 43162 --- [ionShutdownHook] org.hibernate.SQL                        : 
    
    alter table income_transaction 
       drop 
       foreign key FKqy4sbxsch0jfsj8m5qgmobk8w
2023-07-25 23:56:37.615 DEBUG 43162 --- [ionShutdownHook] org.hibernate.SQL                        : 
    
    drop table if exists expense_transaction
2023-07-25 23:56:37.664 DEBUG 43162 --- [ionShutdownHook] org.hibernate.SQL                        : 
    
    drop table if exists income_transaction
2023-07-25 23:56:37.705 DEBUG 43162 --- [ionShutdownHook] org.hibernate.SQL                        : 
    
    drop table if exists transaction
2023-07-25 23:56:37.748  INFO 43162 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown initiated...
2023-07-25 23:56:37.823  INFO 43162 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown completed.

> Task :api:check
> Task :api:build
> Task :core:bootJarMainClassName
> Task :core:bootJar SKIPPED
> Task :core:assemble UP-TO-DATE
> Task :core:compileTestJava
> Task :core:processTestResources NO-SOURCE
> Task :core:testClasses
> Task :core:test
> Task :core:check
> Task :core:build

대략적으로 살펴보면 아래와 같은 build task 실행 시 아래의 sub task들이 수행됩니다.

  • compileJava
    • JDK 컴파일러를 사용하여 프로덕션 Java 소스 파일을 컴파일합니다.
    • Byte 코드를 생성합니다.
  • processResources
    • resources를 빌드 디렉토리 resources로 복사합니다.
  • classes
    • compileJava 태스크와 processResources 태스크에 의존합니다. 즉 classes 태스크를 실행하면 compileJava, processResources 태스크가 실행됩니다.
  • jar
    • classes 태스크에 의존합니다.
    • 기본 클래스를 포함하는 jar아카이브들을 조립합니다.
  • assemble
    • jar 태스크에 의존합니다.
  • compileTestJava
    • 대상이 Test 디렉토리이고 동작방식은 compileJava와 같습니다
  • processTestResources
    • 대상이 Test 디렉토리이고 동작방식은 processResources와 같습니다
  • testClasses
    • 대상이 Test 디렉토리이고 동작방식은 classes와 같습니다
  • test
    • unit Test를 실행합니다.
  • check
    • test 태스크에 의존적입니다.
  • build
    • check, assemble 태스크에 의존적입니다.
  • bootJarMainClassName
    • build 디렉토리에 bootJarMainClassName이라는 파일에 프로그램의 시작점인 mainClass의 풀패키지 경로가 기록됩니다.
  • bootJar
    • bootJarMainClassName 태스크에 의존적입니다.
    • build.gradle 파일에 bootJar { false } 라는 옵션을 추가하면 해당 모듈은 실행가능하지 않은 xxx-plain.jar 파일만 libs 폴더에 빌드됩니다.

bootJar 태스크와 bootJarMainClassName 태스크는 그래들에서 기본제공하는 태스크가 아니라 스프링부트에서 제공하는 태스크입니다.
id 'org.springframework.boot' version '2.7.14' 적용에 따라서 추가되는 task 입니다.
이 링크를 참조하시면 됩니다. https://docs.spring.io/spring-boot/docs/current/gradle-plugin/reference/htmlsingle/#packaging-executable.jars
위의 문서에 따르면 bootJar 태스크는 assemble 태스크 실행시 자동 실행되도록 설정되어 있다고 설명하고 있습니다.

image


image

bootJar, jar 옵션 추가 확인 사항

어느 블로그에서 공통으로 사용할 모듈은 실행할 모듈이 아니므로 bootJar 옵션을 false로 설정하는 것이 좋은 방향이라는 글을 읽었습니다.

bootJar를 false 설정 할 시 jar { enabled = true } 옵션을 줘야한다는 글을 읽고 확인 없이 해당 옵션들을 적용했습니다.

이번 기회에 공부를 하면서 bootJar 옵션을 꺼도 xxx-plain.jar 파일이 빌드된다는 사실을 알게 되었고, 공통 모듈인 core 모듈에 적용되어 있는 jar { enabled = true } 옵션을 삭제한다면 어떻게 동작할까 라는 궁금증이 들었습니다.

실제 테스트를 해보니 bootJar를 { enabled = false }로 설정해도 xxx-plain.jar 파일이 생성되고 정상적으로 서버가 동작하는 것을 확인했습니다.

조금 더 확인해보니 스프링부트 2.4.11 버전 이전에는 따로 설정하지 않는 한 jar task가 SKIP되어 xxx-plain.jar 파일이 생성되지 않았고, bootJar를 false 설정했으므로 jar task가 실행이 안될 것이기에 의도적으로 jar { enabled = true} 설정을 줘야한다는 의도의 글이었던 것 같습니다.

이 프로젝트는 2.7이상의 버전이므로 core 모듈의 jar { enabled = true} 옵션은 전혀 필요없을 것으로 판단했고, 실제 테스트 해보니 해당 옵션을 지워도 서버가 잘 동작하는 것을 확인했습니다.

// 공통 모듈인 core모듈의 build.gradle
// 가장 아래쪽에 적용된 jar {enabled = true} 옵션이 필요없는 것으로 확인

plugins {
    id 'java-library'
}

dependencies {
    // spring-data-jpa
    api 'org.springframework.boot:spring-boot-starter-data-jpa'

    // h2
    testRuntimeOnly 'com.h2database:h2'

    // test
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

bootJar {
    enabled = false
}

// jar {
//     enabled = true
// }

겪었던 문제를 해결할 수 있는 추가적인 방법

build과정에서 테스트를 수행하므로 스프링부트를 구동시켜서 DB 커넥션이 발생하는 것이 빌드가 실패한 주요 원인이었습니다.
특정 상황에서는 테스트만 건너뛰고 빌드를 할 수 없을까라는 생각이 들었고 그래들을 조금 더 찾아보았고 아래와 같은 추가적인 옵션들이 있는 것을 발견했습니다.

  1. 빌드 과정에서 test task를 제외하기
./gradlew build --exclude-task test
  1. 빌드 과정에서 다음에 실행되는 task들에서 fail이 발생해도 계속 진행하기
./gradlew build --continue

위의 옵션들은 정말 필요에 따라 사용 여부를 판단할 수 있을 것 같습니다.


reference

1개의 댓글

comment-user-thumbnail
2023년 8월 17일

많은 도움이 되었습니다, 감사합니다.

답글 달기

관련 채용 정보