Spring Boot + GitHub Actions + AWS CodeDeploy를 활용한 CI/CD 구축

이민우·2023년 9월 19일
1

DevOps

목록 보기
3/5

서론

최근 진행한 프로젝트에서 CI/CD를 구축했는데, 장점을 몸소 느껴 문서화하면서 복습하고자 글을 작성하게 되었습니다

CI/CD 적용하는 이유

CI/CD를 적용하는 이유는 이전에 작성한 게시글이 있으니 참고해주시면 감사하겠습니다.

기본적으로 EC2는 생성되어있다고 가정하고 진행하겠습니다.

개발 환경(꼭 확인)

모든 자료나 오픈 소스를 참고할 땐 시작하기 전에, 개발 환경 체크는 필수입니다
EC2, MySql , S3, Spring Boot 2.7.6

추가로 Linux로 진행습니다.

과정


https://velog.io/@bluewind8791/Github-Actions

흐름을 위 이미지와 같습니다.
1. 깃허브상으로 코드를 Push 하게 되면 GitHub Actions이 자동으로 실행되어 CI (빌드) 작업을 수행하게 됩니다.
2. 코드상에 문제가 없다면 이때 빌드 작업을 통해 jar파일이 생성되게 되고 사전에 작성한 배포 스크립트 파일과 함께 AWS의 스토리지 서비스인 S3에게 전달되어 저장되게 됩니다.
3. S3에 성공적으로 저장되었다면 EC2에 설치한 CodeDeply Agent가 S3에 저장된 프로젝트 파일을 가져와서 내려받습니다.
4. 그 후, 배포 스크립트 파일을 읽어 들여서 프로젝트 실행을 시작합니다.

즉, 깃허브 상으로 코드를 push 하기만 하면 자동으로 최신 버전의 프로젝트가 EC2에 내려받아져서 이 프로젝트를 실행하게 됩니다.👍

목차

  1. CI Actions 설정
  2. IAM 설정
  3. S3 설정
  4. GitHub Actions secrets 추가
  5. depley shell script 작성
  6. CD Actions S3 업로드 설정
  7. AWS CodeDeploy & AWS EC2 연동
  8. CD Actions Deploy 작성

1. CI Actions 설정

프로젝트 레포지토리의 Actions 클릭

먼저 본인의 프로젝트 레포지토리에 접근 후 우측 상단에 있는 Actions를 클릭합니다.

set up a workflow yourself 클릭

파란색 글씨의 set up a workflow yourself를 클릭합니다.

CI 작성

# workflow의 이름
name: CI

# 해당 workflow가 언제 실행될 것인지에 대한 트리거를 지정
on:
  push:
    branches: [ master ] # master branch로 push 될 때 실행됩니다.
  pull_request:
    branches: [ master ]  # master branch로 pull request될 때 실행됩니다.

# workflow는 한개 이상의 job을 가지며, 각 job은 여러 step에 따라 단계를 나눌 수 있습니다.
jobs:
  build:
    name: CI
    # 해당 jobs에서 아래의 steps들이 어떠한 환경에서 실행될 것인지를 지정합니다.
    runs-on: ubuntu-latest

    steps:
     # 작업에서 액세스할 수 있도록 $GITHUB_WORKSPACE에서 저장소를 체크아웃합니다.
      - uses: actions/checkout@v2
      - name: Set up JDK 11     #jdk 버전 확인
        uses: actions/setup-java@v2
        with:
          java-version: '11'
          distribution: 'zulu'

      - name: Grant execute permission for gradlew
        run: chmod +x ./gradlew
        shell: bash
      
      - name: Build with Gradle
        run: ./gradlew build
        shell: bash

위 CI 내용을 작성 후 Start commit를 눌러줍니다.

main branch로 push 혹은 pull_request를 수행했을 때 위 CI 액션이 동작하게 되며, 해당 CI의 역할은 ubuntu 상에서 자동으로 프로젝트를 빌드하여 프로젝트의 오류가 있는지 없는지 검증을 수행합니다. 위 작업을 통해 프로젝트의 오류가 있다면 사전에 master branch로의 불미스러운 merge가 일어나지 않도록 막을 수 있으며, 테스트 코드를 작성했다면 테스트 코드 또한 자동으로 수행하여 통합 테스트를 자동으로 수행할 수 있습니다.

2. IAM 설정

AWS IAM 클릭

사용자 추가

AWS의 대시보드에서 IAM에 접근 후 '사용자 추가'를 클릭합니다. 여기서 사용자란 AWS 외부에서 접속할 수 있는 권한을 부여할 대상을 의미합니다.

사용자 추가

사용자의 이름과 액세스 유형을 선택한 뒤 다음을 누릅니다.

권한 설정

해당 페이지에서 AWSCodeDeployFullAccess , AmazonS3FullAccess 이 두 개의 권한을 부여합니다.


다음 3, 4번 과정은 선택사항이기에 그냥 넘어가면 위 이미지와 같이 우리가 권한을 부여한 사용자가 생성되고 ACCESS KEY와 SECRET KEY가 발급됩니다.

위 알림에도 나와있듯이 지금 확인할 수 있는 ACCESS KEY와 SECRET KEY는 지금 이 페이지가 확인할 수 있는 마지막 기회입니다. 따라서 따로 ACCESS KEY와 SECRET KEY를 다른 곳에 메모해 두거나 .csv파일로 다운로드 해놓도록 합니다.

3. S3 설정

대시 보드 접근

마찬가지로 대시보드에서 S3를 클릭하여 접근합니다.

버킷 만들기 클릭



딱히 배포만을 위해서라면 따로 설정해 줄 것이 없습니다. 버킷 이름만 작성해주도록 합니다.

4. GitHub Actions secrets 추가

2번에서 생성한 IAM ACCESS KEY와 SECRET KEY를 깃허브 레포지토리 상에 평문 그대로 노출하게 되면 보안상 매우 큰 문제가 됩니다. 따라서 repository의 'Setting -> Security -> Secrets and variables -> Actions'에서 해당 키 값을 등록하여 노출하지 않고 사용하도록 합니다.

위 이미지처럼 등록 해주면 됩니다

참고로 지역은 ap-northeast-2를 추가해줍니다. 본인이 다른 region을 사용하고 있다면 해당 region을 입력해주면 됩니다.

5. deploy shell script 작성

프로젝트 폴더에 scripts 폴더 생성 후 deploy.sh 작성
#!/bin/bash
BUILD_JAR=$(ls /home/[본인경로]/build/libs/*.jar)
JAR_NAME=$(basename $BUILD_JAR)
echo "> build 파일명: $JAR_NAME" >> /home/[본인경로]/deploy.log

echo "> build 파일 복사" >> /home/[본인경로]/deploy.log
DEPLOY_PATH=/home/[본인경로]/
cp $BUILD_JAR $DEPLOY_PATH

echo "> 현재 실행중인 애플리케이션 pid 확인" >> /home/[본인경로]/deploy.log
CURRENT_PID=$(pgrep -f $JAR_NAME)

if [ -z $CURRENT_PID ]
then
  echo "> 현재 구동중인 애플리케이션이 없으므로 종료하지 않습니다." >> /home/[본인경로]/deploy.log
else
  echo "> kill -15 $CURRENT_PID"
  kill -15 $CURRENT_PID
  sleep 5
fi

DEPLOY_JAR=$DEPLOY_PATH$JAR_NAME
echo "> DEPLOY_JAR 배포"    >> /home/[본인경로]/deploy.log

nohup java -jar $DEPLOY_JAR >> /home/[본인경로]/deploy.log 2>/home/[본인경로]/deploy_err.log &
여기서 각각 path들을 ec2에 올린 스프링 부트 폴더 경로로 꼭 바꾸셔야합니다

본인 프로젝트 폴더에 위와 같이 deploy.sh를 작성하여 추가하도록 합니다.

위 스크립트는 역할은 제일 초반에 설명한 대로 다음과 같습니다.

  1. 깃허브에 push 하게 되면 코드상에 문제가 없다면 이때 빌드 작업을 통해 jar파일이 생성되게 되고 사전에 작성한 배포 스크립트 파일과 함께 AWS의 스토리지 서비스인 S3에게 전달되어 저장되게 됩니다.
  2. S3에 성공적으로 저장되었다면 EC2에 설치한 CodeDeply Agent가 S3에 저장된 프로젝트 파일을 가져와서 내려받습니다.
  3. 그 후, 배포 스크립트 파일을 읽어 들여서 프로젝트 실행을 시작합니다.

즉 스크립트의 코드는 프로젝트를 EC2에서 실행할 때 이전에 실행되고 있는 Spring Boot 프로젝트가 있다면 종료시킨 뒤 새로운 버전의 프로젝트를 실행하도록 합니다.

6.CD Actions S3 업로드 설정

이제 CD.yml도 작성해야 합니다. ./github/workflows에 가서 Add file 클릭 후 Create new file을 클릭하여 아래의 코드를 추가해주도록 합니다.

Add file 후 Create new file 클릭
# workflow의 이름
name: CD

# 해당 workflow가 언제 실행될 것인지에 대한 트리거를 지정
on:
  push:
    branches: [ main ] # main branch로 push 될 때 실행됩니다.

# 해당 yml 내에서 사용할 key - value
env:
  S3_BUCKET_NAME: project-bucket-v1
  PROJECT_NAME: cicdtest
  
# workflow는 한개 이상의 job을 가지며, 각 job은 여러 step에 따라 단계를 나눌 수 있습니다.
jobs:
  build:
    name: CD
    # 해당 jobs에서 아래의 steps들이 어떠한 환경에서 실행될 것인지를 지정합니다.
    runs-on: ubuntu-latest

    steps:
     # 작업에서 액세스할 수 있도록 $GITHUB_WORKSPACE에서 저장소를 체크아웃합니다.
      - uses: actions/checkout@v2
      - name: Set up JDK 11
        uses: actions/setup-java@v2
        with:
          java-version: '11'
          distribution: 'zulu'

      - name: Grant execute permission for gradlew
        run: chmod +x ./gradlew
        shell: bash
      
      - name: Build with Gradle
        run: ./gradlew build
        shell: bash
        
      - name: Make zip file
        run: zip -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: ${{ secrets.AWS_REGION }}
          
      # script files 복사
      - name: Copy script
        run: cp ./scripts/*.sh ./deploy
      
      # S3에 업로드
      - name: Upload to S3
        run: aws s3 cp --region ap-northeast-2 ./$GITHUB_SHA.zip s3://$S3_BUCKET_NAME/$PROJECT_NAME/$GITHUB_SHA.zip

위 코드의 env의 S3_BUCKET_NAME 부분은 반드시 본인의 S3 버킷 이름으로 설정해주셔야 합니다. 본인이 3번에서 작성한 S3의 버킷 이름을 작성해주시면 됩니다.

이제 커밋 후에,
커밋 내역을 살펴보면 CI와 CD가 수행 중인 것을 볼 수 있고 성공적이라면 다음과 같이 체크표시로 변하게 됩니다.

아직까지 CD는 S3에 업로드하는 과정까지만 진행했습니다. 따라서 S3의 버킷에 접속하여 본인의 프로젝트가 성공적으로 업로드가 되었는지 확인합니다.

S3에 성공적으로 업로드 !

7. AWS CodeDeploy & AWS EC2 연동

AWS 설정

이제 배포 시스템인 codedeploy를 통해 EC2 Instance에 배포를 가능하게 하는 작업을 설정할 것입니다.

먼저 codedeploy와 EC2 둘 사이의 접근을 가능하게 하기 위해서는 AWS IAM에서 '역할 만들기'를 해야 합니다. 사용자 등록이 외부에서 AWS 외부에서부터의 접근을 허가하는 것이라면, 역할은 AWS 내에서의 접근 권한이라고 볼 수 있으며 등록 예시는 다음과 같습니다.

  • 다른 계정의 IAM 사용자

  • AWS 리소스에서 작업을 수행해야 하는 EC2 인스턴스에서 실행 중인 애플리케이션 코드

  • 계정 내 리소스에서 작업을 수행하여 기능을 제공해야 하는 AWS 서비스

  • SAML을 통해 인증 연동을 사용하는 사내 디렉토리의 사용자

    다시 AWS IAM 접근

역할 카테고리에 들어간 뒤 역할 만들기 클릭

AWS 서비스 및 EC2 선택

AmazonEC2RoleforAWSCodeDeploy 추가

AmazonEC2RoleforAWSCodeDeploy 권한을 선택합니다.

역할 이름 입력

역할 이름과 설명 정도만 작성한 하고, 그 다음 역할 생성을 눌러주면 됩니다.

EC2에서 해당 역할(마우스 우클릭->보안->IAM 역할 수정)을 사용하도록 저장

EC2에서 방금 작성한 역할을 사용하도록 저장합니다.

역할을 바꾸고 나면 인스턴스를 재부팅하도록 합니다.

이때 꼭 재부팅을 누르셔야 합니다. 종료는 삭제와 동일하기 때문에 말 그대로 종료가 되어버리며 다시 실행할 수 없습니다.

재부팅이 완료되면 EC2 인스턴스에 연결한 뒤 다음 명령어를 통해 codedeploy를 설치합니다.

aws s3 cp s3://aws-codedeploy-ap-northeast-2/latest/install . --region ap-northeast-2

download: s3://aws-codedeploy-ap-northeast-2/latest/install to ./install

ec2에서 다운로드 성공 시 위와 같은 메시지가 출력됩니다.

이제 실행 권한을 주고 실행하여 설치를 마무리합니다. 아래 명령어를 순서대로 입력합니다.

chmod +x ./install
sudo ./install auto

만약 install 부분에서 /usr/bin/env: ruby: No such file or directory과 같은 오류가 발생하는 경우 ruby를 찾을 수 없다는 의미이므로 당황하지 말고 아래 명령어를 통해 ruby를 설치 후 다시 입력하면 됩니다.

sudo yum install ruby;

설치 확인을 위해 아래의 명령어를 입력하면 The AWS CodeDeploy agent is running as PID 메시지가 출력됩니다.

sudo service codedeploy-agent status

CodeDeploy 설정

EC2와 마찬가지로, CodeDeploy에서도 역할을 생성하고 설정해야 합니다.

CodeDeploy 서비스 선택 위에 역할 설정과 동일하게 역할 생성에 들어가서 AWS 서비스 선택 후 사용 사례는 CodeDeploy를 선택합니다.

마찬가지로 이름도 자유롭게 부여해주시면 됩니다.
이제 codedeploy 서비스에서 애플리케이션을 생성합니다.

애플리케이션 이름 및 컴퓨팅 플랫폼 선택!

배포 그룹 생성

애플리케이션을 생성하고, 배포 그룹 생성을 이어서 진행합니다.

이름 및 서비스 역할 입력

서비스 역할은 아까 만들었던 역할을 선택해주시면 됩니다.

환경 구성 설정

환경 구성은 Amazone EC2 인스턴스를 선택합니다.

배포 구성 및 로드 밸런서 설정

배포 구성은 다음과 같이 CodeDeployDefault.AllAtOnce를 선택하고 로드 밸런서는 저는 활성화하지 않았습니다.

이제 프로젝트 폴더에 appspec.yml 파일을 생성합니다. 이 파일은 codedeploy가 서버 환경에 설치를 할 수 있도록 동작을 정의한 내용입니다.

appspec.yml 생성
version: 0.0
os: linux
# S3에 있는 zip 파일이 EC2에 배포될 위치를 지정
files:
  - source: / # CodeDeploy에서 전달해 준 파일 중 destination으로 이동시킬 대상을 루트로 지정(전체파일)
    destination: /home/ec2-user/action/ # source에서 지정된 파일을 받을 위치, 이후 jar를 실행하는 등은 destination에서 옮긴 파일들로 진행
    overwrite: yes

permissions: # CodeDeploy에서 EC2서버로 넘겨준 파일들을 모두 ec2-user권한을 갖도록 합니다.
  - object: /
    pattern: "**"
    owner: ec2-user
    group: ec2-user

# ApplicationStart 단계에서 deploy.sh를 실행시키도록 합
hooks: # CodeDeploy배포 단계에서 실행할 명령어를 지정합니다.
  ApplicationStart: # deploy.sh를 ec2-user권한으로 실행합니다.
    - location: scripts/deploy.sh
      timeout: 60 # 스크립트 실행 60초 이상 수행되면 실패가 됩니다.
      runas: ec2-user

8. CD Actions Deploy 작성

이제 마무리입니다,,, 아까 작성했던 CD.yml에 아래의 Deploy 코드만 추가해주면 됩니다.

  # Deploy
      - name: Deploy
        run: |
          aws deploy create-deployment \
          --application-name cicdtest-project \
          --deployment-config-name CodeDeployDefault.AllAtOnce \
          --deployment-group-name cicd-test-prod \
          --file-exists-behavior OVERWRITE \
          --s3-location bucket=project-bucket-v1,bundleType=zip,key=cicdtest/$GITHUB_SHA.zip \
          --region ap-northeast-2 \

여기서 이 글을 보시는 분들은 다음과 같은 부분을 본인이 설정했던 이름들로 변경해주시면 됩니다.

  • application-name
  • deployment-group-name
  • s3-location bucket
  • key
    여기서 key는 버킷 안에서 저장하고자하는 경로입니다. 즉, s3 파일/디렉토리 명입니다.

정리

master branch에 pull request를 하게 되면 CI에 의해 빌드 검사를 수행하게 되고 push를 하게 되면 자동적으로 배포가 되게 됩니다.

추가적으로 aws-codeDeploy-배포-배포ID-View events에 들어가면 배포 과정에 이상이 없는지 확인할 수 있으며, 아무 이상이 없는데 프로젝트가 실행되지 않는다면 EC2에 java가 설치되어있는지 확인하기 바랍니다.

profile
백엔드 공부중입니다!

0개의 댓글