① IAM > 사용자 탭에서 사용자를 선택하고, 권한으로 AmazonS3FullAccess와 AwsCodeDeployFullAccess를 추가한다.
② 이번엔 EC2 > 인스턴스에 들어가서 인스턴스를 선택한 다음 작업 > 보안 > IAM 역할 수정 버튼을 클릭한다.
③ 새 IAM 역할 생성 버튼을 클릭한다.
④ 역할 만들기 버튼을 클릭한다.
⑤ AWS 서비스, EC2를 선택하고 다음 버튼을 클릭한다.
⑥ 이번에도 AmazonS3FullAccess와 AwsCodeDeployFullAccess를 추가한다.
⑦ IAM Role의 이름과 설명을 입력하고 역할 생성 버튼을 클릭한다.
⑧ 방금 만든 IAM Role을 지정하고 IAM 역할 업데이트 버튼을 클릭한다.
① 아래의 명령을 차례대로 입력한다
$ sudo apt-get update && sudo apt-get upgrade
$ sudo apt install ruby-full
$ sudo apt install wget
$ cd /home/ubuntu
$ sudo wget https://aws-codedeploy-ap-northeast-2.s3.ap-northeast-2.amazonaws.com/latest/install
② ls를 입력했을 때 아래와 같이 install 파일이 나온다면 codedeploy 파일의 설치가 완료된 것이다.
③ 이번에는 아래의 명령을 차례대로 입력하여 설정 및 사용 가능한 codedeploy의 버전을 확인하자.
$ sudo chmod +x ./install
$ sudo ./install auto > /tmp/logfile
$ sudo apt-get install awscli
# 서울 리전에서 사용 가능한 codedeploy 버전 확인
$ sudo aws s3 ls s3://aws-codedeploy-ap-northeast-2/releases/ | grep '\.deb$'
④ 다양한 버전이 있는데 가장 아래에 위치한 최신 버전을 사용하기로 하겠다.
⑤ 아래의 명령을 차례대로 입력한다.
$ sudo ./install auto -v releases/codedeploy-agent_1.6.0-49_all.deb > /tmp/logfile
$ sudo service codedeploy-agent status
⑥ codedeploy-agent service가 active 상태임을 확인하면 CodeDeploy Agent 설치가 모두 완료된 것이다.
① IAM > 역할 탭에 들어가 역할 만들기 버튼을 클릭한다.
② 사용 사례에 CodeDeploy를 선택하고 다음 버튼을 클릭한다.
③ 사용 사례에 CodeDeploy를 선택하면, 자동으로 CodeDeploy에 대한 모든 권한이 추가된다.
④ 역할 이름과 설명을 입력하고, 역할 생성 버튼을 클릭하여 IAM Role 생성을 완료한다.
① 이번엔 CodeDeploy 서비스로 들어가 애플리케이션 생성 버튼을 클릭한다.
② 애플리케이션 이름과 컴퓨팅 플랫폼을 지정하고, 애플리케이션 생성 버튼을 클릭한다.
③ 배포 그룹 생성 버튼을 클릭한다.
④ 배포 그룹 이름을 지정하고 서비스 역할로 방금 만든 IAM Role을 선택한다. 배포 유형은 현재 위치를 선택한다.
⑤ 환경 구성에서 본인의 EC2 인스턴스를 선택하면 된다.
⑥ 배포 설정은 CodeDeployDefault.AllAtOnce를 선택하고, 로드 밸런서를 사용하는 경우에는 로드 밸런싱 활성화에 체크한다.
프로젝트의 root 디렉토리 하위로 appspec.yml, deploy.sh 파일을 추가한다.
AWS S3는 이미 있다고 가정하고 실습을 진행한다. AWS S3 생성 방법을 모른다면, 아래의 링크를 참조하기 바란다.
>> AWS S3 생성하기
이전 포스팅에서 작성했던, ci.yml 파일에 우클릭 > Refactor > Rename으로 cicd.yml로 파일명을 변경한다. 이후 아래의 내용을 입력한다. 본인의 프로젝트에 알맞게 수정해주어야 할 값도 많기 때문에 주의 깊게 읽어보자.
# 워크 플로의 이름
name: CI/CD
# 워크 플로의 시작 조건: main 브랜치에 push
on:
push:
branches:
- '*'
env:
PROJECT_NAME: hello-there # 본인이 원하는 이름을 입력한다.
BUCKET_NAME: chrome-bucket # S3 Bucket 명
CODE_DEPLOY_APP_NAME: hello-there # CodeDeploy 애플리케이션 명
DEPLOYMENT_GROUP_NAME: hello-there-group # CodeDeploy 배포 그룹 명
jobs:
build:
runs-on: ubuntu-latest # 실행 환경
# 작업의 실행 단계를 정의
steps:
- uses: actions/checkout@v3
with:
token: ${{ secrets.GIT_TOKEN }}
submodules: true
- uses: actions/setup-java@v3
with:
distribution: 'zulu' # 자바 배포판
java-version: '17' # 자바 버전
- name: Grant execute permission for gradlew
run: chmod +x ./gradlew
shell: bash
- name: Set YML
run: |
mkdir -p src/main/resources
echo "${{ secrets.APPLICATION_YML }}" | base64 --decode > src/main/resources/application.yml
# 파이어베이스 설정 파일이 있는 경우에만 입력
echo "${{ secrets.SERVICE_ACCOUNT_KEY_JSON }}" | base64 --decode > src/main/resources/ServiceAccountKey.json
find src
# Redis를 사용하는 경우에만 입력
- name: Start Redis
uses: supercharge/redis-github-action@1.1.0
with:
redis-version: 6
# <-- 아래의 내용부터는 변경할만한 내용 없음 -->
- name: Build with Gradle
run: ./gradlew clean build
# zip 파일 생성
- name: Make zip file
run: zip -r ./$GITHUB_SHA.zip .
shell: bash
# AWS 자격 증명
- 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: ap-northeast-2
- name: Upload to S3 # S3 업로드
run: aws s3 cp --region ap-northeast-2 ./$GITHUB_SHA.zip s3://$BUCKET_NAME/$PROJECT_NAME/$GITHUB_SHA.zip
- name: Code Deploy # CodeDeploy에 배포 요청
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
version: 0.0
os: linux
files:
- source: /
destination: /home/ubuntu/.ssh/HelloThere
overwrite: yes
file_exists_behavior: OVERWRITE
permissions:
- object: /home/ubuntu/.ssh/HelloThere
owner: ubuntu
group: ubuntu
hooks:
AfterInstall:
- location: deploy.sh
timeout: 60
runas: ubuntu
① version
② files
③ permissions
④ hooks
⑤ AfterInstall
#!/usr/bin/env bash
BUILD_JAR=$(ls /home/ubuntu/.ssh/HelloThere/build/libs/*.jar | tail -n 1) # /home/ubuntu/.ssh/HelloThere/build/libs/hello_there-0.0.1-SNAPSHOT.jar
REPOSITORY=/home/ubuntu/.ssh/HelloThere
cd $REPOSITORY
APP_NAME=hello_there-0.0.1-SNAPSHOT.jar
JAR_NAME=$(ls $REPOSITORY/build/libs/ | grep 'SNAPSHOT.jar' | tail -n 1) # hello_there-0.0.1-SNAPSHOT.jar
JAR_PATH=$REPOSITORY/build/libs/$JAR_NAME # /home/ubuntu/.ssh/HelloThere/build/libs/hello_there-0.0.1-SNAPSHOT.jar
DEPLOY_PATH=/home/ubuntu/.ssh/HelloThere/
cp $BUILD_JAR $DEPLOY_PATH # cp /home/ubuntu/.ssh/HelloThere/build/libs/hello_there-0.0.1-SNAPSHOT.jar /home/ubuntu/.ssh/HelloThere/
CURRENT_PID=$(pgrep -f $APP_NAME) # 기존 서버 프로세스의 pid
if [ -z $CURRENT_PID ] # current pid의 길이가 zero이면(= 기존 프로세스가 없으면)
then
echo "> 종료할 애플리케이션이 없습니다."
else
echo "> kill -15 $CURRENT_PID"
kill -15 $CURRENT_PID
sleep 5
fi
DEPLOY_JAR=$DEPLOY_PATH$JAR_NAME # /home/ubuntu/.ssh/HelloThere/hello_there-0.0.1-SNAPSHOT.jar
chmod +x $DEPLOY_JAR
echo "> Deploy - $JAR_PATH "
nohup java -jar $DEPLOY_JAR > /dev/null 2> /dev/null < /dev/null &
① CI/CD의 동작여부를 테스트해보기 위해 commit과 push를 진행한다.
② 만약 테스트를 사용하지 않는데, 아래와 같은 에러가 발생하면 src > test > java > com.example.hello_there > HelloThereApplicationTests 파일에서 @SpringBootTest 어노테이션을 제거해야 한다.
HelloThereApplicationTests > contextLoads() FAILED
java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:98
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException at ConstructorResolver.java:800
Caused by: org.springframework.beans.factory.BeanCreationException at ConstructorResolver.java:658
Caused by: org.springframework.beans.BeanInstantiationException at SimpleInstantiationStrategy.java:185
Caused by: org.springframework.boot.autoconfigure.jdbc.DataSourceProperties$DataSourceBeanCreationException at DataSourceProperties.java:186
1 test completed, 1 failed
③ 깃허브 Repository > Actions에 아래와 같이 all-checked가 나와야 한다.
④ Actions에 all-checked가 나오면 AWS CodeDeploy에서 배포 내역을 확인할 수 있다. 배포에는 10분 정도의 시간이 소요된다.
① 만약 EC2 인스턴스를 생성할 때 용량을 8GB로 설정했으면 용량 부족 문제로 배포가 안 될 수 있다. (처음부터 EC2 용량을 넉넉하게 잡았다면 이 과정은 건너뛰어도 된다.)
② 만약 아래와 같이 df -h를 했을 때 Use가 100%라고 나온다면 EC2의 용량을 늘려주어야 한다.
③ EC2의 인스턴스에서 본인의 인스턴스를 선택한 뒤, 스토리지 탭 > 블록 디바이스에 있는 볼륨 ID를 클릭한다.
④ 볼륨 ID에 우클릭 > 볼륨 수정 버튼을 클릭한다.
⑤ 프리티어 최대 용량인 30GB로 수정한다. 이후 인스턴스를 재부팅한다.
⑥ df -h를 다시 입력해보면 아래와 같이 EC2 용량이 늘어난 것을 확인할 수 있다.
① 깃허브의 최신 코드를 pull 받은 후 프로젝트를 빌드한다. 최신 코드를 pull 받는 방법은 프로젝트 디렉토리에서 아래와 같이 입력하면 된다.
$ git stash # 현재 Staging 영역에 있는 파일의 변경사항을 스택에 저장
$ git pull
$ git stash pop # 변경 사항을 적용하고, 스택에서 제거
② 업데이트한 코드를 수동으로 EC2에 무중단 배포한다.
③ 코드를 임의로 수정하고 다시 commit & push한다. 여기서는 아래의 cd-test API를 UserController에 추가하고 commit & push 하기로 한다.
// CI/CD의 정상 동작을 확인하는 API
@GetMapping("/cd-test")
public String cdcheck() {
return "OK";
}
④ 깃허브 액션에서 결과를 확인한다.
⑤ CodeDeploy의 배포 내역에서 배포 상태가 "성공"이 될 때까지 기다린다. 10분 정도 소요된다.
⑥ 만약 10분이 훨씬 지나도 배포가 진행되지 않는다면, EC2 > 로드밸런서 > 대상그룹 > 대상 탭에 들어가 본인의 인스턴스가 unhealthy 상태인지 확인해보자.
⑦ 인스턴스를 healthy 상태로 만들려면, HTTP status 200을 반환하는 임의의 api를 만들어 서버에 올려주어야 한다. 그리고 해당 API의 엔드포인트를 상태 검사의 경로로 넣어주어야 한다.
⑧ CodeDeploy의 상태가 "성공"이 되면, 다시 API를 호출할 수 있는 상태가 된다.
⑨ 새롭게 추가한 cd-test API가 잘 호출되는지 확인해보자.
이로써 깃허브에 코드를 push하면, 서버에 자동으로 배포하는 프로세스가 완성되었다.