[Node.js] 서버 CI/DI 솔루션 [GitHub Actions][AWS EC2]
오늘은 Github Action을 이용하여 서버 CI/CD 파이프라인을 작성하는 법에 대해 작성해보려 한다.
들어가기 전 참고한 영상 첨부합니당
CI/CD 란?
CI/CD는 소프트웨어 개발 프로세스에서 중요한 역할을 하는 두 가지 개념이며, 소프트웨어 개발의 효율성과 품질을 향상시키기 위해 자동화된 빌드, 테스트, 배포 프로세스를 구현하는 방법론이다.
CI (Continuous Integration)는 새로운 코드 변경 사항이 발생할 때마다 자동으로 애플리케이션을 빌드하고 테스트하는 과정이다. 이 과정을 통해 개발자는 수동으로 빌드와 테스트를 수행할 필요 없이, 코드 변경 사항이 코드베이스에 통합될 때 자동적으로 품질 검사를 받을 수 있다. CI를 성공적으로 구현하면, 팀의 협업이 원활해지고, 코드 오류를 줄일 수 있다는 장점이 있다.
CD (Continuous Delivery / Continuous Deployment)는 CI 과정을 통해 빌드와 테스트가 완료된 후, 검증된 애플리케이션을 자동으로 배포하는 것이다. Continuous Delivery는 배포 준비가 완료된 상태를 유지하는 것을 의미하며, 실제 배포는 수동으로 진행될 수 있다. 반면, Continuous Deployment는 모든 변경 사항이 자동으로 프로덕션 환경에 배포되는 과정을 의미한다. 이 방식은 소프트웨어의 신뢰성을 높이고, 빠른 피드백을 가능하게 하여 개발 주기를 단축시킨다.
GitHub Actions을 이용한 Self-hosted Runners 설정
GitHub Actions는 특정 이벤트가 발생했을 때 자동으로 작업을 실행할 수 있는 기능이다.
이 기능을 통해 개발 프로세스를 효율적으로 관리할 수 있다.
GitHub Actions 환경
GitHub Actions는 두 가지 환경에서 실행할 수 있다:
- GitHub-hosted runners: GitHub에서 제공하는 가상 머신을 사용하는 형태이다. 이 방식은 서버 자원을 사용할 필요가 없어서 편리하지만, 가상 머신 내부에 직접 접근할 수 없는 단점이 있다.
- Self-hosted runners: 사용자의 로컬 환경이나 AWS 가상 머신을 설정하여 사용하는 방식이다. 이 방법을 통해 GitHub Action이 실행될 때의 과정과 결과물을 직접 확인할 수 있다. AWS EC2를 설정하면 GitHub의 main 브랜치에 코드가 push 될 때마다 자동으로 EC2에서 코드를 가져올 수 있다. 이러면 서버를 재시작하기만 하면 되는 편리한 시스템이 완성된다.
우리는 Self-hosted runners를 설정하고 이것을 AWS EC2와 연결하여 코드가 업데이트 되었을 때 자동으로 서버를 실행하도록 할 것이다.
그럼 이제 설정해보자.
1단계: GitHub Actions의 Self-hosted Runners 설정
1. Repository 접속
원하는 GitHub 레포지토리에 접속한다.
2. New self-hosted runner 생성
[Settings] - [Actions] - [Runners]로 이동하여 New self-hosted runner를 클릭한다.
이후 이어지는 화면에서 AWS EC2의 OS에 맞는 Runner image를 선택한다. (나는 Ubuntu로 설정했으므로 Linux를 선택함)
3. AWS EC2에서 command 실행
제공된 command를 AWS EC2에 접속하여 한 줄씩 실행한다. Configure의 첫 번째 Command까지만 실행한다.
# Create a folder
# 단순히 폴더를 만드는 커맨드라, 원하는 경로로 설정하면 됨
$ mkdir actions-runner && cd actions-runner
# Download the latest runner package
$ curl -o actions-runner-linux-x64-2.313.0.tar.gz -L https://github.com/actions/runner/releases/download/v2.313.0/actions-runner-linux-x64-2.313.0.tar.gz
# Optional: Validate the hash
$ echo "{{해시값}} actions-runner-linux-x64-2.313.0.tar.gz" | shasum -a 256 -c
# Extract the installer
$ tar xzf ./actions-runner-linux-x64-2.313.0.tar.gz
# Create the runner and start the configuration experience
$ ./config.sh --url https://github.com/{{레포지토리명}} --token {{토큰값}}
이후에 있는 run 파일 실행 커맨드는 실행하지 않고 다음으로 넘어가야 한다.
4. AWS EC2 가상머신이 GitHub의 hook을 듣도록 설정
이어서 아래의 command를 실행하면 AWS EC2가 GitHub의 hook을 계속 수신 대기하게 된다.
이제 GitHub Actions가 실행되면 이 EC2 환경에서 작업이 수행될 것이다!
$ sudo ./svc.sh install
$ sudo ./svc.sh start
5. 설정 확인
모든 설정이 완료되면 GitHub의 Runners 페이지에서 설정한 Runner가 idle 상태인지 확인한다.
2단계: GitHub Actions가 실행되는 .yml 파일 작성하기
이제 언제 어떤 행동을 실행할지를 .yml 파일에 정의해보자.
1. Configure 메뉴 클릭
GitHub Repository에 접속하여 Actions를 클릭한다.
이어지는 화면에 보이는 Workflow 템플릿 중 [Continuous Integration] - [Node.js]의 Configure를 클릭한다.
2. .yml 파일 작성
나오는 .yml 파일의 이름을 원하는 대로 변경하고 아래 코드를 붙여넣는다.
# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs
name: Node.js CI/CD test
on:
push:
branches: [ "main" ]
jobs:
build:
runs-on: self-hosted
strategy:
matrix:
node-version: [21.x]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
steps:
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- run: npm ci
- run: |
touch .env
echo "${{ secrets.PROD_ENV }}" > .env
각 속성을 간단하게 정리하면 다음과 같다.
- name: Action의 이름으로 마음대로 지정하면 된다.
- on: GitHub Actions가 언제 실행될지를 정의한다. push: branches: ["main"]는 main 브랜치에 push가 일어날 때 Action을 실행하겠다는 의미이다.
- jobs: 실제로 실행될 Actions을 정의한다.
- build: build라는 job을 정의한다.
- runs-on: self-hosted로 설정했으므로 self-hosted로 지정한다.
- strategy: 자주 사용할 변수들을 설정한다. 현재는 node-version만 설정해뒀다.
- steps: 실제 동작들을 정의한다. name:은 Action에 이름을 붙이기 위한 부분으로 자유롭게 지정할 수 있다.
- actions/checkout@v3: 현재 설정한 branch(여기서는 main)를 가상 머신에 Checkout한다. 이때 action/ 접두사는 Github Actions에서 기본으로 제공하는 Action이라는 것을 의미한다.
- actions/setup-node@v3: node-version에 해당하는 Node.js를 설치한다.
- run: npm ci: npm ci를 실행하여 의존성을 설치한다.
- run: touch .env && echo "${{secrets.PROD_ENV}}" >> .env: .env 파일을 생성하고 여기에 환경 변수를 작성한다. secrets.PROD_ENV는 GitHub에서 설정한 비밀값이다.
- run: pm2 restart BackendAPI: 가상머신 환경에서 pm2 모듈을 이용해 서버를 재시작한다. (아래에서 자세히 다룰 예정)
- build: build라는 job을 정의한다.
더 자세한 내용은 아래 참고.
Workflow syntax for GitHub Actions - GitHub Docs
docs-internal.github.com
3. .yml 파일 추가
Commit Changes를 눌러 .yml 파일을 추가하면 .github/workflows라는 디렉토리에 .yml 파일이 생성된다.
이후 main 브랜치에 push가 일어나는 즉시 Action이 실행된다.
4. 설정 확인
AWS EC2로 접속하여 생성한 GitHub Actions runner 폴더 안에 GitHub의 main 브랜치 파일들이 들어와 있는지를 확인한다. npm ci 명령어 덕분에 node_modules도 자동으로 설치된 것을 확인할 수 있다.
이렇게 GitHub Actions runner를 AWS EC2에 설정함으로써 CI/CD가 동시에 해결된다..!
3단계: AWS EC2에 업데이트된 서버 코드 자동으로 실행하기
이제 연결한 레포지토리의 main 브랜치에 push를 수행하기만 하면 AWS EC2의 서버 코드가 자동으로 업데이트된다.
이렇게 업데이트된 서버 코드를 자동으로 재실행하여, IP 주소로 AWS에 접속해도 해당 내역을 업데이트 할 수 있도록 하기 위해 아래 절차를 따른다.
1. PM2 설치 및 확인
서버 관리에 유용한 Node.js 모듈인 pm2를 AWS EC2에 설치한다. 아래 명령어로 설치한다.
sudo npm install -g pm2
pm2가 정상적으로 설치되었는지 확인하기 위해 아래 명령어를 입력한다. 버전 정보가 제대로 출력되면 설치가 완료된 것이다.
pm2 -v
2. 서버 코드 실행
pm2를 통해 실행해야 할 서버 JavaScript 코드를 찾아 아래 명령어로 실행한다.
pm2 start bin/www --name="원하는 프로세스 이름"
- --name 인자에는 프로세스를 구분하기 위한 이름을 붙여주면 된다.
3. 프로세스 상태 확인
아래 명령어를 실행하여 pm2로 시작한 프로세스가 정상적으로 실행되었는지를 확인한다.
설정한 이름의 프로세스가 online 상태이면 정상적으로 동작하고 있는 것이다.
pm2 list
4. 프로세스 재시작
pm2로 시작한 프로세스를 재실행하는 명령어는 아래와 같다.
pm2 restart Your_Process_Name
이 명령어는 GitHub에 push가 일어나 GitHub Action이 실행된 후에 AWS EC2에 새로운 코드가 모두 받아진 다음 실행되어야 한다. 즉, 작성했던 .yml 파일의 가장 마지막 줄에 추가되어야 한다.
5. .yml 파일에 추가
.yml 파일의 가장 아래쪽에 다음 한 줄을 추가하여 PM2를 통한 프로세스 재시작을 실행한다.
- run: pm2 restart Your_Process_Name
이렇게 설정하면 GitHub main 브랜치에 push가 발생할 때마다 자동으로 AWS EC2에 서버 코드가 업데이트되고, PM2를 통해 서버가 재실행된다. 따라서 항상 최신 상태의 서버를 유지할 수 있다.
이렇게 CI/CD가 제대로 구축된 것을 확인할 수 있다.
끗~