Skip to content
신선한 자몽 농장
Go back

Github CI/CD 구성

CI/CD 자동화가 필요했던 이유

여러 프로젝트를 운영하다 보니 배포 과정이 점점 번거로워졌다.

매번 수동으로 Docker 이미지를 빌드하고, 버전 태그를 달고, Docker Hub에 푸시하는 작업을 반복했다.

게다가 릴리즈 노트를 작성하고 GitHub Release를 생성하는 과정도 손이 많이 갔다.

이런 반복 작업을 자동화하기 위해 GitHub Actions를 활용한 CI/CD 파이프라인을 구성하게 되었다.


GitHub Actions Workflow 기본 구성

GitHub Actions는 .github/workflows/ 디렉토리에 YAML 파일로 워크플로우를 정의한다.

기본적인 구조는 다음과 같다.

name: Docker Build and Publish

on:
  push:
    tags:
      - 'v*.*.*'

env:
  DOCKER_IMAGE: username/image-name

jobs:
  build-and-push:
    runs-on: ubuntu-latest
    permissions:
      contents: write
      packages: write

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      # 이후 단계들...

트리거 설정

워크플로우 실행 조건을 정의하는 부분이다.

on:
  push:
    tags:
      - 'v*.*.*'  # v1.0.0, v2.1.3 등의 태그가 push될 때 실행

태그 기반으로 트리거를 설정하면 버전 관리가 명확해진다.

만약 브랜치 기반으로도 실행하고 싶다면 다음과 같이 설정할 수 있다.

on:
  push:
    branches: [ main ]
    tags: [ 'v*' ]
  workflow_dispatch:  # 수동 실행 허용

Docker 이미지 빌드 및 배포

이제 본격적으로 Docker 이미지를 빌드하고 배포하는 과정을 살펴보자.

1. 버전 정보 추출

태그에서 버전 정보를 추출한다.

- name: Extract version from tag
  id: version
  run: |
    VERSION=${GITHUB_REF#refs/tags/}
    echo "version=${VERSION}" >> $GITHUB_OUTPUT
    echo "Version: ${VERSION}"

GITHUB_REFrefs/tags/v1.0.0 형식이므로 앞부분을 제거해서 순수 버전 정보만 추출한다.

2. Docker Buildx 설정

멀티 플랫폼 빌드를 지원하는 Buildx를 설정한다.

- name: Set up Docker Buildx
  uses: docker/setup-buildx-action@v3

3. Docker Hub 로그인

Docker Hub에 이미지를 푸시하기 위해 로그인한다.

- name: Log in to Docker Hub
  uses: docker/login-action@v3
  with:
    username: ${{ secrets.DOCKER_USERNAME }}
    password: ${{ secrets.DOCKER_PASSWORD }}

DOCKER_USERNAMEDOCKER_PASSWORD는 GitHub Repository의 Settings > Secrets에서 설정해야 한다.

4. Docker 이미지 빌드 및 푸시

실제로 이미지를 빌드하고 푸시하는 단계다.

- name: Build and push Docker image
  uses: docker/build-push-action@v5
  with:
    context: .
    platforms: linux/amd64
    push: true
    tags: |
      ${{ env.DOCKER_IMAGE }}:${{ steps.version.outputs.version }}
      ${{ env.DOCKER_IMAGE }}:latest
    cache-from: type=gha
    cache-to: type=gha,mode=max

주요 옵션 설명:

5. 메타데이터 활용 (고급)

더 세밀한 태그 관리가 필요하다면 메타데이터 액션을 활용할 수 있다.

- name: Extract metadata
  id: meta
  uses: docker/metadata-action@v5
  with:
    images: ${{ env.DOCKER_IMAGE }}
    tags: |
      # main 브랜치 푸시 시 latest 태그
      type=raw,value=latest,enable={{is_default_branch}}
      # 태그 푸시 시 버전 태그 (예: v2.1.2 -> 2.1.2)
      type=semver,pattern={{version}}

- name: Build and push Docker image
  uses: docker/build-push-action@v5
  with:
    context: .
    push: true
    tags: ${{ steps.meta.outputs.tags }}
    labels: ${{ steps.meta.outputs.labels }}

CHANGELOG.md를 활용한 버전 관리

프로젝트의 변경 이력을 체계적으로 관리하기 위해 CHANGELOG.md 파일을 활용한다.

CHANGELOG.md 형식

# Changelog

## [1.1.5] - 2026-03-14

### Technical
- Alpine Linux 기반 Docker 이미지로 복원
- 디버깅 로그 제거

이렇게 버전별로 변경 사항을 명확히 기록해두면 릴리즈 노트 생성이 편리해진다.

CHANGELOG 자동 추출

특정 버전의 CHANGELOG만 추출하는 스크립트다.

- name: Extract CHANGELOG for this version
  id: changelog
  run: |
    VERSION=${{ steps.version.outputs.version }}
    # 해당 버전의 changelog 추출
    sed -n "/## \[${VERSION#v}\]/,/## \[/p" CHANGELOG.md | sed '$d' > release-notes.md
    # 비어있으면 기본 메시지 사용
    if [ ! -s release-notes.md ]; then
      echo "Release $VERSION" > release-notes.md
    fi

sed 명령어로 CHANGELOG.md에서 특정 버전 섹션만 추출한다.

예를 들어 v1.1.5 태그를 푸시하면 ## [1.1.5]부터 다음 ## [까지의 내용만 추출된다.


GitHub Release 생성

추출한 릴리즈 노트를 바탕으로 GitHub Release를 자동 생성한다.

릴리즈 에셋 준비

배포에 필요한 파일들을 준비한다.

- name: Prepare release assets
  run: |
    mkdir -p release-assets
    cp .env.example release-assets/.env.example
    cp docker-compose.yml release-assets/docker-compose.yml

Release 생성

- name: Create GitHub Release
  uses: softprops/action-gh-release@v1
  with:
    name: Release ${{ steps.version.outputs.version }}
    body_path: release-notes.md
    files: |
      release-assets/.env.example
      release-assets/docker-compose.yml
    draft: false
    prerelease: false
  env:
    GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

GITHUB_TOKEN은 자동으로 제공되므로 별도 설정이 필요 없다.

자동 릴리즈 노트 (간단한 방법)

CHANGELOG.md를 직접 관리하지 않고, GitHub의 자동 릴리즈 노트 기능을 활용할 수도 있다.

- name: Create GitHub Release
  uses: softprops/action-gh-release@v1
  with:
    generate_release_notes: true
    body: |
      ## Docker Images

      `docker pull username/image-name:${{ steps.version.outputs.VERSION }}`

      ## 배포 방법

      ```bash
      docker compose pull
      docker compose up -d
      ```

이 방식은 GitHub가 이전 릴리즈와의 커밋 차이를 자동으로 분석해서 릴리즈 노트를 생성해준다.


멀티 서비스 빌드 구성

Backend와 Frontend를 별도로 빌드해야 하는 경우도 있다.

env:
  BACKEND_IMAGE_NAME: username/backend
  FRONTEND_IMAGE_NAME: username/frontend

jobs:
  build-and-push-backend:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Build and push Backend
        uses: docker/build-push-action@v5
        with:
          context: ./backend
          file: ./backend/Dockerfile
          push: true
          tags: |
            ${{ env.BACKEND_IMAGE_NAME }}:${{ steps.version.outputs.version }}
            ${{ env.BACKEND_IMAGE_NAME }}:latest

  build-and-push-frontend:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Build and push Frontend
        uses: docker/build-push-action@v5
        with:
          context: ./frontend
          file: ./frontend/Dockerfile
          push: true
          tags: |
            ${{ env.FRONTEND_IMAGE_NAME }}:${{ steps.version.outputs.version }}
            ${{ env.FRONTEND_IMAGE_NAME }}:latest

  create-release:
    needs: [build-and-push-backend, build-and-push-frontend]
    runs-on: ubuntu-latest
    if: startsWith(github.ref, 'refs/tags/v')
    steps:
      # Release 생성 로직...

needs 키워드로 이전 job이 성공해야만 실행되도록 의존성을 설정할 수 있다.


배포 결과 요약

워크플로우 실행 결과를 깔끔하게 요약해서 보여주는 것도 좋다.

- name: Upload release artifacts summary
  run: |
    echo "### 🚀 Release Summary" >> $GITHUB_STEP_SUMMARY
    echo "" >> $GITHUB_STEP_SUMMARY
    echo "**Version:** ${{ steps.version.outputs.version }}" >> $GITHUB_STEP_SUMMARY
    echo "**Docker Image:** \`${{ env.DOCKER_IMAGE }}:${{ steps.version.outputs.version }}\`" >> $GITHUB_STEP_SUMMARY
    echo "" >> $GITHUB_STEP_SUMMARY
    echo "**Deployment Command:**" >> $GITHUB_STEP_SUMMARY
    echo "\`\`\`bash" >> $GITHUB_STEP_SUMMARY
    echo "docker compose pull" >> $GITHUB_STEP_SUMMARY
    echo "docker compose up -d" >> $GITHUB_STEP_SUMMARY
    echo "\`\`\`" >> $GITHUB_STEP_SUMMARY

이렇게 설정하면 GitHub Actions 실행 화면에 배포 정보가 깔끔하게 표시된다.


실제 사용 예시

이제 실제로 버전을 올리고 배포하는 과정을 살펴보자.

1. CHANGELOG.md 업데이트

## [1.2.0] - 2026-03-16

### Added
- 사용자 인증 기능 추가
- JWT 토큰 기반 인증 구현

### Fixed
- 로그인 시 세션 만료 문제 수정

2. 태그 생성 및 푸시

git add CHANGELOG.md
git commit -m "chore: release v1.2.0"
git tag v1.2.0
git push origin main
git push origin v1.2.0

3. 자동 실행

태그가 푸시되면 자동으로:

  1. Docker 이미지 빌드
  2. Docker Hub에 푸시 (v1.2.0, latest)
  3. CHANGELOG에서 릴리즈 노트 추출
  4. GitHub Release 생성
  5. 릴리즈 에셋 첨부

모든 과정이 자동으로 진행된다.


마무리

GitHub Actions를 활용한 CI/CD 구성으로 배포 과정이 훨씬 간편해졌다.

이제 태그만 푸시하면 모든 배포 과정이 자동으로 진행된다.

CHANGELOG.md를 통한 버전 관리도 체계적으로 할 수 있게 되어서 프로젝트 이력 관리가 명확해졌다.

처음에는 설정이 복잡해 보일 수 있지만, 한 번 구성해두면 반복 작업이 크게 줄어든다.

앞으로 더 많은 프로젝트에 CI/CD를 적용해볼 계획이다.



Previous Post
Keepalived VRRP를 사용한 소프트웨어 HA 환경 구성
Next Post
Nginx를 사용하여 도메인 연결 - 3편