본문 바로가기
Book/스프링 부트와 AWS 웹 서비스

[AWS] EC2 서버에서 프로젝트 배포하기

by 달의 조각 2023. 2. 25.
이 글은 이동욱 님의 스프링 부트와 AWS로 혼자 구현하는 웹 서비스 책을 읽으며 정리한 글입니다.

 

1. EC2에 프로젝트 clone
2. 배포 스크립트 만들기
3. RDS 접근하기

 

ssh aws_apupu_ec2 # EC2 터미널 접속하기

1. EC2에 프로젝트 clone 받기

sudo yum install git
git --version
# 프로젝트를 저장할 디렉토리 생성
mkdir ~/app && mkdir ~/app/step1
cd ~/app/step1
git clone 깃허브 주소

 

2. 프로젝트 테스트

# 테스트
./gradlew test

테스트를 돌리니 아래와 같이 오류가 발생했다. application-oauth.properties 파일은 보안상의 문제로 깃허브에 올리지 않았었는데, 이 파일 없이 테스트를 돌리니 오류가 발생했던 것이다. 파일을 FileZilla를 통해 전송해 주었다.

PostsRepositoryTest > 게시글저장_불러오기() FAILED
    java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:98
        Caused by: org.springframework.beans.factory.BeanCreationException at ConstructorResolver.java:658
            Caused by: org.springframework.beans.BeanInstantiationException at SimpleInstantiationStrategy.java:185
                Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException at DefaultListableBeanFact

 

3. 배포 스크립트 만들기

  1. git clone 혹은 git pull을 통해 새 버전의 프로젝트를 받는다.
  2. Gradle이나 Maven을 통해 프로젝트를 테스트하고 빌드 한다.
  3. EC2 서버에서 해당 프로젝트를 실행 및 재실행한다.

VIM을 다루는 방법은 이 링크를 참조했다.

vim ~/app/step1/deploy.sh # 파일 생성
#!/bin/bash

REPOSITORY=/home/ec2-user/app/step1 # 프로젝트 디렉토리 주소를 변수로 저장
PROJECT_NAME=Apupu

cd $REPOSITORY/$PROJECT_NAME/ # git clone 받은 주소로 이동

echo "> Git Pull"

git pull

echo "> 프로젝트 Build 시작"

./gradlew build # 프로젝트 내부의 gradlew로 build 수행

echo "> step1 디렉토리로 이동"

cd $REPOSITORY

echo "> Build 파일 복사"

cp $REPOSITORY/$PROJECT_NAME/build/libs/*.jar $REPOSITORY/ # build 결과물인 jar 파일을 복사

echo "> 현재 구동중인 애플리케이션 pid 확인"

CURRENT_PID=$(pgrep -f ${PROJECT_NAME}.*.jar) # 기존 수행 중이던 애플리케이션을 종료 / -f: 프로세스 이름으로 찾는다

echo "현재 구동 중인 애플리케이션 pid: $CURRENT_PID"

# 현재 구동 중인 프로세스가 있는지 판단
if [ -z "$CURRENT_PID" ]; then
        echo "> 현재 구동 중인 애플리케이션이 없으므로 종료하지 않습니다."
else
        echo "> kill -15 $CURRENT_PID"
        kill -15 $CURRENT_PID
        sleep 5
fi
echo "> 새 애플리케이션 배포"

JAR_NAME=$(ls -tr $REPOSITORY/ | grep jar | tail -n 1) # 새로 실행할 jar 파일 찾기 / tail -n: 최신 파일을 변수에 저장

echo "> JAR Name: $JAR_NAME"

nohup java -jar $REPOSITORY/$JAR_NAME 2>&1 & # 찾은 jar 파일을 nohup으로 실행
# nohup: java -jar 명령어는 터미널 접속을 끊으면 종료되지만 nohup은 계속 구동된다
chmod +x ./deploy.sh # 스크립트에 실행 권한 부여하기
ll
./deploy.sh # 스크립트 실행
vim nohup.out # 로그 보기

 

위에서 application-oauth.properties 파일을 src/main/resources 위치에 넣었었는데, 상위 폴더인 step1에서만이 아닌 step2에서도 사용하기 위해서 그 상위 폴더인 app로 위치하고, 이 파일을 사용하도록 deploy.sh를 수정했다.

nohup java -jar \
       -Dspring.config.location=classpath:/application.properties,/home/ec2-user/app/application-oauth.properties \
       $REPOSITORY/$JAR_NAME 2>&1 &
  • -Dspring.config.location: 스프링 설정 파일 위치를 지정한다. classpath가 붙으면 jar 안에 있는 resources 디렉토리를 기준으로 경로가 생성된다. application-oauth.properties는 외부에 파일이 있으므로 절대 경로를 사용한다.

 

4. RDS 접근하기

현재 RDS는 MariaDB를 사용하고 있다. 스프링 부트 프로젝트를 실행하기 위해서 아래 작업이 필요하다.

  1. 테이블 생성: H2에서는 자동 생성해 주던 테이블을 직접 쿼리를 위해 생성해 주어야 한다.
  2. 프로젝트 설정: 데이터베이스 드라이버를 프로젝트를 추가해야 한다.
  3. EC2(리눅스 서버) 설정: 데이터베이스 접속 정보를 외부에 노출하지 않고 EC2 내에서만 관리하도록 해야 한다.

🍓 RDS 테이블 생성

  • JPA가 사용할 엔티티 테이블: 테스트 코드 수행 시 로그로 생성되는 쿼리를 사용하면 된다.
  • 스프링 세션이 사용될 테이블: schema-mysql.sql 파일에서 확인할 수 있다.
create table posts (
    id bigint not null auto_increment,
    created_date datetime(6),
    modified_date datetime(6),
    author varchar(255),
    content TEXT not null,
    title varchar(500) not null,
    primary key (id)
) engine=InnoDB

create table users (
    id bigint not null auto_increment,
    created_date datetime(6),
    modified_date datetime(6),
    email varchar(255) not null,
    name varchar(255) not null,
    picture varchar(255),
    role varchar(255) not null,
    primary key (id)
) engine=InnoDB

 

🍓 MariaDB 드라이버 등록

# build.gradle
implementation("org.mariadb.jdbc:mariadb-java-client")

위의 설정 후 서버에서 구동될 환경을 구성하기 위해 src/main/resources/에 application-real.properties 파일을 추가한다. 실제 운영될 환경이기 때문에 보안/로그상 이슈가 될 만한 설정을 모두 제거하며 RDS 환경 profile 설정이 추가된다.

spring.profiles.include=oauth,real-db
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
spring.session.store-type=jdbc

 

🍓 EC2 설정

RDS 접속 정보는 보호해야 할 정보이니 서버에 직접 설정 파일을 두어야 한다.

vim ~/app/application-real-db.properties
spring.jpa.hibernate.ddl-auto=none # DB 테이블 자동 생성을 막는다
spring.jpa.show_sql=false

spring.datasource.hikari.jdbc-url=jdbc:mariadb://☆RDS 주소☆:☆포트명(기본 3306)☆/☆DB 이름☆
spring.datasource.hikari.username=☆DB 계정☆
spring.datasource.hikari.password=☆DB 비밀번호☆
spring.datasource.hikari.driver-class-name=org.mariadb.jdbc.Driver

설정 파일을 만들고, deploy.sh가 real profile을 쓸 수 있도록 개선한다.

nohup java -jar \
    -Dspring.config.location=classpath:/application.properties,classpath:/application-real.properties,/home/ec2-user/app/application-oauth.properties,/home/ec2-user/app/application-real-db.properties \
    -Dspring.profiles.active=real \
    $JAR_NAME > $REPOSITORY/nohup.out 2>&1 &

deploy.sh를 실행한 뒤 nohup.out 파일을 열어서 성공적으로 수행되는지 확인한다. 8080 포트로 톰캣 서버가 실행되었는지 확인하고, 아래 명령어로 html 코드가 정상적으로 보이면 된다.

curl localhost:8080

 

🤔 no main manifest attribute in 에러

나는 로그를 확인하니 아래의 문구가 떠 있었다. 검색해 보니 jar파일에서 처음 호출할 Main 메소드를 찾지 못했다는 에러라고 한다. jar 파일이 잘 생성되었는지 확인하고, 잘 복사되는지 확인했는데 문제가 없었다.

no main manifest attribute in

이 문제는 Spring Boot 2.5.0 버전 이상부터 나타날 수 있는 문제다. gradle로 빌드를 할 때 아래와 같이 jar 파일이 두 개가 생성되기 때문이다. Apupu-1.0-SNAPSHOT.jar 파일이 배포가 되어야 하는데 Apupu-1.0-SNAPSHOT-plain.jar가 배포되었던 것이다.

이를 해결하기 위해 build.gradle에 빌드 시 plain.jar 파일이 생성되지 않도록 하는 명령어를 추가했다. 추가한 이후에 EC2에 남아 있던 plain.jar 파일을 jar 파일이 생성되는 곳과 복사된 곳 두 위치에서 삭제해 주자!

jar {
    enabled = false
}

 

6. EC2에서 소셜 로그인하기

  1. AWS 보안 그룹 변경: 스프링 부트 프로젝트가 8080 포트로 배포되었으니 8080 포트가 보안 그룹에 열려 있는지 확인한다.
  2. AWS EC2 도메인으로 접속: http://ec2-☆.ap-northeast-2.compute.amazonaws.com:8080/
  3. 구글에 EC2 주소 등록: https://console.cloud.google.com/
  4. 네이버에 EC2 주소 등록: https://developers.naver.com/apps/#/myapps

  • 서비스 URL: 로그인을 시도하는 서비스가 네이버에 등록된 것인지 확인한다.
  • Callback URL: 전체 주소를 등록한다. http://ec2☆.ap-northeast-2.compute.amazonaws.com:8080/login/oauth2/code/naver

댓글