JUINTINATION

EC2 재배포 프로세스와 배포 자동화 스크립트 본문

Amazon Web Services

EC2 재배포 프로세스와 배포 자동화 스크립트

DEOKJAE KWON 2024. 7. 11. 17:40
반응형

이 글은 지난 EC2 프로젝트 cron 주기적 실행에서 이어지는 내용이다. 현재 인스턴스는 새로 만들지 않고 아래와 같이 기존에 만들었던 파일들은 모두 지우고 crontab도 원상태로 복구한 상태로 시작한다.

 

EC2 프로젝트 cron 주기적 실행

프로젝트를 배포하면 실행되는 도중 부하가 심해지거나 에러가 발생하는 등의 이유로 서버가 종료될 수 있다. 지난 글에서 만들었던 err.log 파일에서 서버가 종료되었다는 로그를 확인할 수 있

juintination.tistory.com

재배포 프로세스의 이해

  1. 기존 서버 중지
    • 서버를 중지시키기 위해 spring-stop.sh 스크립트를 만들어서 다음의 과정을 실행하도록 했다.
      1. PID 찾기
      2. 찾은 PID로 프로세스 kill 하기
  2. aws-v1 폴더 통째로 삭제
    • rm -rf /home/ubuntu/aws-v1
  3. 프로젝트 다운로드
    • git clone https:github.com/codingspecialist/aws-v1.git
  4. gradlew 실행 권한 부여하기
    1. cd /home/ubuntu/aws-v1
    2. chmod u+x gradlew
  5. 빌드
    • ./gradlew clean build
  6. jar 실행
    • nohup java -jar /home/ubuntu/aws-v1/build/libs/v1-0.0.1-SNAPSHOT.jar 1>log.out 2>err.out &
  7. cron으로 자동 재시작
    • deploy.sh을 cron에 등록하여 spring-restart.sh 파일이 주기적으로 실행되게 하였다.

위처럼 재배포를 위해선 기존 서버를 멈추고 다시 서버를 배포하고 실행해야 하는데 위의 내용으로 스크립트를 작성한 경우 cron이 발동하여 서버를 재시작해버릴 수 있다는 문제점이 있다. 해당 문제점은 아래의 내용을 통해 해결할 수 있다.

환경 변수

환경 변수는 프로세스가 컴퓨터에서 동작하는 방식에 영향을 미치는 동적인 값들의 모임으로, 아래와 같이 export 명령어로 환경 변수를 적용시킬 수 있으며 $로 등록되어 있는 변수를 찾을 수 있다.

export LOVE="I love you"
echo $LOVE

하지만 export로 환경 변수를 등록해도 터미널을 종료한 뒤 다시 접속한다면 적용되어 있지 않다. 이러한 환경 변수를 영구이 기록하려면 숨겨진 .bashrc라는 환경 변수를 저장하는 리눅스의 설정 파일을 수정해야 한다. 이 설정 파일은 어떤 shell을 사용하는지에 따라 달라질 수 있는데, 현재 사용중인 ubuntu는 bashrc만 알고 있으면 된다.

 

이 파일은 ubuntu 유저에게 쓰기 권한이 있기 때문에 $ vi ./.bashrc 명령어를 통해 바로 수정이 가능하다.

 

bashrc 맨 아래에 위와 같이 환경 변수를 등록해 주었다.

 

bashrc 파일은 컴퓨터를 부팅할 때 적용이 되기 때문에 강제 적용을 위해 $ source ./.bashrc 명령어를 사용하였고, $ echo $LOVE 명령어를 실행했을 때 "I love you"가 정상적으로 출력되는 것을 확인할 수 있다.

스크립트 파일로 환경 변수 생성

이제 환경 변수 생성을 위한 스크립트 파일을 작성해볼 것이다. 먼저 $ vi var.sh 명령어로 var.sh 파일을 생성한 뒤 아래와 같이 입력한다.

 

#!/bin/bash

bin 폴더에는 bash 말고도 여러 종류의 shell이 있는 것을 볼 수 있는데, 최상단에 있는 이 라인은 bash라는 shell로 작성할 것이라고 알려주는 설정이다.

또한 PROJECT_NAMEsettings.gradle 파일 내부에 설정되어 있는 rootProject.name으로 설정해야 하며, PROJECT_VERSIONbuild.gradle 파일 내부에 설정되어 있는 version으로 설정해야 한다.

이후 $ vi deploy.sh 명령어로 deploy.sh 파일을 생성한다.

 

source ./var.sh 라인으로 var.sh 파일 안에 선언해둔 변수들을 환경 변수로 사용하지 않고 deploy.sh 파일에서만 사용하도록 설정한다.

 

위에서 볼 수 있듯이 var.sh 파일에서 선언된 $GITHUB_ID는 deploy.sh에서 source 명령어로 선언돼서 환경 변수로 등록된 것 같았지만 직접 $ echo $GITHUB\_ID 명령어를 실행하면 아무 것도 출력되지 않는 것을 볼 수 있다.

재배포를 고려한 cron 종료

crontab을 비워주는 이유는 최초 배포할 때는 상관없지만, 재배포 시에는 EC2에 있는 기존 프로젝트를 종료시킨 후 다시 프로젝트를 다운받고 jar 파일을 빌드하고 실행하는 과정이 필요하다. 즉, 어차피 프로젝트를 종료하는 것이 당연하기 때문에 종료되었는지 1분마다 확인할 필요가 없기 때문에 재배포 도중에는 cron이 작동될 필요가 없는 것이다.

만약 재배포를 위해 프로젝트가 종료되었는데, 프로젝트가 종료된 것을 확인하고 cron이 작동돼서 서버를 다시 실행시키게 되면 로직이 꼬여서 문제가 발생할 수 있기 때문에 기존 cron을 종료시키는 과정이 필요하다.

실제로 아무 내용이 없는 crontab은 아래와 같이 모두 주석 처리가 되어있고, 사실상 빈 페이지이다.

 

다시 $ vi deploy.sh 명령어를 실행하여 아래와 같이 내용을 수정한다.

 

2번째 문단에서 $ touch crontab_delete 명령어로 crontab_delete라는 빈 파일을 생성하고 $ crontab crontab_delete 명령어로 crontab에 빈 파일을 적용시킨 후 $ rm crontab_delete 명령어로 해당 파일은 삭제한다.

이후, 다시 $ vi deploy.sh 명령어를 실행하여 아래와 같이 내용을 추가한다.

 

3번째 문단의 if문에서는 PROJECT_PID 면수에 어떤 값이라도 담겨있다면 프로그램이 실행중이라는 뜻이므로 프로세스를 kill하고, 이는 재배포시에만 실행된다. 아래의 else문에서는 최초 배포시에만 실행되므로 jdk 설치 및 타임존 설정과 같은 각종 설정을 진행한다. 이때 1>/dev/null 은 업데이트나 설치가 되고 있는 로그 표준 출력을 눈으로 볼 필요가 없기 때문에 특정 파일로 옮기는 것이 아닌 쓰레기통으로 버리는 설정이다.

다시 $ vi deploy.sh 명령어를 실행하여 아래와 같이 내용을 추가한다.

 

4번째 문단에서 기존의 프로젝트 폴더를 삭제하고, 5번째 문단에서 clone으로 재배포할 프로젝트를 다운받으면서 apt 명령은 무조건 동기식으로 실행되기 때문에 sleep이 필요하지 않지만, git clone은 비동기식으로 진행될 수도 있기 때문에 안전하게 다운받기 위해 3초를 대기한다.

이후 6번째 문단에서 gradlew에 실행권한을 부여하고 7번째 문단에서 빌드를 진행한다.

 

그리고나서 $ ./deploy.sh 명령어를 실행하면 위와 같이 최초 배포가 빌드까지 진행되는 것을 확인할 수 있다.

 

이후 위와 같이 프로젝트를 빌드한 후에 코드의 컨트롤러 부분을 확인해보면 아래와 같다.

package site.metacoding.awsv2;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@RestController
public class HelloController {

    @GetMapping("/aws/v2")
    public String hello(@RequestParam(defaultValue = "1") Integer number) {
        if (number == 1) { // info 로그
            log.info("/aws/v2 이 호출되었어요. info 로그 #####################################");
        } else if (number == -1) { // error 로그
            log.error("/aws/v2 이 호출되었어요. error 로그 #####################################");
        } else if (number == 0) { // warn 로그
            log.warn("/aws/v2 이 호출되었어요. warn 로그 #####################################");
        }

        return "<h1>aws v2</h1>";
    }
}

hello 메서드 위의 @GetMapping 어노테이션을 보면 /aws/v2 로 설정되어있기 때문에 {인스턴스의 IPv4 주소}:8080/aws/v2에 접속해보면 아래와 같이 사이트에 연결할 수 없다고 뜬다.

그 이유는 aws-v2 프로젝트의 설정 파일에 active profile 설정이 dev 파일을 바라보고 있기 때문이다.

 

resources/application.yml

spring:
  profiles:
    active:
    - dev

resources/application-dev.yml

server:
  port: 8081

위에서 볼 수 있듯이 개발용인 dev 파일에는 포트가 8081로 설정되어 있지만 현재 인스턴스의 인바운드 설정에는 8081 포트가 열려있지 않기도 하고, 우리는 8080 포트에 열리는 배포용 파일을 보고 싶다.

 

그렇기 때문에 다음과 같이 약간의 옵션을 추가해야 한다. $ java -jar -Dspring.profiles.active=prod aws-v2-0.0.0.jar

다시 브라우저로 접속해보면 정상적으로 접속이 되고, 로그 또한 잘 남는 것을 볼 수 있다.

cron 등록

다시 $ vi deploy.sh 명령어를 실행하여 아래와 같이 내용을 추가한다.

 

먼저 8번째 문단에서 nohup으로 프로젝트를 실행하고 log.out과 err.out에 로그를 남기도록 설정한다.

그리고 마지막으로 9번째 문단에서 crontab_new를 생성한 뒤 check-and-restart.sh 파일의 내용을 적용시키고 cron에 등록한 뒤 파일을 삭제한다.

 

check-and-restart.sh의 내용은 위와 같다. 해당 파일에서도 환경 변수가 필요하기 때문에 source ./var.sh 라인으로 환경 변수 파일을 등록해준 뒤 nohup으로 프로젝트를 재시작한다.

또한 해당 스크립트에도 $ chmod u+x check-and-restart.sh 명령어로 실행 권한을 부여해줘야 한다.

 

임의로 서버를 프로세스를 종료시키면서 테스트해보면 위와 같이 실제로 재시작이 잘 작동하는 것을 확인할 수 있다.


결론

이번엔 배포 자동화 스크립트에 대해 공부해 봤다. 어렵기만 한 코드는 아니었지만 직접 코드를 분석하면서 안되는 이유에 대해 생각하는 과정이 즐거웠던 것 같다. 날이 점점 더워져서 축축 늘어지는 그런 날이었지만 마음을 다잡고 의자에 엉덩이를 붙이길 잘 했던 것 같다. 앞으로도 화이팅해보자..!

728x90
Comments