공부의 일상

[배포 자동화] aws, github actions을 활용한 자동 배포 파이프라인 구축기

leeds123 2025. 2. 2. 15:29

1. 시작 전 준비사항

 

1. aws 계정 및 ec2 인스턴스 (Linux) 생성

먼저, aws 계정은 프리티어로 생성해주었다.
그런 뒤 ec2 인스턴스를 생성하고 설정을 잡아주었다.

[설정사항]
1. 퍼블릭 ip주소
ec2 > 인스턴스 > 탄력적 ip > 탄력적 ip 주소 할당

2. 인바운드 / 아웃바운드 설정
ec2 > 보안그룹 > {보안그룹 이름} > 인바운드 규칙 / 아웃바운드 규칙
SSH 접속과 톰캣을 내장하고 있는 스프링 부트 프레임워크를 was를 띄울 것이기에 443, 8080 포트를 인바운드에 열어주었다.

 

2. s3 생성

빌드 파일 (.jar)와 shell 스크립트를 s3 버킷에 업로드 할 것이기 때문에 s3도 생성해준다.

s3 > 버킷 > 버킷 만들기

누구나 s3 버킷의 데이터에 접근해 다운로드하지 못하도록 퍼블릭 액세스를 차단해주었다.

 

 

3. EC2 인스턴스에 AWS Code deploy agent 설치하기

sudo apt update

# ruby
sudo apt install ruby-full

# wget
sudo apt install wget

# download install file
wget https://<bucket-name>.s3.region-<identifier>.amazonaws.com/latest/install

# ap-northeast-2
wget https://aws-codedeploy-ap-northeast-2.s3.ap-northeast-2.amazonaws.com/latest/install

chmod +x ./install
sudo ./install auto

위 명령어를 통해 code deploy agent를 설치하고 정상 설치를 확인해보자.

sudo service codedeploy-agent status

 

정상 설치 된 것을 확인할 수 있다.

 

4. IAM 계정 생성

IAM > 사용자 > 사용자 생성

사용자의 권한은 AmazonS3FullAccess, AWSCodeDeployFullAccess, AWSCodeDeployRole 이렇게 3가지 설정해주었다.

IAM 계정 생성 완료 후, 액세스 키와 비밀 액세스 키가 나오는데 github secrets에 등록해야하므로, 꼭 기억하고 있어야한다.

 

 

 

actions

1. Actions Wrokflow 작성하기

클라우드 설정이 끝났다면, 

./github/workflows 폴더 아래에 yml 파일을 작성해주자.

해당 경로는 약속된 경로이기에 지켜주어야한다.

 

프로세스를 설명

1. 코드 변경사항이 main 브랜치에 push 될 때

2.Java 애플리케이션 빌드 및 배포 파일 생성

3. 배포 파일을 S3에 업로드

4. AWS CodeDeploy를 통해 EC2에 애플리케이션 배포

name: Build and Deploy to EC2

on:
  push:
    branches: [ "main" ]

permissions:
  contents: read

env:
  AWS_REGION: ap-northeast-2
  AWS_S3_BUCKET: ***
  AWS_CODE_DEPLOY_APPLICATION: ***
  AWS_CODE_DEPLOY_GROUP: ***

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v3
    - name: Set up JDK 17
      uses: actions/setup-java@v3
      with:
        java-version: '17'
        distribution: 'temurin'

    - name: Grant execute permission for gradlew
      run: chmod +x ./gradlew
      shell: bash

    - name: Build with Gradle
      run: ./gradlew clean build

    - name: Make Directory
      run: mkdir -p deploy

    - name: Copy Jar
      run: cp ./build/libs/*.jar ./deploy

    - name: Copy appspec.yml
      run: cp appspec.yml ./deploy

    - name: Copy script
      run: cp ./*.sh ./deploy

    - name: Make zip file
      run: zip -r ./githubActionDeploy.zip ./deploy
      shell: bash

    - name: AWS credential 설정
      uses: aws-actions/configure-aws-credentials@v2
      with:
        aws-region: ${{ env.AWS_REGION }}
        aws-access-key-id: ${{ secrets.CICD_ACCESS_KEY }}
        aws-secret-access-key: ${{ secrets.CICD_SECRET_KEY }}

    - name: Upload to S3
      run: aws s3 cp --region ap-northeast-2 ./githubActionDeploy.zip s3://$AWS_S3_BUCKET/$GITHUB_SHA.zip

    - name: Deploy to EC2 using AWS CodeDeploy
      run: |
        aws deploy create-deployment \
          --application-name $AWS_CODE_DEPLOY_APPLICATION \
          --deployment-group-name $AWS_CODE_DEPLOY_GROUP \
          --s3-location bucket=$AWS_S3_BUCKET,key=$GITHUB_SHA.zip,bundleType=zip \
          --region $AWS_REGION

 

2. deploy.sh, appspec.yml 작성하기 

위 actions가 실행되면 빌드된 jar, deploy.sh, appspec.yml 파일이 zip 파일에 들어있다.

 

deploy.sh

현재 구동중인 프로세스를 중지시키고 새로 빌드된 jar 파일을 빌드시키는 shell script

#!/usr/bin/env bash

REPOSITORY=/home/ubuntu/app

echo "> 현재 구동 중인 애플리케이션 pid 확인"

# 기존 애플리케이션 PID 찾기
CURRENT_PID=$(pgrep -fla java | grep -e "SNAPSHOT" | awk '{print $1}')

echo "현재 구동 중인 애플리케이션 pid: $CURRENT_PID"

if [ -z "$CURRENT_PID" ]; then
  echo "현재 구동 중인 애플리케이션이 없으므로 종료하지 않습니다."
else
  echo "> kill -15 $CURRENT_PID"
  kill -15 $CURRENT_PID
  sleep 5

  # 프로세스가 여전히 실행 중인지 확인
  if ps -p $CURRENT_PID > /dev/null; then
    echo "프로세스가 종료되지 않아 강제 종료합니다."
    echo "> kill -9 $CURRENT_PID"
    kill -9 $CURRENT_PID
  else
    echo "프로세스가 정상적으로 종료되었습니다."
  fi
fi


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

# S3에서 ZIP 파일 다운로드
echo "> S3에서 ZIP 파일 다운로드"
aws s3 cp s3://$AWS_S3_BUCKET/$GITHUB_SHA.zip $REPOSITORY/$GITHUB_SHA.zip

# ZIP 파일 압축 해제
echo "> ZIP 파일 압축 해제"
unzip -o $REPOSITORY/$GITHUB_SHA.zip -d $REPOSITORY

# JAR 파일 찾기
JAR_NAME="$REPOSITORY/******************.jar"

echo "> JAR NAME: $JAR_NAME"

# JAR 파일에 실행 권한 추가 (sudo 사용)
echo "> $JAR_NAME 에 실행권한 추가"
sudo chmod +x $JAR_NAME

# JAR 파일 실행 (sudo 사용)
echo "> $JAR_NAME 실행"
nohup sudo java -jar -Duser.timezone=Asia/Seoul $JAR_NAME > /dev/null 2>&1 &

 

jar 파일에 실행권한만 필요하고, 읽기, 쓰기 권한은 변경할 이유가 없기 때문에 

sudo +x 명령어를 사용해 jar 파일 실행 권한만 추가했다.

 

(주의) chmod 777는 보안에 매우 취약한 설정이다.

chmod 777를 남발하지말고 상황에 따른 권한 부여를 생각하는 것이 중요하다.

 

처음에는 kill -9 명령어를 사용해 프로세스를 강제종료했다.

그러나 프로세스를 안전하게 종료하기 위해

파일 닫기, 메모리 해제, 데이터 저장 등을 수행한 후 종료하는 kill -15 명령어를 사용했다.

 

kill -15명령어로 프로세스가 종료되지 않을 경우를 대비해 기존 kill -9 명령어도 "프로세스가 종료되지않았을 때"라는조건을 추가해 유지했다.

 

터미널이 닫혀도 프로세스를 백그라운드에서 유지하기 위해 nohup 명령어를 사용했다.

 

AWS CodeDeploy에서 사용되는 구성 파일인 appspec.yml을 작성해주자.

version: 0.0
os: linux
files:
  - source: /
    destination: /home/ubuntu/app/
    overwrite: yes

permissions:
  - object: /home/ubuntu/app/deploy.sh
    pattern: "**"
    owner: ubuntu
    group: ubuntu
    mode: "755" 

hooks:
  ApplicationStart:
    - location: deploy.sh
      timeout: 60
      runas: ubuntu

 

배포할 파일을 /home/ubuntu/app/ 디렉토리로 복사

deploy.sh 스크립트에 대해 755 파일 권한을 설정

애플리케이션 시작 시 deploy.sh 스크립트를 실행하고, 이 스크립트를 ubuntu 사용자로 실행

 

이제 모든 설정이 끝났다.

workflow에 작성한 브랜치로 push를 하고 정상 배포를 확인해보았다.

1. Actions

2. AWS S3

s3에 정상적으로 저장된 것을 확인해보고, zip 파일을 다운로드해서 jar 파일과 deploy.sh, appspec.yml이 들어있는 것을 확인할 수 있다.

 

3. Code Deploy

Code Deploy에서 상태 확인도 확인하자.

 

최종적으로 그런 뒤, 퍼블릭 ip주소로 접속해보면 수정사항이 반영된 배포를 확인할 수 있다.

 

 

긴 글 읽어주셔서 감사합니다. 😀