Github Actions 을 이용한 프로젝트 자동배포
프론트 팀이 자동배포 안하면 날 죽일것 같아 공부해 보았다. CI/CD 가 도대체 뭐길래 안하면 큰일이 나는걸까.
1. 어쩌다보니 자동배포? 🤯
프로젝트를 진행하면서 개인서버에서 클라우드서버로 이전하게 된 사건이 있었습니다.
그 때 이전하면서 서버에 파일을 전송하는 프로그램 없이 그냥 깃허브에 푸시한 순간 배포가 되게 할 수는 없을까? 하는 생각이 들었고, 그렇게 자동배포의 여정이 시작 되었습니다.
2. CI 와 CD 란?
🧑🏻🏫 CI와 CD는 소프트웨어 개발과 배포에서 중요한 역할을 하는 모범 사례 및 원칙들 입니다.
2.1. CI (Continuous Integration, 지속적 통합)
- CI는 개발자가 코드 변경 사항을 공유 저장소에 빈번하게 병합(통합)할 것을 권장하는 개발 방법론입니다.
- CI 과정에는 보통 자동화된 빌드와 테스트가 포함됩니다. 이렇게 하면 코드 변경 사항이 현재의 코드베이스나 다른 코드 변경 사항과 문제 없이 잘 작동하는지 확인할 수 있습니다.
- CI의 주요 목표는 통합 문제를 신속하게 발견하고 해결하여 소프트웨어 품질을 높이는 것입니다.
2.2. 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 등이 있습니다.
3. 깃허브 액션 vs 젠킨스
🧑🏻🏫 가장 많이 들어보고, 그만큼 많이 쓰이는 두 개의 CI/CD 도구에 대해 알아봅시다.
3.1. Jenkins
- 서버에 젠킨스를 설치해야 합니다.
- 작업이 동기화 되어야 하기 때문에 배포하는 데 더 많은 시간이 소요 됩니다.
- 계정 및 트리거를 기반으로 하며 깃허브 이벤트를 준수하지 않는 빌드를 중심으로 합니다.
- 환경 호환성을 위해 도커 이미지에서 실행해야 합니다.
- 캐싱 매커니즘을 지원하기 위해 플러그인을 사용할 수 있습니다.
- 공유할 수 없습니다.
- 문서가 다양합니다.
- 규모가 큰 프로젝트에 적합 합니다.
3.2. GitHub Actions
- 서버에 설치할 필요가 없습니다.
- 비동기로 CI / CD 를 달성할 수 있습니다.
- 모든 깃허브 이벤트에 대한 작업을 제공하고, 다양한 언어와 프레임 워크를 지원합니다.
- 모든 환경과 호환 됩니다.
- 캐싱이 필요한 경우 자체 캐싱 매커니즘을 작성해야 합니다.
- 깃허브 마켓 플레이스를 통해 공유가 가능합니다.
- 젠킨스에 비해 문서가 없습니다.
- 프로젝트 규모가 크지 않거나 외부 클라우드 서비스를 사용하는 경우 적합합니다.
3.3. 어떤 것을 선택했나요? 🤔
저희 프로젝트의 경우 규모가 작고, 외부 클라우드(AWS) 에 서비스가 배포되기 때문에 깃허브 액션을 선택 하였습니다.
4. 아키텍쳐로 알아보는 자동배포
세세하게 들어가기 전, 아키텍쳐로 큰 그림을 이해해 봅시다.
4.1. 배포과정 살펴보기
- 열심히 코드 작업을 한 뒤, 깃허브로 푸시를 합니다.
- 깃허브 액션은 깃허브에 코드가 올라오면 이를 알아채 배포과정을 밟게 됩니다.
- 깃허브 액션은 워크 플로우에 작성된 순서대로 작업을 수행합니다. 저의 경우,
- jdk 설치
- 프로젝트 빌드
- 프로젝트 압축
- S3 에 전달
- 코드 디플로이가 S3 에 배포된 압축된 프로젝트를 EC2 서버에 배포
이렇게 작성하였습니다.
- EC2 에 배포한 후에 미리 작성한 appspec.yml 대로 작업이 수행됩니다. 저의 경우,
- 실행되고 있는 부트 프로젝트가 있는지 점검
- 실행되고 있다면 종료 후, 새로 배포한 프로젝트 실행
이렇게 작성하였습니다.
5. 깃허브액션 워크 플로우 작성해보기
5.1. deploy.yml
- 최상위 디렉토리에 .github/workflows **를 생성해 주어야 합니다.
- 서브모듈 토큰의 경우 계정의 세팅 → developer … 에서 생성 해주세요.
-
생성한 토큰과 시크릿 키들은 배포할 코드가 들어있는 레포지토리의 설정에 등록해주세요.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
## 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: $
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: $
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 \
5.2. appspec.yml
⚠️ 이름 주의해서 작성해주세요!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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
- AfterInstall : 디플로이가 서버에 배포를 완료하면 실행할 스크립트 입니다.
- ApplicationStart: 애플리케이션을 실행할때 사용할 스크립트 입니다.
5.3. Script 작성하기
⚠️ 최상단 경로에 scripts 폴더를 만들고, 그 안에 스크립트 파일을 넣어 주세요.
5.4. stop.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/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
5.5. start.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/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
6. Code Deploy
6.1. EC2 가 두개라면 코드 디플로이는 어떻게 구분할까?
저희 프로젝트처럼 ec2 가 두개인 경우를 생각해봅시다. 코드 디플로이는 S3 에서 압축된 파일을 가져와 적절한 서버에 압축을 풀어 배포합니다. 이때 서버를 어떻게 구분하는 걸까요?
6.2. 배포그룹을 사용하여 올바른 서버 찾아가기
6.3. 주의해야할 점은?
이 배포그룹과 적절한 EC2 를 연결해줘야 합니다.
✅ 아래와 같이 배포그룹 생성시 EC2 에 설정한 태그를 제대로 작성해주세요.