Github Actions로 CI/CD 환경 구축하기 (+ Spring Boot, AWS EC2, S3, CodeDeploy)

Jonnie·2022년 11월 14일
0

본 글은 AWS EC2, S3 설정에 대한 내용을 다루고 있지 않으며,
EC2 인스턴스 생성과 S3 버킷 생성이 완료되었다는 가정 하에 진행됩니다.

1. Github workflow yml 파일 생성


name: Build and Deploy for PROJECT_NAME to AWS EC2

# Event Trigger
on:
  push:
  	# master(main) branch 기준으로 동작
    branches: [ master ]
    # PR도 반영되게 할 거라면 아래 부분 주석 해제
#  pull_request:
#    branches: [ master ]

jobs:
  build:
    # 실행 환경 지정
    runs-on: ubuntu-latest

    # Task의 sequence 명시
    steps:
    - uses: actions/checkout@v2
    
    # JAVA 버전은 프로젝트에 맞게 설정
    - name: Set up JDK 17
      uses: actions/setup-java@v1
      with:
        java-version: 17
    
    - name: Grant execute permission for gradlew
      run: chmod +x gradlew
    
    # Gradle build (Test 제외)
    - name: Build with Gradle
      run: ./gradlew clean build -x test

2. Workflow에 이상이 없는지 확인하기

  • 나는 이 과정에서 contextLoads() FAILED 오류가 발생했는데
    https://enant.tistory.com/29 이 블로그를 보니 MySQL Server를 시작하지 않아서 나는 문제였다. 블로그처럼 해결을 해도 되고 위 코드처럼 -x test로 해당 빌드만 제외하고 실행해도 된다.

3. AWS 인스턴스에 접속

접속 방법(MAC OS)
1. pem키가 있는 디렉터리 경로로 이동
2. chmod 400 파일명.pem
3. ssh -i "파일명.pem" ec2-user@퍼블릭DNS!

헷갈릴 경우 인스턴스 - 연결로 들어가면 복사만 해서 사용 가능하게 방법을 다 안내해준다.

이렇게 연결 후 Ubuntu 서버에 맞게 CodeDeploy를 설치하면 된다.
보통 Ubuntu Server를 사용하니 아래 문서를 참고하면 된다.
https://docs.aws.amazon.com/ko_kr/codedeploy/latest/userguide/codedeploy-agent-operations-install-ubuntu.html
그런데 내 경우에는 AWS 설정을 다른 팀원이 진행했더니 계속 문제가 생겨서 버전 확인이 필요했다.

4. Linux 버전 확인

hostnamectl

위 코드를 실행하고 Operation System을 확인하면 버전을 알 수 있는데
우리 팀은 Amazon Linux로 설정되어 있었다. Ubuntu가 아니었다니.
https://docs.aws.amazon.com/ko_kr/codedeploy/latest/userguide/codedeploy-agent-operations-install-linux.html
Amazon Linux의 CodeDeploy 문서는 별도로 있어서 이 사이트를 참고해서 진행했다.

5. Amazon Linux용 CodeDeploy 에이전트 설치

sudo yum update
sudo yum install ruby
sudo yum install wget
cd /home/ec2-user
wget https://aws-codedeploy-ap-northeast-2.s3.ap-northeast-2.amazonaws.com/latest/install
# wget https://bucket-name.s3.region-identifier.amazonaws.com/latest/install
chmod +x ./install
sudo ./install auto # 최신 버전 CodeDeploy 설치

bucket-name은 해당 리전의 CodeDeploy 리소스 키트 파일이 포함되어 있는 Amazon S3 버킷의 이름입니다. region-identifier는 리전의 식별자이다.
예를 들어, 미국 동부(오하이오) 리전의 경우 bucket-name을 aws-codedeploy-us-east-2로 바꾸고 region-identifier를 us-east-2로 바꾸면 된다고 한다.
위 코드는 아시아 태평양 (서울) 기준이므로 다신 리전을 사용할 경우 아래 사이트를 참고하여 변경해주자.
bucket-name & region-identifier 확인하기

6. 서비스가 실행 중인지 확인하기

sudo service codedeploy-agent status


"The AWS CodeDeploy agent is running" 메시지가 표시되어야 하며 혹시 "error: No AWS CodeDeploy agent running" 에러 메시지가 발생하면

sudo service codedeploy-agent start
sudo service codedeploy-agent status

이 두 명령어를 한 번에 하나씩 실행해준다.

7. CodeDeploy 애플리케이션 생성 (배포그룹)


애플리케이션 이름과 컴퓨팅 플랫폼을 선택 후 애플리케이션을 생성해준다. 이제 배포 그룹을 생성해주면 된다.

배포 그룹 이름을 입력하고, 서비스 역할은 위에서 설정한 IAM의 CodeDeploy 역할을 선택해준다. ![]
태그 그룹의 키는 EC2 인스턴스에 생성했던 태그의 키를 가져오면 된다. 만약 EC2 인스턴스 생성 시 태그를 추가하지 않았다면 추가해주자. 추가해주었는데 클릭했을 때 표시되지 않는다면 국가, 리전도 잘 확인해보자...ㅎ 배포 설정까지 확인하고 로드 밸런서 활성화 토글도 해제한 뒤 이제 배포 그룹을 생성해주자.

8. CodeDeploy 사용을 위한 IAM 역할 생성

나는 기존에 S3 설정을 위해 생성해둔 사용자가 있기 때문에 해당 사용자에 권한을 추가하는 방식으로 진행하게 되었다. 만약 사용자가 없다면 IAM에서 사용자를 추가해 AmazonS3FullAccess 권한을 부여하면 동일한 상태가 될 것이다. 권한 추가를 눌러 이동한 뒤 아래 사진과 같이 진행해준다.
AWS에서 제공하는 정책 중 AWSCodeDeploy와 관련한 권한을 설정해준다.

CodeDeploy 사용을 위해 역할 생성과 연결까지 진행해보겠다. IAM - 역할 - 역할 만들기로 접근 후, 아래 사진과 같이 엔터티를 설정해준다. 다음으로 넘어가 역할 이름을 입력한 뒤 생성을 완료한다. 생성 완료!

9. 사용자에 대한 Access Key와 Secret Key 준비하기

사용자를 새롭게 생성했다면 생성 마지막 화면에 Access Key와 Secret Key를 csv로 다운 받을 수 있게 표시될 것이다. 꼭 다운로드 받아두자.
나처럼 Key 받아두는 것을 깜빡했더라도 걱정하지 말자. 사용자로 들어가 보안 자격 증명 - 액세스 키 만들기를 통해 다시 생성이 가능하다.
사용하지 않는 키는 삭제하거나 비활성화 해두자. 나는 다른 팀원이 쓸 수도 있어서 우선 그대로 두었다.

10. Github에 Secret 값으로 설정해주기

보안을 위해 Github 레포지터리 안에 Secrets로 위의 키 값들을 저장해둘 수 있다.

레포지터리에 들어간 후 Settings - Secrets - Actions - New repository secret으로 이동한다.

사용할 Name을 써주고 Secret 안에 키 값을 담아준다.
이렇게 Access key와 Secret key를 설정 해주면 아래 Repository secrets에서 확인이 가능하다. (키 값이 확인 가능한 것은 아니니 csv 파일은 계속 잘 보관해두자)

11. workflow yml 파일 수정

env, 권한부여, 압축파일 만들기, S3, CodeDeploy 부분 추가

name: Build and Deploy for MoramMoram server to AWS EC2

# Event Trigger
on:
  push:
    branches: [ master ]
#  pull_request:
#    branches: [ master ]

# 사용할 인프라의 이름을 변수 형태로 저장 가능
env:
  # 버킷에 저장할 프로젝트 폴더 이름
  PROJECT_NAME: morammoram_backend
  # S3 생성 시 지정했던 이름
  BUCKET_NAME: morambucket
  # CodeDeploy의 앱 이름
  CODE_DEPLOY_APP_NAME: moram-codedeploy
  # CodeDeploy의 배포그룹 이름
  DEPLOYMENT_GROUP_NAME: moram-codedeploy-deployment-group
  

jobs:
  build:
    # 실행 환경 지정
    runs-on: ubuntu-latest

    # Task의 sequence를 명시한다.
    steps:
    - uses: actions/checkout@v2
    
    - name: Set up JDK 17
      uses: actions/setup-java@v1
      with:
        java-version: 17
    
    - name: Grant execute permission for gradlew
      run: chmod +x gradlew
    
    # Gradle build (Test 제외)
    - name: Build with Gradle
      run: ./gradlew clean build -x test
      
    # 압축 파일 만들기
    - name: Make Zip File
      run: zip -qq -r ./$GITHUB_SHA.zip .
      shell: bash
      
    # 권한 부여
    - 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
      
    # S3로 파일을 업로드하는 동작
    - name: Upload to S3
      run: aws s3 cp --region ap-northeast-2 ./$GITHUB_SHA.zip s3://$BUCKET_NAME/$PROJECT_NAME/$GITHUB_SHA.zip

    # CodeDeploy에 배포단위를 생성하는 동작
    - name: Code Deploy
      run: aws deploy create-deployment --application-name $CODE_DEPLOY_APP_NAME --deployment-config-name CodeDeployDefault.OneAtATime --deployment-group-name $DEPLOYMENT_GROUP_NAME --s3-location bucket=$BUCKET_NAME,bundleType=zip,key=$PROJECT_NAME/$GITHUB_SHA.zip

12. AppSec 파일 & 배포용 쉘 스크립트 작성

https://docs.aws.amazon.com/ko_kr/codedeploy/latest/userguide/application-specification-files.html
AppSpec 파일을 작성하여 프로젝트의 어떤 파일들을 EC2의 어떤 경로에 복사할 지 설정하고, 배포 후 수행할 스크립트를 지정해서 자동으로 서버를 띄울 수도 있다고 한다.

# appsec.yml 파일 - 반드시 소스코드 루트 디렉터리에 위치 시킬 것
version: 0.0
os: linux

files:
  - source: /
    destination: /home/ec2-user/MoramMoram-backend
permissions:
  - object: /home/ec2-user/MoramMoram-backend/
    owner: ec2-user
    group: ec2-user
hooks:
  AfterInstall:
    - location: scripts/deploy.sh
      timeout: 60
      runas: ec2-user
#!/usr/bin/env bash

REPOSITORY=/home/ec2-user/MoramMoram-backend/
cd $REPOSITORY

APP_NAME=MoramMoram-backend
JAR_NAME=$(ls $REPOSITORY/build/libs/ | grep 'SNAPSHOT.jar' | tail -n 1)
JAR_PATH=$REPOSITORY/build/libs/$JAR_NAME

CURRENT_PID=$(pgrep -f $APP_NAME)

if [ -z $CURRENT_PID ]
then
  echo "> 종료할것 없음."
else
  echo "> kill -9 $CURRENT_PID"
  kill -15 $CURRENT_PID
  sleep 5
fi

echo "> $JAR_PATH 배포"
nohup java -jar $JAR_PATH > /dev/null 2> /dev/null < /dev/null &

이렇게 파일을 생성하고 실행했는데 오류가 났다. 두둥.
An error occurred (DeploymentLimitExceededException) when calling the CreateDeployment operation: The Deployment Group 'moram-codedeploy-deployment-group (id=c9d2b3df-22ca-4684-91cc-850f793a6166)' already has an active Deployment 'd-B7SVXLF6K'
이미 배포가 이루어져서 안 된다고 해서 해당 에러를 찾아보니
https://kimtaehyun98.tistory.com/136
이 블로그에서 같은 문제를 겪으신 분을 발견할 수 있었다.
이미 파일이 존재할 경우 삭제 후 진행할 수도 있고, overwrite도 가능했는데 우선은 overwrite로 설정을 진행했다.
file_exists_behavior: OVERWRITE
appsec.yml에 코드를 추가해주었고 빌드에 성공했다!

마무리

처음 한 것 치고는...오류가 많이 안 난 게 아닐까 위안을 삼아 본다ㅎ

https://www.youtube.com/watch?v=UF2Giz9PE-E
https://bcp0109.tistory.com/363

이 두 곳의 자료가 없었으면 한참 또 헤맸을 것 같은데 다행이다.
이제 다시 열심히 개발해봐야지.

역시 세팅이 제일 어렵다는 느끼는 하루였다.
(분명 10분짜리 영상 보고 시작했는데 쉬는 시간 포함 4시간은 걸린 것 같다)

profile
부딪히며 배우는 백엔드 개발자

0개의 댓글