새소식

ETC

Github Action 알아보기

  • -

기본 flow

작동 단계

 

  1. github push
  2. event trigger
  3. job 순서대로 작업 수행

 

이벤트 트리거

  • repository에서 일어나는 push, fort 등의 작업
  • github project, issue에서 일어나는 작업
  • 사용자가 직접 트리거

 

코드 작성

 

.github.workflows 폴더 하위에 github action으로 실행하고자 하는 일들을 파일별로 정의하면 별도의 설정 과정 없이 project의 actions 탭들을 통해 실행할 수 있습니다. actions 탭에서는 워크플로우 실행 결과를 확인할 수 있습니다.

 

workflow

workflow란 이벤트 발생 시, 어떠한 행위를 할 것인가에 대한 작업 정의합니다.

 

  • runner : job별 별도의 공간에서 실행하는 작업 공간에 대한 정의로 작업 간 내용 공유는 기본적으로 제공되지 않습니다.
  • job : 워크플로우 내 작동하는 작업 단위
  • step : job 내 개별 수행되는 액션에 대한 정의

 

name: Sample # workflow의 이름 => github action 버튼에서 노출
on: workflow_dispatch # 이벤트 트리거 정의 => 여기서는 수동 트리거
jobs:
  build: # job 1
    runs-on: ubuntu-latest # 해당 작업을 어떤 환경에서 수행할 것인지
    steps: # 순차적 step 정의
    - name: Checkout 
      uses: actions/checkout@v2
    - name: Build project
      run: |
        echo "Build Project"
    - name: Run tests
      run: |
        echo "Run Test"
  deploy: # job 2
    needs: build 
    runs-on: ubuntu-latest
    steps:
    - name: Deploy to production
      run: |
        echo "Deploying to production server"

 

기본 사용법

변수 사용법

  • workflow 파일에서 사용되는 동적인 값으로 주로 환경 변수(environment variables), 시크릿(secret)값을 저장하고 사용합니다.
  • workflow 파일에 env 키로 정의하여 단일 workflow에서 정의할 수도 있고 github organization이나 repository에서 정의하여 전체 workflow 내에서 global 하게 사용할 수도 있습니다.
  • 이외에도 Default로 제공되는 변수도 있습니다.
    • prefix로 GITHUB_X 또는 RUNNER_X로 네이밍 되어 있습니다.

 

workflow level에서 정의

name: variable-1
on: workflow_dispatch

env:
  fruit: Apple

jobs:
  build_1:
    runs-on: ubuntu-latest
    env:
      fruit: Orange
    steps:
    - name: Step 1 
      run: |
        echo "Run Step 1, Make $fruit Juice!" 
  build_2:
    runs-on: ubuntu-latest
    steps:
    - name: Step 1
      run: |
        echo "Run Step 1, Make $fruit Juice!"

 

build_1에서는 fruit이 재정의 되어있으므로 Orange가 출력되고 build_2에서는 바깥에 정의된 Apple을 사용하게 됩니다.

 

repo, organization level

 

위 탭에서 전역으로 사용할 변수를 지정할 수 있으나 다음과 같은 제약 조건이 있습니다. secret 탭에서는 보안에 관련된 변수들을 지정해 두고 사용할 수 있습니다.

 

  • 이름에는 영문 숫자 또는 언더바만 사용 가능
  • 공백 허용 X
  • Default variable과 겹치지 않도록 권장
  • 숫자로 시작 불가
  • 대소문자를 구분 X

 

name: global variable
on: workflow_dispatch
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - name: Checkout 
      uses: actions/checkout@v2
    - name: Build project
      run: |
        echo "Build Project ${{ vars.PROJECT_NAME }}"

 

실제로 사용하는 시점에는 vars.를 붙여서 사용할 수 있습니다.

 

Step 간 데이터 전달

  • $GITHUB_OUTPUT 을 활용해 전달합니다.
  • Key=value 형태로 $GITHUB_OUTPUT 에 기록합니다.
  • 이후 steps.<step_id>.outputs.<key> 로 데이터 접근합니다.
  • 기본적으로 job간의 공유는 불가능하고 외부 스토리지나 Artifact를 활용해야 합니다.
name: Step Output
on: workflow_dispatch
jobs:
  test-job:
    runs-on: ubuntu-latest
    steps:
    - name: Checkout 
      uses: actions/checkout@v2
    - id: generate-random-id
      name: Generate random string
      run: echo "random_id=$RANDOM" >> "$GITHUB_OUTPUT"
    - id: build-project
      name: Build project
      run: |
        echo "Random ID: ${{ steps.generate-random-id.outputs.random_id }}"

 

조건과 연산자

  • startsWith : 특정 문자열로 시작 시, true로 반환
  • endsWith : 특정 문자열로 끝날 시, true 반환
  • format : 특정 포맷의 string 값을 변수로 변경
  • join : array 배열의 문자열 값을 연결
  • if: 조건문
  • success, failure, canceled, always : 전 step의 상태에 따른 step 수행 결정

if문을 사용할 수 있습니다.

name: if
on: workflow_dispatch
jobs:
  build:
    runs-on: ubuntu-latest
    if: ${{ github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/master' }}
    steps:
      - name: Say Hello
        run: |
          echo "Say Hello to $GITHUB_ACTOR"    

 

issue를 활용할 수도 있습니다.

name: issue
on: 
  issues:
    types: [opened, edited, labeled, unlabeled]
jobs:
  auto-assignee:
    runs-on: ubuntu-latest
    permissions:
      issues: write
    if: ${{ contains(github.event.issue.labels.*.name, 'bug') }}
    steps:
    - name: Auto assign issue
      uses: pozil/auto-assign-issue@v1
      with:
        assignees: ${{ vars.BUG_HUNTERS }}

 

bug라는 라벨이 포함되어 있으면 assignees를 bug_hunters로 지정하는 job입니다.

 

workflow

trigger

 

  • workflow가 위치한 repository에서 발행한 이벤트
  • repository 외 관련 서비스에서 발생항 이벤트
  • 예약된 시간에 트리거
  • 수동으로 직접 이벤트 트리거(workflow_dispatch)

 

repository에서 발생한 이벤트

push, pull_request, fork, release, tag, workflow 등의 이벤트를 트리거로 사용할 수 있습니다. on절에 명시합니다. paths-ignore을 통해 무시할 수 있는 경우도 추가할 수 있습니다.

name: branch push
on: 
  push:
    branches:
      - '!test-**'
      - '!main'
      - '*'
    paths-ignore:
      - 'README.md'
      - '.github/workflows/**'
jobs:
  build:
    ...

 

workflow 연동

name: branch push
on: 
  push:
    tags:
      - 'v1.**'
jobs:
  build:
    ...
name: workflow next
on: 
  workflow_run:
    workflows: ["branch push"]
    types:
      - completed
jobs:
  build:
    ...

 

schedule

name: cron
on: 
  schedule:
  - cron: '*/5 * * * *'
jobs:
  build:
    ...

 

수동 트리거 input

name: PART3 - CH2 - Workflow Dispatch
on: 
  workflow_dispatch:
    inputs:
      tag:
        description: 'Tag to deploy'
        required: true
        default: '1.0.0'

jobs:
  test-job:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v2
      - id: input
        name: input test
        run: echo "input=${{ github.event.inputs.tag }}"

 

github repo에서 수동으로 트리거하는 경우 input 값을 받아서 사용할 수도 있습니다.

 

artifact


GitHub Action에서는 아티팩트(artifacts)라는 기능을 통해 워크플로우(Workflow)가 실행하는 동안 이전에 동일 업로드된 아티팩트(artifacts)를 다운로드할 수 있게 지원합니다. Job 간의 특정 공유가 필요한 경우 사용합니다.

name: artifact
on: workflow_dispatch
jobs:
  job-1:
    runs-on: ubuntu-latest
    steps:
    - name: Get Random UUID
      id: uuid
      run: echo "::set-output name=uuid::$(uuidgen)"
    - name: Print UUID
      run: |
        echo "UUID: ${{ steps.uuid.outputs.uuid }}"
        echo "${{ steps.uuid.outputs.uuid }}" > uuid.txt
    - name: Upload UUID
      uses: actions/upload-artifact@v3
      with:
        name: uuid
        path: uuid.txt
  job-2:
    runs-on: ubuntu-latest
    needs: job-1
    steps:
    - name: Download UUID
      uses: actions/download-artifact@v3
      with:
        name: uuid
    - name: Print UUID
      run: |
        echo "UUID: $(cat uuid.txt)"

 

job

구조

name: job sample
on: workflow_dispatch
jobs:
  build: # id : job에 대한 고유 식별자
    name: Build and Test # job의 이름으로 github ui 상으로 표기
    runs-on: ubuntu-latest # runs-on : runner에 대한 정의, 작업을 수행할 머신의 형식 설정
    defaults: # run단계에서 적용될 shell과 working directory 설정
      run:
        shell: bash
        working-directory: ./build_sample/python    
    steps: # job 내의 실행 step 설정
      - id: checkout # step 고유 식별자
        name: Checkout # step 이름으로 ui 상에 표기
        uses: actions/checkout@v2
      - id: build-test
        name: Run npm build
        run: | # 쉘을 사용하여 명령어 실행. 단일/다중 명령 실행 가능
          npm ci
          npm run build
      - name: Login to Docker Hub
        if: always() # if문 
        uses: docker/login-action@v1
        with:
          username: ${{ secrets.DOCKERHUB_USER }}
          password: ${{ secrets.DOCKERHUB_PASSWORD }}

 

runner

보통 실행기(Runner)에서 코드를 다운로드하고, 빌드를 위한 소프트웨어 설치 및 다양한 사전 실행 작업 및 빌드 행위를 수행합니다. 그렇기 때문에 다양한 OS 환경을 지원합니다. runner는 크게 두 가지 방식으로 제공합니다.

  • GitHub Hosting Runners
    • github에서 무료로 제공하는 공식 실행기(runner)
    • github에서 자체 관리하므로 최신 os와 보안 업데이트
    • 다양한 운영체제 환경과 버전에 맞춘 실행기
    • 동시에 실행할 수 있는 job에 제한이 있습니다.(월간 사용량, 계정 유형에 따라 상이)
    • 제공되는 환경
      • ubuntu, windows, macOS
      • 3가지 모두 같은 방식으로 지원
        • latest 지정 : ex) ubuntu-latest
        • 버전 지정 : ex) ubuntu-22.04
  • Self-hosted runners
    • 자체 관리 : 사용자가 직접 관리하는 방식으로 직접 인스턴스를 설치하고 해당 인스턴스를 지정
    • 해당 인스턴스에 github runner를 설치
    • 등록하여 사용 가능한 OS가 제한이 있지만 대부분을 지원

 

self-host 지정 & 사용

 

settings 탭에서 self-host runner를 생성하면 가이드가 나와있으니 참고하여 github-runner로 사용할 인스턴스 내부에 github runner를 설치해야 합니다.

name: Self-Hosted Runner
on: 
  workflow_dispatch:
jobs:
  build:
    name: Build on self-hosted
    runs-on: self-hosted
    steps:
      - name: Say Hello
        run: |
          echo "Say Hello to $GITHUB_ACTOR"    

 

matrix 사용법

name: Multi Matrix Runner 
on: workflow_dispatch
jobs:
  build:
  name: Multi OS Build 
  strategy:
    matrix:
      os: [ubuntu-20.04, ubuntu-22.04]
      version: [10, 12, 14] 
  runs-on: ${{ matrix.os }} 
  steps:
    - id: checkout
      name: Checkout
      uses: actions/checkout@v2 
    - id: setup-node
      name: Setup Node.js
      uses: actions/setup-node@v2 
      with:
        node-version: ${{ matrix.version }}

 

matrix를 사용하면 여러 환경에 대해 여러 번 수행이 가능합니다. 위 케이스에서는 ubuntu-20.04 환경에서 10, 12, 14 변수를 차례로 사용하여 수행하고 ubuntu-22.04에서도 마찬가지입니다. 즉, 총 6번을 수행하게 됩니다.

 

동시성

Job은 별도의 설정이 없다면 정의된 job들이 동시에 병렬 수행됩니다. 이를 제어하려면 needs 키워드를 사용합니다. 만약 grouping 하여 group 내 포함된 job들끼리 동시성을 제어하고자 한다면 group 키워드를 사용합니다.

 

needs를 사용한 의존성

name: needs
on: workflow_dispatch
jobs:
  job-1:
    name: Concurrency Job Test 1
    runs-on: ubuntu-latest
    steps:
    - id: checkout
      name: Checkout 
      uses: actions/checkout@v3
    - id: test-1
      name: Test 1
      run: echo "Test 1"
  job-2:
    name: Concurrency Job Test 2
    runs-on: ubuntu-latest
    needs: job-1
    steps:
    - id: checkout
      name: Checkout 
      uses: actions/checkout@v3
    - id: test-2
      name: Test 2
      run: echo "Test 2"

 

Group

name: Groups
on: workflow_dispatch
jobs:
  job-1:
    name: Concurrency Job Test 1
    runs-on: ubuntu-latest
    concurrency:
      group: ${{ github.workflow }}-${{ github.ref }}-1
    steps:
    - id: checkout
      name: Checkout 
      uses: actions/checkout@v3
    - id: test-1
      name: Test 1
      run: echo "Test 1"
  job-2:
    name: Concurrency Job Test 2
    runs-on: ubuntu-latest
    concurrency:
      group: ${{ github.workflow }}-${{ github.ref }}-1
    steps:
    - id: checkout
      name: Checkout 
      uses: actions/checkout@v3
    - id: test-2
      name: Test 2
      run: echo "Test 2"
  job-3:
    name: Concurrency Job Test 3
    runs-on: ubuntu-latest
    concurrency:
      group: ${{ github.workflow }}-${{ github.ref }}-2
    steps:
    - id: checkout
      name: Checkout 
      uses: actions/checkout@v3
    - id: test-3
      name: Test 3
      run: echo "Test 3"

 

job-1, job-2는 같은 그룹으로 매핑되어 있고 job-3는 별도의 group으로 매핑되어 있습니다. 따라서 job-3는 바로 수행되지만 job-1, 2는 동시 실행을 시도하게 되고, 그중 하나가 먼저 실행을 시작하고 나머지 하나는 앞선 job이 완료된 이후 수행됩니다. 의존성을 추가하고 싶다면 needs를 마찬가지로 추가해 줘서 순서를 지정할 수 있습니다.

 

권한

실행하고자 하는 행위가 Github이나 issue, discussion 등등이라면 해당 서비스에 접근하기 위해 권한이 필요로 합니다.

  • permission
    • action : github action 작업 수행 권한 설정
    • checks : 검사 실행
    • contents : repository 콘텐츠 접근 작업
    • discussions : discussion 접근
    • id-token : OIDC 토큰을 가져오는 작업
    • issues : issue 접근 작업
    • pages : pages 접근 작업
    • pull-requests : pr 관련 작업
    • security-events : github 코드 검사, dependabot 경고 관련 작업
  • 권한 설정
    • write : 쓰기
    • read : 읽기
    • none: 지정하지 않음. 기본적으로 permissions에 키를 지정하지 않으면 모두 none
permissions: 
  contents: read 
  pull-requests: write 
  issues: none

 

보안

secret

 

secret 탭에서는 보안에 관련된 변수들을 지정해 두고 사용할 수 있습니다.

name: Github Secrets
on: workflow_dispatch
jobs:
  build:
    name: Build and Test
    runs-on: ubuntu-latest
    permissions:
      contents: read
      actions: read
    steps:
    - name: Checkout 
      uses: actions/checkout@v2
    - name: Build project
      env:
        SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
      run: |
        echo "$SLACK_WEBHOOK_URL"

 

Github Action 토큰 인증

워크플로우(Workflow) 실행 시, GitHub 관련 서비스에 접근하여 작업을 처리해야 하는 경우 해당 서비스에 접근하기 위한 API인증절차가 필요합니다. GITHUB_TOKEN 변수를 사용하면 github action에서 토큰을 자동으로 생성해 주고 Job이 완료되거나 최대 24시간 내에 만료됩니다.

 

github_token은 다음과 같은 권한 설정이 가능합니다.

 

SCOPE 허용 제한
actions read/write none
checks read/write none
contents read/write none
deployments read/write none
id-token none none
issues read/write none
metadata read read
packages read/write none
pages read/write none
pull-requests read/write none
repository-projects read/write none
security-events read/write none
statuses read/write none

 

name: Github Token
on:
  - pull_request_target

jobs:
  triage:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      pull-requests: write
    steps:
      - uses: actions/labeler@v4
        with:
          repo-token: ${{ secrets.GITHUB_TOKEN }}

 

workflow 캐시

 

반복적으로 다운로드하게 되는 종속성을 가진 패키지를 매번 다운로드하는 대신, 캐시 영역에 저장 후 캐시 히트 시 저장된 파일을 바로 사용하게 하여 패키지 설치 과정을 최소화할 수 있습니다. 캐싱을 사용하려면 github action에서 제공해 주는 cache action을 사용해야 합니다. cache action은 unique 키를 통해 캐시 공간 생성 및 히트 여부를 확인합니다. 패키지 매니저 캐싱을 위해서는 setup-XX action을 설정해야 사용 가능합니다. 캐시 설정에는 다음과 같은 설정이 필요합니다.

  • key(필수) : 캐시 저장 시 캐싱 여부를 결정하는 키값으로 최대 512자
  • path(필수) : 캐시 히트 되었다고 판단 시, runner에 캐싱된 파일들을 복원하는 경로로 절대/상대 경로 모두 지정 가능
  • restore-keys(옵션) : 캐싱 키를 찾지 못할 경우, 차선으로 restore-keys 값에 선언된 순서대로 키를 추가 확인(키가 완전히 매치되지 않아도 prefix를 확인하며 추가로 히트 여부를 확인)
  • enableCrossOsArchive(옵션) : 다른 OS의 Runner에서 생성된 캐시라도 캐싱되면 사용 가능하도록 설정
  • cache-hit : cache에 설정된 키와 일치하여 히트 여부 확인하는 것으로 히트된 경우 true 값을 리턴합니다.
  • cache version : 캐시 된 데이터를 저장하는 데 사용된 압축툴과 디렉터리 경로를 조합하여 생성된 hash값으로 runner가 다른 OS인 경우 캐시 key가 동일해도 복원되지 않습니다.
name: Caching Dependencies
on: workflow_dispatch
jobs:
  build-using-cache:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Move JS Sample code
        run: |
          mv cache_sample/* ./
      - name: Setup Node
        uses: actions/setup-node@v3
        with:
          node-version: '18.x'
          registry-url: 'https://registry.npmjs.org/'
      - id: node-cache
        name: Cache node modules
        uses: actions/cache@v3
        env:
          cache-name: cache-node-modules
        # node_mudules 위치에 있는 것을 캐싱
        with:
          path: node_modules
          key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
          restore-keys: |
            ${{ runner.os }}-build-${{ env.cache-name }}-
            ${{ runner.os }}-build-
            ${{ runner.os }}-
      - name: Install dependencies
        # 캐시가 히트되지 않은 경우 install
        if: steps.node-cache.outputs.cache-hit != 'true'
        run: npm install
        # 캐시가 히트되었을때 잘 가져왔는지 확인
      - name: List packages from node modules
        continue-on-error: true
        run: npm list

 

캐시 사용 시 유의 사항

  • 7일 이상 액세스 되지 않은 캐시는 자동으로 삭제
  • 저장할 수 있는 캐시 수는 제한이 없으나 모든 캐시 총합은 최대 10GB
  • 저장소가 한계에 도달 시 가장 오래된 캐시부터 삭제 수행

'ETC' 카테고리의 다른 글

멀티 프로젝트 헥사고날 아키텍처로 구축하기  (0) 2024.07.03
Tomcat의 file upload (feat. octet-stream, multipart)  (0) 2023.12.31
가비지 컬렉터  (0) 2023.12.30
HTTP/HTTPS  (0) 2023.12.30
Blocking과 Non-Blocking, Sync와 Async  (0) 2023.12.30
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감/반응 부탁드립니다.