[AWS] Github Actions를 이용한 EC2로의 Flask CI/CD

홍종훈·2023년 8월 10일
0

AWS

목록 보기
7/9
post-thumbnail

해당 글은 아래의 tistory를 참고했습니다.

https://rachel0115.tistory.com/entry/Github-Actions%EB%A1%9C-CICD-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0-EC2-S3-CodeDeploy

이번 프로젝트에선 Python Flask를 이용하여 서버를 구축했다.

이전부터 하고싶었던 CI/CD작업을 해보고자 Github Action을 이용해서 EC2 인스턴스에 Flask 서버를 배포하는 방법을 찾아봤지만,

eb에 배포하는 내용들밖에 없었다.

결국엔 Java를 사용해 CI/CD를 구축하신 분의 블로그를 참고하여 Python의 Flask를 배포해보기로 했다.

정말 많은 우여곡절들이 있었지만, 위의 블로그가 기본 인프라 구축에 많은 도움이 됐다.

CI/CD 구조

Github Actions를 통한 CI/CD구조는 다음과 같다.

  1. 특정 브랜치에 Push, 또는 Merge 동작을 수행한다.
  2. Github Actions에서 push 혹은 merge를 감지하고 정해진 Action을 수행한다.
  3. Update된 코드를 바탕으로 Build와 Test를 수행한다.
  4. 코드를 압축하여 zip파일을 생성한다.
  5. S3에 zip파일을 업로드한다.
  6. Code Deploy에 배포 요청을 내린다.
  7. S3로부터 zip파일을 받는다.
  8. 지정한 EC2 인스턴스에 애플리케이션 파일을 전달한다.
  9. EC2 인스턴스에서 쉘 스크립트를 실행함으로써 배포를 완료한다.

S3버킷 생성하기

CI/CD의 구조에서 S3버킷은 Github Actions로부터 만들어진 zip를 저장하는 역할을 한다.

Actions Runner를 aws 서비스에 접근 가능한 상태로 만들기 위해서 AWS계정에 대한 ACCESS_TOKEN이 필요하므로
S3FullAccess, CodeDeployFullAccess를 가진 IAM사용자를 만든 뒤 배포하고자 하는 github repository에 엑세스키를 등록한다.

액세스 키 등록은 아래의 경로로 들어가서 등록할 수 있다.
github repository의 Settings -> Secrets and variables -> Actions -> New repository secret

EC2 역할 생성하기

EC2는 S3에서 zip파일을 가져와 CodeDeploy를 수행하고, 애플리케이션을 배포하는 역할을 한다.

따라서 EC2인스턴스에 S3FullAccess, CodeDeployFullAccess 권한을 추가한다.

EC2 인스턴스에 CodeDeploy Agent 설치하기

실습에 사용된 운영체제 및 버전은 Ubuntu - 22.04 version이다.
아래의 명령어를 차례대로 입력한다.

# apt 업데이트
sudo apt-get update && sudo apt-get upgrade

# JDK 11 설치
sudo apt-get install openjdk-11-jdk

# Ruby 설치 (3.xx 버전 설치됨)
sudo apt install ruby-full

# wget 모듈 설치
sudo apt install wget

cd /home/ubuntu

# codedeploy 파일 다운로드
sudo wget https://aws-codedeploy-ap-northeast-2.s3.ap-northeast-2.amazonaws.com/latest/install

모든 명령어를 차례대로 입력했다면 디렉토리에 install 파일이 추가된 것을 볼 수 있다.

다시 다음 명령어들을 차례대로 입력하여 설정 및 사용 가능한 Codeploy의 버전을 확인한다.

# 권한 설정
sudo chmod +x ./install

# 출력을 임시 로그파일에 씀
sudo ./install auto > /tmp/logfile

# aws cli 설치
sudo apt-get install awscli

# 서울 리전에서 사용 가능한 codedeploy 버전 확인
sudo aws s3 ls s3://aws-codedeploy-region-identifier/releases/ | grep '\.deb$'

실습에선 가장 최신버전인 1.6.0버전을 사용했다.

# 1.6.0-49 버전의 codedeploy-agent 설치
sudo ./install auto -v releases/codedeploy-agent_1.6.0-49_all.deb > /tmp/logfile

# 서비스가 실행중인지 확인
sudo service codedeploy-agent status

AWS CodeDeploy 생성

  1. CodeDeploy에 대한 IAM역할을 생성한다.

  1. CodeDeploy 애플리케이션을 생성한다.

  1. 배포그룹을 생성한다.

Github Actions를 통해 만들어진 .github/workflows 파일에 배포 동작을 추가한다.

name: Build and Deploy Flask to AWS EC2

on:
  push:
    branches: [ "master" ]
  pull_request:
    branches: [ "master" ]

env:
  PROJECT_NAME: jonghun_project
  BUCKET_NAME: jonghun-cicd-bucket
  CODE_DEPLOY_APP_NAME: jonghun_cicd
  DEPLOYMENT_GROUP_NAME: jonghun_instance
  MONGO_USER: ${{ secrets.MONGO_USER }}
  MONGO_PASS: ${{ secrets.MONGO_PASS }}
  SECRET_KEY: ${{ secrets.SECRET_KEY }}
  FLASK_SECRET_KEY: ${{ secrets.FLASK_SECRET_KEY }}
  GOOGLE_CLIENT_ID: ${{ secrets.GOOGLE_CLIENT_ID }}
  GOOGLE_CLIENT_SECRET: ${{ secrets.GOOGLE_CLIENT_SECRET }}
  KAKAO_CLIENT_ID: ${{ secrets.KAKAO_CLIENT_ID }}

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2

      - name: Set up Python
        uses: actions/setup-python@v2
        with:
          python-version: 3.8

      - name: Install dependencies
        run: |
          python -m venv venv
          . venv/bin/activate
          pip install -r requirements.txt

      - name: Create .env file
        run: |
          echo "MONGO_USER=${{ secrets.MONGO_USER }}" > .env
          echo "MONGO_PASS=${{ secrets.MONGO_PASS }}" >> .env
          echo "SECRET_KEY=${{ secrets.SECRET_KEY }}" >> .env
          echo "FLASK_SECRET_KEY=${{ secrets.FLASK_SECRET_KEY }}" >> .env
          echo "GOOGLE_CLIENT_ID=${{ secrets.GOOGLE_CLIENT_ID }}" >> .env
          echo "GOOGLE_CLIENT_SECRET=${{ secrets.GOOGLE_CLIENT_SECRET }}" >> .env
          echo "KAKAO_CLIENT_ID=${{ secrets.KAKAO_CLIENT_ID }}" >> .env

      - name: Make Zip File
        run: |
          . venv/bin/activate
          zip -qq -r ./$GITHUB_SHA.zip .
        shell: bash

      - 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_PRIVATE_ACCESS_KEY }}
          aws-region: ap-northeast-2

      - name: Upload to S3
        run: |
          aws s3 cp --region ap-northeast-2 ./$GITHUB_SHA.zip s3://$BUCKET_NAME/$PROJECT_NAME/$GITHUB_SHA.zip




      - name: Code Deploy To EC2 instance
        run: aws deploy create-deployment 
                --application-name $CODE_DEPLOY_APP_NAME 
                --deployment-config-name CodeDeployDefault.AllAtOnce
                --deployment-group-name $DEPLOYMENT_GROUP_NAME 
                --s3-location bucket=$BUCKET_NAME,bundleType=zip,key=$PROJECT_NAME/$GITHUB_SHA.zip

env: yml 파일에서 사용되는 변수를 선언

  1. PROJECT_NAME: 프로젝트 이름을 설정한다. S3 경로 설정에 사용된다.
  2. BUCKET_NAME: 앞서 생성한 S3 버킷 이름을 넣어준다.
  3. CODE_DEPLOY_APP_NAME: CodeDeploy '애플리케이션 이름'을 넣어준다.
  4. DEPLOYMENT_GROUP_NAME: CodeDeploy '배포 그룹 이름'을 넣어준다.
    앞서 생성한 S3 및 CodeDeploy 의 이름을 넣어주면 된다.
  5. 나머지 프로젝트에 대한 환경변수들을 github repository에 등록 후
    "KEY=${{ secrets.KEY}}"로 추가한다.

appspec.yml 생성

배포할 프로젝트의 root 디렉토리에 appspec.yml파일을 추가한다. CodeDeploy가 참조하는 파일이다.

version: 0.0
os: linux

files:
  - source: /
    destination: /home/ubuntu/jonghun
permissions:
  - object: /home/ubuntu/jonghun/
    owner: ubuntu
    group: ubuntu
hooks:
  AfterInstall:
    - location: scripts/deploy.sh
      timeout: 60
      runas: ubuntu
  1. version
    appspec.yml 파일 버전을 정의한다. 현재는 0.0 이외의 버전이 지원되지 않는다.

  2. files
    배포할 파일 및 디렉토리를 정의한다. 위의 예시에서는 애플리케이션을 루트 디렉토리에서 가져와 "/home/ubuntu/jonghun" 디렉토리로 복사한다.

  3. permissions
    애플리케이션 파일에 대한 권한을 정의한다. 위의 예시에서는 "/home/ubuntu/jonghun" 디렉토리의 그룹과 소유자를 ubuntu로 설정한다.

  4. hooks
    배포 단계에서 실행할 훅 스크립트를 정의한다. 위의 예시에서는 배포 후 실행할 스크립트인 deploy.sh 파일을 지정하고, 스크립트 실행 시간 제한을 60초로 설정한다.

  5. AfterInstall
    여러 배포 단계 중 AfterInstall 단계에서 스크립트를 실행한다.

scripts/deploy.sh 배포 쉘 스크립트 생성

배포할 프로젝트의 root 디렉토리에 scripts폴더를 생성한 후 deploy.sh를 추가한다.

#!/usr/bin/env bash

REPOSITORY=/home/ubuntu/jonghun
FLASK_APP_DIR=/home/ubuntu/jonghun
ENV_PATH=$FLASK_APP_DIR/.env
cd $REPOSITORY

# Flask 앱 인스턴스 종료
FLASK_PID=$(pgrep -f gunicorn)
if [ -z $FLASK_PID ]
then
  echo "> 종료할 Flask 애플리케이션이 없습니다."
else
  echo "> kill Flask app with PID: $FLASK_PID"
  kill -15 $FLASK_PID
  sleep 5
fi

if [ -f $ENV_PATH ]; then
    source $ENV_PATH
fi


echo "> Removing existing venv directory"
rm -rf $FLASK_APP_DIR/venv

echo "> Setting up new virtual environment"
python3 -m venv $FLASK_APP_DIR/venv
source $FLASK_APP_DIR/venv/bin/activate

echo "> Installing dependencies"
pip install -r $FLASK_APP_DIR/requirements.txt

# Flask 앱 시작
echo "> Starting Flask app with gunicorn"
cd $FLASK_APP_DIR
source $FLASK_APP_DIR/venv/bin/activate
nohup gunicorn -w 4 app:app -b 0.0.0.0:5002 > /dev/null 2> /dev/null < /dev/null &

명령어를 통해 애플리케이션을 실행하는 것 처럼 순서대로 적으면 된다.

CI/CD가 정상적으로 이루어진 모습


업로드중..
업로드중..

profile
Search Engineer

0개의 댓글