이 글은 공부를 하면서 알게 된 내용들을 기록하는 글 입니다. 오류나 고쳐야 할 사항들이 있다면 지적 부탁드립니다!
🎯 목표
Github Actions와 AWS CodeDeploy, AWS S3, AWS EC2를 이용해서 개발자가 특정 브랜치에 PR/Push를 했을 때,
AWS 운영 환경에 서비스가 자동으로 배포되게끔 CICD 자동화를 해보자.
🖥️ 개발 환경
스프링 버전: Spring boot 3.2.1
빌드툴: Gradle
JDK: openjdk 17
데이터베이스: mariadb, mongodb
✅ AWS EC2 생성 및 환경 설정
개발한 서비스를 AWS EC2 인스턴스에 배포해도록 하자.
AWS에 처음 가입하면 1년간 프리티어 수준으로 서비스를 이용할 수 있는데, EC2의 경우에는 `12개월 동안 매월 750시간`을 제공한다.
750을 24로 나눴을 때 31을 넘기 때문에, 프리티어를 사용하고 있을 때에는 인스턴스 하나 정도는 무료로 사용할 수 있다 :)
2. Docker 통해 mariaDB 설치 및 계정 설정
3. Docker 통해 mongoDB 설치 및 계정 설정
4. EC2에 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
설치 이후에 밑의 명령어를 통해 codedeploy-agent가 제대로 설치되었는지 확인하자.
이후에 밑의 사진처럼 active로 뜬다면 설치가 제대로 된 것이다.
sudo service codedeploy-agent status
5. EC2에 Java 설치하기
sudo apt update
sudo apt install openjdk-17-jdk
✅ AWS IAM 사용자 생성 후, Github Actions 값 입력
우리는 개발자가 특정 브랜치(ex: production)에 `PR` 혹은 `Push`를 했을 때 `Github Actions`가 적절한 일을 하여 AWS 운영 환경에 서비스를 올리기를 바랬다.
그런데 우리도 AWS 서비스를 이용하려면 로그인을 통해 권한을 얻어야하는데, `Github Actions`에서도 이러한 과정이 있어야하지 않을까?
따라서 필요한 권한들을 가진 `AWS IAM 사용자`를 생성하고,
IAM 사용자를 사용할 수 있는 값을`Github Actions`에 `secret`으로 설정하여 `Github Actions`에서 필요한 일들을 할 수 있도록 하자.
1. AWS IAM 사용자 생성하기
1) AWS IAM 서비스 접속 후, `엑세스 관리` - `사용자` - `사용자 생성` 선택
2) `사용자 이름`에 원하는 이름 설정
3) `직접 정책 연결`을 누른 후, 밑에 해당하는 정책들을 추가
AmazonS3FullAccess
AWSCodeDeployFullAccess
AWSCodeDeployRole
2. IAM 사용자의 엑세스 키 만들기
`Github Actions`에게 IAM 사용자 정보를 전달하기 위한 엑세스 키를 만들어보자.
1) IAM 사용자 목록에서 방금 생성한 IAM 사용자를 클릭
2) 요약 란의 `엑세스 키 만들기` 선택
3) `기타` 선택 이후 생성을 누르면 엑세스 키 생성
엑세스 키 생성이 완료되면, `access key`와 `secret key`가 나오는데, 이 둘을 복사해서 안전한 곳에 저장하거나
.csv 파일을 안전한 곳에 다운받아놓자.
3. Github Actions에 IAM 정보 입력하기
우리가 배포하고 싶은 코드가 있는 Repository에 들어가자.
`Github Settings` - `Secrets and variables` - `Actions` - `New repository secret`를 누른 후,
위에서 발급받은 값을 각각 입력해주자
✅ EC2에서 S3에 접근하기 위한 IAM 역할 생성
위에서 EC2 인스턴스에 CodeDeploy agent를 설치했는데, 이 agent는 S3에 접근하여 빌드 파일을 가져오는 역할을 한다.
따라서 EC2 인스턴스에 S3에 접근할 수 있는 IAM 역할을 부여해주어야 한다.
1. AWS IAM 역할 생성하기
1) AWS IAM 서비스 접속
2) `엑세스 관리` - `역할` - `역할 생성` 선택
3) 신뢰할 수 있는 엔티티 선택에 `AWS 서비스` 선택
4) 사용 사례에 `EC2` 선택
5) 권한 추가에 `AmazonS3FullAccess` 선택
6) 역할 이름에 사용하고자하는 이름 입력
2. EC2에 역할 부여하기
이전에 생성한 EC2 인스턴스 선택 - `작업` - `보안` - `IAM 역할 수정` 선택
IAM 역할 목록에서 생성한 IAM 역할을 연결해주자
IAM 역할 연결 이후에, EC2 인스턴스의 세부 정보를 확인해보면 우리가 부여한 IAM 역할이 설정되어 있음을 확인할 수 있다.
✅ S3 설정하기 - 버킷 생성
Github Actions의 워크플로우를 통해서 빌드 파일(.jar)을 생성하게 되는데, 이를 zip 형식으로 S3에 저장할 것이다.
빌드 파일을 저장할 `버킷`을 생성하자.
AWS S3 서비스 접속 - 버킷 - 버킷 만들기 선택
버킷 이름에는 원하는 이름을 작성하고 생성 버튼을 누르면 된다.
✅ AWS CodeDeploy 설정하기
AWS CodeDeploy는 배포 그룹에 해당되는 EC2 인스턴스에게 적절한 스크립트를 통해 서비스를 실행하도록 한다.
1. AWS CodeDeploy용 IAM 역할 생성하기
1) AWS IAM 서비스 접속 - 역할 생성
2) 신뢰할 수 있는 엔터티 유형에 `AWS 서비스` 선택
3) 사용 사례에 `CodeDeploy` 선택
기본적으로는 AWSCodeDeployRole이 적용되어 있으므로 그대로 생성하면 된다 :)
2. 애플리케이션 생성
이제 CodeDeploy에게 `애플리케이션`을 생성하고 `EC2` 인스턴스를 설정해주어 어떤 EC2 인스턴스에서 작업을 하게 할지 알려주자.
1) AWS CodeDeploy 접속 - 애플리케이션 생성 선택
2) 애플리케이션 이름에는 사용하고 싶은 이름 선택
3) 컴퓨팅 플랫폼에 `EC2/온프레미스` 선택
3. 배포 그룹 생성
생성한 AWS CodeDeploy 어플리케이션에 배포 그룹을 만들어 EC2 인스턴스를 설정해주자
1) 생성한 애플리케이션 - `배포 그룹 생성`
2) 배포 그룹 이름에는 사용하고 싶은 이름 입력
3) 서비스 역할에 위에서 생성한 CodeDeploy용으로 생성한 IAM 역할 선택
4) 배포 유형: 현재 위치 선택
5) 환경 구성: `Amazon EC2 인스턴스` 선택 - 값에 배포에 사용하고자하는 EC2 인스턴스 이름 선택
6)AWS CodeDeploy 에이전트 설치에 `한 번만` 선택
7) 배포 구성에 `CodeDeployDefault.AllAtOnce` 선택
8) 로드밸런서는 비활성화
✅ Github Actions workflow 파일 작성 - main.yml
이제 Github Actions의 workflow 파일을 작성하자.
`.github` - `workflow` 폴더에 `.yml` 파일이 있으면, Github Actions가 해당 파일들을 확인하고 조건에 맞는 워크플로우를 실행한다.
name: Build and Deploy to EC2
# 워크플로우가 언제 실행될 것인지 조건 명시
on:
push:
branches: [ "production"]
pull_request:
branches: [ "production" ]
# AWS 관련 값 변수로 설정
env:
AWS_REGION: ap-northeast-2
AWS_S3_BUCKET: gitget-deploy-bucket
AWS_CODE_DEPLOY_APPLICATION: GitGet-Application-CD
AWS_CODE_DEPLOY_GROUP: GitGet-Deployment-Group
jobs:
deploy:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
# JDK 17 설치
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
# 공개되면 안되는 정보를 담은 .yml 파일을 생성
- name: make application.yml
run: |
mkdir -p ./src/main/resources
cd ./src/main/resources
touch ./application.yml
touch ./application-common.yml
touch ./application-prod.yml
echo "${{ secrets.APPLICATION }}" > ./application.yml
echo "${{ secrets.COMMON }}" > ./application-common.yml
echo "${{ secrets.PROD }}" > ./application-prod.yml
# 권한 부여
- name: Grant execute permission for gradlew
run: chmod +x ./gradlew
shell: bash
- name: Build and Test
run: ./gradlew build test
# 빌드 파일을 zip 형식으로 압축 - S3에서는 jar 저장이 안되기 때문에 zip으로 생성
- name: Make zip file
run: zip -r ./$GITHUB_SHA.zip .
shell: bash
# AWS 권한
- name: AWS credential 설정
uses: aws-actions/configure-aws-credentials@v1
with:
aws-region: ${{ env.AWS_REGION }}
aws-access-key-id: ${{ secrets.CICD_ACCESS_KEY }}
aws-secret-access-key: ${{ secrets.CICD_SECRET_KEY }}
# S3 버킷에 빌드파일(zip 파일)을 업로드
- name: Upload to S3
run: aws s3 cp --region ap-northeast-2 ./$GITHUB_SHA.zip s3://$AWS_S3_BUCKET/$GITHUB_SHA.zip
# EC2 인스턴스에 S3에 저장되어 있던 zip 파일을 받아와 배포 시작
- name: EC2에 배포
run: aws deploy create-deployment --application-name ${{ env.AWS_CODE_DEPLOY_APPLICATION }} --deployment-config-name CodeDeployDefault.AllAtOnce --deployment-group-name ${{ env.AWS_CODE_DEPLOY_GROUP }} --s3-location bucket=$AWS_S3_BUCKET,key=$GITHUB_SHA.zip,bundleType=zip
여기에서 눈에 여겨볼 step은 `make application.yml` 이다.
서비스 개발을 하면서 민감한 정보(DB 비밀번호, JWT 시크릿 티 등)을 .yml 파일로 등록하고 이를 `.gitignore`에 저장하고 사용한다.
배포할 때 이러한 yml 파일이 필요한데 이를 Github Actions secrets로 저장해놓고, Github action이 실행될 때 yml 파일을 생성하도록 하자.
이 링크에서 더 자세한 내용을 확인할 수 있다.
✅ appspec.yml 작성
AWS CodeDeploy가 `appspec.yml`의 내용을 읽고 이 흐름대로 작동한다.
`appspec.yml` 파일은 프로젝트의 제일 루트 경로에 존재해야 제대로 작동한다.
version: 0.0
os: linux
files:
- source: /
destination: /home/ubuntu/app
overwrite: yes
permissions:
- object: /
owner: ubuntu
group: ubuntu
hooks:
ApplicationStart:
- location: scripts/deploy.sh
timeout: 60
✅ deploy.sh 작성
EC2 인스턴스에 설치한 CodeDeploy agent는 `appspec.yml`에서 설정해준 스크립트를 읽고 작업을 실행한다.
`appspec.yml`의 `location`에서 `scripts/deploy.sh`로 설정했으므로, 밑과 같은 위치에 스크립트가 있어야 한다.
밑의 스크립트가 하는 일은 다음과 같다.
1) build 파일이 있는 위치를 통해 build 파일을 특정 경로(DEPLOY_PATH)에 복사
2) 현재 EC2 인스턴스에서 java 어플리케이션이 돌아가고 있다면 모두 일괄 종료
3) build 파일을 백그라운드 옵션(`nohup java -jar`)으로 실행
##!/bin/bash
BUILD_JAR=$(ls /home/ubuntu/app/build/libs/*.jar)
JAR_NAME=$(basename $BUILD_JAR)
echo ">>> build 파일명: $JAR_NAME" >> /home/ubuntu/deploy.log
echo ">>> build 파일 복사" >> /home/ubuntu/deploy.log
DEPLOY_PATH=/home/ubuntu/app/
cp $BUILD_JAR $DEPLOY_PATH
echo ">>> 현재 실행중인 애플리케이션 pid 확인 후 일괄 종료" >> /home/ubuntu/deploy.log
sudo ps -ef | grep java | awk '{print $2}' | xargs kill -15
DEPLOY_JAR=$DEPLOY_PATH$JAR_NAME
echo ">>> DEPLOY_JAR 배포" >> /home/ubuntu/deploy.log
echo ">>> $DEPLOY_JAR의 $JAR_NAME를 실행합니다" >> /home/ubuntu/deploy.log
nohup java -jar $DEPLOY_JAR >> /home/ubuntu/deploy.log 2> /home/ubuntu/deploy_err.log &
✅ Github Actions 실행
Github Actions 워크플로우 파일인 main.yml에서 우리는 production 브랜치에 PR 혹은 Push를 했을 때 배포 프로세스가 실행되도록 했다.
production 브랜치에 PR을 날렸을 때, 밑과 같이 Github Actions에서 워크플로우가 실행이 되고, 그 결과 배포가 정상적으로 됨을 확인할 수 있다 :)
🧨 Trouble shooting
🔥 AWS Codedeploy 배포 실패: `UnknownError - Missing credentials`
AWS EC2 인스턴스에 IAM 역할(S3)도 부여하고 Codedeploy-agent도 설치를 했는데, 막상 Codedeploy 단계에 들어가면 첫 단계인 ApplicationStop에서부터 오류가 발생했다 :(
0. `/var/log/aws/codedeploy-agent`에서 오류 메세지 확인하기
`cd`를 통해 /var/log/aws/codedeploy-agent 위치로 이동 후, `ls` 명령어를 통해 내부에 있는 파일 리스트를 확인해보면`codedeploy-agent.log`를 확인할 수 있다.
ubuntu@ip-:~$ cd /var/log/aws/codedeploy-agent
ubuntu@ip-:/var/log/aws/codedeploy-agent$ ls
codedeploy-agent.20241024.log codedeploy-agent.log codedeploy-agent.log.age
ubuntu@ip-:/var/log/aws/codedeploy-agent$ vi codedeploy-agent.log
나의 경우에는 해당 로그 파일을 확인했을 때 `Missing credentials`를 확인할 수 있었다.
InstanceAgent::Plugins::CodeDeployPlugin::CommandPoller: Missing credentials - please check if this instance was started with an IAM instance prof
1. AWS EC2 인스턴스에 CodeDeploy-agent가 설치 & 실행 중인지 확인해볼 것
EC2 인스턴스에 접속하여 밑의 명령어를 실행했을 때 `active (running)`으로 떠야 CodeDeploy-agent가 실행 중인 것이다.
만일 active가 아니라면 EC2 인스턴스에서 CodeDeploy 서비스의 요청을 받지 못하기 때문에 오류가 발생할 것이다.
systemctl status codedeploy-agent
2. AWS EC2 인스턴스의 CodeDeploy-agent 재시작
EC2 인스턴스에 IAM 역할을 제대로 넣었고, CodeDeploy-agent도 정상적으로 실행 중인데도 같은 오류가 발생하는 경우가 있다.
이 경우 대체로 EC2 인스턴스에 IAM role을 부여하기 전에, CodeDeploy-agent가 설치 & 실행되어 IAM 역할을 인식 못하는 것이라고 한다.
이 경우에는 CodeDeploy-agent 를 재시작하면 해결이 된다.
sudo service codedeploy-agent restart
✅ 참고 링크
https://diary-developer.tistory.com/32
https://be-developer.tistory.com/56