본문 바로가기
CI·CD

[CI/CD] GitHub Actions CI/CD Workflow

by okms1017 2024. 12. 15.
728x90

✍ Posted by Immersive Builder  Seong

 

 

1. What is GitHub Actions 

GitHub Actions 란?

GitHub Actions는 GitHub 에서 제공하는 서버리스 CI/CD 자동화 도구입니다. GitHub Actions를 사용하여 레포지토리에서 바로 소프트웨어 개발 워크플로우를 자동화, 사용자 지정 및 실행합니다. CI/CD를 포함하여 원하는 작업을 수행하기 위한 작업을 검색, 생성 및 공유하고 완전히 사용자 정의된 워크플로우에서 작업을 결합할 수 있습니다. 

 

GitHub에서 워크플로우를 실행하기 위한 Linux, Windows, macOS 기반의 가상 머신을 제공하거나, 사용자 고유의 데이터 센터 또는 클라우드 인프라에서 셀프 호스티드 실행기를 호스트할 수 있습니다. 

 

▶ GitHub Actions 공식문서 : https://docs.github.com/ko/actions

 

GitHub Actions 설명서 - GitHub Docs

GitHub Actions를 사용하여 리포지토리에서 바로 소프트웨어 개발 워크플로를 자동화, 사용자 지정 및 실행합니다. CI/CD를 포함하여 원하는 작업을 수행하기 위한 작업을 검색, 생성 및 공유하고 완

docs.github.com

 


구성 요소 

GitHub Actions의 구성 요소로는 Workflow, Event, Runner, Job, Step, Action 등이 있습니다. 

 

GitHub Actions Workflows

 

  • Workflow : 자동화된 작업의 집합으로 하나의 레포지토리 내에 여러 워크플로우를 정의할 수 있습니다. 
  • Event : Push/PR, 이슈 생성/댓글 작성 등 워크플로우를 트리거하는 모든 이벤트를 지칭합니다. 
  • Runner : 각 작업을 실행하는 자체 가상 머신 실행기 또는 컨테이너를 의미합니다. 
  • Job : 워크플로우 내에서 독립적으로 실행되는 작업 단위로서 하나 이상의 단계(Step)로 구성됩니다. Job을 여러 개 구성하여 병렬로 실행이 가능합니다. 
  • Step : 작업(Job) 내에서 실행되는 명령어, 스크립트 또는 액션(Action)을 포함합니다. 
  • Action : 자주 반복되는 작업을 수행하는 사용자 지정 어플리케이션으로 반복 코드의 양을 줄일 수 있습니다. 

 


기본 문법

GitHub Actions 워크플로우는 YAML 형식을 사용하여 작성합니다. 워크플로우 정의 파일이 위치하는 기본 경로는 .github/workflows이며, 해당 경로에서 GitHub Actions가 파일을 인식하여 워크플로우를 실행하게 됩니다. 

 

$ mkdir -p .github/workflows/

$ touch .github/workflows/my-workflow.yaml

 

아래는 워크플로우 정의 파일을 작성한 예시입니다. 

 

name: Hello World    # Workflow 이름 (Github Actions 웹 사이드바에 표시됨)
on:
  workflow_dispatch:
  push:

jobs:
  build:             # Job 이름
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Hello World         # Step 이름
        run: echo "Hello World"
      - name: Print Env
        run: env

 

  • on: 워크플로우를 트리거하는 이벤트 목록입니다.  
    • workflow_dispatch: GitHub 웹 페이지에서 수동으로 워크플로우 실행이 가능합니다. 
    • push: 저장소에 코드를 푸시할 때마다 워크플로우를 실행합니다. (scope 범위 설정 가능)
  • jobs: 작업의 목록을 나타냅니다. ex) build, deploy 
  • runs-on: GitHub에서 제공하는 가상 머신의 OS를 지정합니다.  
    • ubuntu-latest:  해당 가상 머신에는 빌드와 관련된 환경변수, 툴, 패키지 등이 설치 및 설정되어 있습니다. 
  • steps: 실행하고자 하는 단계와 코드를 정의합니다. 
    • uses: 사용하고자 하는 액션을 지정합니다. ex) actions/checkout은 내장된 액션으로 코드를 체크아웃함
    • name: 각 단계(step)의 이름을 지정합니다. (옵션)
    • run: 명령어를 실행합니다. ex) echo "timestamp", "python3 my_script.py", env  

 


2. GitHub Actions CI/CD Workflow

GitHub Actions Exercise 1 : Basic 

GitHub Actions를 이용하여 간단한 CI/CD 자동화 작업을 실행해보겠습니다. 로컬 환경과 GitHub 레포지토리가 연결되어 있고, 대상 서버가 배포된 상태에서 시작합니다. 우선 로컬 환경에서 소스 코드를 푸시할 때 GitHub Actions를 트리거하여 워크플로우를 실행할 수 있도록 .github/workflows 디렉토리 하위 경로에 아래와 같이 정의 파일을 작성합니다. 어플리케이션을 배포할 대상 서버에 SSH로 접속하여 최신 소스 코드를 가져오고, 웹 서비스를 실행하는 아주 간단한 워크플로우입니다. 

 

  • .github/workflows/deploy.yaml

 

그리고 GitHub Actions가 트리거되면 러너가 자동으로 실행되는데, 이 러너가 작업을 수행하려면 대상 서버에 접근할 수 있어야 합니다. 이 때 SSH Private Key, Public IP와 같은 접속 정보는 GitHub Actions 실행 로그나 GitHub 레포지토리에 노출되면 보안상 취약하므로 시크릿으로 생성합니다. 시크릿은 워크플로우 파일에서 ${{ secrets.SSH_PRIVATE_KEY }}, ${{ secrets.EC2_PIP }}와 같은 형식으로 사용됩니다. 

 

 

이제 로컬에서 소스 코드와 함께 워크플로우 정의 파일(.github/workflows/deploy.yaml

)을 커밋/푸시하여 GitHub Actions의 동작 과정을 확인합니다.  

 

 

이벤트에 의해 GitHub Actions가 트리거되어, CICD-1 워크플로우가 자동으로 실행됩니다. 워크플로우 정의 파일에 명시한 단계에 따라 작업(deploy)이 순차적으로 수행됩니다. 

 

1. CICD-1 워크플로우 생성
2. deploy 작업 생성
3. Set up job
4. Configure the SSH Private Key Secret
5. Set Strict Host Key Checking
6. Git Pull
7. Run service & Complete job

 

대상 서버에 접속하여 웹 서비스가 실행중인지 확인합니다. 

 

웹 서비스 배포 확인

 

소스 코드(server.py)를 "CICD-1-2" 문구가 출력되도록 업데이트합니다. 그리고 워크플로우명을 CICD-2로, 작업명을 deploy-2로 변경하여 다시 워크플로우를 실행합니다. 

 

server.py 업데이트
deploy.yaml 업데이트

 

아래와 같이 변경 사항이 잘 반영된 것을 확인할 수 있습니다. 

 

웹 서비스 재배포 확인

 


GitHub Actions Exercise 2 :  GitHub Actions(Code/Build/Test) + Extensions

GitHub Actions 러너가 '코드 - 빌드 - 테스트 - 배포' 등 모든 작업을 대상 서버에서 수행하게 되면 대상 서버에 부하가 발생할 수 있습니다. 따라서 GitHub Actions 러너에서 '코드 - 빌드 - 테스트' 작업을 수행하고, 완성된 아티팩트만 대상 서버에 전달하여 배포하는 방식으로 구성하면 대상 서버는 오로지 어플리케이션만 서비스할 수 있습니다.  

GitHub Actions CI/CD Workflow

 

러너에서 빌드와 관련된 제반 작업을 수행하고 나서 어플리케이션 배포에 필요한 아티팩트만 추출합니다. 배포 단계에서 대상 서버로 아티팩트를 원격으로 전달하기 위해 SCP Extension을 사용할 수 있습니다. Extension은 단순히 스크립트만으로 구현하기 어려운 기능들을 액션으로 구현한 것으로 GitHub Marketplace에서 다양한 Extensions를 제공하고 있습니다. 그 중에 SCP Files는 SSH 프로토콜을 통해 파일이나 아티팩트를 원격으로 전송하는 액션입니다. 

 

▶ GitHub Marketplcae : https://github.com/marketplace

 

▶ SCP Files : https://github.com/appleboy/scp-action

 

GitHub - appleboy/scp-action: GitHub Action that copy files and artifacts via SSH.

GitHub Action that copy files and artifacts via SSH. - appleboy/scp-action

github.com

 

여기서는 server.py 파일이 최종 아티팩트라고 가정하고, SCP Files를 이용하여 파일을 대상 서버로 전달할 것입니다. 저는 개발자가 아니므로 빌드, 테스트 단계는 생략하고 구성 방식에 집중하겠습니다. '이런 방식으로 구성하면 이렇게 동작하는구나' 정도로 이해하시면 충분합니다. 설정한 워크플로우는 아래와 같습니다. 

 

 

  • name: copy file via ssh
  • uses: appleboy/scp-action@v0.1.7 액션을 사용합니다. 
  • with:
    • source: 소스 파일을 지정합니다. 
    • target: 타켓 디렉토리 경로를 지정합니다. 

 

로컬에서 server.py 파일의 문자열을 "SCP Test"로 업데이트합니다. 그리고 워크플로우를 실행한 후 대상 서버에 접속하여 변경된 파일이 전송된 것을 확인합니다. 

 

Server.py - "SCP Test"
GitHub Action - Workflow Log

 

파일 전송 확인

 

이번에는 변경된 파일을 전송하고 웹 서비스를 재기동하는 작업이 포함된 워크플로우를 작성합니다. 

 

 

마찬가지로 소스 코드를 업데이트하고 워크플로우를 실행합니다. 

 

Server.py - "SCP & Restart Web Service"

 

정상적으로 업데이트한 내용이 반영되어 웹 서비스가 실행되는 것을 확인할 수 있습니다.  

 

 


GitHub Actions with Ansible

워크플로우 정의 파일은 스크립트 방식으로 실행되기 때문에 동일한 작업이 중복되어 수행될 가능성이 있습니다. 이 때, 앤서블을 함께 사용하면 선언적으로 형태를 관리할 수 있습니다. 앤서블을 설치하여 러너가 앤서블 컨트롤러로서 동작하도록 합니다. 아래는 앤서블을 GitHub Actions와 함께 사용한 예시입니다. 

 

name: Run Ansible
on:
  workflow_dispatch:
  push:
    branches:
      - main

jobs:
  run-playbooks:
    runs-on: ubuntu-latest
    steps:
      - name: Github Repository Checkout
        uses: actions/checkout@v4

      - name: Setup Python 3
        uses: actions/setup-python@v5
        with:
          python-version: "3.8"

      - name: Upgrade Pip & Install Ansible
        run: |
          python -m pip install --upgrade pip
          python -m pip install ansible

      - name: Implement the Private SSH Key
        run: |
          mkdir -p ~/.ssh/
          echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
          chmod 600 ~/.ssh/id_rsa

      - name: Ansible Inventory File for Remote host
        run: |
          mkdir -p ./devops/ansible/
          export INVENTORY_FILE=./devops/ansible/inventory.ini
          echo "[my_host_group]" > $INVENTORY_FILE
          echo "${{ secrets.EC2_PIP }}" >> $INVENTORY_FILE

      - name: Ansible Default Configuration File
        run: |
          mkdir -p ./devops/ansible/
          cat <<EOF > ./devops/ansible/ansible.cfg
          [defaults]
          ansible_python_interpreter = '/usr/bin/python3'
          ansible_ssh_private_key_file = ~/.ssh/id_rsa
          remote_user = ubuntu
          inventory = ./inventory.ini
          host_key_checking = False
          EOF

      - name: Ping Ansible Hosts
        working-directory: ./devops/ansible/
        run: |
          ansible all -m ping

 

  • 실행 결과

 

 


[출처]

1) CloudNet@, CI/CD Study

728x90