1. CI/CD란?
CI/CD는 코드 변경 사항을 빠르고 안전하게 프로덕션까지 전달하기 위한 자동화 프랙티스입니다. 사람이 수동으로 빌드·테스트·배포하는 과정을 파이프라인으로 대체합니다.
| 용어 | 의미 |
|---|---|
| CI (Continuous Integration) | 코드를 자주 통합하고, 커밋마다 자동으로 빌드·테스트하여 문제를 조기 발견 |
| CD (Continuous Delivery) | 테스트를 통과한 빌드를 언제든 배포 가능한 상태로 유지 (배포는 수동 승인) |
| CD (Continuous Deployment) | 승인 없이 통과된 빌드를 자동으로 프로덕션까지 배포 |
| Pipeline | CI/CD의 각 단계를 정의한 자동화 워크플로우 |
2. 파이프라인 단계
전형적인 파이프라인은 아래 단계를 순서대로 거치며, 각 단계가 실패하면 다음 단계로 진행하지 않습니다.
| 단계 | 내용 |
|---|---|
| Lint / Format | 정적 분석으로 코드 스타일·잠재적 버그 검사 |
| Build | 소스 컴파일, 의존성 설치, 아티팩트 생성 |
| Test | 단위/통합 테스트 실행, 커버리지 측정 |
| Package | Docker 이미지 빌드 등 배포 가능한 산출물 생성 |
| Deploy | 스테이징 → 프로덕션 순으로 배포 |
| Verify | 헬스체크, 스모크 테스트로 배포 성공 확인 |
3. GitHub Actions 기초
# .github/workflows/ci.yml
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- run: npm run buildyaml
| 키워드 | 의미 |
|---|---|
on | 워크플로우를 트리거하는 이벤트 (push, pull_request, schedule 등) |
jobs | 병렬로 실행되는 작업 단위. 기본적으로 독립된 러너에서 실행 |
steps | Job 내에서 순차 실행되는 명령어/액션 목록 |
uses | 재사용 가능한 Action(마켓플레이스 또는 자체 제작) 호출 |
secrets | Repository/Organization Settings에 등록한 민감 값 참조 |
4. 빌드 & 테스트 파이프라인
name: Test
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18, 20, 22] # 여러 버전에서 동시 테스트
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- run: npm ci
- run: npm test -- --coverage
- uses: actions/upload-artifact@v4
with:
name: coverage-${{ matrix.node-version }}
path: coverage/
if: always()yaml
strategy.matrix는 Job을 조합별로 병렬 실행합니다. Node 버전 호환성 검증, OS별 테스트(ubuntu/windows/macos) 등에 유용합니다.5. Docker 이미지 빌드 & 레지스트리 푸시
name: Build & Push
on:
push:
branches: [main]
tags: ['v*']
jobs:
docker:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write # GHCR 푸시 권한
steps:
- uses: actions/checkout@v4
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/build-push-action@v6
with:
context: .
push: true
tags: |
ghcr.io/${{ github.repository }}:${{ github.sha }}
ghcr.io/${{ github.repository }}:latest
cache-from: type=gha
cache-to: type=gha,mode=maxyaml
Dockerfile 작성 규칙과 멀티스테이지 빌드는 Docker 가이드를 참고하세요.
6. Kubernetes 배포 자동화
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: azure/setup-kubectl@v4
- name: Configure kubeconfig
run: echo "${{ secrets.KUBECONFIG_B64 }}" | base64 -d > kubeconfig
- name: Rolling update
run: |
export KUBECONFIG=kubeconfig
kubectl set image deployment/myapp app=ghcr.io/${{ github.repository }}:${{ github.sha }}
kubectl rollout status deployment/myapp --timeout=120syaml
장기 자격 증명(kubeconfig, 클라우드 키)을 CI에 그대로 두면 유출 위험이 큽니다. 가능하면 OIDC 기반 단기 토큰 발급(GitHub → AWS/GCP/Azure) 방식을 사용하세요.
7. GitOps (ArgoCD)
GitOps는 "배포하고 싶은 상태"를 Git 저장소에 선언하고, 클러스터 내부의 컨트롤러가 그 상태와 실제 상태를 지속적으로 동기화하는 방식입니다. CI가 클러스터에 직접 kubectl apply하는 대신, Git에 매니페스트만 반영하면 배포가 완료됩니다.
# ArgoCD Application 정의
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: myapp
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/org/myapp-manifests
targetRevision: main
path: overlays/production
destination:
server: https://kubernetes.default.svc
namespace: production
syncPolicy:
automated:
prune: true # Git에서 삭제된 리소스는 클러스터에서도 제거
selfHeal: true # 수동 변경을 감지하면 Git 상태로 자동 복구yaml
| Push 기반 CI/CD (기존) | GitOps (Pull 기반) |
|---|---|
| CI가 클러스터 자격 증명을 직접 보유 | 클러스터 내부 컨트롤러만 Git을 읽음 (자격 증명 노출 최소화) |
| 배포 이력이 CI 로그에 흩어짐 | 배포 이력이 Git 커밋 히스토리로 남음 |
| 드리프트(수동 변경) 감지 어려움 | selfHeal로 드리프트 자동 복구 |
8. 배포 전략
| 전략 | 동작 방식 | 장단점 |
|---|---|---|
| Rolling Update | 구버전 Pod를 하나씩 신버전으로 교체 | K8s 기본 전략, 리소스 효율적 / 롤백 중 두 버전 공존 |
| Blue-Green | 신버전(Green)을 완전히 띄운 뒤 트래픽을 한 번에 전환 | 즉시 롤백 가능 / 리소스 2배 필요 |
| Canary | 신버전에 트래픽 일부(예: 5%)만 우선 전달 후 점진 확대 | 실사용자 기반 검증 / 라우팅 설정 복잡도 증가 |
| Feature Flag | 배포와 기능 노출을 분리, 코드는 배포하되 플래그로 온/오프 | 배포·릴리스 분리 / 플래그 관리 부채 발생 가능 |
# Canary 예시 — Ingress 트래픽 분할 (nginx-ingress annotation)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myapp-canary
annotations:
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-weight: "5" # 신버전으로 5% 트래픽
spec:
ingressClassName: nginx
rules:
- host: myapp.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: myapp-v2
port: { number: 80 }yaml
9. 다음 단계
CI/CD 심화 로드맵
• Progressive Delivery: Argo Rollouts / Flagger로 Canary·Blue-Green을 자동화하고 메트릭 기반 자동 롤백
• Supply Chain 보안: SBOM 생성, 이미지 서명(Cosign), 취약점 스캔(Trivy)을 파이프라인에 통합
• Self-hosted Runner: GitHub Actions/Jenkins Agent를 자체 인프라(Kubernetes)에서 운영
• 모니터링 연동: 배포 이벤트를 Grafana 대시보드에 마킹하여 배포-장애 상관관계 추적
연계 가이드: Jenkins 가이드 · Docker 가이드 · Kubernetes 실전/심화 가이드 · 📊 Grafana 가이드
• Progressive Delivery: Argo Rollouts / Flagger로 Canary·Blue-Green을 자동화하고 메트릭 기반 자동 롤백
• Supply Chain 보안: SBOM 생성, 이미지 서명(Cosign), 취약점 스캔(Trivy)을 파이프라인에 통합
• Self-hosted Runner: GitHub Actions/Jenkins Agent를 자체 인프라(Kubernetes)에서 운영
• 모니터링 연동: 배포 이벤트를 Grafana 대시보드에 마킹하여 배포-장애 상관관계 추적
연계 가이드: Jenkins 가이드 · Docker 가이드 · Kubernetes 실전/심화 가이드 · 📊 Grafana 가이드