ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 8장) EC2 서버에 프로젝트를 배포해 보자
    Java & Spring/스프링 부트와 AWS로 혼자 구현하는 웹 서비스 2020. 7. 14. 23:55
    반응형

    EC2에 프로젝트 Clone 받기

    • git 설치

      • sudo yum install git으로 git 설치
      • git --version으로 설치 상태 체크
    • 프로젝트 저장할 디렉토리 만들기

      • mkdir ~/app && ~app/step1 생성
      • cd ~/app/step1 이동
    • git clone하기

      • git clone 깃헙주소
    • 테스트로 코드 검증하기

      • ./gradlew test
      • 성공했다면, BUILD SUCCESSFUL이 뜬다.
      • 여기서 한 번 실패가 떴는데, 실행권한이 없다는 표시가 떴다.
      • chmod +x ./gradlew로 권한을 부여해줘서 해결

    배포 스크립트 만들기

    • 배포할 때마다 개발자가 하나하나 명령어를 실행하는 것은 비효율적

      • 쉘 스크립트로 작성해서 스크립트만 실행하면, 위의 과정이 진행되도록 생성

      • 쉘 스크립트

        • .sh라는 파일 확장자를 가진 파일
        • 노드JS가 .js라는 파일을 통해 서버에서 작동하듯, 쉘 스크립트는 리눅스에서 기본적으로 사용 가능한 스크립트 파일의 한 종류
        • Vim은 편집도구
      • ~/app/step1/에 deploy.sh 파일 생성

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

          REPOSITORY=/home/ec2-user/app/step2
          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

          echo "> 새 어플리케이션 배포"

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

          echo "> JAR Name: $JAR_NAME"

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

        • REPOSITORY=/home/ec2-user/app/step2

          • 프로젝트 디렉토리 주소는 스크립트 내에서 자주 사용 ~> 변수로 저장
          • PROJECT_NAME 도 마찬가지
          • 쉘에서는 타입 없이 선언해서 저장
          • $ 변수명으로 변수 사용
        • cd $REPOSITORY/$PROJECT_NAME/

          • git clone을 받았던 디렉토리로 이동
        • git pull

          • 디렉토리 이동 후, master 브랜치에서 최신 내용을 pull
        • ./gradlew build

          • 프로젝트 내부의 gradlew로 build 수행
        • cp $REPOSITORY/PROJECT_NAME/build/libs/*.jar $REPOSITORY/

          • build의 결과물인 jar 파일을 복사해 jar 파일을 모아둔 위치로 복사함
        • CURRENT_PID=$(pgrep -f ${PROJECT_NAME}.*.jar)

          • 기존에 수행 중이던 스프링 부트 애플리케이션 종료
          • pgrep => process id만 추출하는 명령어
          • -f 옵션 => 프로세스 이름으로 찾음
        • JAR_NAME=$(ls -tr $REPOSITORY/ | grep jar | tail -n 1)

          • 새로 실행할 jar 파일명을 찾는다.
          • 여러 jar파일이 생겨서, tail -n으로 가장 나중의 jar 파일(최신)을 변수에 저장
        • nohup java -jar $JAR_NAME > $REPOSITORY/nohup.out 2>&1 &

          • 찾은 jar파일명으로 해당 jar 파일을 nohup으로 실행
          • 일반적으로 자바를 실행할 때는 java -jar라는 명령어를 사용 ~> 사용자가 터미널 접속을 끊을 때 어플리케이션도 종료됨
          • 애플리케이션 실행자가 터미널을 종료해도 애플리케이션은 계속 구동될 수 있도록 nohup 명령어를 사용
      • 쉘 스크립트에 권한 추가

        • chmod +x ./deploy.sh
      • 쉘 스크립트 실행

        • ./deploy.sh
        • 여기서 오류가 한 번 발생했었는데, 쉘 스크립트 내부에서 변수를 선언하고 저장할 때 띄어쓰기를 하면 안된다. (a=1 (O) <------> a = 1 (X) 이걸로 시간을 조금 낭비했다)
        • 성공적으로 실행이 되면 BUILD SUCCESSFUL이 나온다.
      • 실행되는 애플리케이션에서 출력되는 내용 확인

        • vim nohup.out
        • nohup으로 실행하면, 기본적으로 로그가 nohup.out에 저장된다.
        • 위의 실행결과로 Fail이 뜨는데, Security 파일인(application-oauth.properties가 없기 때문)

    외부 Security 파일 등록하기

    • 위에서 ClientRegistrationRepository 생성 하려고 할 때, clientId와 clientSecret이 없었다. (생성 시 필수) ~> application-oauth.properties가 .gitignore에 포함됐기 때문 ( 민감정보 보호 )
    • step1이 아닌, app 디렉토리에 properties 파일을 생성 (다른 디렉토리에서도 사용하기 위해)
      • vim /home/ec2-user/app/application-oauth.properties
        • 로컬에 있는 application-oauth.properties를 그대로 복붙하기
          • putty에서는 로컬에서 저장한 클립보드를 오른쪽 마우스 클릭으로 붙여넣기 가능
    • 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
        • 스프링 설정 파일 위치를 지정
        • classpath가 붙으면 jar 안에 있는 resources 디렉토리를 기준으로 경로가 생성
        • application-oauth.properties은 절대경로로 사용 ~> 외부에 파일이 있기 때문에
      • 여기에서도 에러가 한 번 발생했었다. 띄어쓰기에 주의해야함

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

    • RDS는 MariaDB 사용, 다음 세 작업이 필요
      • 테이블 생성 : H2에서 자동으로 생성해주던 테이블들을 MariaDB에선 직접 쿼리로 생성
      • 프로젝트 설정 : 자바 프로젝트가 MariaDB에 접근하려면 데이터베이스 드라이버가 필요
        ~> MariaDB에서 사용 가능한 드라이버를 프로젝트에 추가
      • EC2(리눅스 서버) 설정 : DB 접속 정보는 중요하게 보호해야할 정보 ~> EC2 서버 내부에서 접속 정보를 관리하도록 설정
    • RDS 테이블 생성
      • JPA가 사용될 엔티티 테이블과 스프링 세션이 사용될 테이블 2 종류를 생성
        • JPA가 사용될 엔티티 테이블
          • 테스트 코드 수행 시 로그로 생성되는 쿼리 사용
          • Hibernate: create table posts , Hibernate: create table user ~
        • 스프링 세션 테이블
          • schema-mysql.sql 파일에서 복붙
          • Shift + ctrl + N에서 위의 파일 검색
    • 프로젝트 설정
      • MariaDB 드라이버 build.gradle에 등록하기
        • compile("org.mariadb.jdbc:mariadb-java-client")
      • 서버에 구동될 환경 구성하기
        • 로컬 src/main/resources/에 application-real.properties 파일 추가
          • profile=real인 환경이 구성된다고 생각하면 된다.
            spring.profiles.include=oauth,real-db
            spring.jpa.properties.hibernate.dialect=org.hibernate.spring.session.store-type=jdbc
          • 이후 커밋 ~> 푸시 ~> 서버에서 ./deploy.sh 실행 (쉘 스크립트에 풀 명령 존재)
    • EC2 설정

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

        • vim ~/app/application-real-db.properties

          spring.jpa.hibernate.ddl-auto=none
          
          spring.datasource.url=jdbc:mariadb://rds주소:포트명(기본은 3306)/database명
          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(생성하지 않음)으로 지정
            • RDS에는 실제 운영으로 사용될 테이블 ~> 절대 스프링 부트에서 새로 만들지 않도록 설정
            • 매우매우매우 중요한 옵션 ~> 이 옵션을 안하면 테이블이 모두 새로 생성될 수 있음
      • deploy.sh 개선

        ...
        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 &
        • -Dspring.profiles.active=real
          • application-real.properties를 활성화
          • application-real.properties의 spring.profiles.include=oauth,real-db 옵션 때문에 real-db 역시 함께 활성화 대상에 포함된다.
      • curl 명령어로 확인하기

        • curl localhost:8088(포트번호)
          • html 코드가 보이면 성공

    EC2에서 소셜 로그인하기

    • AWS 보안 그룹 변경
      • EC2 보안그룹의 인바운드에 해당 포트가 열려있는지 확인
      • 안돼있다면, 포트 열어주기
    • AWS EC2 도메인으로 접속
      • 퍼블릭 DNS 확인하기 => EC2에 할당된 도메인
      • 퍼블릭 DNS 주소:8088(포트번호) 로 서버에 접근
      • google login과 naver login을 누르면, 작동하지 않음 ~> google, naver 서비스 등록해야함
    • 구글에 EC2 주소 등록
      • API 및 서비스 ~> Ouath 동의 화면 ~> 승인된 도메인에 EC2 퍼블릭 DNS 추가하기
        (이 때, http://는 제거해줘야한다.)
      • 사용자 인증 정보 ~> 승인된 리디렉션 URI
        • http://퍼블릭 DNS:8088/login/oauth2/code/google 추가
      • 이 과정에서 500에러가 떠서 몇 시간을 고생했다.
        • 첫번째, db에 접근할 수 없다고 나왔다.
          • 위의 경우, 서버 용량 초과 or 서버 과부화(?) 문제라는 검색 결과가 있었다.
          • ~> 재부팅하니까 해결 ( 불필요한 데몬 등이 돌아가고 있어서 과부화 가능성 )
        • 두번째, line9 에서 username을 찾을 수 없다고 나왔다.
          • 삭제하고 다시 해보고, 이번 장에서 진행한 코드들을 다 확인해봐도 이상이 없었다. 구글 설정이 잘못됐나 확인했는데도 아니었다. 그래서 nohup 로그를 뒤져보니까 mustache에서 에러가 뜬 것을 확인했다. index 파일에서 문제가 생겼음을 직감했고, 클래스 파일과 머스태치 파일을 확인했다. 그 결과, index.mustache의 9번째 줄에 userName을 출력하는 과정에서, 변수 명을 userName이 아닌 username으로 작성한 것을 발견했다... ~> 해결 (근데, username으로 해도 로컬에서는 id가 떴고 에러가 없었다.. 그래서 나는 내가 설정한 이름이 그건줄... 이상하다... ㅠ)
      • 구글 등록이 정상적으로 완료되면, 이름이 뜬다.
    • 네이버에 EC2 주소 등록
      • 서비스 URL에 EC2 퍼블릭 DNS 주소 입력
        • http://퍼블릭DNS주소/ 입력
        • 로그인을 시도하는 서비스가 네이버에 등록된 서비스인지 판단
        • 포트번호는 제외하고 실제 도메인 주소만 입력
        • 네이버 서비스는 한번에 하나의 서비스 URL만 지원하기 때문에, 로컬에서도 이용하고 싶으면, 서비스 하나를 더 만들어서 키 하나 더 발급받기
      • Callback URL에 redirect 받을 서버 주소 입력
        • http://퍼블릭DNS주소:8088/login/oauth2/code/naver 입력
    반응형

    댓글

Designed by Tistory.