[GIT&AWS] 깃헙액션으로 CI/CD 배포 자동화 하기!
📖 EC2에 프로젝트 빌드하여 올리는 것까지 완료했다. 이제는 CI/CD 배포 자동화를 해볼 차례!
실제 배포된 인스턴스는 ubuntu이지만 포스팅이 죄다 linux 인스턴스로 되어있어서 일단 연습할 겸 새 인스턴스는 linux 기반으로 생성했다. 연습은 linux대로 하고 실제 적용할 땐 ubuntu 기준으로 살짝만 변경해주면 될 것이다.
1. ec2 인스턴스에서 자동배포를 위해 필요한 codedeploy를 설치해줘야 한다.
# 패키지 매니저 업데이트, ruby 설치
sudo yum update
sudo yum install ruby
sudo yum install wget
# 서울 리전에 있는 CodeDeploy 리소스 키트 파일 다운로드
cd /home/ec2-user
wget https://aws-codedeploy-ap-northeast-2.s3.ap-northeast-2.amazonaws.com/latest/install
# 설치 파일에 실행 권한 부여
chmod +x ./install
# 설치 진행 및 Agent 상태 확인
sudo ./install auto
sudo service codedeploy-agent status
Agent 상태 확인을 했을 때 위와 같이 나오면다면 성공이다.
2. 깃허브 액션에서 new workflow를 들어간다. 우리는 springboot를 사용했고, 이 프로젝트를 서버에 배포할 것이기 때문에 'Java with Gradle' 으로 선택한다.
3. gradle.yml 파일에 아래 내용으로 넣어준다. 설명은 아래에서 차근차근 살펴보도록 하자.
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
# This workflow will build a Java project with Gradle and cache/restore any dependencies to improve the workflow execution time
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-gradle
name: Java CI with Gradle
on:
push:
branches: [ "develop" ]
pull_request:
branches: [ "develop" ]
permissions:
contents: read
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: Build with Gradle
# uses: gradle/gradle-build-action@67421db6bd0bf253fb4bd25b31ebb98943c375e1
# with:
# arguments: build
- uses : actions/checkout@v3
#1
# 해당 부분은 상당히 중요함 (글에서 부가설명)
# application.properties는 외부에 노출되면 안되므로 Actions가 빌드될때마다 해당 Repository의 Secret 설정을
# 이용하여서 설정 파일을 생성해줌 (github에 commit 되는게 아님!)
- run : touch ./src/main/resources/application.yml
- run : echo "${{ secrets.APPLICATION }}" > ./src/main/resources/application.yml
- run : cat ./src/main/resources/application.yml
# gradlew에 권한 부여
- name: Grant execute permission for gradlew
run: chmod +x gradlew
# gradlew 빌드
- name: Build with Gradle
run: ./gradlew clean build
# 이름은 run 에서 설정 가능
# 빌드를 성공하면 해당 프로젝트를 zip로 만듬
- name: Make zip file
run: zip -r ./<자신이 만든 ZIP 파일 이름>.zip .
shell: bash
#2
# AWS 계정 설정
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1
with:
# 깃허브 Secret에 넣어둔 Access key
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
# 깃허브 Secret에 넣어둔 Secret key
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
# 깃허브 Secret에 넣어둔 Region
aws-region: ${{ secrets.AWS_REGION }}
#3
# 만들어 놓은 S3에 해당 zip 파일 저장
- name: Upload to S3
run: aws s3 cp --region ${{ secrets.AWS_REGION }} ./<자신이 만든 ZIP 파일 이름>.zip s3://<만들어 놓은 버킷 이름>/<자신이 만든 ZIP 파일 이름>.zip
#4
# AWS CodeDeploy에 배포
- name: Deploy
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
run: |
aws deploy create-deployment \
--application-name <AWS codeDeploy 애플리케이션 이름> \
--deployment-group-name <codeDeploy group 이름> \
--file-exists-behavior OVERWRITE \
--s3-location bucket=<만들어 놓은 버킷 이름>,bundleType=zip,key=<자신이 만든 ZIP 파일 이름>.zip \
--region ${{ secrets.AWS_REGION }}
#1 application.yml 설정하기
보안상 이유로 application.yml는 github에 올리지 않는다. 그러면 어떻게 CI를 구축할 수 있을까?
[ Repository > Settings > 좌측에 Secrets and variables > Actions ] 탭을 들어간 뒤 'New repository secret' 을 클릭한다. #1 주석의 ${{ secrets.APPLICATION }}는 secrets의 APPLICATION이라는 이름으로 Secret을 작성해주고, 내용은 application.yml의 내용을 붙여넣는다.
이렇게 해주면 CI가 작동될 때마다 secret에서 코드를 가져와 application.yml을 자체적으로 생성 후 빌드해준다. 결과적으로 yml 파일을 github에 올리지 않더라도 CI를 구축할 수 있다.
#2 AWS 계정 설정하기
여기에 있는 ${{ secrets.AWS_ACCESS_KEY_ID }}와 ${{ secrets.AWS_SECRET_ACCESS_KEY }}도 마찬가지로 Secret을 생성해주면 되며, 각각 IAM의 액세스 키와 시크릿 키를 넣어준다. ${{ secrets.AWS_REGION }}은 사용하고 있는 리전 주소를 Secret에 넣어주면 된다. ex) ap-northeast-2
#3 CI를 통해 만든 zip 파일을 S3에 저장하기
gradle.yml에 파일에서 자신이 만들 ZIP 파일 이름을 그대로 넣어주고, 자신이 AWS에서 만들어놓은 S3 버킷 이름을 넣어주면 된다.
#4 AWS CodeDeploy에 배포하기
${{ secrets.AWS_ACCESS_KEY_ID }}와 ${{ secrets.AWS_SECRET_ACCESS_KEY }}는 앞서 넣어둔 액세스키와 시크릿 키가 들어갈 테고, run 부분에서 주석에 맞게끔 값들을 넣어준다.
주의할 점
Gradle.yml이 어떤 브랜치에 있는지에 따라 나뉜다. 작성후 커밋할 때 delvelop 브랜치 쪽으로 머지해주는 과정이 필요하다.
여기까지 완료했으면 이번엔 AWS에서 세팅을 시작해보자.
1. IAM 역할을 생성한다.
[ IAM > 좌측 '역할' > 역할만들기 ] 를 클릭한다.
일반 사용 사례는 EC2를 선택하고 '다음'으로 간다.
위 2개의 정책을 선택하고 넘어간다.
마지막으로 역할 이름을 입력하고 역할 생성을 눌러준다.
2. EC2 인스턴스에 생성한 역할을 연결한다.
3. CodeDeploy 역할을 하나 더 만들어준다. 이번엔 'AWSCodeDeployRole' 정책만 추가한다.
4. CodeDeploy 애플리케이션을 생성해준다.
[ AWS > CodeDeploy > 애플리케이션 > 애플리케이션 생성 ]을 클릭한다.
위와 같이 설정하고 생성을 누른다.
5. 배포 그룹을 생성해준다.
위 값들을 넣어준다.
나머지 설정은 그대로 두고 로드 밸런싱 활성화 체크는 해제한 뒤 '배포 그룹 생성'을 클릭한다.
혹시 위와 같은 에러창이 뜬다면 설정해줄 부분이 있다.
[ IAM > 역할 ]에서 내가 선택한 역할을 들어간 뒤 '신뢰관계' 탭으로 들어간다.
신뢰 정책 편집에 들어가서 위에 박스친 부분처럼 수정해준다.
6. IAM 사용자를 추가한다. 기존에 사용자가 있다면 'AWSCodeDeployFullAccess' 권한을 추가해준다.
이 사용자의 Access Key와 Secret Access Key는 당연하겠지만 잘 보관해두자.
7. Github secret에 환경변수를 추가해준다.
위 화면에선 이미 다 추가한 모습이 있는데, 위와 같이 하나씩 추가해주면 된다.
이번엔 코드로 돌아가서 좀더 세팅을 해보자
1. 프로젝트 내부 최상단에 appspec.yml 파일을 만들어준다.
version: 0.0
os: linux
files:
- source: /
destination: /home/ec2-user/app/deploy
hooks:
AfterInstall:
- location: scripts/stop.sh
timeout: 60
runas: ec2-user
ApplicationStart:
- location: scripts/start.sh
timeout: 60
runas: ec2-user
destination의 경우 EC2내부에 저장 될 경로이다. 그리고 hooks를 통해 쉘 스크립트를 실행해줄 것인데 location으로 해당 쉘의 위치를 잡아 주고 실행줄 것이다.
2. 프로젝트 최상단에 scripts 폴더를 만들어 start.sh와 stop.sh를 만들어준다.
start.sh
#!/usr/bin/env bash
PROJECT_ROOT="/home/ec2-user/app/deploy"
JAR_FILE="$PROJECT_ROOT/build/libs/sparta-0.0.1-SNAPSHOT.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
#!/usr/bin/env bash
PROJECT_ROOT="/home/ec2-user/app/deploy"
JAR_FILE="$PROJECT_ROOT/build/libs/sparta-0.0.1-SNAPSHOT.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
여기까지가 끝이다. 이제 develop 브랜치에서 푸시하여 CI/CD를 작동시켜보자.
[ AWS > CodeDeploy ]로 들어가면 배포그룹에 현재 진행중인 것을 볼 수 있다.
https://zzang9ha.tistory.com/404
https://zzang9ha.tistory.com/404
디버깅