BOOK 2 - 스프링부트와 AWS로 혼자 구현하는 웹 서비스(7)

스프링부트와 AWS로 혼자 구현하는 웹 서비스 - 7

EC2 서버에 프로젝트 배포

git clone 후 테스트 수행

git 설치 및 프로젝트 클론

  • EC2 접속 후 인스턴스에 git을 먼저 설치한다.

    sudo yum install git
    
    • 설치 후 git --version 명령어로 설치 상태를 확인한다.
  • git 설치 후 디렉토리를 생성하여 프로젝트를 클론한다.

    mkdir ~/app/step1
    git clone [깃허브 주소]
    

프로젝트 테스트 수행

  • 프로젝트 디렉토리로 이동하여 테스트를 실행한다.

    ./gradlew test
    

    image

    • 만약 gradlew 실행 권한이 없으면 실행 권한을 추가한 뒤 다시 수행하면 된다.

      chmod +x ./gradlew
      
    • 현재 EC2에는 gradle을 설치하지 않았지만 프로젝트 내부에 포함된 gradlew 파일로 gradle task를 수행할 수 있다.

      • gradlew : gradle이 설치되지 않은 환경 혹은 버전이 다른 상황에서도 해당 프로젝트에 한해서 gradle을 쓸 수 있도록 지원하는 wrapper 파일

배포 스크립트 생성

  • 코드를 실제 서버에 반영하는 것을 배포라고 하고, 넓게는 다음의 과정을 모두 포함한다.
    • git clone 혹은 git pull 을 통해 새 버전의 프로젝트를 받음
    • Gradle이나 Maven을 통해 프로젝트 테스트와 빌드
    • EC2 서버에서 해당 프로젝트 실행 및 재실행
  • 위의 모든 과정을 배포할 때마다 개발자가 명령어를 실행하는 것은 불편하고 비효율적이다.
    • 해당 명령어들을 shell script로 작성하여 자동으로 위 과정들이 진행되도록 한다.

deploy.sh

  • vim으로 deploy.sh 스크립트 파일을 프로젝트 디렉토리 하위에 생성한다.

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

REPOSITORY=/home/ec2-user/app/step1
PROJECT_NAME=all-review

cd $REPOSITORY/$PROJECT_NAME/

echo "> Git Pull"

git pull

echo "> Project Build Start"

./gradlew build

echo "> Move to REPOSITORY directory"

cd $REPOSITORY

echo "> Copy Build File"

cp $REPOSITORY/$PROJECT_NAME/build/libs/*.jar $REPOSITORY/

CURRENT_PID=$(pgrep -f ${PROJECT_NAME}*.jar)

echo "> Check Application PID : $CURRENT_PID"

if [ -z "$CURRENT_PID" ]; then
	echo "> There's no executing application"
else
	echo "> Kill $CURRENT_PID"
	kill -15 $CURRENT_PID
	sleep 5
fi

JAR_NAME=$(ls -tr $REPOSITORY/ | grep *.jar | tail -n 1)

echo "> New Applicaion Deployment : $JAR_NAME"

nohup java -jar $REPOSITORY/$JAR_NAME 2>&1 &
  • REPOSITORY=/home/ec2-user/app/step1
    • 프로젝트 디렉토리 주소와 프로젝트 이름은 스크립트 내에서 자주 사용하는 값이기 때문에 변수로 저장한다.
    • shell에서는 타입 없이 선언한다.
    • $변수명 으로 변수를 사용할 수 있다.
  • ./gradlew build : 프로젝트 내부의 gradlew 로 build를 수행한다.
  • CURRENT_PID=$(pgrep -f ${PROJECT_NAME}*.jar)
    • 기존에 수행중이던 springboot application의 pid값을 추출한다.
      • pgrep 은 process id만 추출하는 명령어이다.
      • -f 옵션은 프로세스 이름으로 탐색하는 옵션이다.
  • JAR_NAME=$(ls -tr $REPOSITORY/ | grep *.jar | tail -n 1)
    • 새로 실행할 jar 파일명을 추출한다.
    • 여러 jar 파일 중 최신 jar 파일을 찾아 이름을 저장해야 하기 때문에 tail -n 명령어를 사용한다.
  • nohup java -jar $REPOSITORY/$JAR_NAME 2>&1 &
    • 찾은 jar 파일명으로 해당 jar 파일을 nohup 으로 실행한다.
    • 일반적으로 자바를 실행할 때는 java -jar 라는 명령어를 사용하지만, 이렇게 실행할 경우 사용자가 터미널 접속을 끊을 때 application도 같이 종료된다.
    • 실행자가 터미널을 종료해도 application은 계속 구동될 수 있도록 nohup 명령어를 사용한다.

deploy.sh 스크립트 파일 실행

  • 다음 명령어로 스크립트 파일을 실행한다.

    ./deploy.sh	
    
  • 실행되는 application에서 출력되는 모든 내용은 nohup.out 파일을 통해 확인할 수 있다.

    vim nohup.out
    

Security 파일 등록

  • 이전에 application-oauth.properties 파일을 git에서 제외시켜 현재 ec2 서버에는 해당 파일이 존재하지 않는다.
    • 서버에서 clientIdclientSecret 값을 직접 가지고 있도록 한다.

application-oauth.properties

  • app 디렉토리에 해당 설정 파일을 생성한다.

    vim /home/ec2-user/app/application-oauth.properties
    
  • 로컬 설정 파일에 있는 내용을 그대로 붙여넣는다.

deploy.sh에 설정 파일 지정

  • application-oauth.properties 설정 파일을 프로젝트가 사용하도록 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
      • 스프링 설정 파일 위치를 저장한다.
      • 기본 옵션을 담고 있는 application.properties 파일과 OAuth 설정을 담고 있는 application-oauth.properties 파일의 위치를 지정한다.
      • classpath 가 붙으면 jar 안에 있는 resources 디렉토리를 기준으로 경로가 생성된다.
      • application-oauth.properties 파일은 외부에 존재하기 때문에 절대경로를 사용한다.

deploy.sh 스크립트 실행

  • 수정한 deploy.sh 스크립트를 다시 실행한다.

    image

SpringBoot 프로젝트에 RDS 연동

RDS 테이블 생성

Entity 테이블 생성

CREATE TABLE review (
	id bigint not null auto_increment primary key,
	title varchar(500) not null,
	content TEXT not null,
	userId bigint not null,
	createdAt datetime,
	modifiedAt datetime
) engine=InnoDB;
CREATE TABLE user (
	id bigint not null auto_increment primary key,
  name varchar(255) not null,
  email varchar(255) not null,
  picture varchar(255),
  role varchar(255) not null,
  createdAt datetime,
  modifiedAt datetime
) engine=InnoDB;

Spring Session 테이블 생성

  • schema-mysql.sql 파일에 스프링 세션 테이블 생성 쿼리문이 있어 해당 쿼리문을 실행해주면 된다.

    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)
    ) ENGINE=InnoDB ROW_FORMAT=DYNAMIC;
      
    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 BLOB 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
    ) ENGINE=InnoDB ROW_FORMAT=DYNAMIC;
      
    

프로젝트 설정

  • MariaDB 드라이버를 build.gradle 에 등록한다.

    compile("org.mariadb.jdbc:mariadb-java-client")
    
  • src/main/resources 하위에 application-production.properties 파일을 생성하여 production 환경의 RDS 설정을 추가해준다.

    • application-production.properties 파일을 생성하면 profile=production 인 환경이 구성된다.
    spring.profiles.include=oauth,prod-db
    spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
    spring.session.store-type=jdbc
    

EC2 설정

  • RDS 접속 정보 역시 github 같은 공개적인 저장소에 업로드할 수 없는 보호해야할 정보이므로 EC2 서버에 직접 설정 파일을 생성한다.

prod-db 설정 파일 생성

  • app 디렉토리에 application-prod-db.properties 파일을 생성한다.

    spring.jpa.hibernate.ddl-auto=none
    spring.datasource.url=jdbc:mariadb://rds주소:포트번호/DB명
    spring.datasource.username=db계정
    spring.datasource.password=db계정 비밀번호
    spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
    
    • spring.jpa.hibernate.ddl-auto=none
      • JPA로 테이블이 자동 생성되는 옵션을 none으로 지정한다.
      • 즉, 스프링부트에서 테이블을 새로 생성하지 않도록 설정해준다.

deploy.sh에 production profile 설정

...
nohup java -jar \-Dspring.config.location=classpath:/application.properties,/home/ec2-user/app/application-oauth.properties,/home/ec2-user/app/application-prod-db.properties -Dspring.profiles.active=production \$REPOSITORY/$JAR_NAME 2>&1 &
  • Dspring.profiles.active=production
    • application-production.properties 설정 파일을 활성화 시킨다.
    • application-production.propertiesspring.profiles.include 옵션 때문에 prod-db 역시 함께 활성화 된다.
  • 수정 후 deploy.sh 파일을 다시 실행하면 어플리케이션이 정상적으로 구동되는 것을 확인할 수 있다.
    • application 구동 후 [인스턴스의 퍼블릭 DNS]:8080/hello 로 요청을 보내면 정상적으로 oauth 로그인 페이지가 나오는 것을 볼 수 있다.

마지막으로 해당 EC2 인스턴스의 도메인 주소를 구글과 로그인 oauth2 클라이언트의 Callback URL(Redirection URI)로 설정해주면 인스턴스 서버에서 로그인이 가능해진다.