본문 바로가기
웹 개발/블로그 만들기 프로젝트

git action을 이용한 자동 배포

by L3m0n S0ju 2023. 7. 23.

 

 

 

 

처음에는 젠킨스를 통해서 자동 배포를 하려 했으나 배보다 배꼽이 큰 느낌이 나서 더 간단하게 자동 배포를 할 수 있는 git action을 사용하기로 했다. 젠킨스를 사용하려면 프리티어 메모리로는 용량이 부족해서 swapfile을 만들어서 메모리 자원을 늘리는 방식으로 진행하였다. gui 환경에서 편하게 배포를 관리할 수 있었으나 아무래도 조금 개인 프로젝트에서 사용하기에는 부담스러웠다. 젠킨스는 아래 블로그를 참고하였다.

 

https://velog.io/@appti/Jenkins-CodeDeploy%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-EC2%EC%97%90-%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B6%80%ED%8A%B8-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EB%B0%B0%ED%8F%AC-%EC%9E%90%EB%8F%99%ED%99%94

 

Jenkins + CodeDeploy를 활용한 EC2에 스프링 부트 프로젝트 배포 자동화

Jenkins + CodeDeploy를 활용한 EC2에 스프링 부트 프로젝트 배포 자동화

velog.io

 

 

 

 

git action은 아래 블로그를 참고했다.

 

https://bcp0109.tistory.com/363

 

Github Actions CD: AWS EC2 에 Spring Boot 배포하기

Overview 애플리케이션을 개발하면 외부에서도 접근 가능하도록 클라우드 환경에 배포합니다. 이전에 포스팅 했던 AWS 1편에서는 마지막에 scp 명령어로 로컬에 존재하는 빌드 파일을 EC2 인스턴스

bcp0109.tistory.com

 

 

git action으로 자동 배포를 하는 순서는 위 블로그에 따라하면 된다. 하지만 아무래도 양이 생각보다 많기 때문에 따라하는 도중에 에러가 발생하지 않을 수가 없다고 생각하면 된다. 생각보다 AWS에서 환경을 구성하는 작업에서는 오류가 나지 않았다. 처음에는 오류가 AWS에서 발생했는지 github에서 발생했는지 어디서 오류가 난지 잘 모르겠어서 고군분투하였다. 처음에 appspec 파일에 문제가 있을거라 예상했지만 대부분의 오류는 workflow 파일과 application.yml 환경 파일에서 발생했다.

 

 

 

 

appspec.yml

version: 0.0
os: linux
files:
  - source:  /
    destination: /home/ec2-user/cicd
    overwrite: yes

permissions:
  - object: /
    pattern: "**"
    owner: ec2-user
    group: ec2-user

hooks:
  AfterInstall:
    - location: scripts/stop.sh
      timeout: 60
      runas: ec2-user
  ApplicationStart:
    - location: scripts/start.sh
      timeout: 60
      runas: ec2-user

 

start.sh

#!/bin/bash

PROJECT_ROOT="/home/ec2-user/cicd"
JAR_FILE="$PROJECT_ROOT/spring-webapp.jar"

APP_LOG="$PROJECT_ROOT/application.log"
ERROR_LOG="$PROJECT_ROOT/error.log"
DEPLOY_LOG="$PROJECT_ROOT/deploy.log"

TIME_NOW=$(date +%c)

# build 파일 복사
echo "$TIME_NOW > $JAR_FILE 파일 복사" >> $DEPLOY_LOG
cp $PROJECT_ROOT/build/libs/*.jar $JAR_FILE

# jar 파일 실행
echo "$TIME_NOW > $JAR_FILE 파일 실행" >> $DEPLOY_LOG
nohup java -jar $JAR_FILE > $APP_LOG 2> $ERROR_LOG &

CURRENT_PID=$(pgrep -f $JAR_FILE)
echo "$TIME_NOW > 실행된 프로세스 아이디 $CURRENT_PID 입니다." >> $DEPLOY_LOG

 

stop.sh

#!/bin/bash

PROJECT_ROOT="/home/ec2-user/cicd"
JAR_FILE="$PROJECT_ROOT/spring-webapp.jar"

DEPLOY_LOG="$PROJECT_ROOT/deploy.log"

TIME_NOW=$(date +%c)

# 현재 구동 중인 애플리케이션 pid 확인
CURRENT_PID=$(pgrep -f $JAR_FILE)

# 프로세스가 켜져 있으면 종료
if [ -z $CURRENT_PID ]; then
  echo "$TIME_NOW > 현재 실행중인 애플리케이션이 없습니다" >> $DEPLOY_LOG
else
  echo "$TIME_NOW > 실행중인 $CURRENT_PID 애플리케이션 종료 " >> $DEPLOY_LOG
  kill -15 $CURRENT_PID
fi

 


처음에는 stop.sh 또는 start.sh에서 문제가 생겼을거라 예상했었는데 지금 생각해보면 s3에 업로드도 실패했는데 왜 여기서 문제가 발생했을거라 예상했는지 생각한 과거의 내가 부끄럽다. 만약 stop.sh나 start.sh에서 문제가 발생하면 echo로 로그가 출력되는데 로그가 출력되지 않는다면 stop.sh 혹은 start.sh 문제도 아니다.

 

이전에 말했듯이 문제는 git action의 workflow 파일인 deploy.yml에서 많이 발생했다. 일단은 아래 코드를 보면 띄어쓰기를 통해 변수 구조가 생기는데 여기에 띄어쓰기 대신에 Tab으로 문서를 작성하면 yml 문법이 아니라는 오류가 발생했다. 처음에는 조금 어이가 없었다. Tab이랑 띄어쓰기 차이로 에러가 발생하는게 잘 이해가 안가지만 어쨌든 조심하자. 정리하면 git action에서 Create deploy.yml가 실패하면 workflow의 deploy.yml 파일 문법에 오류가 있을 확률이 매우 크다.

 

 

 

 

deploy.yml

name: Deploy to Amazon EC2

on:
  push:
    branches:
      - main

# 본인이 설정한 값을 여기서 채워넣습니다.
# 리전, 버킷 이름, CodeDeploy 앱 이름, CodeDeploy 배포 그룹 이름
env:
  AWS_REGION: ap-northeast-2
  S3_BUCKET_NAME: s3-cicd-lemonsoju-bucket
  CODE_DEPLOY_APPLICATION_NAME: lemonsoju-app
  CODE_DEPLOY_DEPLOYMENT_GROUP_NAME: lemonsoju-codedeploy-group

permissions:
  contents: read

jobs:
  deploy:
    name: Deploy
    runs-on: ubuntu-latest
    environment: production

    steps:
    # (1) 기본 체크아웃
    - name: Checkout
      uses: actions/checkout@v3

    # (2) appcliation.yml secrets 세팅
    - name: Replace secrets in application.yml
      run: |
        # GitHub Actions에서 secrets 값을 읽어옴
        DB_URL="${{ secrets.DB_URL }}"
        DB_USERNAME="${{ secrets.DB_USERNAME }}"
        DB_PASSWORD="${{ secrets.DB_PASSWORD }}"
        S3_ACCESS_KEY="${{ secrets.S3_ACCESS_KEY }}"
        S3_SECRET_KEY="${{ secrets.S3_SECRET_KEY }}"
        JWT_KEY="${{ secrets.JWT_KEY }}"
        
        # application.yml 파일 내의 프로퍼티를 secrets 값으로 대체
        sed -i "s|url: .*|url: $DB_URL|" src/main/resources/application.yml
        sed -i "s|username: .*|username: $DB_USERNAME|" src/main/resources/application.yml
        sed -i "s|password: .*|password: $DB_PASSWORD|" src/main/resources/application.yml
        sed -i "s|access-key: .*|access-key: $S3_ACCESS_KEY|" src/main/resources/application.yml
        sed -i "s|secret-key: .*|secret-key: $S3_SECRET_KEY|" src/main/resources/application.yml
        sed -i "s|key: .*|key: $JWT_KEY|" src/main/resources/application.yml

    # (3) JDK 11 세팅
    - name: Set up JDK 11
      uses: actions/setup-java@v3
      with:
        distribution: 'temurin'
        java-version: '11'

    - name: Run chmod to make gradlew executable
      run: chmod +x ./gradlew

    # (4) Gradle build
    - name: Build with Gradle
      uses: gradle/gradle-build-action@0d13054264b0bb894ded474f08ebb30921341cee
      with:
        arguments: clean build

    # (5) AWS 인증 (IAM 사용자 Access Key, Secret Key 활용)
    - name: Configure AWS credentials
      uses: aws-actions/configure-aws-credentials@v1
      with:
        aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
        aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY_ID }}
        aws-region: ${{ env.AWS_REGION }}

    # (6) 빌드 결과물을 S3 버킷에 업로드
    - name: Upload to AWS S3
      run: |
        aws deploy push \
          --application-name ${{ env.CODE_DEPLOY_APPLICATION_NAME }} \
          --ignore-hidden-files \
          --s3-location s3://$S3_BUCKET_NAME/cicd/$GITHUB_SHA.zip \
          --source .

    # (7) S3 버킷에 있는 파일을 대상으로 CodeDeploy 실행
    - name: Deploy to AWS EC2 from S3
      run: |
        aws deploy create-deployment \
          --application-name ${{ env.CODE_DEPLOY_APPLICATION_NAME }} \
          --deployment-config-name CodeDeployDefault.AllAtOnce \
          --deployment-group-name ${{ env.CODE_DEPLOY_DEPLOYMENT_GROUP_NAME }} \
          --s3-location bucket=$S3_BUCKET_NAME,key=cicd/$GITHUB_SHA.zip,bundleType=zip

 

 

 

 

 


필자는 거의 모든 부분에서 에러가 발생해서 50번이 넘는 배포를 시도했다.

 

첫번째 마주친 에러는 위에서 말했듯이 yml 파일 문법 실수로 배포가 시작조차 되지 않았고 두 번째 에러는 배포는 정상적으로 실행되었지만 aws codeDeploy 과정에서 실패했다. 만약 Create deploy.yml는 성공했지만 aws codeDeploy 에서 실패하면  아마도 인증에서 생기는 문제일 것이다. 기억이 잘 안나지만 iam에서 역할 혹은 사용자를 생성하고 인스턴스 혹은 codedeploy, git action 과 연결하면서 작업을 계속 진행하는데 여기서 인스턴스를 재부팅해야 변경 사항이 적용되는데 필자는 재부팅을 하지 않으면서 계속 안되는 이유를 다른 곳에서 찾느라 거의 반나절을 날려버렸다. 조금 더 차분하고 여유롭게 작업했으면 더 빠르게 해답을 찾지 않았을까 생각한다.

 

세번째 에러는 S3에 업로드 하는 것 까지 해결했는데 EC2에서 해당 파일들을 가져오면 필요한 jar 파일만 빼고 있어서 당황했다. workflow 파일에 아래 명령어로 빌드를 실행했는데 아마도 with: build를 뒤에 추가해야 빌드까지 진행한다. 이전에는 잘 되다가 안되는 것으로 보아 작업하다가 실수로 뒤에 with: build를 지워서 갑자기 동작이 안한 것으로 예상한다.

 

- name: Build with Gradle

  uses: gradle/gradle-build-action@0d13054264b0bb894ded474f08ebb30921341cee

 

네번째 에러는 많이 부끄럽다. application.yml 파일을 원래 src/main/resources에 저장하는데 최근에 nestjs 작업을 많이 해서 그런지 그냥 루트 디렉토리에 하나 더 놔두고 진행해서 계속 환경 파일을 지웠는데도 환경 변수들이 적용이 되서 혼자 반나절을 써버렸다. 이것은 조금 피곤한 상태에서 작업해서 그런 거라고 변명을 할 수 있겠다.

 

마지막 에러는 application.yml에 ${{ 변수명 }} 같이 placeholder에 변수 값들이 안들어가고 그대로 빌드되는 문제가 발생했다. 현재 workflow 파일에서 두번째 단계를 생략을 했었는데 계속 secrets 값들을 가져오지 못해서 갑자기 왜 안될까 많은 고민을 하다가 설정 파일이 어느 순간 바뀐 것을 확인했다. 이것도 원래는 잘 동작하다가 갑자기 안되는 것으로 보아 아마 작업하다가 push -force를 써서 이전에 작업한 파일이 바뀌어서 그런 것 같다... 개인 프로젝트라서 일단 저지르자는 마음가짐으로 진행하다보니 push -force를 무분별하게 사용했던 것 같다. 다시는 push -force는 main에서 쓰지 않기로 다짐해야겠다.

'웹 개발 > 블로그 만들기 프로젝트' 카테고리의 다른 글

패치 조인, 인덱싱, 캐싱 적용 및 성능 실험  (0) 2023.09.06
이팩티브 자바 따라하기  (0) 2023.08.11
Jwt 적용하기  (0) 2023.07.05
aws 비용 줄이기  (0) 2023.07.04
toast ui vs React-Quill  (0) 2023.06.29

댓글