💡 참고자료
https://velog.io/@sangwoong/CICD-GitHub-Action으로-CICD-구축하기
https://rachel0115.tistory.com/entry/Github-Actions로-CICD-구축하기-EC2-S3-CodeDeploy
어떤 툴을 이용해 배포를 할 것인지 정했고 (S3, CloudFront), 그럼 이제 배포 자동화 구축을 위해선 어떤 CI/CD 툴을 이용할 건지, 사용법은 어떤지 간략하게 알아보자.
➡️ 이 중 현업에서 많이 사용하면서 무료로 사용할 수 있고 빌드용 서버가 따로 필요 없는 Github Action을 사용하기로 함.
root
.github
ㄴ workflows
ㄴ cicd-file-name.yml
-> .github/workflows/cicd.yml
push
, pull request
, merge
등- name: Environment Setup
run: ${{ secrets.등록한_환경변수_이름 }}
🔗 FE CI/CD pipeline
1. 특정 브랜치에 push나 merge 동작 수행
2. Github Actions 에서 push 혹은 merge 를 감지하고 정해진 action 을 수행
3. 업데이트된 코드를 바탕으로 Build 수행
4. 프론트엔드 빌드 및 S3 업로드
5. CloudFront 캐시 무효화
name: Deploy React App
on:
push:
branches:
- main
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Set up Node.js
uses: actions/setup-node@v2
with:
node-version: '14'
- name: Install dependencies
run: |
npm install
- name: Build project
run: |
npm run build
- name: Deploy to S3
run: |
aws s3 sync build/ s3://your-frontend-bucket --delete
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
- name: Invalidate CloudFront cache
run: |
aws cloudfront create-invalidation --distribution-id your-distribution-id --paths "/*"
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
🔗 BE CI/CD pipeline
1. 특정 브랜치에 push나 merge 동작 수행
2. Github Actions 에서 push 혹은 merge 를 감지하고 정해진 action 을 수행
3. 업데이트된 코드를 바탕으로 Build 와 Test 수행
4. 코드를 압축하여 Zip 파일 생성
5. S3에 zip 파일 업로드
6. CodeDeploy 에 배포 요청
7. S3로부터 zip 파일 다운로드
8. 지정한 EC2 인스턴스에 애플리케이션 파일 전달
9. EC2 인스턴스에서 쉘 스크립트를 실행함으로써 배포 완료
# Deploy.yml
name: Deploy Backend with Gradle
on:
push:
branches:
- main
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Set up JDK
uses: actions/setup-java@v2
with:
distribution: 'adopt'
java-version: '11'
- name: Build with Gradle
run: ./gradlew build
- name: Run tests with Gradle
run: ./gradlew test
- name: Zip build files
run: zip -r backend.zip build/libs/
- name: Upload to S3
run: aws s3 cp backend.zip s3://your-backend-bucket/backend.zip
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
- name: Deploy to EC2 using CodeDeploy
run: |
aws deploy create-deployment \
--application-name your-codedeploy-app \
--deployment-group-name your-codedeploy-deployment-group \
--s3-location bucket=your-backend-bucket,key=backend.zip,bundleType=zip
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
# appspec.yml
# CodeDeploy가 파일을 어디에 배치하고 어떤 스크립트를 실행할지 정의한다
version: 0.0
os: linux
files:
- source: /
destination: /home/ec2-user/app
hooks:
AfterInstall:
- location: deploy.sh
timeout: 300
runas: ec2-user
# deploy.sh 스크립트
#!/bin/bash
# 애플리케이션 디렉토리로 이동
cd /home/ec2-user/app
# Gradle을 사용하여 애플리케이션 실행
./gradlew bootRun
package-lock.json
의 경우, 새로운 작업 소스 코드와 비교했을 때 변동 사항이 없을 시 굳이 디펜던시를 다시 설치할 필요 없음.- name: Cache Dependencies
id: dependecy-cache
uses: actions/ache@v3
with:
path: "**/node_modules"
# 캐시 대상이 되는 파일
# node_module 디렉토리의 하위 내용을 모두 캐시하겠다는 의미
key: npm-package-${{ hashFiles('**/package-lock.json') }}
# 캐시를 식별할 때 사용하는 옵션
# 해싱이 되어 저장된 package-lock.json 파일과
# 새로운 commit 코드의 package-lock.json 파일이 일치하는지 확인
restore-keys: |
npm-package-${{ hashFiles('**/package-lock.json') }}
npm-package-
# 캐싱이 되어있는 해시 파일을 찾는다
# 해시 파일을 찾는 기준은 restore-keys에 등록된 순으로 찾는다
# Caches에 npm-package-1, npm-package-2 라는 파일이 있다고 가정할 때,
# package-lock.json의 해싱 파일이 npm-package-1 로 되어 있다면 npm-package-1을 기준 캐시 데이터로 저장한다/
# 지정된 캐시 파일은 commit의 package-lock.json 파일과 비교하는 작업을 수행한다
Slack API의 수신웹후크를 사용하여 CI/CD의 결과를 Slack 메세지로 보내는 방법
앱 추가 절차
웹훅 URL
정보 확인 (Github Repository의 환경변수로 지정해준다.)슬랙 메시지 전송 job 구성하기
서버에 코드를 배포하는 job이 종료되면 CI/CD의 결과를 슬랙 메시지로 전송하기 위한 job
jobs:
slack_notification:
if: ${{ always() }}
needs: [ local, test, main ]
runs-on: ubuntu-latest
- name: CUSTOM_STATUS SET UP
run: |
if [ "${{ github.ref }}" = "refs/heads/develop" ];
then
BUILD_NAME="Staging Deploy"
elif [ "${{ github.ref }}" = "refs/heads/main" ];
then
BUILD_NAME="Production Deploy"
else
BUILD_NAME="Local Build & Test"
fi
if문
을 통해 조건을 생성한다.BUILD_NAME
변수에 값을 동적으로 지정해줄 수 있다.fi
를 작성하여 구문을 종료한다.