본문 바로가기
부트캠프 개발일기/Pre-Project

92일차: Pre-Project Day 12(배포: EC2, S3, RDS, gradle)

by shyun00 2023. 6. 26.

프리 프로젝트를 하면서 가장 오류를 많이 겪은 날이었다.

배포가 생각보다 쉽지 않을것 같다는 생각은 했지만, 이렇게 단계마다 에러를 겪게 될줄이야...

백엔드 팀원 세명의 코드를 모두 합치고 충돌나는 부분을 수정해서 코드를 정리했다.

로컬 환경에서 테스트를 할때는 인메모리 DB인 H2 데이터베이스를 사용했으나,

실제 배포 환경에서 인메모리 DB는 애플리케이션을 재실행할때마다 데이터가 날아가는 문제가 발생한다. 

따라서 MySQL을 사용하기로 했고 AWS의 EC2, RDS, S3를 통해 애플리케이션을 배포하기로 했다.

 

먼저 MySQL을 사용할 수 있도록 코드를 수정하고, AWS의 서비스 인스턴스들을 생성했다.

이후 코드를 EC2에 클론해와서 실행시키는 순서로 진행했다.

 

크게 세가지 문제를 겪었고 다음과 같이 해결했다.

 

1. EC2에서 애플리케이션 build 중 멈춤: 프리티어 인스턴스의 RAM 용량이 1GB라서 빌드 규모가 커지면 멈춰버린다고 한다.

    -> 디스크 일부를 대신 사용하도록 설정해서 해결했다. (메모리 스왑)

sudo dd if=/dev/zero of=/mnt/swapfile bs=1M count=2048
sudo mkswap /mnt/swapfile
sudo swapon /mnt/swapfile

위 코드를 순서대로 실행해서 스왑 메모리를 생성할 수 있었다.

bs=1M은 dd 명령어가 한번에 읽거나 쓸 블록의 크기를 1MB로 지정하는 것이며, count=2048은 dd명령어가 읽거나 쓰는 블록의 수를 말한다. 총 2048MB(2GB)의 데이터를 스왑한다. (다만 디스크는 RAM 보다 느릴 수 있으므로 임시로 사용하는것이 좋다.)

 

EC2사양이 올라가 스왑 메모리를 사용하지 않아도 된다면 아래 코드를 실행시켜 스왑 메모리를 해제할 수 있다.

sudo swapoff -v /mnt/swapfile
sudo rm /mnt/swapfile

 

2. Could not find mysql:mysql-connector-java: MySQL 버전에 따라 라이브러리 추가할 때 명령어가 다르다고 한다.

    MySQL 8.0.31. 이후 버전부터는 mysql:mysql-connector-j 를 사용한다고 한다.

    -> RDS의 MySQL 버전을 낮추고 아래와 같이 정확한 버전 정보를 명시해주었다.

implementation 'mysql:mysql-connector-java:8.0.28'
runtimeOnly 'mysql:mysql-connector-java:8.0.28'

build 파일을 위와 같이 수정하고, application.yml 파일에도 MySQL 설정을 아래와 같이 추가해주었다.

  • 로컬 테스트용(application-local.yml)
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/{데이터베이스명}}?useSSL=false&serverTimezone=UTC
    username: root
    driver-class-name: com.mysql.cj.jdbc.Driver
  jpa:
    database: mysql
    database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
    hibernate:
      ddl-auto: update
    show-sql: true
    properties:
      hibernate:
        format_sql: true
  • 서버 배포용(application-server.yml)
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://{RDS엔드포인트}}:{포트번호}/{데이터베이스명}}?useSSL=false&characterEncoding=UTF-8&serverTimezone=UTC
    username: {RDS username}
    password: {RDS passwore}
  jpa:
    database: mysql
    database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
    hibernate:
      ddl-auto: update
    show-sql: true
    properties:
      hibernate:
        format_sql: true

 

3. build시 test 부분에서 실패: 코드제작용, 로컬테스트용, 서버테스트용으로 application.yml 파일을 다르게 설정해두었다.

    ./gradlew build 를 사용해 빌드할 경우 테스트 과정에서 설정에 따라 fail이 발생할 수 있다고 한다.

    -> ./gradlew bootjar를 통해 빌드하고, java -jar -Dspring.profiles.active={설정파일명} {jar파일명} 을 입력해 실행했다.

 

먼저, 기존에 build 했던 내용이 있어 빌드에 따른 결과물을 명확히 확인하기 위해 아래 명령어로 초기화 해주었다.

./gradlew clean

이후 아래 명령어로 build를 진행했다. (로컬 환경에서는 ./gradlew build도 문제가 없었으나 EC2에서는 test에서 에러가 발생했다.)

./gradlew bootjar

다음과 같이 build가 완료된것을 확인할 수 있었다. 확인을 위해 인텔리제이에서 결과물을 표시했는데, EC2에서도 정상 생성되었다.

(초기에 Spring boot initializr를 통해 프로젝트를 생성하면서 패키지 이름에 대문자가 들어갔는데

클래스명과의 구분을 위해 일반적으로는 소문자를 사용하는것을 원칙으로 한다고 한다. 다음 프로젝트부터는 해당 부분을 고려해야겠다.)

EC2에서도 정상적으로 빌드가 완료되어 .jar 파일이 생성되어있었다.

마지막으로, 백그라운드에서 실행하기 위해 nohup & 명령어를 붙여서 아래와 같이 코드를 실행했다.

nohup java -jar -Dspring.profiles.active=server StackOverFlow-clone-0.0.1-SNAPSHOT.jar &

엔드포인트로 접속했을때 Get 루트경로 응답이 잘 나오고 있는것을 확인할 수 있었다.


 현재는 실행중인 애플리케이션을 종료하고 업데이트된 애플리케이션을 다시 실행하기 위해 

ps -ef | grep java 를 통해 조회하고 kill -9 를 통해 종료하는 방법을 사용하고 있었다.

이번에는 시간적 문제로 스크립트까지 작성하지는 못했지만, 가능하다면 빌드 - 기존 프로그램 종료 - 새로운 프로그램 실행까지 한번에 수행하는 스크립트를 작성해서 활용했으면 좋았을 것 같다.

 

프론트분들께서 S3를 통해 프론트 서버 배포도 완료해주셨고, 백엔드 서버와도 연동되어 잘 작동하는걸 확인할 수 있었다.

 

참고자료

[Spring] bootjar과 그냥 build의 차이

[Springboot] 배포 환경 별로 설정파일 분리하기(feat.gradle)

AWS EC2 npm build 퍼센트 안넘어갈때, 멈출때

@Builder를 추가했더니 잭슨에서 에러가?

[Spring + MySQL] 스프링과  MySQL 연동하기

[스프링 부트] MySQL 연결 및 JPA - 2

AWS solution 1 - Session Manager (Public 접근)

AWS - S3 프리티어로 사용해보기

Could not find mysql:mysql-connector-java 해결

[AWS] RDS 알아보기 + RDS 생성하기(MySQL)

[AWS RDS] 데이터베이스 보안 그룹 규칙 설정

[AWS] EC2와 RDS 진짜 Free-tier 사용하기