76DAYS) [Cloud] 배포 자동화 - Github Actions를 통한 배포 자동화

nacSeo (낙서)·2023년 2월 6일
0

◉ 학습목표

1. Github Action을 통한 배포 자동화 Flow를 설명할 수 있다.
2. 자동화를 진행하는 파일들의 단계별 진행 내용을 이해할 수 있다.
  1. Github Actions를 통한 배포 Flow

⦿ 학습내용

☞ Github Actions

✔︎ Github가 공식적으로 제공하는 빌드, 테스트 및 배포 파이프라인을 자동화할 수 있는 CI / CD 플랫폼
✔︎ Pull Request, push 같은 이벤트를 트리거로 GitHub 작업 워크플로(Workflow) 구성 가능
✔︎ 워크플로

  • 하나 이상의 작업이 실행되는 자동화 프로세스
  • 각 작업은 자체 가상 머신 또는 컨테이너 내부에서 실행
  • .yml (또는 .yaml) 파일에 의해 구성
  • 테스트, 배포 등 기능에 따라 여러 개의 워크플로 생성 가능
  • 생성된 워크플로는 .github/workflows 디렉토리에 위치

☞ Github Actions를 통한 배포 Flow


✔︎ Github Actions

  • 설정 파일(.yml)에 따라 Github Repository에 특정 변동사항을 트리거로 작동

✔︎ S3

  • 일반 배포 실습에서는 정적 웹 페이지 배포하는 데 사용
  • Github Actions를 통한 배포에서는 저장소로써 사용
  • Github Actions에서 빌드한 결과물이 압축되어 S3로 전송되고, 버킷에 저장

✔︎ Code Deploy

  • Github Actions에서 배포 명령을 받은 Code Deploy는 S3에 저장되어 있는 빌드 결과물을 EC2 인스턴스로 이동
  • 프로젝트 최상단에 위치한 appspec.yml 설정 파일에 의해 쉘 스크립트 등 단계에 따라 특정 동작 수행

✔︎ EC2

  • Code Deploy에 의해 빌드 과정을 거친 프로젝트가 EC2 인스턴스로 전달됨
  • .yml (설정 파일)과 .sh (쉘 스크립트)에 의해 각 배포 결과를 로그로 저장하여 빌드 파일 (.jar) 실행
  • EC2 인스턴스에 Code Deploy Agent와 JDK 설치 필요
  1. 리소스 설정

⦿ 학습내용

☞ Github Actions 생성

✔︎ Public Repository 생성 및 프로젝트 업로드
✔︎ Git Actions 생성

  • 레포지토리의 Actions 탭 클릭
  • 워크플로 생성
    ex) Java with GradleConfigure
  • Start commitCommit new file 클릭
  • gradle.yml 파일 생성, 워크플로에 작성되어있는 트리거(main 브랜치의 push)로 인해 Github Actions 실행

< gradle.yml 파일 내용 >

name: Java CI with Gradle

on:
  push:
    branches: [ "main" ]

permissions:
  contents: read

jobs:
  build:

    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v3
    - name: Set up JDK 11
      uses: actions/setup-java@v3
      with:
        java-version: '11'
        distribution: 'temurin'
    - name: Build with Gradle
      uses: gradle/gradle-build-action@67421db6bd0bf253fb4bd25b31ebb98943c375e1
      with:
        arguments: build

☞ Git Action 수정

✔︎ Github Secret 등록

  • 레포지토리의 Settings 클릭
  • SecretsActionsNew repository secret 클릭
  • AWS_ACCESS_KEY 값, AWS_SECRET_ACCESS_KEY 값을 추가

✔︎ Github Action 설정파일 수정

  • gradle.yml 파일 코드 추가
...

permissions:
  contents: read
  
# 해당 부분 추가 (1)
env:
  S3_BUCKET_NAME: be-0-name		# 해당되는 버킷 이름 입력

...

# 해당 부분 추가 (2)

    # build한 후 프로젝트를 압축합니다.
    - name: Make zip file
      run: zip -r ./practice-deploy.zip .
      shell: bash
    
    # Access Key와 Secret Access Key를 통해 권한을 확인합니다.
    # 아래 코드에 Access Key와 Secret Key를 직접 작성하지 않습니다.
    - name: Configure AWS credentials
      uses: aws-actions/configure-aws-credentials@v1
      with:
        aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY }} # 등록한 Github Secret이 자동으로 불려옵니다.
        aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} # 등록한 Github Secret이 자동으로 불려옵니다.
        aws-region: ap-northeast-2
    
    # 압축한 프로젝트를 S3로 전송합니다.
    - name: Upload to S3
      run: aws s3 cp --region ap-northeast-2 ./practice-deploy.zip s3://$S3_BUCKET_NAME/practice-deploy.zip

🌟 pull 진행하여 레포지토리와 로컬 파일을 동일하게 만들어줘야 함 ‼️

  • S3 버킷 객체 확인 : 워크플로가 성공적으로 완료되면 S3 버킷에 압축파일이 전송됨

🚨 주의❗️

  • 운영체제 문제로 '~/gradlew' is not executable. 에러 발생 가능
  • 에러 발생 시 Build with Gradle 이전 ./gradlew에 권한 부여하는 단계 추가
  • 줄바꿈이 있을 땐 각 단계가 같은 깊이를 유지하도록 간격에 유의
  • 예시 코드
- name: Add permission
  run: chomod +x ./gradlew
  1. 빌드파일 배포 및 실행

⦿ 학습내용

☞ Code Deploy 설정

✔︎ CodeDeploy 애플리케이션 생성

  • AWS CodeDeploy로 이동 ⇒ 배포애플리케이션애플리케이션 생성 클릭
  • 애플리케이션 이름 작성 (ex. be-00-name) ⇒ 컴퓨팅 플랫폼 선택 (ex.EC2/온프레미스)

✔︎ 배포 그룹 생성

  • 애플리케이션이 생성되면 해당 애플리케이션 내 배포 그룹 생성 클릭
  • 직전에 생성한 애플리케이션의 정보 확인
  • 배포 그룹 이름 입력 (ex. be-00-name-group)
  • 서비스 역할 : CodeDeploy IAM 역할 연결 (ex. githubAction-CodeDeploy-Role)
  • 환경 구성 (ex. Amazon EC2 인스턴스 ⇒ 키 : Name, 값 : be-00-name)
  • 로드 밸런싱 활성화 (ex. 체크 해제) ⇒ 배포 그룹 생성

☞ .yml 파일 설정

✔︎ 최상위 디렉토리 구조에 appspec.yml 파일 생성

< appspec.yml 파일 내용 >

version: 0.0
os: linux
files:
  - source:  /
    destination: /home/ubuntu/action	# 왼쪽 디렉토리 내 배포 진행
    overwrite: yes

permissions:
  - object: /
    pattern: "**"
    owner: ubuntu
    group: ubuntu

hooks:
  ApplicationStart:
    - location: scripts/deploy.sh	# 최상위 디렉토리에 scripts 내 쉘 스크립트 실행
      timeout: 60
      runas: ubuntu

✔︎ shell script 생성

  • scripts 폴더 생성 후 deploy.sh 파일 생성
#!/bin/bash
# 빌드 파일의 이름이 콘텐츠와 다르다면 다음 줄의 .jar 파일 이름을 수정하시기 바랍니다.
BUILD_JAR=$(ls /home/ubuntu/action/build/libs/practice-githubAction-deploy-0.0.1-SNAPSHOT.jar)
JAR_NAME=$(basename $BUILD_JAR)

echo "> 현재 시간: $(date)" >> /home/ubuntu/action/deploy.log

echo "> build 파일명: $JAR_NAME" >> /home/ubuntu/action/deploy.log

echo "> build 파일 복사" >> /home/ubuntu/action/deploy.log
DEPLOY_PATH=/home/ubuntu/action/
cp $BUILD_JAR $DEPLOY_PATH

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

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


DEPLOY_JAR=$DEPLOY_PATH$JAR_NAME
echo "> DEPLOY_JAR 배포"    >> /home/ubuntu/action/deploy.log
sudo nohup java -jar $DEPLOY_JAR >> /home/ubuntu/deploy.log 2>/home/ubuntu/action/deploy_err.log &
- EC2 배포 진행 상황별 로그를 기록하고 새로 배포된 빌드 파일 실행

☞ Github Action 수정

✔︎ CodeDeploy 연결

  • gradle.yml 파일 하단에 Code Deploy 배포 명령 추가

< gradle.yml 파일 내용>

...

    # CodeDeploy에게 배포 명령을 내립니다.
    - name: Code Deploy
      run: >
        aws deploy create-deployment --application-name be-0-name
        --deployment-config-name CodeDeployDefault.AllAtOnce
        --deployment-group-name be-0-name-group
        --s3-location bucket=$S3_BUCKET_NAME,bundleType=zip,key=practice-deploy.zip
  • 레포지토리에 push하여 Github Actions 실행
  1. 배포 결과 및 로그 확인

⦿ 학습내용

☞ 배포 결과 확인

✔︎ 배포 결과 확인

  • EC2 인스턴스 주소로 접근 가능한지 확인
    🚨 주의 ❗️
    : 포트 번호까지 입력해야 함

    ※ 응답 페이지를 구성하지 않아서 나타낸 화면이므로 당황 ❌

✔︎ EC2 인스턴스에서 배포 로그 확인

  • appspec.ymldeploy.sh에서 작성한대로 action 디렉토리 내 빌드 파일로 이동해 실행중인 프로세스 검색

    ✅ 결과

☞ 배포 로그 확인

✔︎ EC2 인스턴스에서 배포 로그 확인

  • 두 개의 로그 파일 생성
  • deploy.log : EC2 인스턴스에서의 빌드 과정 기록
  • deploy_err.log : 빌드 파일을 정상적으로 실행 ❌
  • 에디터 또는 cat 명령어로 로그 확인 가능

✔︎ 파일 수정 후 배포 자동화 테스트

  • 프로젝트 수정
  • 수정 후 push 진행
  • 배포 자동화 과정 실행
  • 새로운 로그 확인

    ※ 이전 배포된 빌드 파일이 실행 중인 경우, 종료하고 업데이트된 빌드 파일을 실행
  • 다시 EC2 주소로 접근하면 배포 내용이 반영된 새로 작성된 응답 페이지 확인 가능

☞ 애플리케이션이 실행되지 않을 때

✔︎ 이전 실습 내용이 보여지는 경우

  • 프로세스를 종료(kill)하고 Github Actions 재시도

✔︎ EC2 인스턴스에 빌드 결과물이 없는 경우

  • S3 버킷에 저장 내용을 확인하고 CodeDeploy의 배포 내역 점검

✔︎ EC2 인스턴스에 빌드 결과물이 있는 경우

  • EC2 인스턴스에 기록된 로그 확인

◉ 느낀 점

☞ 어제 AWS Pipeline 배포 자동화 실습에 이어 오늘은 Github Actions를 이용한 배포 자동화를 실습해봤다.

수월히 진행되어 예상된 시간보다 훨씬 빨리 마무리할 것으로 보였으나...

오늘도 역시 맘대로 안된다!!!

첫 번째 에러 핸들링
배포 결과 및 로그는 확인되는데 git 레포지토리와 로컬 파일의 불일치가 발생하여 pullpush 명령어가 먹통이 되어버렸다... git push origin +main 명령어를 통해 강제로 입력하다보니 레포지토리의 gradle.yml파일이 날라간다...

내가 actions를 생성하면서 만들어진 gradle.yml 파일을 까먹고 로컬에 pull하지 않은 채, 로컬 프로젝트 내용을 바꿔서 강제 push 해버리는 바람에 뒤엉켜버린 것이다 🥲

교육 와아아아안~전 초창기 때 git을 학습하면서 충돌 상황이 발생했던 것이 떠올라 다시 해당 내용을 훑어보며 되돌릴려고 노력했다.
7일차 학습내용 참고

직접 소스 코드도 되돌리고 수정해봤지만 이미 강제 푸쉬를 해버리는 바람에 가버린 것 같다...

결국 다시 처음부터 작성 ^^❗️❗️❗️

두 번째 에러 핸들링
다시 작성하고 github action 생성 후 만들어진 gradle.yml 파일을 로컬에 pull 해주는 것도 잊지 않았다!!

마지막까지 이상없이 작성하였는데... EC2 주소에 접속이 되지 않는다 😂

EC2 인스턴스에 연결하여 알아보니 빌드는 됐는데, 자동으로 실행이 안됐다...

java -jar build/libs/ ~ .jar 명령어를 통해 수동으로 실행해보니 잘된다 🤔

그러나 우리의 목적은 배포 자동화이기에 이것 저것 뒤적뒤적 만져보며 무엇이 문제인지 고민했다.

AWS의 Code Deploy에 배포 그룹에 들어가보니 상태가 실패... install 과정에서 실패가 발생했단다 🥲
github 상에 빌드는 잘됐고, 수동으로 실행은 잘됐는데 왜 배포에서 오류가 나지 고민을 엄청 많이 했다.

배포 내역의 실패된 배포 ID를 들어가 오류 내용을 확인해보았다.

배포에 실패한 개별 인스턴스가 너무 많거나 배포에 사용할 수 있는 정상 인스턴스가 너무 적거나 배포 그룹의 일부 인스턴스에 문제가 발생하여 전체 배포가 실패했습니다.

... 무슨 말?!!!
느낌 상으로는 인스턴스가 많이 생성된 문제 같아 이전 실습 내용때문인가 해서 EC2 인스턴스의 프로세스 상황을 다시 확인하니 이상이 없다 😕 그렇다면 아마 내가 pull push 문제로 새로 만들기 전 처음 생성한 action과 겹치는 문제라고 얼추 생각이 들었다.

AWS 배포 내역에 실패된 배포 ID 안의 내용들을 좀 더 살펴보았다.
하단에 배포 생명 주기 이벤트라는 곳에 실패된 이벤트가 있길래 거기에 View Event라는 것을 눌러보았다.
추가적인 에러 메세지를 확인할 수 있었다.

이미 존재한단다.
그리고 친절히 경로까지 알려줬다.

이걸 보고 느낌이 딱! 와서 다시 EC2 인스턴스에 연결하여 /home/ubuntu의 action 디렉토리를 죽였다.

sudo rm -rf action

죽이고 다시 action을 동작시켜 실행하니 잘 동작됐다!!! 나도 모르게 실제 환호가 나왔다 😊😊

에러는 해결할 때마다 언제나 늘 짜릿 ⚡️ :D

◉ 내일의 키워드

・ Proxy Server
・ 수평 확장
・ 웹 서버
・ VPC
profile
백엔드 개발자 김창하입니다 🙇‍♂️

0개의 댓글