Super Kawaii Cute Cat Kaoani 스프링 부트와 AWS로 혼자 구현하는 웹 서비스(8)

스프링 부트와 AWS로 혼자 구현하는 웹 서비스(8)

2024. 1. 22. 01:42
728x90
SMALL

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

⚡ EC2에 프로젝트 Clone 받기

✔ 깃허브에서 코드를 받아올 수 있게 EC2에 깃 설치!

sudo yum install git

 

 설치가 되면 버전 확인

git --version

 

 설치가 되면 git clone으로 프로젝트를 저장할 디렉토리 생성

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

 

 생성된 디렉토리로 이동

cd ~/app/step1

 

 본인의 깃허브 웹페이지에서 https 주소 복사
-> git clone 진행

git clone 복사한 주소

 

git clone이 끝났으면 클론된 프로젝트로 이동해서 파일들이 잘 복사되었는지 확인

cd 프로젝트명
ll

 

-> 프로젝트의 코드들이 모두 있으면 성공!

 

✔ 잘 수행되는지 검증

./gradlew test

 

  • 5장의 "기존에 Security 적용하기"까지 잘 적용됐다면 테스트 통과
  • 테스트가 실패해서 수정하고 깃허브에 푸시했다면 프로젝트 폴더 안에서 다음 명령어 사용 git pull
  • 만약 gradlew 실행 권한이 없다는 메세지가 뜬다면
    • -bash: ./gradlew: Permission denied
    • chmod +x ./gradlew

나는 자꾸 중간에 멈춤 현상이 발생해서 에러도 안떴음...
참고 포스팅
용량 문제라고 해서 포스팅 참고해서 해결!!

 

-> 깃을 통해 프로젝트 클론과 풀까지 잘 진행되었음

 

 

 

⚡ 배포 스크립트 만들기

배포 : 작성한 코드를 실제 서버에 반영하는 것

  • git clone 혹은 git pull을 통해서 새 버전의 프로젝트를 받음
  • Gradle이나 Maven을 통해 프로젝트 테스트와 빌드
  • EC2 서버에서 해당 프로젝트 실행 및 재실행

 배포할 때마다 개발자가 하나하나 명령어를 실행하는 것은 불편함

  쉘 스크립트로 작성해 스크립트만 실행하면 앞의 과정이 차례대로 진행됨

  • 쉘 스크립트와 빔은 서로 다른 역할임
    • 쉘 스크립트 : .sh라는 파일 확장자를 가진 파일
      • 리눅스에서 기본적으로 사용할 수 있는 스크립트 파일의 한 종류
    • 빔 : 리눅스 환경과 같이 GUI가 아닌 환경에서사용할 수 있는 편집 도구
      • 리눅스에선 빔 외에도 이맥스, 나노 등 지원(가장 대중적인 것은 빔)
  •  

 

 

🔶 ~/app/step1deploy.sh 파일을 하나 생성함

vim ~/app/step1/deploy.sh
  • 빔을 처음 사용하는 사람들이라면?
    • 빔만의 사용법이 있으니 빔 가이드를 참고해서 공부!!추천

 

#!/bin/bash

REPOSITORY=/home/ec2-user/app/step1
PROJECT_NAME=springboot-webservice

cd $REPOSITORY/$PROJECT_NAME

echo "> Git Pull"
git pull

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

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

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

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

echo "현재 구동 중인 애플리케이션 pid: $CURRENT_PID"
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/ec2-user/app/step1

  • 프로젝트 디렉토리 주소는 스크립트 내에서 자주 사용하는 값이기 때문에 이를 변수로 지정
  • 마찬가지로 PROJECT_NAME=springboot-webservice도 동일하게 변수로 자정
  • 쉘에서는 타입 없이 선언하여 저장
  • 쉘에서는 $변수명으로 변수 사용

cd $REPOSITORY/$PROJECT_NAME/

  • 제일 처음 git clone 받았던 디렉토리로 이동
  • 바로 위의 쉘 변수 설명을 따라 /home/ec2-user/app/step1/springboot-webservice 주소로 이동

git pull

  • 디렉토리 이동 후, master 브랜치의 최신 내용을 받음

./gradlew build

  • 프로젝트 내부의 gradlew로 build 수행

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

  • build의 결과물인 jar 파일을 복사해 jar 파일을 모아둔 위치로 복사함

CURRENT_PID=$(pgrep -f springboot-webservice)

  • 기존에 수행 중이던 스프링 부트 애플리케이션을 종료
  • pgrep은 process id만 추출하는 명령어
  • -f 옵션은 프로세스 이름으로 찾음

if ~ else ~ fi

  • 현재 구동 중인 프로세스가 있는지 없는지를 판단해서 기능 수행
  • process id 값을 보고 프로세스가 있으면 해당 프로세스를 종료

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

  • 새로 실행할 jar 파일명 찾음
  • 여러 jar 파일이 생기기 때문에 tail -n로 가장 나중의 jar 파일(최신 파일)을 변수에 저장

nohup java -jar @REPOSITORY/$JAR_NAME 2>&1 &

  • 찾은 jar 파일명으로 해당 jar 파일을 nohub으로 실행
  • 스프링 부트의 장점으로 특별히 외장 톰캣을 설치할 필요가 없음
  • 내장 톰캣을 사용해서 jar 파일만 있으면 바로 웹 애플리케이션 서버를 실행할 수 있음
  • 일반적으로 자바를 실행할 때는 java -jar라는 명령어를 사용하지만, 이렇게 하면 사용자가 터미널 접속을 끊을 때 애플리케이션도 같이 종료됨
  • 애플리케이션 실행자가 터미널을 종료해도 애플리케이션은 계속 구동될 수 있도록 nohup 명령어 사용

 

🔶 생성한 스크립트에 실행 권한 추가

chmod +x ./deploy.sh
  •  x 권한이 추가된 것을 확인할 수 있음

 

🔶 이 스크립트를 명령어로 실행

./deploy.sh
  •  로그들이 출력되며 애플리케이션이 실행됨

 

🔶 nohup.out 파일을 열어 로그 확인

vim nohup.out
  • 실행되는 애플리케이션에서 출력되는 모든 내용을 가지고 있음
  • 제일 아래로 가면 ClientRegistrationRepository를 찾을 수 없다(that could not be found.)는 에러가 발생하면 애플리케이션 실행에 실패한 것임!
  • ClientRegistrationRepository를 생성하려면 clientId와 clientSecret가 필수임
    • 로컬 PC에서 실행할 때는 application-oauth.yml이 있어서 실행됨
    • 지금은 .gitignore로 git에서 제외 대상이라서 실행되지 않음
    • 애플리케이션을 실행하기 위해 공개된 저장소에 ClientId와 ClientSecret을 올릴 수는 없으니 서버에서 직접 이 설정들을 가지고 있게 함
    • 이 책에서는 모두 깃허브의 공개된 저장소를 기반으로 함 -> 이후에 사용하게 될 CI 서비스인 Travis CI는 비공개된 저장소를 사용할 경우 비용 부과

 

❗ 외부 Security 파일을 등록하면서 해결

 

반응형

 

⚡ 외부 Security 파일 등록하기

🔶 app 디렉토리에 yml 파일 생성

vim /home/ec2-user/app/application-oauth.yml
  • 로컬에 있는 내용 그대로 복붙
  • :wq으로 저장하고 종료

 

🔶 방금 만든 yml파일을 사용하도록 deploy.sh파일 수정

...nohup java -jar \ -Dspring.config.location-classpath:/application.yml,/home/ec2-user/app/application-oauth.yml \ $REPOSITORY/$JAR_NAME 2>&1 &

-Dspring,config.location

  • 스프링 설정 파일 위치를 지정
  • 기본 옵션들을 담고 있은 application.yml과 OAuth 설정들을 담고 있는 application-oauth.yml의 위치를 지정
  • classpath가 붙으면 jar 안에 잇는 resources
    디렉토리를 기준으로 경로가 생성됨
  • application-oauth.yml은 절대 경로를 사용
    • 외부에 파일이 있기 때문

 

🔶 수정을 하고 deploy.sh 다시 실행
성공!

 

 

 

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

✔ RDS는 MariaDB를 사용 중

  • MariaDB에서 스프링부트로 프로젝트를 실행하기 위해선 몇 가지 작업이 필요함
    • 테이블 생성 : H2에서 자동 생성해주던 테이블들을 MariaDB에선 직접 쿼리를 이용해서 생성해야 함
    • 프로젝트 설정 : 자바 프로젝트가 MariaDB에 접근하려면 데이터베이스 드라이버가 필요
      • MariaDB에서 사용 가능한 드라이버를 프로젝트에 추가
    • EC2(리눅스 서버)설정 : 데이터베이스의 접속 정보는 중요하게 보호해야 할 정보임
      • 공개되면 외부에서 데이터를 모두 가져갈 수 있기 때문
      • 프로젝트 안에 접속 정보를 가지고 있다면 깃허브와 같이 오픈된 공간에선 누구나 해킹할 위험이 있음
      • EC2 서버 내부에서 접속 정보를 관리하도록 설정
    •  
  •  

 

RDS 테이블 생성

✔ 생성할 테이블

  • JPA가 사용될 엔티티 테이블
    • 테스트 코드 수행 시 로그로 생성되는 쿼리를 사용하면 됨
    • create table 부터 복사해서 사용
Hibernate: create table member (created_date datetime(6), id bigint not null auto_increment, modified_date datetime(6), email varchar(255) not null, name varchar(255) not null, picture varchar(255), role enum ('GUEST','USER') not null, primary key (id)) engine=InnoDB 
Hibernate: create table posts (created_date datetime(6), id bigint not null auto_increment, modified_date datetime(6), title varchar(500) not null, author varchar(255), content TEXT not null, primary key (id)) engine=InnoDB
  • 스프링 세션이 사용될 테이블
    • schema-mysql.sql 파일에서 스프링 세션 테이블 확인
    • File 검색(Command + Shift + O)
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;

 

로그에서 engine=innoDB가 안떠서 yml을 수정했다

 

spring:
  jpa:
    show_sql: true
    properties:
      hibernate:
        dialect: org.hibernate.dialect.MySQL57Dialect

프로젝트 생성

✔ MariaDB 드라이버를 build.gradle에 등록
implementation("org.mariadb.jdbc:mariadb-java-client")

 

✔ 서버에서 구동될 환경 구성

  • application-real.yml 생성
    • application-real.yml로 파일을 만들면 profile=real인 환경이 구성됨
    • 실제 운영될 환경이기 때문에 보안/로그상 이슈가 될 만한 설정들을 모두 제거하며 RDS 환경 profile 설정이 추가됨
spring:
  jpa:
    properties:
      hibernate:
        dialect: org.hibernate.dialect.MySQL57Dialect
  session:
    store-type: jdbc
  profiles:
    include: oauth,real-db

-> github로 푸시

 

 

EC2 설정

✔ OAuth와 마찬가지로 RDS 접속 정보도 보호해야 할 정보임

  •  EC2 서버에 직접 설정 파일 둠

 

app 디렉토리에 application-real-db.yml파일 생성

vim ~/app/application-real-db.yml
spring:
  datasource:
    hikari:
      driver-class-name: org.mariadb.jdbc.Driver
      username: db계정
      jdbc-url: jdbc:mariadb://rds주소:포트명(기본은 3306)/database명
      password: db 계정 비번
  jpa:
    hibernate:
      ddl-auto: none

🔶 spring.jpa.hibernate.ddl-auto=none

  • JPA로 테이블이 자동 생성되는 옵션을 None(생성 X)으로 지정
  • RDS에는 실제 운영으로 사용될 테이블이니 절대 스프링 부트에서 새로 만들지 않도록 해야 함
  • 이 옵션을 하지 않으면 자칫 테이블이 모두 새로 생성될 수 있음
  • 중요함

🔶 책에는 hikari가 없었는데 그럼 자꾸 오류가 났다 이걸 해줘야하나보다 앞으로

 

 

 

deploy.shreal profile을 쓸 수 있도록 개선

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

🔶 -Dspring.profiles.active=real

  • application-real.yml을 활성화시킴
  • application-real.ymlspring.profiles.include=oauth, real-db 역시 활성화

🔶 다시 deploy.sh 실행

🔶 nohup.out에 해당 로그가 있으면 성공

 

  • curl localhost:8081 쳤을 때 html 코드가 나오면 성공!

ㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠ
계속 error 'jdbcSessionDataSourceScriptDatabaseInitializer' 어쩌구저쩌구 에러가 뜨는데
해결이 안돼서 한참 걸렸다..
혹시나 하고 application.yml 파일에 있는 datasources 부분을 주석처리 했더니 성공...

 

EC2에서 소셜 로그인 하기

✔ AWS 보안 그룹 변경

  • EC2에 스프링 부트 프로젝트가 8081 포트로 배포되었으니, 8081 포트가 보안 그룹에 열려있는지 확인
    • ec2 페이지에서 보안그룹 -> 포트 범위 확인

✔ AWS EC2 도메인으로 접속

  • 인스턴스 -> 퍼블릭 DNS
    • 이 주소가 EC2에 자동으로 할당된 도메인임
    • 이 주소로 EC2 서버에 접근 가능
    • 이 주소에 8081 포트를 붙여 브라우저에 입력

✔ EC2의 도메인을 등록

  •  localhost만 등록함

 

🔶 구글에 EC2 주소 등록

  • 구글 웹 콘솔로 접속 -> 내 프로젝트 -> API 및 서비스 -> 사용자 인증 정보
  • OAuth 동의 화면 -> 승인된 도메인에 http:// 없이 EC2의 퍼블릭 DNS 등록
  • 사용자 인증 정보 -> 내 서비스 이름 클릭
  • 승인된 리다이렉션 URI에 http://퍼블릭DNS주소:8081/login/oauth2/code/google

 

🔶 네이버에 EC2 주소 등록

  • 네이버 개발자 센터로 접속해서 내 애플리케이션으로 이동
  • 내 프로젝트로 들어가서 API 설정
  • PC웹 -> 서비스 URL, Callback URL 2개 수정
  • 서비스 URL
    • 로그인을 시도하는 서비스가 네이버에 등록된 서비스인지 판단하기 위한 항목
    • 8081 포트는 제외하고 실제 도메인 주소만 입력
    • 네이버에서 아직 지원되지 않아 하나만 등록 가능
      • EC2의 주소를 등록하면 localhost는 이제 안됨
    • 개발 단계에서는 등록하지 않는 것을 추천
    • localhost도 테스트하고 싶으면 네이버 서비스를 하나 더 생성해서 키를 발급받으면 됨
  •  
  • Callback URL
    • 전체 주소를 등록
      • EC2 퍼블릭 DNS:8081/login/oauth2/code/naver

 

❗ EC2에 배포 완료

 

문제점

  • 수동 실행되는 Test
    • 본인이 짠 코드가 다른 개발자의 코드에 영향을 끼치지 않는지 확인하기 위해 전체 테스트를 수행해야만 함
    • 현재 상태에선 항상 개발자가 작업을 진행할 때마다 수동으로 전체 테스트를 수행해야만 함
  • 수동 Build
    • 다른 사람이 작성한 브랜치와 본인이 작성한 브랜치가 합쳐졌을 때(Merge) 이상이 없는지는 Build를 수행해야만 알 수 있음
    • 이를 매번 개발자가 직접 실행해봐야만 함

-> 깃허브에 푸시를 하며 자동으로 Test & Build & Deploy가 진행되도록 수정해야 함

 

 

 

 

스프링 부트와 AWS로 혼자 구현하는 웹 서비스_이동욱 지음 책을 읽고 정리하는 글입니다

 

 

 

 

 


PREV

 

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

📌 AWS에 데이터베이스 환경을 만들기 - AWS RDS 웹 서비스 백엔드에서는 코드 작성만큼 중요한 것이 데이터베이스를 다루는 일임 직접 데이터베이스를 설치하지 않고 AWS에서 제공하는 관리형 서

nyeroni.tistory.com

 

NEXT

 

스프링 부트와 AWS로 혼자 구현하는 웹 서비스(9)

📌 Travis CI배포 자동화 코드가 푸시되면 자동으로 배포한다. 여러 개발자의 코드가 실시간으로 병합되고 테스트가 수행되는 환경, master 브랜치가 푸시되면 배포가 자동으로 이루어지는 환경을

nyeroni.tistory.com

 

728x90
LIST

BELATED ARTICLES

more