Skip to content

[BE] Git Action 학습 정리

이승관 edited this page Dec 2, 2024 · 1 revision

Jenkins

장점

  • 유연성

    다양한 플러그인과 모듈을 통해 거의 모든 환경에 맞춰 설정할 수 있습니다. 필요에 따라 커스터마이징이 가능합니다.

  • 다양한 지원 언어

    여러 프로그래밍 언어와 빌드 도구를 지원하며, 거의 모든 언어에 대해 사용 가능한 플러그인이 존재합니다.

  • 독립성

    자체 서버에서 운영되기 때문에 외부 서비스에 의존하지 않고 독립적으로 실행할 수 있습니다.

  • 강력한 커뮤니티

    오랜 역사와 많은 사용자 기반으로 인해 풍부한 문서와 커뮤니티 지원이 존재합니다.

단점

  • 설정 복잡성

    초기 설정과 구성이 복잡할 수 있으며, Jenkins 서버 및 플러그인 관리가 번거롭습니다.

  • 자원 소모

    자체 서버를 운영해야 하므로 인프라 비용과 관리 부담이 큽니다.

  • 스케일링 문제

    대규모 프로젝트에서 확장성이 제한적일 수 있으며, 여러 노드에서 실행하려면 복잡한 설정이 필요합니다.

GitHub Actions

장점

  • 통합된 환경

    GitHub와 원활하게 통합되어 있으며, 레포지토리와 관련된 작업을 쉽게 설정할 수 있습니다. 코드 변경이 발생하면 자동으로 트리거됩니다.

  • 간단한 설정

    YAML 파일을 통해 간단히 설정할 수 있으며, UI에서 쉽게 수정할 수 있습니다.

  • 무료 사용

    GitHub의 무료 플랜에서 일정량의 실행 시간이 제공되어 소규모 프로젝트에 적합합니다.

  • 빠른 시작

    새로운 프로젝트를 시작할 때 즉시 사용할 수 있으며, 초기 설정이 필요 없습니다.

단점

  • 제한된 유연성

    Jenkins와 비교할 때 커스터마이징과 유연성이 제한적입니다. 기본 제공되는 Action 외에 커스터마이즈가 필요한 경우 적합하지 않을 수 있습니다.

  • GitHub에 의존

    GitHub 레포지토리와 연동하여만 사용할 수 있으며, 타깃이 GitHub이 아닐 경우 불리합니다.

  • 리소스 제한

    무료 플랜의 경우 시간 제한이 있어 대규모 프로젝트에는 비효율적일 수 있습니다.

결론

확장성을 고려하면 Jenkins를 선택하는 것이 좋아보입니다.

하지만 Github Actions만의 고유한 특징들이 너무 매력적입니다.

  • Github와 원할하게 통합되어 있고, repository와 관련된 작업을 쉽게 설정할 수 있습니다.
  • 간단한 yaml파일 설정을 통해 작성이 가능하고 무료로 사용이 가능합니다.
  • 초기 설정이 따로 필요하지 않아서 진행하고 있는 소규모 프로젝트에 적합하다고 판단했습니다.

단점을 살펴봤을 때 커스터 마이즈가 필요한 action이 필요할 경우 추후에 도구를 변경하는 판단을 하면 될 것 같습니다. 또한 리소스 제한 같은 경우에도 저희에게는 넉넉한 양이기 때문에 큰 문제가 되지않을 것 같습니다.

그렇기에 일단 Github Actions를 이용해서 CI/CD를 시작하는 것이 좋다고 생각했습니다.

Github Actions 설명서

on

트리거

on:
  push or pull_request:
    branches:
      - main
      - develop

push, pull_request같은 트리거를 작성하여 워크플로를 실행합니다.

schedule

on:
  schedule:
    - cron: "0 0 * * *"  # 매일 자정에 실행

설정한 일정에 따라 워크플로를 주기적으로 실행합니다.

issue, release

on:
  issues:
    types: [opened, closed]
  release:
    types: [published]

issue, release의 타입에 따라 워크플로를 실행할 수 있습니다.

workflow_dispatch

on:
  workflow_dispatch:
    inputs:
      environment:
        description: "Choose the environment to deploy to"
        required: true
        default: "staging"
      version:
        description: "Specify the version to deploy"
        required: false
        default: "latest"
  • 매개변수 설명
    • on: workflow_dispatch
      • 이 설정을 통해 워크플로가 수동으로 실행될 수 있음을 나타냅니다.
    • inputs
      • 수동으로 워크플로를 실행할 때 사용자에게 제공할 수 있는 입력값을 정의합니다.
    • input_name
      • 입력값의 이름을 정의합니다. 사용자가 입력할 수 있는 변수의 이름입니다.
    • description
      • 입력값에 대한 설명을 제공하는 문자열입니다. 사용자가 입력할 때 어떤 값을 넣어야 하는지를 안내합니다.
    • required
      • 입력값이 필수인지 여부를 지정합니다. true로 설정하면 사용자가 해당 입력값을 반드시 입력해야 합니다.
    • default
      • 입력값의 기본값을 설정합니다. 사용자가 값을 입력하지 않을 경우 이 값이 사용됩니다.

environment는 필수 입력값, 배포할 환경을 선택하는 것입니다. 기본값은 staging

version은 선택 입력값, 배포할 버전을 지정할 수 있습니다. 기본값을 latest입니다.

jobs

build

코드 빌드 및 컴파일을 처리하는 작업입니다. 주로 애플리케이션의 소스 코드를 빌드하고, 필요한 패키지를 설치하는 등의 단계가 포함됩니다.

  • build작업의 기본 단계

    환경 설정 → 종속성 설치 → 소스 코드 컴파일 → 자원 준비 → 테스트 실행 → 아티팩트 생성

    💡

    아티팩트란 ??

    아티팩트(artifact)란, 소프트웨어 개발 및 CI/CD(지속적 통합/지속적 배포) 과정에서 생성되는 모든 파일과 데이터를 의미합니다. 이에는 빌드 결과물, 테스트 결과, 로그 파일, 문서, 구성 파일 등이 포함될 수 있습니다. 아티팩트는 여러 가지 목적을 위해 사용됩니다:

    1. 빌드 결과물

    • 예시: 컴파일된 바이너리 파일, 패키지(예: .jar, .war, .zip 등).
    • 빌드 프로세스에서 생성된 최종 산출물로, 배포를 위해 사용됩니다.

    2. 테스트 결과

    • 예시: JUnit XML 파일, HTML 형식의 테스트 리포트.
    • 자동화된 테스트의 결과를 기록하여, 테스트가 성공했는지 실패했는지를 확인할 수 있게 합니다.

    3. 로그 파일

    • 예시: 빌드 또는 테스트 과정에서 생성된 로그.
    • 디버깅이나 모니터링을 위해 유용하게 사용됩니다.

    4. 문서

    • 예시: API 문서, 사용자 매뉴얼.
    • 소프트웨어에 대한 설명이나 사용 방법을 기록한 문서입니다.
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v2

      - name: Set up Node.js
        uses: actions/setup-node@v1
        with:
          node-version: '20.x'

      - name: Install dependencies
        run: npm install

      - name: Build project
        run: npm run build

test

코드의 테스트를 실행하는 작업입니다. 유닛 테스트, 통합 테스트 등을 수행하며, 코드의 품질을 검증하는 데 사용됩니다.

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v2

      - name: Set up Node.js
        uses: actions/setup-node@v1
        with:
          node-version: '20.x'

      - name: Install dependencies
        run: npm install

      - name: Run tests
        run: npm test

  build:
    runs-on: ubuntu-latest
    needs: test  # test job이 성공적으로 완료된 경우에만 실행
    steps:
      - name: Checkout code
        uses: actions/checkout@v2

      - name: Set up Node.js
        uses: actions/setup-node@v1
        with:
          node-version: '20.x'

      - name: Install dependencies
        run: npm install

      - name: Build the application
        run: npm run build 

위의 build에서도 test를 진행할 수는 있지만 각 작업의 역할을 분리하기 위해 test따로 두는 것이 좋아보입니다. 이런식으로 test가 성공적이라면 build를 진행하는 워크플로를 작성할 수 있습니다.

  • 테스트 보고서 생성방법
    1. 테스트 프레임 워크 설정

      npm install --save-dev jest jest-junit
    2. package.json 설정

      {
        "jest": {
          "reporters": [
            "default",
            [ "jest-junit", {
                "outputDirectory": "./reports/junit",
                "outputName": "test-results.xml"
              }]
          ]
        }
      }
    3. github actions 워크플로 설정

      - name: Upload test report
        uses: actions/upload-artifact@v2
        with:
        name: test-results
        path: ./reports/junit/test-results.xml
    4. 결과 확인

      테스트가 실행이 되면 test-result.xml 파일이 github actions의 아티팩트로 업로드됩니다.

    5. HTML 리포터로도 사용할 수 있습니다.(추가기능)

deploy

애플리케이션을 서버나 클라우드 서비스에 배포하는 작업입니다. 이 과정에서 Docker 이미지 빌드, 서버에 파일 전송, 환경 설정 등이 포함될 수 있습니다.

속성에 대한 설명

  • runs-on

    runs-on은 GitHub Actions에서 각 작업(job)이 실행될 환경을 지정하는 데 사용됩니다. 이 속성은 해당 작업이 실행될 운영 체제와 환경을 설정합니다.

  • name

    각 단계의 이름을 지정합니다. 이 이름은 워크플로우 실행 시 사용자에게 보여지며, 어떤 작업이 수행되고 있는지를 쉽게 이해할 수 있도록 도와줍니다.

  • uses

    다른 GitHub Actions 또는 액션을 사용할 때 지정합니다. 이 속성은 미리 정의된 액션을 참조하여 재사용할 수 있게 해줍니다. uses는 액션의 경로(예: 레포지토리와 태그)를 지정합니다.

    - uses: actions/checkout@v2

    위의 예시는 actions/checkout 액션의 v2 버전을 사용하여 코드 리포지토리를 체크아웃합니다.

  • with

    • uses로 지정된 액션에 인수를 전달합니다. 이 인수들은 액션의 동작을 구성하는 데 필요한 설정값들을 포함합니다.
    - uses: actions/setup-node@v1
      with:
        node-version: '20.x'

    위의 예시는 Node.js의 버전을 20.x로 설정하는 데 필요한 인수를 전달하고 있습니다.

  • run

    쉘 명령어나 스크립트를 실행하는 데 사용됩니다. 이 속성을 통해 사용자 정의 명령을 직접 입력할 수 있으며, 리포지토리에서 소스 코드를 빌드하거나 테스트하는 등의 작업을 수행할 수 있습니다.

deploy예시

deploy:
    runs-on: ubuntu-latest
    needs: build  # build 작업이 완료되어야 이 작업이 실행됨
    steps:
      - name: Checkout code
        uses: actions/checkout@v2

      - name: Login to DockerHub
        uses: docker/login-action@v1
        with:
          username: ${{ secrets.DOCKER_HUB_USERNAME }}
          password: ${{ secrets.DOCKER_HUB_PASSWORD }}

      - name: Deploy to server
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.SSH_HOST }}
          username: ${{ secrets.SSH_USERNAME }}
          password: ${{ secrets.SSH_PASSWORD }}
          key: ${{ secrets.SSH_KEY }}
          port: ${{ secrets.SSH_PORT }}
          script: |
            cd /app
            docker-compose down
            docker-compose up -d

lint

코드 스타일 검사 및 정적 분석을 수행하는 작업입니다. ESLint, Prettier와 같은 도구를 사용하여 코드 품질을 유지합니다.

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
			...(test, build와 동일한 패턴)
      - name: Run linter
        run: npm run lint

이 단계를 통해서 코드를 자동으로 검사하여 스타일과 문법 문제를 찾아내고, 개발자가 코드 품질을 향상시킬 수 있도록 지원합니다.

lint 보고서

- name: Upload Lint Report
  uses: actions/upload-artifact@v2
  with:
    name: lint-report
    path: path/to/lint-report-file

결과 요약: Lint 보고서에는 다음과 같은 정보가 포함될 수 있습니다.

  • 발견된 문제의 개수
  • 각 문제의 유형 및 심각도
  • 각 문제의 위치(파일명 및 줄 번호)
  • 권장 수정 사항

build_and_deploy

코드 빌드와 배포를 동시에 처리하는 작업입니다. builddeploy 단계를 연속으로 실행하여 빌드 후 바로 배포를 진행합니다.

build_and_deploybuilddeploy 작업을 결합한 것으로, 전체 CI/CD 파이프라인의 일부분을 구성합니다. 이 작업은 소스 코드를 빌드하고, 테스트한 후, 최종적으로 결과물을 배포하는 과정을 포함합니다.

구체적인 역할

  1. Build:
    • 애플리케이션의 소스 코드를 컴파일하고, 필요한 의존성을 설치하며, 테스트를 실행하는 단계입니다.
    • 이 단계에서 오류가 없으면 최종 제품을 만들기 위한 준비가 완료됩니다.
  2. Deploy:
    • 빌드가 완료된 애플리케이션을 실제 서버나 클라우드 환경에 배포하는 단계입니다.
    • 이는 컨테이너화된 이미지를 서버에 푸시하고, 새로운 버전의 애플리케이션을 실행하는 과정을 포함합니다.

결합의 장점

  • 효율성: 빌드와 배포를 한 번의 작업으로 처리하여 시간을 절약하고, 여러 단계를 동시에 관리할 수 있습니다.
  • 일관성: 동일한 워크플로우를 통해 빌드와 배포가 이루어지므로, 배포 과정에서의 일관성을 유지할 수 있습니다.
  • 간편한 관리: CI/CD 파이프라인을 관리하는 측면에서 빌드와 배포를 하나의 작업으로 묶으면 관리가 용이해집니다.

cleanup

CI/CD 프로세스에서 생성된 임시 파일이나 컨테이너를 정리하는 작업입니다. 필요 없는 파일을 삭제하거나, 종료된 Docker 컨테이너를 정리하는 등의 역할을 합니다.

주요 역할 및 기능

  1. 임시 파일 삭제:
    • 빌드 및 테스트 과정에서 생성된 임시 파일, 캐시, 로그 파일 등을 삭제하여 디스크 공간을 확보합니다.
  2. 종속성 정리:
    • 특정 작업이 완료된 후 필요 없는 라이브러리나 종속성을 정리합니다. 이는 프로젝트의 경량화를 도와줍니다.
  3. Docker 이미지 및 컨테이너 정리:
    • Docker를 사용하는 경우, 더 이상 필요하지 않은 이미지와 컨테이너를 삭제하여 Docker 환경을 정리합니다.
  4. 리소스 해제:
    • 클라우드 리소스를 사용하는 경우, 사용이 끝난 리소스(예: 테스트 서버, 데이터베이스 등)를 종료하거나 삭제하여 비용을 절감합니다.
  5. 상태 초기화:
    • 빌드 환경을 초기 상태로 되돌려 다음 빌드를 위해 준비된 상태를 유지합니다.
jobs:
  cleanup:
    runs-on: ubuntu-latest
    steps:
      - name: Remove temporary files
        run: |
          rm -rf ./temp/*
      
      - name: Cleanup Docker images
        run: |
          docker system prune -af

예외처리

(작성해야함)

워크 플로의 기본적인 순서

  1. 환경 설정 체크
  2. 의존성 설치
  3. 코드 빌드
  4. 테스트 실행
  5. 릴리스 또는 배포
  6. 클린업(정리)
💡

테스트보다 왜 빌드를 먼저할까요??

빌드 후 테스트

  • 장점:
    • 빨리 피드백: 빌드가 성공하면 즉시 테스트를 진행할 수 있어, 문제가 있는 코드가 쉽게 추적됩니다.
    • 의존성 문제: 빌드가 먼저 완료되면, 그 후의 테스트에서 의존성 문제나 코드 결함을 확인할 수 있습니다.
  • 단점:
    • 자원 낭비: 빌드가 실패해도 불필요하게 빌드하는 과정이 발생할 수 있습니다.

테스트 후 빌드

  • 장점:
    • 자원 효율성: 테스트가 통과해야만 빌드가 진행되므로, 실패한 코드에 대한 자원 낭비를 줄일 수 있습니다.
    • 빠른 피드백: 코드가 빌드되지 않더라도, 개발자는 테스트 결과를 통해 코드의 품질을 즉시 확인할 수 있습니다.
  • 단점:
    • 복잡한 설정: 일부 경우에서는 테스트가 빌드에 의존할 수 있어, 환경 구성이 더 복잡해질 수 있습니다.
on:
  pull_request:
    branches:
      - dev-be
jobs:
	build:
    runs-on: ubuntu-latest
    steps:
    - name: Checkout repository
	    run: actions/checkout@v2  
		- name: Docker Hub login
			uses: docker/login-action@v1
      with:
        username: ${{ secrets.DOCKER_HUB_USERNAME }}
        password: ${{ secrets.DOCKER_HUB_TOKEN }}
    - name: Build and Push Docker images
      run: |
        docker-compose -f ./docker-compose.yml build
        docker-compose -f ./docker-compose.yml push
    - name: send files & deploy script
      uses: appleboy/scp-action@master
      with:
        host: ${{ secrets.SSH_HOST }}
        username: ${{ secrets.SSH_USERNAME }}
        password: ${{ secrets.SSH_PASSWORD }}
        port: ${{ secrets.SSH_PORT }}
        source : ${{ secrets.DOCKER_IMAGE }}
        target: /corinee
        overwrite: true
    - name: docker run
	      uses: appleboy/ssh-action@master
	      host: ${{ secrets.SSH_HOST }}
        username: ${{ secrets.SSH_USERNAME }}
        password: ${{ secrets.SSH_PASSWORD }}
        port: ${{ secrets.SSH_PORT }}
          script: |
            docker pull ${{ secrets.DOCKER_HUB_USERNAME }}/corinee-server}
            docker pull ${{ secrets.DOCKER_HUB_USERNAME }}/corinee-client}
            docker-compose down
            docker-compose up -d
            rm -rf docker-compose.yml
      - name: Generate Error Report
        if: failure()  
        run: |
          echo "Deployment Report" > report.txt
          echo "===================" >> report.txt
          echo "Commit SHA: ${{ github.sha }}" >> report.txt
          echo "Branch: ${{ github.ref }}" >> report.txt
          echo "Deployment Status: Failed" >> report.txt
          echo "Error Details: ${{ job.status }}" >> report.txt  # 오류 상태 추가
          echo "===================" >> report.txt
          cat report.txt

      - name: Upload Error Report
        if: failure()  
        uses: actions/upload-artifact@v2
        with:
          name: deployment-error-report
          path: report.txt

  - name: Deploy with SSH
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.SSH_HOST }}
          username: ${{ secrets.SSH_USERNAME }}
          password: ${{ secrets.SSH_PASSWORD }}
          key: ${{ secrets.SSH_KEY }}
          port: ${{ secrets.SSH_PORT }}
          script: |
            cd /app
            docker pull ${{ secrets.DOCKER_HUB_USERNAME }}/web16-b1g1-fe:${{ github.sha }}
            docker pull ${{ secrets.DOCKER_HUB_USERNAME }}/web16-b1g1-be:${{ github.sha }}
            docker stop $(docker ps -a -q)
            docker rm $(docker ps -a -q)
            docker-compose up -d
            rm -rf .env docker-compose.yml
  • Checkout repository
  • docker hub 로그인
  • docker build
  • ssh 접속 후 이미지 전송
  • docker run

main.yml

name: Build and Deploy

on:
  push:
    branches:
      - feature-be-10

jobs:
  build_and_deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4  

      - name: Set up Node.js
        uses: actions/setup-node@v1
        with:
          node-version: '20.x'

      - name: Install yarn
        run: npm install -g yarn
          
      - name: Docker Hub login
        uses: docker/login-action@v1
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_PASSWORD }}

      - name: Build and Push Docker images
        run: |
          docker build -t ${{ secrets.DOCKERHUB_USERNAME }}/corinee-server -f ./dockerfile-server .
          docker push ${{ secrets.DOCKERHUB_USERNAME }}/corinee-server
          docker build -t ${{ secrets.DOCKERHUB_USERNAME }}/corinee-client -f ./dockerfile-client .
          docker push ${{ secrets.DOCKERHUB_USERNAME }}/corinee-client

      - name: Send files & deploy script
        uses: appleboy/scp-action@master
        with:
          host: ${{ secrets.SSH_HOST }}
          username: ${{ secrets.SSH_USERNAME }}
          password: ${{ secrets.SSH_PASSWORD }}
          port: ${{ secrets.SSH_PORT }}
          source: ${{ secrets.DOCKER_IMAGE }}
          target: /corinee
          overwrite: true

      - name: Docker run
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.SSH_HOST }}
          username: ${{ secrets.SSH_USERNAME }}
          password: ${{ secrets.SSH_PASSWORD }}
          port: ${{ secrets.SSH_PORT }}
          script: |
            docker pull ${{ secrets.DOCKERHUB_USERNAME }}/corinee-server
            docker pull ${{ secrets.DOCKERHUB_USERNAME }}/corinee-client
            cd /corinee
            docker-compose down
            docker-compose up -d
            

      - name: Generate Error Report
        if: failure()  
        run: |
          echo "Deployment Report" > report.txt
          echo "===================" >> report.txt
          echo "Commit SHA: ${{ github.sha }}" >> report.txt
          echo "Branch: ${{ github.ref }}" >> report.txt
          echo "Deployment Status: Failed" >> report.txt
          echo "Error Details: ${{ job.status }}" >> report.txt
          echo "===================" >> report.txt
          cat report.txt

      - name: Upload Error Report
        if: failure()  
        uses: actions/upload-artifact@v3
        with:
          name: deployment-error-report
          path: report.txt

docker-compose.yml

services:
  # db:
  #   image: mysql:5.7
  #   environment:
  #     MYSQL_ROOT_PASSWORD: root
  #     MYSQL_DATABASE: mydb
  #     MYSQL_USER: user
  #     MYSQL_PASSWORD: password
  #   volumes:
  #     - db-data:/var/lib/mysql
  #   networks:
  #     - app-network

  server:
    build:
      context: .
      dockerfile: dockerfile-server
    image: seunggwan/corinee-server
    ports:
      - "3000:3000"
    # environment:
    #   # DB_HOST: db
    #   # DB_USER: user
    #   # DB_PASSWORD: password
    #   # DB_DATABASE: mydb
    networks:
      - app-network

  client:
    build:
      context: .
      dockerfile: dockerfile-client
    image: seunggwan/corinee-client
    ports:
      - "80:80"
    depends_on:
      - server
    networks:
      - app-network

volumes:
  db-data:

networks:
  app-network:

💻 개발 일지

💻 공통

💻 FE

💻 BE

🙋‍♂️ 소개

📒 문서

☀️ 데일리 스크럼

🤝🏼 회의록

Clone this wiki locally