github-actions
GitHub Actions CI/CD 파이프라인 생성, 관리, 디버깅 — 워크플로우 작성부터 비밀관리, 자동 배포, 실패 트러블슈팅까지
GitHub Actions CI/CD 자동화
GitHub Actions 워크플로우를 생성, 수정, 디버깅하는 스킬. PR 생성→테스트→빌드→배포 전체 파이프라인을 다룹니다.
전제 조건
- GitHub 인증 완료 (github-auth 스킬 참고)
- 대상 레포에 push 권한
1. 워크플로우 기본 구조
모든 워크플로우는 .github/workflows/ 아래 YAML 파일로 작성:
name: <워크플로우 이름>on:
push:
branches: [main, develop]
pull_request:
branches: [main]
# 수동 실행도 가능하게
workflow_dispatch:
jobs:
job-name:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# ... 추가 스텝
2. 주요 트리거 이벤트
on:
push:
branches: [main]
paths: ['src/', 'tests/'] # 특정 경로 변경 시만
pull_request:
types: [opened, synchronize, reopened]
schedule:
- cron: '0 9 1' # 매주 월요일 09:00 UTC
workflow_dispatch: # 수동 실행
workflow_call: # 재사용 가능한 워크플로우
3. 자주 쓰는 워크플로우 템플릿
3.1 파이썬 테스트 + 린트
name: Python CIon:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ['3.10', '3.11', '3.12']
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install pytest ruff
- name: Lint with ruff
run: ruff check .
- name: Run tests
run: pytest tests/ -v --tb=short
- name: Upload coverage
if: matrix.python-version == '3.12'
uses: actions/upload-artifact@v4
with:
name: coverage-report
path: htmlcov/
3.2 Node.js 빌드 + 테스트
name: Node.js CIon:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build-and-test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18, 20]
steps:
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run linter
run: npm run lint
- name: Run tests
run: npm test
- name: Build
run: npm run build
3.3 Docker 이미지 빌드 + 푸시
name: Docker Build & Pushon:
push:
branches: [main]
tags: ['v*']
jobs:
docker:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository }}
tags: |
type=ref,event=branch
type=semver,pattern={{version}}
type=sha
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
3.4 Vercel 자동 배포
name: Deploy to Vercelon:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Deploy to Vercel (Production)
uses: amondnet/vercel-action@v25
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
vercel-args: '--prod'
3.5 릴리즈 자동화 (태그 기반)
name: Auto Releaseon:
push:
tags: ['v*']
jobs:
release:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Generate changelog
id: changelog
run: |
PREV_TAG=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "")
if [ -n "$PREV_TAG" ]; then
CHANGES=$(git log ${PREV_TAG}..HEAD --pretty=format:"- %s (%h)" --no-merges)
else
CHANGES=$(git log --pretty=format:"- %s (%h)" --no-merges -20)
fi
echo "changes<> $GITHUB_OUTPUT
echo "$CHANGES" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
body: |
## Changes
${{ steps.changelog.outputs.changes }}
generate_release_notes: true
4. Secrets & 환경변수 관리
Secrets 설정 (CLI)
# 시크릿 추가 (대화형으로 값 입력)
gh secret set SECRET_NAME --repo owner/repo파일에서 시크릿 읽기
gh secret set SECRET_NAME --repo owner/repo < secret.txt환경별 시크릿
gh secret set SECRET_NAME --repo owner/repo --env production시크릿 목록 확인
gh secret list --repo owner/repo시크릿 삭제
gh secret delete SECRET_NAME --repo owner/repo
환경변수 (비민감 데이터)
# 리포 수준 환경변수
gh variable set VAR_NAME --repo owner/repo --body "value"환경별
gh variable set VAR_NAME --repo owner/repo --env production --body "value"
워크플로우에서 Secrets 사용
env:
API_KEY: ${{ secrets.API_KEY }}
# GitHub 자동 제공
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
5. 캐싱 전략
# pip 캐시
- uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-npm 캐시 (setup-node에 내장)
- uses: actions/setup-node@v4
with:
cache: 'npm'Docker 레이어 캐시
- uses: docker/build-push-action@v5
with:
cache-from: type=gha
cache-to: type=gha,mode=max
6. 워크플로우 실행 관리
# 실행 목록 확인
gh run list --repo owner/repo --limit 10특정 실행 상세 보기
gh run view --repo owner/repo실패한 실행 로그 보기
gh run view --repo owner/repo --log-failed수동 실행 트리거
gh workflow run --repo owner/repo특정 브랜치로 수동 실행
gh workflow run --repo owner/repo -f branch=develop실행 재시도
gh run rerun --repo owner/repo실행 취소
gh run cancel --repo owner/repo아티팩트 다운로드
gh run download --repo owner/repo --name artifact-name
7. 매트릭스 & 병렬 실행
jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
version: [1.18, 1.19, 1.20]
exclude:
- os: windows-latest
version: 1.18
include:
- os: ubuntu-latest
version: 1.21
experimental: true fail-fast: false # 하나 실패해도 나머지 계속
steps:
- run: echo "Testing ${{ matrix.os }} with version ${{ matrix.version }}"
8. 재사용 가능한 워크플로우
호출부 (caller.yml)
name: CI Pipeline
on:
push:
branches: [main]jobs:
test:
uses: ./.github/workflows/reusable-test.yml
with:
python-version: '3.12'
secrets: inherit
재사용부 (reusable-test.yml)
name: Reusable Test
on:
workflow_call:
inputs:
python-version:
required: false
type: string
default: '3.11'
secrets:
API_KEY:
required: falsejobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ inputs.python-version }}
- run: pytest
9. 트러블슈팅
실패 디버깅 흐름
gh run list로 최근 실행 확인gh run view로 실패 로그 읽기--log-failed - 원인 파악 후 코드 수정
git push로 재트리거 또는gh run rerun
자주 발생하는 문제
| 문제 | 원인 | 해결 |
|------|------|------|
| permission denied | GITHUB_TOKEN 권한 부족 | permissions: 필드 추가 |
| timeout | 기본 6시간 초과 | timeout-minutes: 설정 |
| out of disk | 캐시/아티팩트 과다 | actions/cache 정리 |
| rate limit | API 호출 과다 | 캐싱 또는 토큰 로테이션 |
| flaky test | 비결정적 테스트 | retry 액션 사용 또는 테스트 안정화 |
Tmate 디버깅 (SSH 접속)
steps:
- name: Setup tmate session
if: ${{ failure() }}
uses: mxschmitt/action-tmate@v3
with:
limit-access-to-actor: true
timeout-minutes: 15
10. 모범 사례
- 최신 액션 버전 사용 —
@v4이상, 해시 SHA 고정 권장 - 최소 권한 —
permissions:필드로 필요한 권한만 부여 - 핀 액션 — 태그 대신 커밋 SHA 사용:
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 - 캐싱 활용 — 빌드 시간 단축
- fail-fast: false — 매트릭스에서 하나 실패해도 전체 결과 확인
- 환경 분리 — production/staging 환경별 secrets 분리
- 워크플로우 파일명 — 의미있는 이름 사용 (예:
ci-python.yml,deploy-prod.yml)
11. 유용한 서드파티 액션
| 액션 | 용도 |
|------|------|
| actions/checkout@v4 | 레포 체크아웃 |
| actions/setup-python@v5 | 파이썬 설정 |
| actions/setup-node@v4 | Node.js 설정 |
| actions/cache@v4 | 파일 캐싱 |
| actions/upload-artifact@v4 | 빌드 산출물 업로드 |
| docker/build-push-action@v5 | Docker 빌드+푸시 |
| softprops/action-gh-release@v2 | GitHub 릴리즈 생성 |
| peaceiris/actions-gh-pages@v4 | GitHub Pages 배포 |
| guibranco/github-status-action-v2 | 커스텀 상태 체크 |
| mxschmitt/action-tmate@v3 | SSH 디버깅 세션 |