EC2 서버에 프로젝트를 배포해 보자

짱J·2022년 7월 6일
0

AWS

목록 보기
3/6
post-thumbnail

이제까지 스프링 부트로 서비스 코드를 개발했고, EC2+RDS를 이용하여 배포 환경을 구성하였다.

이제 이것을 합체시켜 실제 서비스를 한번 배포해보자!


EC2에 프로젝트 Clone 받기

깃허브에서 코드를 받아오기 위해 git이 설치되어 있어야 한다.
아래 명령어로 git을 설치하고, 설치 상태를 확인하자.

sudo apt install git
git --version

git이 설치되었다면 git clone으로 프로젝트를 저장할 디렉토리를 생성하고, 생성된 디렉토리로 이동하자.

mkdir ~/app & mkdir ~/app/step1
cd ~/app/step1

이제 https 주소로 git clone을 진행한다.
그리고 ll 명령어로 복사도 잘 되었는지 확인해보자

코드들이 잘 수행되는지 테스트로 검증해보자

./gradlew clean test

clean은 현재 테스트 환경을 한 번 정리하는 명령어로 테스트하면서 꼬인 것이 발생할 때 사용한다.

야호 ^ㅅ^ !!

  • 만약 코드 상 문제로 테스트가 실패했다면, 수정하고 git pull을 하자.
  • Permission denied가 뜬다면 chmod +x ./gradlew 명령어로 실행 권한을 추가한 뒤 다시 테스트를 수행하자.

현재 EC2에는 gradle을 설치하지 않았지만, gradle task를 수행할 수 있다.
이는 프로젝트 내부에 포함된 gradlew 파일 때문이다.
gradlew는 gradle이 설치되지 않은 환경 혹은 버전이 다른 상황에서도
해당 프로젝트에 한해서 gradle을 쓸 수 있도록 지원하는 Wrapper 파일이다.

😡 에러는 아닌 것 같은데, test가 너무 오래 걸린다면 ?!

나도 처음 했을 때는 execute 단계에서 5분이 넘어도 진척이 없었다 :(
인스턴스 문제인가 싶어 EC2도 건드려봤지만 해결되지 않았다...!
그러다 생각난 UMC . . . !
디스코드 질문방에서 해결 방법을 찾을 수 있었다! ( 최강 대학생 IT 연합 동아리 UMC! )

어쨌든 서론은 여기까지고 !!
AWS 프리티어의 메모리가 적어 발생하는 문제였다. (현업에서는 발생하지 않을 것이다.)
도커나 swap 메모리(가상 메모리)를 사용하여 해결할 수 있다.

🦦 Ubuntu swap 파일 생성


배포 스크립트 만들기

작성한 코드를 실제 서버에 반영하는 것을 배포라고 한다.

  • git clone, git pull 등을 통해 새 버전의 프로젝트 받음
  • gralde이나 maven을 통해 프로젝트 테스트와 빌드
  • EC2 서버에 해당 프로젝트 실행 및 재실행

앞선 과정을 배포할 때마다 하나하나 명령어를 실행하는 것은 불편함이 많다.
이를 쉘 스크립트로 작성해 스크립트만 실행하면 앞의 과정이 차례로 진행되도록 하자.

쉘 스크립트?
.sh라는 파일 확장자를 가진 파일로, 리눅스에서 기본적으로 사용할 수 있는 스크립트 파일의 한 종류이다.

프로젝트가 있는 ~/app/step1/ 디렉토리에 deploy.sh 파일을 하나 생성한다.
Vim, Emacs, Nano 등 편한 텍스트 편집 도구를 사용하자. 나는 nano를 사용할 것이다!

nano ~/app/step1/deploy.sh
#!/bin/bash

REPOSITORY=/home/ubuntu/app/step1
PROJECT_NAME=freelec-springboot2-webservice

cd $REPOSITORY/$PROJECT_NAME

echo "> Git Pull"
git pull

echo "> 프로젝트 Build 시작"
./gradlew clean build

echo "> step1 디렉토리 이동"
cd $REPOSITORY

echo "> Build 파일 복사"
cp $REPOSITORY/$PROJECT_NAME/build/libs/*.jar $REPOSITORY/

echo "> 현재 구동적인 애플리케이션 pid 확인"
CURRENT_PID=$(pgrep -f ${PROJECT_NAME}.*.jar)

if [ -z "$CURRENT_PID" ]; then
	echo "> 현재 구동 중인 애플리케이션이 없으므로 종료하지 않습니다."
else
	echo "> kill -15 $CURRENT_PID"
	kill -15 $CURRENT_PID
	sleep 5
fi

ehco "> 새 애플리케이션 배포"
JAR_NAME=$(ls -tr $REPOSITORY/ | grep jar | tail -n 1)

echo "> JAR Name: $JAR_NAME"

nohup java -jar $REPOSITORY/$JAR_NAME 2>&1 &
  • REPOSITORY=/home/ubuntu/app/step1 : 스크립트 내에서 자주 사용하는 값을 변수로 저장 (PROJECT_NAME도 마찬가지)
    • 타입 없이 선언하여 저장
    • $(변수명)으로 변수 사용
  • cp $REPOSITORY/$PROJECT_NAME/build/libs/*.jar $REPOSITORY/ : build의 결과물인 jar 파일을 복사해 jar 파일을 모아둔 위치로 복사
  • URRENT_PID=$(pgrep -f ${PROJECT_NAME}.*.jar) : 기존에 수행 중이던 스프링 부트 애플리케이션을 종료
    • pgrep으로 process id 추출
    • -f 옵션으로 프로세스 이름을 찾음
  • if ~ els ~ fi : 현재 구동 중인 프로세스가 있는지 없는지 판단하여 기능을 수행
    • 프로세스가 있으면 해당 프로세스를 종료
  • JAR_NAME=$(ls -tr $REPOSITORY/ | grep jar | tail -n 1) : 새로 실행할 jar 파일명을 찾음
    • tail -n으로 가장 최신 파일을 변수에 저장
  • nohup java -jar $REPOSITORY/$JAR_NAME 2>&1 & : 찾은 jar 파일명으로 해당 jar 파일을 nohup으로 실행
    • 스프링 부트는 내장 톰캣을 사용해서 jar 파일만 있으면 바로 웹 애플리케이션 서버를 실행 가능
    • 애플리케이션 실행자가 터미널을 종료해도 애플리케이션은 계속 구동될 수 있도록 nohup 명령어를 사용

스크립트 생성을 완료했으면 스크립트에 실행 권한을 추가하고 실행해보자. ( ll 명령어로 확인해보면, deploy.sh에 x 권한이 추가된 것을 확인 가능 )

chmod +x ./deploy.sh
./deploy.sh

오케이 잘 되구 있어 ~
이번에는 nohup.out 파일을 열어 로그를 보자.
nohup.out은 실행되는 애플리케이션에서 출력되는 모든 내용을 가지고 있다.

애플리케이션 실행에 실패를 했다...
뚝딱뚝딱 고치러 가보자 🪛


외부 Security 파일 등록하기

ClientRegistrationRepository를 생성하기 위해서는 clientId과 clientSecret이 필수이다.
이 정보는 application-oauth.properties에 있는데, 이 파일은 보안 상의 이유로 git에서 제외했기 때문에 깃허브에는 없다...!

app 디렉토리에 properites 파일을 생성하고, 로컬에 있는 application-oauth.properties의 내용을 복붙하자.
( step 1 디렉토리에 하지 않는 이유는 나중에 step2, step3에서도 쓸 것이기 때문이다 )

그 다음 deploy.sh 파일을 수정하자.

nohup java -jar \
-Dspring.config.location=classpath:/application.properties,/home/ubuntu/app/application-oauth.properties \
$REPOSITORY/$JAR_NAME 2>&1 &
  • 스프링 설정 파일 위치를 지정
  • \ : 다음 라인도 같은 줄로 인식하도록 함
  • application.properties와 application-oauth.properties 사이 , 다음에 띄어쓰기 하지 말 것 > 나도 이것 대문에 계속 오류 났음

이제 deploy.sh를 다시 실행해보자.

구웃 ^ㅅ^


스프링 부트 프로젝트로 RDS 접근하기

이번에는 스프링 부트 프로젝트에서 RDS를 접근할 수 있도록 해보자.
RDS에서는 MariaDB를 사용 중인데, 이를 사용하기 위해선 몇 가지 작업이 필요하다.

  • 테이블 생성 - 기존 H2가 자동 생성해주던 테이블을 직접 쿼리로 생성해야 한다.
  • 프로젝트 설정 - 데이터베이스 드라이버 추가
  • EC2 설정 - EC2 서버 내부에서 접속 정보를 관리하도록 설정
    • 데이터베이스 접속 정보는 중요하게 보호해야 할 정보

RDS 테이블 생성

JPA 엔티티로 사용될 post, user 테이블과 스프링 세션이 사용될 테이블이 필요하다.
나는 저자의 깃허브를 참고하였다.
( 원래 post, user은 테스트 코드에서, 스프링 세션은 schema-my.sql에서 확인 가능하다. )
( Command+Shift+O로 schema-my.sql 검색해서 파일 확인 가능 )

create table posts (id bigint not null auto_increment, created_date datetime, modified_date datetime, author varchar(255), content TEXT not null, title varchar(500) not null, primary key (id)) engine=InnoDB;
create table user (id bigint not null auto_increment, created_date datetime, modified_date datetime, email varchar(255) not null, name varchar(255) not null, picture varchar(255), role varchar(255) not null, primary key (id)) engine=InnoDB;

CREATE TABLE SPRING_SESSION (
	PRIMARY_ID CHAR(36) NOT NULL,
	SESSION_ID CHAR(36) NOT NULL,
	CREATION_TIME BIGINT NOT NULL,
	LAST_ACCESS_TIME BIGINT NOT NULL,
	MAX_INACTIVE_INTERVAL INT NOT NULL,
	EXPIRY_TIME BIGINT NOT NULL,
	PRINCIPAL_NAME VARCHAR(100),
	CONSTRAINT SPRING_SESSION_PK PRIMARY KEY (PRIMARY_ID)
);

CREATE UNIQUE INDEX SPRING_SESSION_IX1 ON SPRING_SESSION (SESSION_ID);
CREATE INDEX SPRING_SESSION_IX2 ON SPRING_SESSION (EXPIRY_TIME);
CREATE INDEX SPRING_SESSION_IX3 ON SPRING_SESSION (PRINCIPAL_NAME);

CREATE TABLE SPRING_SESSION_ATTRIBUTES (
	SESSION_PRIMARY_ID CHAR(36) NOT NULL,
	ATTRIBUTE_NAME VARCHAR(200) NOT NULL,
	ATTRIBUTE_BYTES LONGVARBINARY NOT NULL,
	CONSTRAINT SPRING_SESSION_ATTRIBUTES_PK PRIMARY KEY (SESSION_PRIMARY_ID, ATTRIBUTE_NAME),
	CONSTRAINT SPRING_SESSION_ATTRIBUTES_FK FOREIGN KEY (SESSION_PRIMARY_ID) REFERENCES SPRING_SESSION(PRIMARY_ID) ON DELETE CASCADE
);

프로젝트 설정

먼저 build.gradle에 MariaDB 드라이버를 추가한다.

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.MySQL57Dialect
spring.jpa.properties.hibernate.dialect.storage_engine=innodb
spring.datasource.hikari.jdbc-url=jdbc:h2:mem://localhost/~/testdb;MODE=MYSQL

spring.session.store-type=jdbc

설정이 완료되었다면, 깃허브에도 푸시하자!

EC2 설정

OAuth와 마찬가지로 RDS 접속 정보도 보호해야 할 정보이니 EC2서버에 직접 설정 파일을 둔다.

nano ~/app/application-real-db.properties
spring.jpa.hibernate.ddl-auto=none
spring.datasource.url=jdbc:mariadb://(RDS 주소):3306/(데이터베이스 이름)
spring.datasource.username=(계정)
spring.datasource.password=(계정 비밀번호)
spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
  • spring.jpa.hibernate.ddl-auto=none : JPA로 테이블이 자동 생성되는 옵션을 None으로 지정
    • RDS에는 실제 운영으로 사용될 테이블이니 새로 만들어지지 않도록 주의할 것!

real profile을 쓸 수 있도록 deploy.sh도 수정하자.

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

deploy.sh를 다시 실행하자.

curl localhost:8080

을 입력했을 때 html 코드가 보인다면 성공적으로 수행된 것이다 :)
( html 코드여도 그 내용이 500 Error이면 실패한 것임! )

🪛 삽질 기록

분명 ddl-auto 옵션을 none으로 변경해주었는데도, 테이블이 계속 자동 생성되는 문제로 몇 시간동안 삽질하였다😡

⚡️ 내 프로젝트 : spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL57Dialect
⚡️ 책과 동일한 버전의 프로젝트 : spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect

MySQL5InnoDBDialect 가 아닌 MySQL57Dialect 임에 주의하자 !!!

(+) 만약 8080 포트가 사용 중이라고 뜬다면, EC2를 재부팅해보자 !


EC2에서 소셜 로그인하기

curl 명령어로 EC2에 서비스가 잘 배포된 것을 확인했다.
이번에는 브라우저에서 확인해보자.

그 전에 몇 가지 밑작업을 해야 한다.

AWS 보안 그룹 변경

인바운드 규칙을 아래와 같이 추가하여 8080 포트를 열어준다

AWS EC2 도메인으로 접속

EC2 대시보드에서 인스턴스의 퍼블릭 DNS를 복사한다.

(퍼블릭 DNS):8080

으로 접속하면 브라우저에서 우리의 EC2 서버에 접근할 수 있게 된다!

하지만! 현재 상태에서는 해당 서비스에 EC2의 도메인을 등록하지 않았기 때문에 구글과 네이버 로그인이 작동하지 않는다.

구글과 네이버 로그인 서비스에 EC2의 도메인을 추가하자.

⚓️ 구글에 EC2 주소 등록

https://console.cloud.google.com/ 로 들어가자

API 및 서비스 클릭
OAuth 동의 화면 클릭 > 앱 수정 클릭
승인된 도메인에서 도메인 추가

사용자 인증 정보 클릭 > 서비스 이름 클릭
승인된 리디렉션 URI에 http://(퍼블릭 DNS):8080/login/oauth2/code/google를 추가하고 저장한다.

잘 된다 헤헤

⚓️ 네이버에 EC2 주소 등록

https://developers.naver.com 로 들어간 뒤 내 애플리케이션으로 들어간다.

API 설정 클릭 > 아래로 스크롤하여 PC 웹 항목 찾기
서비스 URL과 Callback URL 수정

  • 서비스 URL - 로그인을 시도하는 서비스가 네이버에 등록된 서비스인지 판단하기 위한 항목
    • 8080 포트를 제외하고 도메인 주소만 입력
    • 하나만 등록 가능하므로 EC2의 주소를 등록하면 localhost가 안됨
    • localhost도 하고 싶으면 네이버 서비스 하나 더 생성하여 키를 발급받는 것도 방법
  • Callback URL - 전체 주소를 등록

이렇게 프로젝트 마무리까지 끝났다!

profile
[~2023.04] 블로그 이전했습니다 ㅎㅎ https://leeeeeyeon-dev.tistory.com/

0개의 댓글