[CI/CD] Github Action, AWS 를 통한 자동 배포!
개요
백엔드 개발 시 main 브랜치 push 에 따라 자동 배포를 통해 개발 서버를 유지하고자 한다. 이를 위해 Github action 와 AWS 의 S3, CodeDeploy, EC2 를 사용해서 배포한다. 서버는 Spring Boot 를 사용하였다!
전체적인 구조는 위와 같다.
- Github 에 push 시에 Github Action 이 동작한다.
- Github Action 을 통해 S3 에 코드가 저장되며 CodeDeploy 가 실행된다.
- AWS CodeDeploy 는 S3 에 존재하는 코드를 EC2 에 복사하고 실행시킨다.
해당 프로세스를 통해 Github push 를 통해 개발 서버를 배포할 수 있다!
1. AWS 세팅
IAM
1. IAM 유저 생성
가장 먼저 IAM 유저를 생성해야한다.
User 를 생성하면 user Group 을 생성하라는 메시지를 볼 수 있다. 여기서 Group 을 생성하고 두 가지 권한을 설정하자.
- AmazonS3FullAccess
- AWSCodeDeployFullAccess
Group 이 생성되었으면 생성하던 User 를 해당 Group 에 포함시킨다.
생성 후의 모습은 위와 같다. User 는 두 가지 권한을 가지고 있어야한다.
2. IAM Role 생성
Role 을 생성해주어야한다.
EC2 에서 S3 에 접근할 수 있는 Role
CodeDeploy 에서 S3 에 접근할 수 있는 Role
Role 생성 결과는 위와 같다.
3. Access Key 생성
IAM 을 접근할 수 있는 Key Pair 를 생성한다.
생성한 Key 는 이후 사용해야하므로 잘 저장해 놓도록 하자.
EC2
1. 인스턴스 생성
개발 서버를 배포할 EC2 인스턴스를 생성한다. Spring boot 를 생성하기에 인바운드에 8080 포트를 열어놓도록 하자.
2. EIP 설정
Elastic IP Address 또한 설정해 유지하도록 하자.
3. CodeDeploy Agent 설치
이후 해당 EC2 서버에 접속해 CodeDeploy Agent 를 설치해야한다.
sudo apt update
sudo apt install ruby-full
sudo apt install wget
cd /home/ubuntu
wget https://aws-codedeploy-ap-northeast-2.s3.ap-northeast-2.amazonaws.com/latest/install
chmod +x ./install
sudo ./install auto > /tmp/logfile
Agent 의 소유자도 미리 변경해준다.
sudo service codedeploy-agent stop
sudo sed -i 's/""/"ubuntu"/g' /etc/init.d/codedeploy-agent
sudo systemctl daemon-reload
sudo chown ubuntu:ubuntu -R /opt/codedeploy-agent/
sudo chown ubuntu:ubuntu -R /var/log/aws/
다음의 커맨드를 통해 잘 설치 되었는 지 확인할 수 있다.
sudo service codedeploy-agent status
Spring 실행을 위한 jdk 도 함께 설치해주도록 하자.
sudo apt update
sudo apt install openjdk-17-jdk
4. EC2 에 IAM Role 설정
EC2 가 S3 에 접근해서 코드를 가져올 수 있도록 미리 만들어놓은 Role 을 세팅해주어야한다.
S3
1. Bucket 생성
이제 코드를 저장할 S3 를 생성한다. 내부적으로 접근할 예정이므로 Public Access 는 닫아놔도 된다.
CodeDeploy
1. CodeDeploy 생성
CodeDeploy 를 생성한다.
2. EC2 와 연결
이후 CodeDeploy 와 이전에 생성한 EC2 를 연결해주는 작업을 해야한다.
2. 코드 작성
Github Action
1. Github Action Workflows
Github Action 을 위한 workflows 의 기본 형태는 자동으로 생성할 수 있다.
Java With Gradle 을 사용하였다.
name: Java CI with Gradle
on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
env:
AWS_REGION: ap-northeast-2
AWS_S3_BUCKET: cicd-bucket-name
AWS_CODE_DEPLOY_APPLICATION: cicd-CodeDeploy
AWS_CODE_DEPLOY_GROUP: cicd-CodeDeploy-group
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'corretto'
- name: gradlew permmision
run: chmod +x ./gradlew
- name: Setup Gradle
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0
- name: Build with Gradle Wrapper
run: ./gradlew clean build -x test
- name: AWS credential
uses: aws-actions/configure-aws-credentials@v1
with:
aws-region: $
aws-access-key-id: $
aws-secret-access-key: $
- name: S3 upload
run: aws deploy push --application-name $ --ignore-hidden-files --s3-location s3://$AWS_S3_BUCKET/cicdtest/$GITHUB_SHA.zip --source .
- name: CodeDeploy
run: aws deploy create-deployment --application-name $ --deployment-config-name CodeDeployDefault.AllAtOnce --deployment-group-name $ --s3-location bucket=$AWS_S3_BUCKET,key=cicdtest/$GITHUB_SHA.zip,bundleType=zip
dependency-submission:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'corretto'
- name: Generate and submit dependency graph
uses: gradle/actions/dependency-submission@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0
build 후 S3 에 업로드 하고 CodeDeploy 를 실행시키는 과정이다.
2. Repository Secrets
AWS credential 을 위한 IAM Key Pair 가 여기서 사용된다. Repository Secrets 을 사용한다. 여기서 application.property 도 작성하면 여러 설정들을 세팅해놓을 수 있다.
3. Appspec.yml
CodeDeploy 가 실행시킬 appspec.yml 을 루트 디렉토리에 작성해주어야한다.
version: 0.0
os: linux
files:
- source: /
destination: /home/ubuntu/spring-github-action
overwrite: yes
permissions:
- object: /
owner: ubuntu
group: ubuntu
hooks:
AfterInstall:
- location: scripts/stop.sh
timeout: 60
ApplicationStart:
- location: scripts/start.sh
timeout: 60
이름에서 유추할 수 있듯이 EC2 에 존재하는 파일에서 stop.sh 이후 start.sh 를 실행시키는 작업을 한다.
4. Shell
프로젝트의 루트 디렉토리에 shell 파일을 작성한다.
stop.sh
#!/bin/bash
ROOT_PATH="/home/ubuntu/spring-github-action"
JAR="$ROOT_PATH/application.jar"
STOP_LOG="$ROOT_PATH/stop.log"
SERVICE_PID=$(pgrep -f $JAR) # 실행중인 Spring 서버의 PID
if [ -z "$SERVICE_PID" ]; then
echo "서비스 NouFound" >> $STOP_LOG
else
echo "서비스 종료 " >> $STOP_LOG
kill "$SERVICE_PID"
# kill -9 $SERVICE_PID # 강제 종료를 하고 싶다면 이 명령어 사용
fi
start.sh
#!/bin/bash
ROOT_PATH="/home/ubuntu/spring-github-action"
JAR="$ROOT_PATH/application.jar"
APP_LOG="$ROOT_PATH/application.log"
ERROR_LOG="$ROOT_PATH/error.log"
START_LOG="$ROOT_PATH/start.log"
NOW=$(date +%c)
echo "[$NOW] $JAR 복사" >> $START_LOG
cp $ROOT_PATH/build/libs/spring-github-action-1.0.0.jar $JAR
echo "[$NOW] > $JAR 실행" >> $START_LOG
nohup java -jar $JAR > $APP_LOG 2> $ERROR_LOG &
SERVICE_PID=$(pgrep -f $JAR)
echo "[$NOW] > 서비스 PID: $SERVICE_PID" >> $START_LOG
결과
Github 메인 브랜치에 push 를 해보자. 여러 트러블 슈팅 과정 후 정상적으로 동작하는 것을 확인할 수 있었다. 실제 EIP 에서도 접속 가능한 것을 확인했다.
실제 변경된 내용이 잘 배포되는 지 마지막으로 확인해보자.
간단하게 메세지만 바꾸었을 때 정상적으로 잘 동작하는 것을 확인할 수 있었다.
댓글남기기