백엔드 CI/CD 파이프라인 구축

이태곤·2024년 2월 10일
0

CS

목록 보기
19/23

Github Action + S3 + CodeDeploy

  • Github Action: 빌드, 테스트 및 파이프라인을 자동화 할 수 있는 CI/CD 플랫폼으로, repository에서 이벤트가 발생할 때 workflow를 실행

  • EC2: 서버

  • S3: 배포할 결과물을 저장

  • CodeDeploy: 배포를 돕는 도구

  1. 변경된 코드가 GitHub에 푸시됨: 개발자가 새로운 코드 변경 사항을 GitHub 리포지토리에 푸시

  2. GitHub Actions 트리거: GitHub 리포지토리에 코드가 푸시되면, 이를 트리거로 GitHub Actions 워크플로우가 시작

  3. 프로젝트 빌드: GitHub Actions 워크플로우는 프로젝트를 빌드하고, 결과물인 jar 파일을 .zip으로 압축

  4. S3 업로드: 압축된 .zip 파일을 Amazon S3 버킷에 업로드

  5. CodeDeploy에 배포 요청: GitHub Actions 워크플로우는 AWS CodeDeploy 서비스에 배포를 요청

  6. CodeDeploy Agent에 배포 명령 전달: AWS CodeDeploy 서비스는 배포할 EC2 인스턴스에 설치된 CodeDeploy Agent에게 배포 명령을 전달

  7. Agent가 appspec.yml 참고: CodeDeploy Agent는 EC2 인스턴스에서 배포 명령을 받으면, appspec.yml 파일을 참고하여 배포 프로세스를 진행

  8. 배포 실행: CodeDeploy Agent는 appspec.yml에 정의된 지시사항에 따라 S3에서 jar 파일을 가져오고 배포 스크립트를 따라 진행


1. S3 Bucket

  • S3 버킷 생성: 배포할 jar 파일을 저장하기 위해 새로운 S3 버킷을 생성

  • 퍼블릭 엑세스 차단 설정: 생성한 S3 버킷의 설정에서 '모든 퍼블릭 엑세스 차단' 옵션 선택

    • 외부에서의 무분별한 접근을 방지한다.
    • IAM에서 AWSAccessKeyId, AWSSecretKey를 발급받고 키를 이용해서 S3 객체에 접근하는 방식 사용
  • 객체 소유권 설정에서 'ACL(액세스 제어 목록) 비활성화'를 선택

    • IAM(Identity and Access Management)을 통해 권한을 관리하겠다는 의미

2. IAM 역할 생성

  • GitHub Actions가 AWS S3 버킷에 접근할 수 있도록 하기 위해 AWS에서 IAM 사용자를 생성하고 해당 사용자에게 S3 접근 권한을 부여한 뒤, 생성된 자격 증명(액세스 키 ID와 비밀 액세스 키)을 GitHub 리포지토리의 Secrets에 추가

  • IAM 사용자에게 AWSCodeDeployFullAccess와 AWSCodeDeployRole 권한을 추가

  • CodeDeploy 역할

    • AmazonS3ReadOnlyAccess: CodeDeploy가 S3 버킷에서 애플리케이션 코드나 구성 파일 등의 배포 자료를 읽을 수 있도록 허용
    • AWSCodeDeployRole: AWS CodeDeploy가 EC2 인스턴스에서 애플리케이션을 배포하는 데 필요한 기본적인 권한을 제공
  • EC2용 역할

    • AWSCodeDeployAccess: AWS CodeDeploy 서비스와 관련된 작업을 수행할 수 있는 권한을 제공
    • AmazonS3FullAccess: AWS S3 서비스에 대한 모든 작업(업로드, 다운로드, 목록 조회 등)을 수행할 수 있는 권한을 제공

3. Ubuntu 환경 세팅

  • jdk17 설치
sudo apt install openjdk-17-jdk
  • CodeDeploy Agent 설치
# 시스템 패키지 목록을 업데이트
$ sudo apt update

# AWS CodeDeploy 에이전트는 Ruby 기반
$ sudo apt install ruby-full

# 파일 다운로드를 위해 wget 패키지를 설치
$ sudo apt install wget

# 홈 디렉토리로 이동
$ cd /home/ubuntu

# AWS CodeDeploy 에이전트 설치 스크립트를 다운로드
$ wget https://aws-codedeploy-ap-northeast-2.s3.ap-northeast-2.amazonaws.com/latest/install

# 다운로드한 설치 스크립트에 실행 권한을 부여
$ chmod +x ./install

# 설치 스크립트를 실행하여 CodeDeploy 에이전트를 자동으로 설치
$ sudo ./install auto > /tmp/logfile

# CodeDeploy 에이전트의 상태를 확인
$ sudo service codedeploy-agent status

# Restart
$ sudo service codedeploy-agent restart
  • EC2 IAM 역할 설정

4. CodeDeploy 애플리케이션 생성

  • AWS Management Console에서 CodeDeploy 서비스로 이동

  • "배포" 섹션 아래의 "애플리케이션" 메뉴를 선택하고, "애플리케이션 생성" 버튼을 클릭

  • "EC2/온프레미스" 옵션을 선택

  • 배포 그룹 생성: 앞서 만든 CodeDeploy용 역할을 추가

  • 배포 유형"으로 "현재 위치"를 선택

    • 애플리케이션의 새 버전을 기존 인스턴스에 바로 배포하는 설정입니다.
  • 환경 구성: "환경 구성"에서 "Amazon EC2 인스턴스"를 선택

  • 인스턴스 태그 선택: 배포할 EC2 인스턴스를 식별할 수 있는 태그를 선택


5. Github Action CI

  • backend-deploy.yml
name: Deploy Backend to Amazon EC2

on:
  push:
    branches: [ "main", "develop" ]
    paths:
      - 'server/**'

#리전, 버킷 이름, CodeDeploy앱 이름, CodeDeploy배포 그룹 이름
env:
  AWS_REGION: ap-northeast-2
  S3_BUCKET_NAME: genesis-airport-deploy
  CODE_DEPLOY_APPLICATION_NAME: genesis-airport-codedeploy-app
  CODE_DEPLOY_DEPLOYMENT_GROUP_NAME: genesis-airport-codedeploy-deployment-group

permissions:
  contents: read

jobs:
  deploy:
    name: Deploy
    runs-on: ubuntu-latest
    environment: production
    # (1)기본 체크아웃
    steps:
    - name: Checkout
      uses: actions/checkout@v4
    # (2) JDK 17세팅
    - uses: actions/checkout@v4
    - name: Set up JDK 17
      uses: actions/setup-java@v4
      with:
        java-version: '17'
        distribution: 'temurin'

    # not executable 에러 방지를 위한 권한 부여
    - name: Run chmod to make gradlew executable
      run: chmod +x gradlew
      working-directory: ./server
      
    # (3) Gradle build
    - name: Build with Gradle without tests
      run: ./gradlew build -x test
      working-directory: ./server


    # (4) 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 }}
        aws-region: ${{ env.AWS_REGION }}

    # (5)빌드 결과물을 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/$GITHUB_SHA.zip \
            --source .


    # (6) 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=$GITHUB_SHA.zip,bundleType=zip

6. appsepc.yml

  • CodeDeploy에서 배포를 위해 참조할 appsepc.yml 파일 작성

  • 루트 디렉토리에 위치

version: 0.0
os: linux

files:
  - source: /server # 배포할 파일 원본 위치
    destination: /home/ubuntu/app/server # 파일을 복사할 대상 위치
    overwrite: yes # 대상 위치에 이미 파일이 있을 경우 덮어쓰기

permissions:
  - object: / #루트('/')로 설정되어 전체 파일 시스템에 적용
    pattern: "**" 
    owner: ubuntu # 파일의 소유자
    group: ubuntu

hooks:
  ApplicationStart: # 애플리케이션 시작 시 실행될 스크립트 정의
    - location: server/script/deploy.sh # 실행할 스크립트의 위치
      timeout: 60 # 스크립트 실행 시 제한 시간
      runas: ubuntu # 스크립트를 실행할 사용자

7. 배포 스크립트 작성

  • deploy.sh
# 환경 변수 파일을 소스로 가져오기
source /env.sh

# 프로젝트 루트 디렉토리 경로를 설정
PROJECT_ROOT="/home/ubuntu/app/server"

# 로그 파일의 경로를 설정
APP_LOG="$PROJECT_ROOT/application.log"
ERROR_LOG="$PROJECT_ROOT/error.log"
DEPLOY_LOG="$PROJECT_ROOT/deploy.log"

# 현재 시간
TIME_NOW=$(date +%c)

# build/libs 디렉토리에 있는 JAR 파일의 경로를 가져오기
BUILD_JAR="$PROJECT_ROOT/build/libs/reservation-0.0.1-SNAPSHOT.jar"
# JAR 파일의 이름만 추출
JAR_NAME=$(basename "$BUILD_JAR")

# JAR 파일 복사에 대한 로그
echo "$TIME_NOW > $JAR_NAME 파일 복사" >> "$DEPLOY_LOG"
# build/libs 디렉토리에서 JAR 파일을 프로젝트 루트로 복사
cp "$BUILD_JAR" "$PROJECT_ROOT/"

# 현재 실행중인 애플리케이션의 PID를 확인하고 로그에 기록
echo "> 현재 실행중인 애플리케이션 pid 확인" >> "$DEPLOY_LOG"
CURRENT_PID=$(pgrep -f "$JAR_NAME")

# 실행중인 애플리케이션 PID가 없으면 종료하지 않고, 있으면 종료
if [ -z "$CURRENT_PID" ]
then
  echo "> 현재 구동중인 애플리케이션이 없으므로 종료하지 않습니다." >> "$DEPLOY_LOG"
else
  echo "> kill -15 $CURRENT_PID" >> "$DEPLOY_LOG"
  kill -15 "$CURRENT_PID"
  sleep 5
fi

# 배포할 JAR 파일의 경로를 설정
DEPLOY_JAR="$PROJECT_ROOT/$JAR_NAME"

# JAR 파일 실행에 대한 로그
echo "$TIME_NOW > $DEPLOY_JAR 파일 실행" >> "$DEPLOY_LOG"
# 배포할 JAR 파일을 nohup으로 실행
nohup java -DDB_USERNAME="$DB_USERNAME" -DDB_PASSWORD="$DB_PASSWORD" -DREDIS_PASSWORD="$REDIS_PASSWORD" -jar "$DEPLOY_JAR" >> "$DEPLOY_LOG" 2>>"$ERROR_LOG" &

0개의 댓글