Search

Github Actions 을 이용한 프로젝트 자동배포

태그
github Actions
CD
자동배포
AWS
분류
DevOps
목차

어쩌다보니 자동배포?

프로젝트를 진행하면서 개인서버에서 클라우드서버로 이전하게 된 사건이 있었습니다.
그 때 이전하면서 서버에 파일을 전송하는 프로그램 없이 그냥 깃허브에 푸시한 순간 배포가 되게 할 수는 없을까? 하는 생각이 들었고, 그렇게 자동배포의 여정이 시작 되었습니다.

CI 와 CD 란?

CI와 CD는 소프트웨어 개발과 배포에서 중요한 역할을 하는 모범 사례 및 원칙들 입니다.

CI (Continuous Integration, 지속적 통합)

CI는 개발자가 코드 변경 사항을 공유 저장소에 빈번하게 병합(통합)할 것을 권장하는 개발 방법론입니다.
CI 과정에는 보통 자동화된 빌드와 테스트가 포함됩니다. 이렇게 하면 코드 변경 사항이 현재의 코드베이스나 다른 코드 변경 사항과 문제 없이 잘 작동하는지 확인할 수 있습니다.
CI의 주요 목표는 통합 문제를 신속하게 발견하고 해결하여 소프트웨어 품질을 높이는 것입니다.

CD (Continuous Delivery & Continuous Deployment, 지속적 전달 및 지속적 배포)

CD는 소프트웨어를 자동화된 방식으로 빠르게, 안정적으로 고객에게 제공할 수 있도록 지원하는 원칙과 관행의 집합입니다.
Continuous Delivery: 소프트웨어 변경 사항이 자동화된 테스트와 빌드를 거쳐 프로덕션 환경으로 배포될 준비가 되었음을 보증합니다. 그러나 실제 배포는 수동으로 수행될 수 있습니다.
Continuous Deployment: Continuous Delivery의 확장판으로, 모든 변경 사항이 자동 테스트를 거친 후 자동으로 프로덕션 환경에 배포됩니다.
CD의 주요 목표는 빠른 피드백 루프를 제공하고, 소프트웨어의 신속한 반복을 가능하게 하며, 사용자에게 지속적으로 가치를 제공하는 것입니다.
CI/CD 파이프라인을 사용하면 팀은 코드의 변경 사항을 자주, 빠르게, 안정적으로 프로덕션 환경에 배포할 수 있게 됩니다. 이로 인해 소프트웨어 제품의 품질이 향상되며, 버그 수정이나 새로운 기능의 배포가 더욱 빠르게 이루어질 수 있습니다. CI/CD 도구로는 Jenkins, Travis CI, GitLab CI/CD, CircleCI, GitHub Actions 등이 있습니다.

깃허브 액션 vs 젠킨스

가장 많이 들어보고, 그만큼 많이 쓰이는 두 개의 CI/CD 도구에 대해 알아봅시다.

Jenkins

서버에 젠킨스를 설치해야 합니다.
작업이 동기화 되어야 하기 때문에 배포하는 데 더 많은 시간이 소요 됩니다.
계정 및 트리거를 기반으로 하며 깃허브 이벤트를 준수하지 않는 빌드를 중심으로 합니다.
환경 호환성을 위해 도커 이미지에서 실행해야 합니다.
캐싱 매커니즘을 지원하기 위해 플러그인을 사용할 수 있습니다.
공유할 수 없습니다.
문서가 다양합니다.
규모가 큰 프로젝트에 적합 합니다.

GitHub Actions

서버에 설치할 필요가 없습니다.
비동기로 CI / CD 를 달성할 수 있습니다.
모든 깃허브 이벤트에 대한 작업을 제공하고, 다양한 언어와 프레임 워크를 지원합니다.
모든 환경과 호환 됩니다.
캐싱이 필요한 경우 자체 캐싱 매커니즘을 작성해야 합니다.
깃허브 마켓 플레이스를 통해 공유가 가능합니다.
젠킨스에 비해 문서가 없습니다.
프로젝트 규모가 크지 않거나 외부 클라우드 서비스를 사용하는 경우 적합합니다.

어떤 것을 선택했나요?

저희 프로젝트의 경우 규모가 작고, 외부 클라우드(AWS) 에 서비스가 배포되기 때문에 깃허브 액션을 선택 하였습니다.

아키텍쳐로 알아보는 자동배포

세세하게 들어가기 전, 아키텍쳐로 큰 그림을 이해해 봅시다.

배포과정 살펴보기

1.
열심히 코드 작업을 한 뒤, 깃허브로 푸시를 합니다.
2.
깃허브 액션은 깃허브에 코드가 올라오면 이를 알아채 배포과정을 밟게 됩니다.
3.
깃허브 액션은 워크 플로우에 작성된 순서대로 작업을 수행합니다. 저의 경우,
jdk 설치
프로젝트 빌드
프로젝트 압축
S3 에 전달
코드 디플로이가 S3 에 배포된 압축된 프로젝트를 EC2 서버에 배포
이렇게 작성하였습니다.
4.
EC2 에 배포한 후에 미리 작성한 appspec.yml 대로 작업이 수행됩니다. 저의 경우,
실행되고 있는 부트 프로젝트가 있는지 점검
실행되고 있다면 종료 후, 새로 배포한 프로젝트 실행
이렇게 작성하였습니다.

깃허브액션 워크 플로우 작성해보기

deploy.yml

최상위 디렉토리에 .github/workflows ****를 생성해 주어야 합니다.
서브모듈 토큰의 경우 계정의 세팅 → developer … 에서 생성 해주세요.
생성한 토큰과 시크릿 키들은 배포할 코드가 들어있는 레포지토리의 설정에 등록해주세요.
# This workflow uses actions that are not certified by GitHub. # They are provided by a third-party and are governed by # separate terms of service, privacy policy, and support # documentation. # This workflow will build a Java project with Gradle and cache/restore any dependencies to improve the workflow execution time # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-gradle name: Java CI with Gradle / dev on: push: branches: [ 감지할 브랜치명을 넣어주세요. ] pull_request: branches: [ 감지할 브랜치명을 넣어주세요. ] permissions: contents: read env: S3_BUCKET_NAME: { s3 버킷 이름을 넣어주세요. } CODE_DEPLOY_APP_NAME: { 코드디플로이 어플리케이션 이름을 넣어주세요. } CODE_DEPLOY_GROUP_NAME: { 코드디플로이 그룹 이름을 넣어주세요.} jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 with: token: ${{ secrets.SUBMODULE_TOKEN }} submodules:true - name: Set up JDK 17 uses: actions/setup-java@v3 with: java-version: '17' distribution: 'temurin' - name: Build with Gradle uses: gradle/gradle-build-action@bd5760595778326ba7f1441bcf7e88b49de61a25 # v2.6.0 with: arguments: build gradle-version: 8.1.1 # 프로젝트 압축 - name: Make zip file run: zip -r ./$GITHUB_SHA.zip ./ shell: bash # AWS 권한 확인 - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v1 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: ap-northeast-2 - name: Upload to S3 run: aws s3 cp --region ap-northeast-2 ./$GITHUB_SHA.zip s3://$S3_BUCKET_NAME/{ 디렉토리가 존재한다면 이곳에 작성해주세요. }/$GITHUB_SHA.zip # Deploy - name: Deploy run: | aws deploy create-deployment \ --application-name { 코드디플로이 어플리케이션 이름을 넣어주세요 } \ --deployment-config-name CodeDeployDefault.AllAtOnce \ --deployment-group-name { 코드디플로이 그룹 이름을 넣어주세요 } \ --file-exists-behavior OVERWRITE \ --s3-location bucket={ s3 버킷 이름을 넣어주세요 },bundleType=zip,key={ 디렉토리가 존재한다면 경로를 넣어주세요. }/$GITHUB_SHA.zip \ --region ap-northeast-2 \
YAML
복사

appspec.yml

 이름 주의해서 작성해주세요!
version: 0.0 os: linux files: - source: / destination: /home/ec2-user/{ 배포 경로를 입력해주세요. } pattern: "*.jar" permissions: - object: / pattern: "*.jar" owner: ec2-user group: ec2-user mode: '755' hooks: AfterInstall: - location: scripts/stop.sh timeout: 60 runas: ec2-user ApplicationStart: - location: scripts/start.sh timeout: 60 runas: ec2-user
YAML
복사
AfterInstall : 디플로이가 서버에 배포를 완료하면 실행할 스크립트 입니다.
ApplicationStart: 애플리케이션을 실행할때 사용할 스크립트 입니다.

Script 작성하기

최상단 경로에 scripts 폴더를 만들고, 그 안에 스크립트 파일을 넣어 주세요.

stop.sh

#!/usr/bin/env bashPROJECT_ROOT="/home/ec2-user/{ 배포 경로 }" JAR_FILE="$PROJECT_ROOT/build/libs/{ 빌드 파일 이름 }" DEPLOY_LOG="$PROJECT_ROOT/deploy.log" // 원하는 경로와 이름으로 바꿔도 무방합니다. TIME_NOW=$(date +%c)# 현재 구동 중인 애플리케이션 pid 확인 CURRENT_PID=$(pgrep -f $JAR_FILE)# 프로세스가 켜져 있으면 종료 if [ -z $CURRENT_PID ]; then echo "$TIME_NOW > 현재 실행중인 애플리케이션이 없습니다" >> $DEPLOY_LOG else echo "$TIME_NOW > 실행중인 $CURRENT_PID 애플리케이션 종료 " >> $DEPLOY_LOG kill -15 $CURRENT_PID fi
Shell
복사

start.sh

#!/usr/bin/env bashPROJECT_ROOT="/home/ec2-user/{ 배포 경로 }" JAR_FILE="$PROJECT_ROOT/build/libs/{ 빌드 파일 이름 }" APP_LOG="$PROJECT_ROOT/application.log" // 원하는 경로와 이름으로 바꿔도 무방합니다. ERROR_LOG="$PROJECT_ROOT/error.log" // 원하는 경로와 이름으로 바꿔도 무방합니다. DEPLOY_LOG="$PROJECT_ROOT/deploy.log" // 원하는 경로와 이름으로 바꿔도 무방합니다. TIME_NOW=$(date +%c)# build 파일 복사 echo "$TIME_NOW > $JAR_FILE 파일 복사" >> $DEPLOY_LOG cp $PROJECT_ROOT/build/libs/*.jar $JAR_FILE # jar 파일 실행 echo "$TIME_NOW > $JAR_FILE 파일 실행" >> $DEPLOY_LOG nohup java -jar $JAR_FILE > $APP_LOG2> $ERROR_LOG & CURRENT_PID=$(pgrep -f $JAR_FILE)echo "$TIME_NOW > 실행된 프로세스 아이디 $CURRENT_PID 입니다." >> $DEPLOY_LOG
Shell
복사

Code Deploy

EC2 가 두개라면 코드 디플로이는 어떻게 구분할까?

저희 프로젝트처럼 ec2 가 두개인 경우를 생각해봅시다. 코드 디플로이는 S3 에서 압축된 파일을 가져와 적절한 서버에 압축을 풀어 배포합니다. 이때 서버를 어떻게 구분하는 걸까요?

배포그룹을 사용하여 올바른 서버 찾아가기

주의해야할 점은?

이 배포그룹과 적절한 EC2 를 연결해줘야 합니다.
 아래와 같이 배포그룹 생성시 EC2 에 설정한 태그를 제대로 작성해주세요.

 주의

organization 의 접근권한 문제

깃허브 액션이 해당 레포지토리에 접근할 수 없는 경우 action 이 실행되지 않습니다.
아래와 같이 수정해주세요.