새롭게 시작한 개인 프로젝트에 CI/CD를 적용해 보기로 결정 했다.
여러가지 툴이 있겠지만 간편하게 사용하기 위해 Github Actions를 사용하기로 결정했고, CI/CD를 적용하였다.
AWS + Github Actions를 이용한 무중단 배포 서비스 구축에 관련된 포스트는 꽤 있지만,
한번에 전체적인 그림을 보여주는 포스트를 사실 발견하진 못했기에, 스스로 정리해보고자 한다.
들어가기 전에, 아래의 일련의 과정을 한번에 세팅 할 수 있다면 좋겠지만, 나의 경우 EC2에 이것 저것 설정하면서 다수의 에러를 만났다.
내가 만났던 큰 에러들을 가장 아래 TroubleShooting에 기술 해 놓았으니, 미리 참고해보면 좋을 것 같다.
시스템 아키텍쳐
이번 포스트에서 구현 할 내 프로젝트의 아케텍쳐는 다음 사진과 같다.
1. CI/CD 구동 순서
본격적으로 들어가기 전에 어떤 식으로 CI/CD가 이루어지는 지 간단히 정리해보자.
- Github 리포지토리에 push 이벤트가 발생한다.
- push 이벤트를 감지한 Github Actions는 일련의 과정을 거쳐 zip 파일을 생성하고, 이를 AWS S3에 업로드 한다.
- Github Actions는 AWS CodeDeploy에게 Deploy하라는 요청을 보낸다.
- Deploy 요청을 받은 AWS CodeDeploy는 S3로 부터 zip파일을 다운받는다.
- CodeDeploy는 EC2에 설치되어 있는 CodeDeployAgent를 이용하여 배포를 진행하게 된다.
(이때, appspec.yml 파일을 통해 도커 컨테이너를 구동시킨다.)
2. Client 접근 시 포트 포워딩
위 구조에서 알 수 있듯이, 나는 프론트엔드, 백엔드, 데이터베이스까지 몽땅 하나의 EC2에 올려놨기 때문에
멀티 포트 포워딩을 해야 했고, 이부분은 Nginx를 이용하여 처리하였다.(nginx를 이용한 포트 포워딩 처리 포스트)
이제 대략적인 시스템 구조에 대한 설명은 끝났고, 본격적으로 어떻게 구축 할 수 있는지 정리해보자.
AWS 환경 생성 및 설정하기
가장 먼저 준비되어야 할 부분은 AWS의 EC2, S3, IAM, CodeDeploy를 생성 및 설정하는 부분이다.
1. EC2 생성하기
EC2를 생성 및 세팅하는 부분은 이전에 작성한 EC2 서비스 시작하기를 참고한다.
2. S3 생성하기
S3를 생성 하는 부분은 이전에 작성한 S3 서비스 시작하기를 참고한다.
3. IAM 역할 생성 및 EC2에 역할 부여
IAM 역할을 생성하고 EC2에 부여 하는 방법은 이전에 작성한 IAM 역할 생성(EC2에 IAM 역할부여)를 참고한다.
4. CodeDeploy 생성 및 배포그룹 설정
CodeDeploy를 생성하고 배포그룹을 설정하는 방법은 이전에 작성한 CodeDeploy 설정 및 생성을 참고한다.
Github Actions 설정하기
1. workflow 생성
이제 우리 CI/CD를 담당 해 줄 Github Actions를 생성해보자.
먼저, 원하는 Repository에 이동하여 Actions탭을 클릭한다.
기존에 생성한 Workflow가 없다면 아래와 같은 화면이 나올 것이고, 여기에서 set up a workflow yourself를 클릭하자.
(물론, 내가 원하는대로 제공되는 suggested workflow가 있다면 그것을 사용해도 무관하다.)
그럼 아래처럼 해당 repository /.github/workflows에 생성할 yml 파일을 작성 할 수 있는 페이지로 이동한다.
파일을 직접 생성하기 전에 간단하게, github actions의 구성에 대해 집고 넘어가자.
github actions는 아래와 같은 요소들로 구성되어 있다.
- Workflow
- 여러 Job으로 구성되고, Event에 의해 트리거될 수 있는 자동화된 프로세스
- Workflow 파일은 YAML으로 작성, Repository의 .github/workflows 폴더 아래에 저장된다.
- Event
- Workflow를 Trigger(실행)하는 특정 활동이나 규칙(push, pr 등)
- Job
- Job은 여러 Step으로 구성되고, 가상 환경의 인스턴스에서 실행
- Step
- Task들의 집합으로, 직접 작성한 커맨드를 실행하거나, 기존에 만들어진 action을 실행할 수 있다.
- Action
- Workflow의 가장 작은 블럭(smallest portable building block)
- 재사용이 가능한 컴포넌트
- Marketplace에 있는 공용 Action을 사용할 수 있다.
- Runner
- Github Action Runner 어플리케이션이 설치된 머신으로, Workflow가 실행될 인스턴스
뭐 당장 바로 이해가 되지 않아도 상관없다. 아래 예시 파일을 직접 작성해 보면 자동으로 정리가 될 것이다.
2. workflow.yml 예시 파일
테스트 예시 파일은 아래와 같다.
여러개의 job으로 나누어 작성해도 좋지만, 작성 편의상 하나의 job에 여러가지 step이 있는 구조로 작성하였다.
name: Build and Deploy TEST
on:
push:
branches:
- main
jobs:
test_job:
name: deploy to staging
environment: deploy
# 해당 workflow를 실행할 runner 설정
runs-on: ubuntu-latest
steps:
# 가장 기본 action, 대상 repository code를 가져온다.
- name: Checkout
uses: actions/checkout@v4.1.0
# frontend env 설정
- name: ⚙️ frontend .env 생성
run: |
touch ./frontend/.env
echo "VUE_APP_KAKAO_REST_API_KEY=${{secrets.VUE_APP_KAKAO_REST_API_KEY}}" >> ./frontend/.env
echo "VUE_APP_KAKAO_REDIRECT_URI=${{secrets.VUE_APP_KAKAO_REDIRECT_URI}}" >> ./frontend/.env
echo "VUE_APP_BASE_URL=${{secrets.VUE_APP_BASE_URL}}" >> ./frontend/.env
# backend env 설정
- name: ⚙️ backend .env 생성
run: |
touch ./backend/.env.dev
echo "KAKAO_CLIENT_ID=${{secrets.KAKAO_CLIENT_ID}}" >> ./backend/.env.dev
echo "KAKAO_REDIRECT_URI=${{secrets.KAKAO_REDIRECT_URI}}" >> ./backend/.env.dev
echo "JWT_SECRET=${{secrets.JWT_SECRET}}" >> ./backend/.env.dev
echo "AT_DURATION=${{secrets.AT_DURATION}}" >> ./backend/.env.dev
echo "DATABASE_URL=${{secrets.DATABASE_URL}}" >> ./backend/.env.dev
# db env 설정
- name: ⚙️ db .env 생성
run: |
touch ./db/.env
echo "MYSQL_ROOT_PASSWORD=${{secrets.MYSQL_ROOT_PASSWORD}}" >> ./db/.env
echo "MYSQL_DATABASE=${{secrets.MYSQL_DATABASE}}" >> ./db/.env
# build를 진행하는 스크립트(makeInstallPackage)를 실행하여, tar.gz파일 생성한다.
- name: 🔧 makeInstallPackage.sh 실행
run: |
chmod +x makeInstallPackage.sh
./makeInstallPackage.sh
# Runners에 AWS 접속을 위한, Configure 세팅을 한다.
# 이때 사용되는 AccessKey와 SecretKey는 IAM 사용자의 것이다.
- name: 🔒 "Configure AWS Credentials"
uses: aws-actions/configure-aws-credentials@v4
with:
aws-region: ${{secrets.AWS_REGION}}
aws-access-key-id: ${{secrets.AWS_ACCESSKEY}}
aws-secret-access-key: ${{secrets.AWS_SECRETKEY}}
# aws cli를 이용하여, 기존에 생성한 tar.gz 파일을 S3에 업로드한다.
# AWS_S3_BUCKET는 버킷 이름을 의미한다.
# AWS_S3_DIR는 버킷내 위치를 의미한다.
- name: 🚛 Upload S3
run: aws s3 cp --region ${{secrets.AWS_REGION}} ./installPackage.tar.gz s3://${{secrets.AWS_S3_BUCKET}}/${{secrets.AWS_S3_DIR}}/installPackage.tar.gz
# CodeDeploy에 배포 명령을 내린다.
# 이 과정을 통해, S3에 있는 tar.gz을 EC2에 내가 원하는 위치에 배포 할 수 있다.
- name: 🚀 Code Deploy
run: |
aws deploy create-deployment \
--deployment-config-name CodeDeployDefault.AllAtOnce \
--application-name ${{ secrets.AWS_CODE_DEPLOY_APPLICATION_NAME }} \
--deployment-group-name ${{ secrets.AWS_CODE_DEPLOY_GROUP_NAME }} \
--s3-location bucket=${{secrets.AWS_S3_BUCKET}},bundleType=tgz,key=${{secrets.AWS_S3_DIR}}/installPackage.tar.gz
3. 주의 사항
먼저, env파일 관리 방법이다.
일반적으로 보안상의 이유로 .env 파일들은 제외하고 github에 업로드를 한다.
그럼 이렇게 환경파일이 없는 상태에서 빌드하게 되면 당연히 에러가 발생하게 되고 제대로 동작하지 않는다.
이러한 점을 방지하기 위해 github actions 는 이러한 보안이 필요한 값들을 관리하는 기능을 제공한다.
해당 Repository > Settings 탭 > 좌측 Secrets and variables 메뉴 > Actions를 클릭하면 아래와 같은 화면이 나오게 된다.
여기에서 내가 원하는 환경값들을 추가 할 수 있다.
그리고 사용할 때에는 위 예시코드 처럼 {{secrets.[SECRET_KEY]}} 형식으로 사용하면 된다.
이제 여기까지 하면, push 이벤트가 발생 하였을 때, S3에 업로드 하고, 그걸 EC2가 받아서 원하는 위치에 설치되어야 할 것같지만,
아직 조금 더 설정해줘야 하는 부분이 있다. (거의 다 왔다 조금만 힘내자! 😇)
appspec.yml 설정하기
appspec.yml은 CodeDeploy에서 배포를 관리하는 데 사용하는 yml 형식 또는 JSON 형식의 파일이다. (자세한 사항은 AWS 참고)
그래서 우리한테 중요한 것은 이 파일이 갑자기 왜 필요하냐? 라는 점이다.
github actions를 통해서 꽤나 많은 일들을 처리했지만, 우리에게는 아래와 같은 추가 작업들이 남아 있다. 그리고 이런 작업들을 자동적으로 처리해주는 것이 마로 appspec.yml 파일이다.
- 배포 위치 설정
- 배포 후, 후 처리 작업(container를 새로 생성 하여 띄우는 등) 실행
appspec.yml 아래처럼 생겼으며, 위치는 리포지토리 최상단에 두면 된다.(이 위치는 매우 중요하다!)
version: 0.0
os: linux
files:
- source: /
# EC2 내 배포 위치 설정
destination: /app/habits
overwrite: yes
permissions:
- object: /
pattern: '**'
owner: ubuntu
group: ubuntu
mode: 755
hooks:
# 배포 완료 이후, 실행할 작업 설정
AfterInstall:
# deploy.sh 라는 스크립트 실행
- location: deploy.sh
# deploy.sh 파일 실행 시 timeout 설정 (180s)
timeout: 180
runas: root
deply.sh 파일에서 나는 docker-compose up --build frontend backend -d 와 같은 작업을 처리해 주었다.
다들 원하는 작업들을 스크립트 형식으로 작성하여, 위 예시 파일처럼 appspec.yml을 설정 해 주면 된다.
AWS EC2 필요 Library 설치
이제 정말 마지막 작업만이 남았다. 🤣 (정상이 멀지 않았습니다!)
지금까지 내가 말한대로 잘 마쳤다면, EC2에 우리가 필요한 라이브러리들만 설치하면 끝이다!
1. EC2 - AWS CLI 설치
우선, ec2에 접속하여 aws-cli를 먼저 설치 해 주자.(AWS 공식 링크)
만약, ec2에 unzip이 설치되어 있지 않다면 아래처럼 apt를 이용해 설치 해 주자.
sudo apt update
sudo apt install unzip
아래 명령어는 Linux x86 (64-bit) 기준이니 다른 환경이라면 공식링크를 참고하자.
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install
# 정상적으로 설치가 끝났다면, 설치된 버전이 보일 것이다.
aws --version
2. EC2 - aws configure 설정
우리가 aws-cli를 이용할 때 사용할 보안정보를 입력해주자.
aws configure 명령어를 입력하면 나오는 입력 칸에 순서대로 IAM 사용자의 AccessKey와 SecretKey를 입력해주면 된다.
sudo aws configure
//IAM 사용자의 AccessKey 입력
//IAM 사용자의 ScretKey 입력
3. EC2 - CodeDeployAgent 설치
이제 EC2에 CodeDeployAgent를 설치해주자.
나는 공식 홈페이지를 참조하여 아래와 같이 설치하였다.(AWS 공식 링크)
만약 ubuntu 환경이라면 아래 명령어를 그대로 입력하면 되고, 아니라면 위 링크에서 각 환경에 맞는 명령어를 입력해주자.
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
systemctl status codedeploy-agent
이렇게 차례대로 명령어를 입력하면, 아래처럼 codedeploy가 정상 작동하는 모습을 볼 수 있다.
이제 모든 과정이 끝났다.
제대로 설정이 되었다면 github에 push하면 AWS에 정상적으로 배포 되는 모습을 볼 수 있을 것이다.
하지만 나는 이렇게 한번에 설정하지 못하였고, 수많은 에러를 만났다.
앞서 말한 것처럼 에러를 만난다면, 아래 TroubleShooting부분을 참고 해 보자.
TroubleShooting
1. No space left on device
프리티어를 사용하면서 부족한 용량 때문에 생긴 에러이다. 더군다나 CodeDeploy를 사용하게 되면 배포이력을 관리하기 위해 default로 5개의 이력 파일들을 관리하게 되면서 용량을 많이 잡아먹게 된다.
아래 명령어를 통해 codedeployagent.yml 파일을 열어 max_revision을 2로 바꿔주었다.
sudo vi /etc/codedeploy-agent/conf/codedeployagent.yml
2. npm install 멈춤 현상
docker image를 생성하기 위해 EC2에서 docker compose up --build frontend -d 를 했을 때, 이미지 생성이 되지 않고, 멈춰버리는 현상이 있었다. 이건 메모리 부족으로 발생하는 현상으로 이전에 작성한 EC2 npm install 멈춤 현상 해결(feat. swapfile)를 참고하면 메모리 증설을 하지않고, swap memory를 통해 해결할 수 있다.
3. EC2 접속 에러 현상
갑자기 aws에 생성한 EC2 키를 통해 터미널에서 접근하려는데, 접근 에러가 발생하는 경우가 있었다. 나의 경우, EC2에 탄력적 IP를 적용하는 과정에 생겼던 에러였으나 구글링 해보니 여러 이슈로 해당 현상이 발생 함을 알 수 있었다. 이전에 작성한 EC2 접속에러를 통해 해당 에러를 해결 할 수 있다.
[참고] Deploy Error Log 위치
배포 과정에서 생기는 에러 로그들은 아래 위치에서 확인 할 수 있다. 배포를 하다 에러가 발생했지만, 무슨 이유에서 에러가 발생했는지 모르겠다면, 아래 명령어를 통해 확인해보자.
tail -f -n 100 /opt/codedeploy-agent/deployment-root/deployment-logs/codedeploy-agent-deployments.log
tail -f -n 100 /var/log/aws/codedeploy-agent/codedeploy-agent.log
# codedeploy-agent default 위치
cd /opt/codedeploy-agent/deployment-root/
나름 자세히 적는다고 적었지만, 빠진 부분이 있을 수도 있고 잘못된 부분이 있을 수도 있습니다.
그런 부분이 있다면 언제든지 댓글로 남겨주시면 감사하겠습니다.
'CI CD' 카테고리의 다른 글
[AWS] EC2 접속에러: REMOTE HOST IDENTIFICATION HAS CHANGED (0) | 2023.12.11 |
---|---|
[AWS] CodeDeploy 설정 및 생성(feat. Deploy 역할 생성) (0) | 2023.12.10 |
[AWS] IAM 역할 생성(EC2에 IAM 역할 부여) (0) | 2023.12.10 |
[AWS] S3 시작하기 (0) | 2023.12.10 |
[AWS] nginx를 이용한 포트 포워딩(feat. 멀티 포트포워딩) (0) | 2023.12.10 |