나의 첫 자동 배포기(feat. GitHub Actions)
2022년 4월 25일배포는 귀찮은 일입니다. Production 배포는 급하게 고쳐야 할 버그가 아니면 한 번씩 진행되지만 코드 변경이 잦은 development 배포는 자주 이루어집니다. 코드 변경이 있을 때마다 서버 업데이트를 진행 해야하는데 이는 상당히 귀찮은 일입니다. 이 고민을 덜기 위해 개발자들은 CI/CD 프로그램을 가지고 빌드와 배포를 자동화하여 뚝딱 처리합니다. 최근 진행했던 프로젝트에서 Github Actions을 가지고 자동 배포를 구성했습니다. 첫 자동 배포를 구성해보았는데 어떻게 자동 배포를 구성했는지에 대해 공유하는 시간을 갖도록 하겠습니다.
Jenkins, Circle CI 등 여러 CI/CD 프로그램이 있지만 이번 프로젝트를 진행하면서 Github Actions을 골랐습니다. 기존에 Jenkins만 써봐서 다른 CI/CD 프로그램은 어떤지 궁금하기도 하고 Github와 궁합이 좋아보이는 Github Actions을 써보기로 결정했습니다. 본격적으로 Github Actions을 쓰기 전에 우리는 왜 Github Actions 즉, CI/CD를 쓰는 것 일까요? 필자는 서버 배포를 위해 다음과 같은 과정을 거쳤습니다.
- React 코드 가져와서 최신화
git pull
- 의존성 설치 및 React 빌드
yarn && yarn build
ornpm i && npm run build
- 빌드된 React 파일 서버로 옮기기
scp
이용
- 빌드된 React 파일 서버에서 실행하기
yarn start
ornpm run start
- 혹은 nginx와 같은 웹서버로 돌리기
이 작업이 한 두번은 괜찮지만 하루에 여러 번 한다면 끔찍합니다. 로컬에서 코드 받아오고 빌드하고 파일 옮기기 서버에 접속해서 실행하고... 썩 유쾌한 일이 아닙니다. 위 작업들이 반복되는 작업임을 깨달으면 자연스럽게 자동화해서 처리하고 싶은 생각이 듭니다. 이 때 바로 코드 최신화, 빌드, 배포 자동화를 도와주는 CI/CD 프로그램 중 하나가 Github Actions입니다.
Github Actions은 Github repository의 최상단 .github/workflow
디렉터리 위치의 .yml
파일 내용을 실행합니다. .yml
파일의 내용에 따라 어떤 브랜치에 어떤 행동을(push, pr) 하면 어떤 일(steps)을 할지 정의합니다. 다음의 예시를 보면서 하나 씩 살펴봅시다.
YAMLname: Github Actions Example
on:
push:
branches: [ develop ]
jobs:
Github Actions Job:
runs-on: ubuntu-18.04
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@v2
- 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
- name: Create ENV File
run: |
touch .env
echo "${{ secrets.DEV_ENV }}" >> .env
- name: Setup Node
uses: actions/setup-node@v3
with:
node-version: '16'
- name: Build
run: |
npm i
CI= npm run build
- name: Compress
run: tar -zcvf test.tar.gz build
- name: Upload to S3
run: |
aws s3 mv --region ap-northeast-2 \
test.tar.gz \
${{ secrets.S3_DEV_FRONT_LOCATION }}/test.tar.gz
- name: Deploy
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
port: ${{ secrets.PORT }}
key: ${{ secrets.KEY }}
script: |
./deploy_front.sh
제가 생각하는 Github Actions의 가장 큰 특징은 Jenkins의 pipeline 처럼 자동화 과정이 steps으로 나뉘어져 있다는 것입니다. 현재 위의 예시에서는 Checkout
, Configure AWS Credentials
, Crete ENV File
, Setup Node
, Build
, Compress
, Upload to S3
, Deploy
8개의 step이 있습니다. 각 step은 name으로 나뉘어지고 그 안에 uses, with, run 같은 속성으로 어떤 동작을 할지 정의합니다. 다시 돌아와서 첫 번째 코드부터 살펴보도록 하겠습니다.
-
Github Actions workflow 이름 정하기
YAMLname: Github Actions Example
-
develop 브랜치에 push를 하면
YAMLon: push: branches: [develop]
-
Github Actions Job이라는 job 이름으로 Ubuntu 18.04 환경에서 최대 10분 동안
YAMLjobs: Github Actions Job: runs-on: ubuntu-18.04 timeout-minutes: 10
-
이런 step들을 진행해라
-
Github 최신 코드로 받기
YAML- name: Checkout uses: actions/checkout@v2
-
AWS 접속을 위한 인증
YAML- 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
-
React 빌드를 위한 .env 파일 작성
YAML- name: Create ENV File run: | touch .env echo "${{ secrets.DEV_ENV }}" >> .env
-
Node 환경 구성
YAML- name: Setup Node uses: actions/setup-node@v3 with: node-version: '16'
-
React 빌드 및 압축
YAML- name: Build run: | npm i CI= npm run build - name: Compress run: tar -zcvf test.tar.gz build
-
압축한 빌드물을 S3에 업로드
YAML- name: Upload to S3 run: | aws s3 mv --region ap-northeast-2 \ test.tar.gz \ ${{ secrets.S3_DEV_FRONT_LOCATION }}/test.tar.gz
-
AWS ssh 접속 하여 스크립트로 배포 진행
YAML- name: Deploy uses: appleboy/ssh-action@master with: host: ${{ secrets.HOST }} username: ${{ secrets.USERNAME }} port: ${{ secrets.PORT }} key: ${{ secrets.KEY }} script: | ./deploy_front.sh
-
전체적인 배포 과정을 정리하면 다음과 같습니다.
Github Actions yaml 파일을 보시면 중간에 ${{ secrets.USERNAME }}
와 같이 secrets 값이 있다는 것을 알 수 있습니다. 외부 노출에 민감한 값들은 Github에 secret 값을 정의해서 쓸 수 있습니다(Setting 탭 → Secrets 탭 → Actions 탭).

총 8개의 step 중 7개의 step을 거치면 압축한 React 빌드물을 S3로 업로드 한 상태입니다. 마지막으로 EC2 서버에서 배포하는 작업만 남아있습니다. 필자는 Github Actions에서 서버로 ssh 접속을 하여 미리 작성한 EC2의 쉘 스크립트를 돌리는 방식으로 자동 배포를 구성하였습니다. 필자가 구성한 React가 nginx 웹 서버로 돌아가기 때문에 새로운 React 빌드물을 받고 nginx를 재시작하면 반영된 서버를 만날 수 있습니다.
BASH#!/bin/sh aws s3 cp s3://[S3_DIRECTORY]/$(date +%Y%m%d).tar.gz front if [ -d front/build ] then rm -rf front/build fi tar -zxvf front/$(date +%Y%m%d).tar.gz -C front rm front/$(date +%Y%m%d).tar.gz docker restart nginx
프로젝트를 진행하면서 처음으로 CI/CD 자동 배포를 구축해보았습니다. 의미있는 결과를 얻었지만 부족한 부분이 많습니다. S3에 빌드물을 올릴 때 날짜를 기준으로 올려 롤백 문제라던가, 한 번에 여러 사람들이 Github Actions를 돌려 배포가 꼬인다는 문제라던가, Github Actions 진행 도중 오류가 나면 정확한 로그를 보여주지 않다던가 등등 개선할 점이 많습니다. 하지만 이제 개발자들이 새로운 코드를 올릴 때마다 수동으로 서버 업데이트를 하지 않는 것만으로 큰 의미가 있다고 생각합니다. 잦은 서버 배포에 지친 여러분들한테 강력하게 추천드립니다. CI/CD 만세!