이제까지 스프링 부트로 서비스 코드를 개발했고, EC2+RDS를 이용하여 배포 환경을 구성하였다.
이제 이것을 합체시켜 실제 서비스를 한번 배포해보자!
깃허브에서 코드를 받아오기 위해 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 파일이다.
나도 처음 했을 때는 execute 단계에서 5분이 넘어도 진척이 없었다 :(
인스턴스 문제인가 싶어 EC2도 건드려봤지만 해결되지 않았다...!
그러다 생각난 UMC . . . !
디스코드 질문방에서 해결 방법을 찾을 수 있었다! ( 최강 대학생 IT 연합 동아리 UMC! )
어쨌든 서론은 여기까지고 !!
AWS 프리티어의 메모리가 적어
발생하는 문제였다. (현업에서는 발생하지 않을 것이다.)
도커나 swap 메모리(가상 메모리)를 사용하여 해결할 수 있다.
작성한 코드를 실제 서버에 반영하는 것을 배포라고 한다.
앞선 과정을 배포할 때마다 하나하나 명령어를 실행하는 것은 불편함이 많다.
이를 쉘 스크립트로 작성해 스크립트만 실행하면 앞의 과정이 차례로 진행되도록 하자.
쉘 스크립트?
.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)
: 기존에 수행 중이던 스프링 부트 애플리케이션을 종료if ~ els ~ fi
: 현재 구동 중인 프로세스가 있는지 없는지 판단하여 기능을 수행JAR_NAME=$(ls -tr $REPOSITORY/ | grep jar | tail -n 1)
: 새로 실행할 jar 파일명을 찾음nohup java -jar $REPOSITORY/$JAR_NAME 2>&1 &
: 찾은 jar 파일명으로 해당 jar 파일을 nohup으로 실행스크립트 생성을 완료했으면 스크립트에 실행 권한을 추가하고 실행해보자. ( ll
명령어로 확인해보면, deploy.sh에 x 권한이 추가된 것을 확인 가능 )
chmod +x ./deploy.sh
./deploy.sh
오케이 잘 되구 있어 ~
이번에는 nohup.out 파일을 열어 로그를 보자.
nohup.out은 실행되는 애플리케이션에서 출력되는 모든 내용을 가지고 있다.
애플리케이션 실행에 실패를 했다...
뚝딱뚝딱 고치러 가보자 🪛
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 &
\
: 다음 라인도 같은 줄로 인식하도록 함,
다음에 띄어쓰기 하지 말 것 > 나도 이것 대문에 계속 오류 났음이제 deploy.sh를 다시 실행해보자.
구웃 ^ㅅ^
이번에는 스프링 부트 프로젝트에서 RDS를 접근할 수 있도록 해보자.
RDS에서는 MariaDB를 사용 중인데, 이를 사용하기 위해선 몇 가지 작업이 필요하다.
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
설정이 완료되었다면, 깃허브에도 푸시하자!
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으로 지정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를 재부팅해보자 !
curl 명령어로 EC2에 서비스가 잘 배포된 것을 확인했다.
이번에는 브라우저에서 확인해보자.
그 전에 몇 가지 밑작업을 해야 한다.
인바운드 규칙을 아래와 같이 추가하여 8080 포트를 열어준다
EC2 대시보드에서 인스턴스의 퍼블릭 DNS를 복사한다.
(퍼블릭 DNS):8080
으로 접속하면 브라우저에서 우리의 EC2 서버에 접근할 수 있게 된다!
하지만! 현재 상태에서는 해당 서비스에 EC2의 도메인을 등록하지 않았기 때문에 구글과 네이버 로그인이 작동하지 않는다.
구글과 네이버 로그인 서비스에 EC2의 도메인을 추가하자.
https://console.cloud.google.com/ 로 들어가자
API 및 서비스 클릭
OAuth 동의 화면 클릭 > 앱 수정 클릭
승인된 도메인에서 도메인 추가
사용자 인증 정보 클릭 > 서비스 이름 클릭
승인된 리디렉션 URI에 http://(퍼블릭 DNS):8080/login/oauth2/code/google
를 추가하고 저장한다.
잘 된다 헤헤
https://developers.naver.com 로 들어간 뒤 내 애플리케이션으로 들어간다.
API 설정 클릭 > 아래로 스크롤하여 PC 웹 항목 찾기
서비스 URL과 Callback URL 수정
이렇게 프로젝트 마무리까지 끝났다!