Skip to content

Github Aciton

Heesung Youn edited this page Feb 9, 2022 · 2 revisions

Github Action

Github Action을 이용하면 github내 push, PR 등의 이벤트가 발생 했을 때 지정해놓은 Job을 수행하도록 만들 수 있다. Job을 통해서 자동 테스트 및 배포 등을 수행할 수 있다.

Test

Pull Request가 수행될 때 마다 자동으로 테스트를 수행하는 Job을 만들 수 있다.

전문은 아래와 같다.

exec-mocha-test.yml

name: Execute Mocha Test

on:
  pull_request:
    branches: [ dev, main ]

jobs:
  build:

    runs-on: ubuntu-latest

    strategy:
      matrix:
        node-version: [10.x]
        
    steps:
    - uses: actions/checkout@v2
    - name: Use Node.js ${{ matrix.node-version }}
      uses: actions/setup-node@v2
    - run: npm install -g mocha
    - run: npm install -g typescript
    - run: npm install -g ts-node
    - run: npm install
    - run: npm audit fix
    - run: npm ci
    - run: npm run build --if-present
    - name: run test code 
      run: npm test
      env:
        NODE_ENV: test
        MYSQL_TST_USERNAME: '${{ secrets.MYSQL_TST_USERNAME }}'
        MYSQL_TST_PASSWORD: '${{ secrets.MYSQL_TST_PASSWORD }}'
        MYSQL_TST_DATABASE: '${{ secrets.MYSQL_TST_DATABASE }}'
        MYSQL_TST_HOST: '${{ secrets.MYSQL_TST_HOST }}'
        MYSQL_TST_PORT: '${{ secrets.MYSQL_TST_PORT }}'
        MYSQL_TST_DIALECT: '${{ secrets.MYSQL_TST_DIALECT }}'
        MONGO_URI: '${{ secrets.MONGO_URI }}'
        CORS_ALLOW_LIST: '${{ secrets.CORS_ALLOW_LIST }}'
        JWT_SECRET: '${{ secrets.JWT_SECRET }}'
        AWS_ACCESS_KEY_ID: '${{ secrets.AWS_ACCESS_KEY_ID }}'
        AWS_SECRET_ACCESS_KEY: '${{ secrets.AWS_SECRET_ACCESS_KEY}}'
        ENCRYPTION_KEY: '${{ secrets.ENCRYPTION_KEY }}'
        ENCRYPT_ALGORITHM: '${{ secrets.ENCRYPT_ALGORITHM }}'
        AWS_S3_URL: '${{ secrets.AWS_S3_URL }}'

name 속성

name은 workflow의 이름을 정의한다. 이 이름은 Github Action 탭에서 확인할 수 있다.

name: Execute Mocha Test

image

on 속성

on은 구독할 이벤트를 정의한다. 자동 테스트의 경우는 PR이 생성되는 경우에만 수행하면 되기 때문에 pull_request만 구독한다. 또한 브랜치는 dev와 main으로 한정한다.

on:
  pull_request:
    branches: [ dev, main ]

jobs 속성

jobs아래에는 각각의 독립된 job을 정의한다. 여기서는 build라는 job을 정의한다.

  • build보다는 test라는 job name이 더 적절해 보인다.

jobs:
  build:
    // ...

하나의 job에는 아래의 속성을 확인할 수 있다.

    runs-on: ubuntu-latest

    strategy:
      matrix:
        node-version: [10.x]
    stpes:
      // ...  
runs-on: ubuntu-latest

먼저 runs-on은 실행할 환경을 정의한다. 자동 테스트의 경우에는 일회성으로만 동작하기 때문에 별도로 지정된 서버에서 돌릴필요가 없다. 따라서 Github에서 제공하는 Ubuntu Server(ubuntu-lateset)를 활용한다. 해당 서버는 테스트를 수행한 이후에 초기화 되기 때문에 Server의 설정값과 Dependency가 없는 상태로 테스트를 수행할 수 있다.

  • self-hosted

    이와 다르게 지정된 서버에서 실행되도록 만드는 self-hosted가 존재한다. 이는 배포에서 다룬다.

    strategy:
      matrix:
        node-version: [10.x]

strategy - matrix 인자를 사용하면 어떤버전에서 테스트할지 확인할 수 있다.

steps

steps는 job을 정의하는 각각의 Comman를 선언한다.

    stpes:
    - uses: actions/checkout@v2
    - name: Use Node.js ${{ matrix.node-version }}
      uses: actions/setup-node@v2
    - run: npm install -g mocha
    - run: npm install -g typescript
    - run: npm install -g ts-node
    - run: npm install
    - run: npm audit fix
    - run: npm ci
    - run: npm run build --if-present
    - name: run test code 
      run: npm test
      env:
        NODE_ENV: test
        MYSQL_TST_USERNAME: '${{ secrets.MYSQL_TST_USERNAME }}'
        MYSQL_TST_PASSWORD: '${{ secrets.MYSQL_TST_PASSWORD }}'
        MYSQL_TST_DATABASE: '${{ secrets.MYSQL_TST_DATABASE }}'
        MYSQL_TST_HOST: '${{ secrets.MYSQL_TST_HOST }}'
        MYSQL_TST_PORT: '${{ secrets.MYSQL_TST_PORT }}'
        MYSQL_TST_DIALECT: '${{ secrets.MYSQL_TST_DIALECT }}'
        MONGO_URI: '${{ secrets.MONGO_URI }}'
        CORS_ALLOW_LIST: '${{ secrets.CORS_ALLOW_LIST }}'
        JWT_SECRET: '${{ secrets.JWT_SECRET }}'
        AWS_ACCESS_KEY_ID: '${{ secrets.AWS_ACCESS_KEY_ID }}'
        AWS_SECRET_ACCESS_KEY: '${{ secrets.AWS_SECRET_ACCESS_KEY}}'
        ENCRYPTION_KEY: '${{ secrets.ENCRYPTION_KEY }}'
        ENCRYPT_ALGORITHM: '${{ secrets.ENCRYPT_ALGORITHM }}'
        AWS_S3_URL: '${{ secrets.AWS_S3_URL }}'

테스트 자동화를 위해서 아래 작업을 정의한다.

  1. checkout

    github에서 최신 버전으로 Pull을 수행한다.

  2. setup-node

    node를 설치한다.

  3. 의존성 설치

    github에서 제공되는 ubuntu 서버는 백지 상태이기 때문에, 의존성 모듈을 설치한다.

  4. run test

    test를 수행한다.

  5. env

    test를 수행할 때 환경 변수를 요구한다. 하지만 Github ubuntu서버에는 환경 변수가 저장되지 않기 때문에 job을 수행할 때 마다 매번 재지정 해줘야 한다. yml파일에 직접 환경변수 값을 지정할 수 있지만 보안 이슈가 있는 환경변수는 Github에서 제공하는 Secret를 활용한다.

    • Github Secret 선언

      Girhub Repository > Settings > Secrets > Actions

      스크린샷 2022-02-10 오전 12 49 18

      Repository secrets 에서 확인할 수 있으며 New repository secret 로 추가할 수 있다.

      환경변수는 생성, 삭제 및 업데이트만 가능하다. 즉 환경변수의 값을 읽을 수는 없다. (읽을 수 없다는 제약을 통해서 해당 값을 보호할 수 있다.)

      스크린샷 2022-02-10 오전 12 49 39

      Secret 생성은 name에 변수명 Value에 값을 넣으면 된다.

      스크린샷 2022-02-10 오전 12 50 04

    워크플로우 결과 화면

     ![스크린샷 2022-02-10 오전 12 48 32](https://user-images.githubusercontent.com/9072200/153237127-13cb1859-933f-4c99-9f7b-c5c59a00dea9.png)
    

각각 항목에 대해서 확대하여 확인 할 수 있다.

self-hosted 로 지정된 서버는 run.sh 스크립트를 실행해줘야지만 Github Action을 통해 이벤트를 수신하여 정해진 스크립트를 수행할 수 있다. 따라서 아래 명령어를 통해서 백그라운드로 설정해주는 것이 좋다.

./run.sh &

Deploy

github Action을 통해서 배포를 수행하기 위해서는 실제 서버에서 동작을 수행해야 한다. 따라서 runs-on을 github에서 제공하는 ubuntu-latest로 지정하는 방법으로는 구현할 수 없다. 임시 리눅스가 아닌 실제 서버에서 작업을 수행하고 싶은 경우에는 서버를 Runner로 등록하는 과정을 수행해줘야 한다.

Self-hosted 설정

Github > Repo > Settings > Actions > Runner에서 New self-hosted runner를 통해 등록해줘야 한다.

스크린샷 2022-02-10 오전 12 50 37

등록하는 방법은 클릭 이후에 나타나는 가이드를 따라가면 된다.

  • ubuntu Computer architecture를 확인 하는 방법

    uname -a

deploy workflow 파일

deploy의 경우 production과 development 버전 각각 하나로 만들었다. deploy-prodction.yml, deploy-developement.yml

Runner을 2개로 만들어서 수행할 수 있지만 이는 오버스펙으로 보여서 workflow 파일을 2개로 나누었다.

deploy-development.yml

name: Deploy development server to ec2

on:
  push:
    branches: 
    - dev

jobs:
  build:
    runs-on: self-hosted
    strategy:
      matrix:
        node-version: [12.x]
    defaults:
      run:
        working-directory: ${{ env.NODE_ENV }}
    steps:
    - uses: actions/checkout@v2
      with:
        path: ${{ env.NODE_ENV }}
    - name: Use Node.js ${{ matrix.node-version }}
      uses: actions/setup-node@v2
      with:
        node-version: ${{ matrix.node-version }}
        cache: 'npm'
        cache-dependency-path: ${{ env.NODE_ENV }}/package-lock.json
    - run: cp ~/config-injection/ecosystem-${{ env.NODE_ENV }}.json ${{ env.PROJECT_PATH }}/ecosystem.json
    - run: cp ~/config-injection/.env ${{ env.PROJECT_PATH }}/.env
    - run: ${{ env.PROJECT_PATH }}/script/prebuild.sh
    - run: ${{ env.PROJECT_PATH }}/script/reload.sh
    env:
      NODE_ENV: development
      PROJECT_PATH: ~/actions-runner/_work/A.fume.Server/A.fume.Server/development

deploy-production.yml

name: Deploy production server to ec2

on:
  push:
    branches: 
    - main

jobs:
  build:
    runs-on: self-hosted
    strategy:
      matrix:
        node-version: [12.x]
    defaults:
      run:
        working-directory: ${{ env.NODE_ENV }}
    steps:
    - uses: actions/checkout@v2
      with:
        path: ${{ env.NODE_ENV }}
    - name: Use Node.js ${{ matrix.node-version }}
      uses: actions/setup-node@v2
      with:
        node-version: ${{ matrix.node-version }}
        cache: 'npm'
        cache-dependency-path: ${{ env.NODE_ENV }}/package-lock.json
    - run: cp ~/config-injection/ecosystem-${{ env.NODE_ENV }}.json ${{ env.PROJECT_PATH }}/ecosystem.json
    - run: cp ~/config-injection/.env ${{ env.PROJECT_PATH }}/.env
    - run: ${{ env.PROJECT_PATH }}/script/prebuild.sh
    - run: ${{ env.PROJECT_PATH }}/script/reload.sh
    env:
      NODE_ENV: production
      PROJECT_PATH: ~/actions-runner/_work/A.fume.Server/A.fume.Server/production

여기서 Test와 다른 부분을 하나씩 살펴보자.

runs-on

    runs-on: self-hosted

runs-on은 우리가 생성한 Runner인 self-hosted로 설정한다. 이는 github에서 제공하는 Ubuntu 서버가 아닌 Runner로 등록한 실제 서버에서 Workflow를 수행하는 것을 의미한다.

defaults

같은 Runner를 사용하기 떄문에 production이든 development이든 동일한 경로를 사용하게 된다. 하지만 이는 충돌이 날 수 있기 떄문에 NODE_ENV를 가지는 폴더 이름으로 분리하였다.

 defaults:
      run:
        working-directory: ${{ env.NODE_ENV }}
 steps:
 - uses: actions/checkout@v2
   with:
     path: ${{ env.NODE_ENV }}
 - name: Use Node.js ${{ matrix.node-version }}
   uses: actions/setup-node@v2
   with:
     node-version: ${{ matrix.node-version }}
     cache: 'npm'
     cache-dependency-path: ${{ env.NODE_ENV }}/package-lock.json

checkout과 setup-node 액션의 경우 default 경로가 workflow가 실행되는 경로에서 수행하기 때문에 Specific한 Path에서 작업하기 위해서는 pathcache-dependency-path 로 직접 지정해줘야 한다.

steps

베포를 위한 코드는 3단계로 이루어진다.

    - run: cp ~/config-injection/ecosystem-${{ env.NODE_ENV }}.json ${{ env.PROJECT_PATH }}/ecosystem.json
    - run: cp ~/config-injection/.env ${{ env.PROJECT_PATH }}/.env
    - run: ${{ env.PROJECT_PATH }}/script/prebuild.sh
    - run: ${{ env.PROJECT_PATH }}/script/reload.sh
    env:
      NODE_ENV: production
      PROJECT_PATH: ~/actions-runner/_work/A.fume.Server/A.fume.Server/production
  1. File Inject
    1. 서버에 사전에 정의된 .env 및 ecosystem.json을 cp 명령어를 통해서 주입한다.
  2. Prebuild
    1. 서버 실행을 위핸 Dependency를 설치하는 작업을 수행한다.
  3. reload
    1. 서버를 실핸하다.
  • 참고 자료