- Published on
CI/CD 파이프라인 심화 가이드 2025: 배포 전략, GitOps, 모노레포, 보안 파이프라인
- Authors

- Name
- Youngju Kim
- @fjvbn20031
- 도입
- 1. 배포 전략 (Deployment Strategies)
- 2. GitOps
- 3. 모노레포 CI (Monorepo CI)
- 4. Feature Flags (피처 플래그)
- 5. 환경 관리 (Environment Management)
- 6. 파이프라인 보안 (Pipeline Security)
- 7. 롤백 전략 (Rollback Strategies)
- 8. 파이프라인 성능 최적화
- 9. 릴리스 관리 (Release Management)
- 10. 멀티 클라우드 배포
- 11. 실전 퀴즈
- 12. CI/CD 성숙도 체크리스트
- 13. 참고 자료
도입
2024년, DORA(DevOps Research and Assessment) 보고서에 따르면, 엘리트 팀은 하루에 여러 번 배포하고 변경 실패율은 5% 미만입니다. 반면 저성과 팀은 한 달에 한 번 배포하며, 복구에 수 주가 걸립니다. 이 격차의 핵심은 바로 CI/CD 파이프라인의 성숙도에 있습니다.
단순한 빌드-테스트-배포를 넘어서, 현대적 CI/CD는 배포 전략(Blue-Green, Canary), GitOps, 모노레포 최적화, 피처 플래그, 보안 파이프라인까지 아우릅니다. 이 가이드는 2025년 현재 프로덕션 환경에서 검증된 심화 CI/CD 전략을 체계적으로 다룹니다.
1. 배포 전략 (Deployment Strategies)
1.1 Rolling Update
가장 기본적인 배포 방식으로, 인스턴스를 순차적으로 교체합니다.
시간 →
인스턴스 1: [v1] [v1] [v2] [v2] [v2]
인스턴스 2: [v1] [v1] [v1] [v2] [v2]
인스턴스 3: [v1] [v1] [v1] [v1] [v2]
트래픽: 100% v1 → 혼합 → 100% v2
# Kubernetes Rolling Update
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-server
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # 최대 1개 추가 Pod
maxUnavailable: 0 # 다운타임 0
template:
spec:
containers:
- name: api
image: myapp:v2
readinessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
장점: 간단, 추가 리소스 최소, 점진적 교체 단점: 배포 중 v1/v2 혼합 트래픽, 즉시 롤백 어려움
1.2 Blue-Green Deployment
두 개의 동일한 환경(Blue/Green)을 유지하고, 트래픽을 한 번에 전환합니다.
┌─────────────────────────────────────────────┐
│ │
│ Load Balancer │
│ │ │
│ ▼ (전환 전: Blue로 트래픽) │
│ ┌───────────┐ ┌───────────┐ │
│ │ Blue │ │ Green │ │
│ │ (v1) │ │ (v2) │ │
│ │ Active │ │ Standby │ │
│ └───────────┘ └───────────┘ │
│ │
│ 전환 후: Green으로 100% 트래픽 전환 │
│ │
│ ┌───────────┐ ┌───────────┐ │
│ │ Blue │ │ Green │ │
│ │ (v1) │ │ (v2) │ │
│ │ Standby │ │ Active │ │
│ └───────────┘ └───────────┘ │
└─────────────────────────────────────────────┘
# AWS ALB를 이용한 Blue-Green (GitHub Actions)
name: Blue-Green Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build and Push Docker Image
run: |
docker build -t myapp:${{ github.sha }} .
docker push myregistry/myapp:${{ github.sha }}
- name: Deploy to Green Environment
run: |
aws ecs update-service \
--cluster production \
--service green-service \
--task-definition myapp:${{ github.sha }}
- name: Run Smoke Tests on Green
run: |
./scripts/smoke-test.sh https://green.myapp.com
- name: Switch Traffic to Green
run: |
aws elbv2 modify-listener \
--listener-arn ${{ secrets.ALB_LISTENER_ARN }} \
--default-actions \
Type=forward,TargetGroupArn=${{ secrets.GREEN_TG_ARN }}
장점: 즉시 롤백 가능 (Blue로 트래픽 전환), 다운타임 없음 단점: 2배의 인프라 비용, 데이터베이스 마이그레이션 복잡
1.3 Canary Deployment
소수의 트래픽에만 새 버전을 먼저 배포하여 검증합니다.
Phase 1: 5% 트래픽을 Canary(v2)로
┌──────────────────────────────┐
│ v1 v1 v1 v1 v1 v1 v1 v1 v1 │ 95%
│ v2 │ 5%
└──────────────────────────────┘
Phase 2: 검증 후 25%로 확대
┌──────────────────────────────┐
│ v1 v1 v1 v1 v1 v1 v1 │ 75%
│ v2 v2 v2 │ 25%
└──────────────────────────────┘
Phase 3: 안정 확인 후 100%
┌──────────────────────────────┐
│ v2 v2 v2 v2 v2 v2 v2 v2 v2 │ 100%
└──────────────────────────────┘
# Istio를 이용한 Canary Deployment
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: myapp
spec:
hosts:
- myapp.example.com
http:
- route:
- destination:
host: myapp
subset: stable
weight: 95
- destination:
host: myapp
subset: canary
weight: 5
---
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: myapp
spec:
host: myapp
subsets:
- name: stable
labels:
version: v1
- name: canary
labels:
version: v2
장점: 리스크 최소화, 실제 트래픽으로 검증, 점진적 롤아웃 단점: 구현 복잡성, 메트릭 기반 자동화 필요
1.4 배포 전략 비교
| 전략 | 다운타임 | 롤백 속도 | 비용 | 복잡도 | 적합 시나리오 |
|---|---|---|---|---|---|
| Rolling | 없음 | 느림 | 낮음 | 낮음 | 일반 서비스 |
| Blue-Green | 없음 | 즉시 | 높음 (2배) | 중간 | 중요 서비스 |
| Canary | 없음 | 빠름 | 중간 | 높음 | 대규모 트래픽 |
| A/B Testing | 없음 | 빠름 | 중간 | 매우 높음 | 기능 실험 |
2. GitOps
2.1 GitOps 원칙
┌──────────────────────────────────────────────────────┐
│ GitOps 원칙 │
│ │
│ 1. 선언적 (Declarative) │
│ - 시스템 상태를 코드로 선언 │
│ │
│ 2. 버전 관리 (Versioned) │
│ - Git이 단일 진실의 원천(SSOT) │
│ │
│ 3. 자동 적용 (Automated) │
│ - 승인된 변경은 자동으로 시스템에 반영 │
│ │
│ 4. 자가 치유 (Self-healing) │
│ - 실제 상태와 원하는 상태의 차이를 자동 수정 │
└──────────────────────────────────────────────────────┘
2.2 ArgoCD 설정
# argocd-application.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: myapp-production
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/myorg/k8s-manifests.git
targetRevision: main
path: apps/myapp/overlays/production
destination:
server: https://kubernetes.default.svc
namespace: production
syncPolicy:
automated:
prune: true # 불필요한 리소스 정리
selfHeal: true # 수동 변경 자동 복구
allowEmpty: false
syncOptions:
- CreateNamespace=true
- PrunePropagationPolicy=foreground
retry:
limit: 5
backoff:
duration: 5s
factor: 2
maxDuration: 3m
2.3 ArgoCD App-of-Apps 패턴
# root-application.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: root-app
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/myorg/k8s-manifests.git
targetRevision: main
path: apps
destination:
server: https://kubernetes.default.svc
# apps/ 디렉토리 구조
# apps/
# myapp/
# application.yaml
# api-gateway/
# application.yaml
# monitoring/
# application.yaml
# cert-manager/
# application.yaml
2.4 ArgoCD vs Flux 비교
| 기능 | ArgoCD | Flux v2 |
|---|---|---|
| UI 대시보드 | 내장 웹 UI | Weave GitOps (별도) |
| 멀티 클러스터 | 지원 | 지원 |
| Helm 지원 | 네이티브 | HelmRelease CRD |
| Kustomize 지원 | 네이티브 | 네이티브 |
| 이미지 자동 업데이트 | ArgoCD Image Updater | 내장 |
| 알림 | ArgoCD Notifications | Notification Controller |
| RBAC | 내장 | Kubernetes RBAC 사용 |
| 접근 방식 | Pull 기반 | Pull 기반 |
| 학습 곡선 | 중간 | 중간-높음 |
2.5 ArgoCD 알림 설정
# argocd-notifications-cm.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: argocd-notifications-cm
namespace: argocd
data:
service.slack: |
token: $slack-token
trigger.on-sync-succeeded: |
- when: app.status.sync.status == 'Synced'
send: [app-sync-succeeded]
trigger.on-sync-failed: |
- when: app.status.sync.status == 'Unknown'
send: [app-sync-failed]
template.app-sync-succeeded: |
slack:
attachments: |
[{
"color": "#18be52",
"title": "{{.app.metadata.name}} 동기화 성공",
"text": "배포 완료: {{.app.spec.source.targetRevision}}"
}]
template.app-sync-failed: |
slack:
attachments: |
[{
"color": "#E96D76",
"title": "{{.app.metadata.name}} 동기화 실패",
"text": "오류: {{.app.status.conditions}}"
}]
3. 모노레포 CI (Monorepo CI)
3.1 모노레포 구조
monorepo/
apps/
web/ # Next.js 프론트엔드
api/ # Express 백엔드
mobile/ # React Native 앱
packages/
ui/ # 공유 UI 컴포넌트
utils/ # 공유 유틸리티
config/ # 공유 설정
turbo.json # Turborepo 설정
nx.json # 또는 Nx 설정
package.json
3.2 Turborepo 설정
{
"globalDependencies": ["**/.env.*local"],
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**", ".next/**"],
"cache": true
},
"test": {
"dependsOn": ["build"],
"outputs": [],
"cache": true
},
"lint": {
"outputs": [],
"cache": true
},
"deploy": {
"dependsOn": ["build", "test", "lint"],
"cache": false
}
}
}
3.3 Nx 변경 감지 기반 빌드
# .github/workflows/monorepo-ci.yml
name: Monorepo CI
on:
pull_request:
branches: [main]
jobs:
affected:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # 전체 히스토리 필요
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- run: npm ci
# 변경된 프로젝트만 빌드/테스트
- name: Lint affected
run: npx nx affected --target=lint --base=origin/main
- name: Test affected
run: npx nx affected --target=test --base=origin/main
- name: Build affected
run: npx nx affected --target=build --base=origin/main
# Remote Caching (Nx Cloud 또는 Turborepo Remote Cache)
remote-cache:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- name: Build with remote cache
run: npx turbo build --team=myteam --token=${{ secrets.TURBO_TOKEN }}
env:
TURBO_TEAM: myteam
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
3.4 Turborepo vs Nx vs Bazel 비교
| 기능 | Turborepo | Nx | Bazel |
|---|---|---|---|
| 학습 곡선 | 낮음 | 중간 | 높음 |
| 언어 지원 | JS/TS | JS/TS, Go, Rust 등 | 다중 언어 |
| 변경 감지 | 파일 해시 | 프로젝트 그래프 | 의존성 그래프 |
| 원격 캐싱 | Vercel RC | Nx Cloud | Remote Execution |
| 설정 복잡도 | 매우 간단 | 중간 | 복잡 |
| 대규모 적합성 | 중규모 | 대규모 | 초대규모 |
| 코드 생성 | 없음 | 내장 Generator | Rules |
4. Feature Flags (피처 플래그)
4.1 피처 플래그의 유형
┌──────────────────────────────────────────────────┐
│ Feature Flag 유형 │
├──────────────────────────────────────────────────┤
│ │
│ 1. Release Flag (릴리스 플래그) │
│ - 미완성 기능을 숨기고 배포 │
│ - 수명: 짧음 (며칠 ~ 몇 주) │
│ │
│ 2. Experiment Flag (실험 플래그) │
│ - A/B 테스트, 기능 비교 │
│ - 수명: 중간 (몇 주 ~ 몇 달) │
│ │
│ 3. Ops Flag (운영 플래그) │
│ - 시스템 동작 제어 (킬 스위치) │
│ - 수명: 영구적 │
│ │
│ 4. Permission Flag (권한 플래그) │
│ - 사용자/조직별 기능 접근 제어 │
│ - 수명: 영구적 │
└──────────────────────────────────────────────────┘
4.2 구현 예제
// featureFlags.ts
import { UnleashClient } from 'unleash-proxy-client';
const unleash = new UnleashClient({
url: 'https://unleash.myapp.com/api/frontend',
clientKey: process.env.UNLEASH_CLIENT_KEY!,
appName: 'myapp-frontend',
refreshInterval: 15,
});
unleash.start();
// React Hook
export function useFeatureFlag(flagName: string): boolean {
const [enabled, setEnabled] = useState(false);
useEffect(() => {
setEnabled(unleash.isEnabled(flagName));
const handler = () => {
setEnabled(unleash.isEnabled(flagName));
};
unleash.on('update', handler);
return () => unleash.off('update', handler);
}, [flagName]);
return enabled;
}
// 컴포넌트에서 사용
function CheckoutPage() {
const newCheckoutEnabled = useFeatureFlag('new-checkout-flow');
if (newCheckoutEnabled) {
return <NewCheckoutFlow />;
}
return <LegacyCheckoutFlow />;
}
4.3 Progressive Rollout (점진적 롤아웃)
// 서버 사이드 피처 플래그 (LaunchDarkly 스타일)
import LaunchDarkly from 'launchdarkly-node-server-sdk';
const client = LaunchDarkly.init(process.env.LD_SDK_KEY!);
async function getFeatureValue(
flagKey: string,
user: LDUser,
defaultValue: boolean
): Promise<boolean> {
await client.waitForInitialization();
return client.variation(flagKey, user, defaultValue);
}
// 점진적 롤아웃 설정 (LaunchDarkly 대시보드 설정과 동일)
// Phase 1: 내부 직원만 (beta-testers 그룹)
// Phase 2: 5% 사용자
// Phase 3: 25% 사용자
// Phase 4: 50% 사용자
// Phase 5: 100% 사용자
// 킬 스위치 패턴
async function processPayment(order: Order) {
const newPaymentEnabled = await getFeatureValue(
'new-payment-gateway',
{ key: order.userId },
false // 기본값: 비활성화
);
if (newPaymentEnabled) {
return newPaymentGateway.process(order);
}
return legacyPaymentGateway.process(order);
}
4.4 LaunchDarkly vs Unleash vs Flagsmith
| 기능 | LaunchDarkly | Unleash | Flagsmith |
|---|---|---|---|
| 가격 | 유료 (비쌈) | 오픈소스 + 엔터프라이즈 | 오픈소스 + 클라우드 |
| 타겟팅 | 매우 정교 | 기본 | 정교 |
| A/B 테스트 | 내장 | 별도 | 내장 |
| SDK 지원 | 25+ 언어 | 15+ 언어 | 15+ 언어 |
| 셀프호스팅 | 불가 | 가능 | 가능 |
| 감사 로그 | 지원 | 지원 | 지원 |
5. 환경 관리 (Environment Management)
5.1 환경 계층 구조
┌─────────────────────────────────────────┐
│ Production (프로덕션) │
│ - 실제 사용자 트래픽 │
│ - 최고 수준의 모니터링 │
│ - 변경 시 승인 프로세스 │
├─────────────────────────────────────────┤
│ Staging (스테이징) │
│ - 프로덕션과 동일한 구성 │
│ - QA 팀 테스트 │
│ - 통합 테스트 실행 │
├─────────────────────────────────────────┤
│ Development (개발) │
│ - 최신 기능 통합 │
│ - 자동 배포 (main 브랜치) │
├─────────────────────────────────────────┤
│ PR Preview (PR별 미리보기) │
│ - PR마다 독립 환경 생성 │
│ - PR 닫히면 자동 삭제 │
└─────────────────────────────────────────┘
5.2 Ephemeral Environments (임시 환경)
# .github/workflows/preview.yml
name: PR Preview Environment
on:
pull_request:
types: [opened, synchronize, reopened]
jobs:
deploy-preview:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Deploy Preview
run: |
PREVIEW_URL="pr-${{ github.event.number }}.preview.myapp.com"
helm upgrade --install \
pr-${{ github.event.number }} \
./charts/myapp \
--namespace previews \
--set image.tag=${{ github.sha }} \
--set ingress.host=$PREVIEW_URL \
--set resources.requests.memory=256Mi \
--set resources.requests.cpu=100m
- name: Comment PR with URL
uses: actions/github-script@v7
with:
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: `Preview deployed: https://pr-${context.issue.number}.preview.myapp.com`
})
cleanup-preview:
if: github.event.action == 'closed'
runs-on: ubuntu-latest
steps:
- name: Delete Preview
run: |
helm uninstall pr-${{ github.event.number }} \
--namespace previews
6. 파이프라인 보안 (Pipeline Security)
6.1 Secrets 관리
# GitHub Actions - OIDC를 이용한 AWS 인증 (비밀키 없이)
name: Secure Deploy
permissions:
id-token: write
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# OIDC 기반 AWS 인증 (장기 비밀키 불필요)
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/github-actions-role
aws-region: ap-northeast-2
# 시크릿 스캔
- name: Scan for secrets
uses: trufflesecurity/trufflehog@v3
with:
path: ./
extra_args: --only-verified
# 컨테이너 이미지 스캔
- name: Scan container image
uses: aquasecurity/trivy-action@master
with:
image-ref: myapp:${{ github.sha }}
severity: 'CRITICAL,HIGH'
exit-code: '1'
6.2 SLSA (Supply-chain Levels for Software Artifacts)
# SLSA Level 3 - 서명된 빌드 + 출처 증명
name: SLSA Build
jobs:
build:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
attestations: write
steps:
- uses: actions/checkout@v4
- name: Build
run: npm run build
# Sigstore를 이용한 아티팩트 서명
- name: Sign artifact
uses: sigstore/cosign-installer@v3
- name: Sign container image
run: |
cosign sign \
--yes \
myregistry/myapp:${{ github.sha }}
# SLSA 출처 증명 생성
- name: Generate SLSA provenance
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v2.0.0
with:
image: myregistry/myapp
digest: ${{ steps.build.outputs.digest }}
6.3 보안 스캔 파이프라인
# .github/workflows/security.yml
name: Security Pipeline
on:
pull_request:
branches: [main]
schedule:
- cron: '0 6 * * 1' # 매주 월요일 06:00
jobs:
sast:
name: Static Analysis
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run Semgrep
uses: semgrep/semgrep-action@v1
with:
config: p/owasp-top-ten
dependency-check:
name: Dependency Audit
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm audit --audit-level=high
- name: Snyk Test
uses: snyk/actions/node@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
container-scan:
name: Container Security
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build image
run: docker build -t myapp:scan .
- name: Trivy scan
uses: aquasecurity/trivy-action@master
with:
image-ref: myapp:scan
severity: 'CRITICAL,HIGH'
format: 'sarif'
output: 'trivy-results.sarif'
- name: Upload SARIF
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: 'trivy-results.sarif'
iac-scan:
name: Infrastructure as Code Scan
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Checkov scan
uses: bridgecrewio/checkov-action@master
with:
directory: ./terraform
framework: terraform
7. 롤백 전략 (Rollback Strategies)
7.1 즉시 롤백
# Kubernetes 롤백
# 마지막 성공 버전으로 즉시 롤백
# kubectl rollout undo deployment/myapp
# GitHub Actions 자동 롤백
name: Deploy with Auto-Rollback
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Deploy new version
id: deploy
run: |
kubectl set image deployment/myapp \
myapp=myregistry/myapp:${{ github.sha }}
kubectl rollout status deployment/myapp --timeout=300s
- name: Run post-deploy checks
id: healthcheck
run: |
for i in $(seq 1 10); do
STATUS=$(curl -s -o /dev/null -w "%{http_code}" https://myapp.com/health)
if [ "$STATUS" != "200" ]; then
echo "Health check failed: $STATUS"
exit 1
fi
sleep 5
done
- name: Rollback on failure
if: failure()
run: |
echo "Rolling back to previous version..."
kubectl rollout undo deployment/myapp
kubectl rollout status deployment/myapp --timeout=300s
7.2 데이터베이스 인식 롤백
배포 과정에서 DB 스키마 변경이 있는 경우:
1. Forward-Compatible 마이그레이션 (안전)
┌─────────────────────────────────────┐
│ Step 1: DB에 새 컬럼 추가 (nullable)│
│ Step 2: 앱 v2 배포 (두 컬럼 모두 사용)│
│ Step 3: 데이터 마이그레이션 │
│ Step 4: 이전 컬럼 제거 │
└─────────────────────────────────────┘
2. Expand-Contract 패턴
Phase 1 (Expand): 새 스키마 추가, 기존 유지
Phase 2 (Migrate): 데이터 이전
Phase 3 (Contract): 이전 스키마 제거
7.3 Forward-Fix vs Rollback 결정 기준
| 상황 | 권장 전략 | 이유 |
|---|---|---|
| 단순 버그 수정 | Forward-Fix | 롤백보다 수정이 빠름 |
| 심각한 장애 | 즉시 롤백 | 서비스 복구가 최우선 |
| DB 마이그레이션 포함 | Forward-Fix | 롤백 시 데이터 손실 위험 |
| 성능 저하 | 카나리 롤백 | 영향 범위 파악 후 결정 |
8. 파이프라인 성능 최적화
8.1 캐싱 전략
# .github/workflows/optimized-ci.yml
name: Optimized CI
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# Node.js 의존성 캐싱
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
# Docker 레이어 캐싱
- uses: docker/setup-buildx-action@v3
- uses: docker/build-push-action@v6
with:
context: .
push: true
tags: myapp:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
# Turborepo 원격 캐싱
- name: Build with cache
run: npx turbo build --cache-dir=.turbo
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: myteam
8.2 병렬화 전략
# 병렬 잡 실행
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npm run lint
unit-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npm run test:unit
type-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npm run type-check
# 모든 검증 통과 후 빌드
build:
needs: [lint, unit-test, type-check]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npm run build
8.3 Self-Hosted Runners
# 자체 호스팅 러너 (ARM64 Mac 등)
jobs:
build-ios:
runs-on: [self-hosted, macOS, ARM64]
steps:
- uses: actions/checkout@v4
- name: Build iOS
run: xcodebuild -workspace MyApp.xcworkspace ...
build-android:
runs-on: [self-hosted, Linux, X64]
steps:
- uses: actions/checkout@v4
- name: Build Android
run: ./gradlew assembleRelease
9. 릴리스 관리 (Release Management)
9.1 Semantic Versioning
MAJOR.MINOR.PATCH (예: 2.1.3)
MAJOR: 호환되지 않는 API 변경
MINOR: 하위 호환 기능 추가
PATCH: 하위 호환 버그 수정
예시:
1.0.0 → 1.0.1 (버그 수정)
1.0.1 → 1.1.0 (새 기능 추가)
1.1.0 → 2.0.0 (Breaking Change)
9.2 자동 릴리스 노트
# .github/workflows/release.yml
name: Release
on:
push:
tags: ['v*']
permissions:
contents: write
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Generate Changelog
id: changelog
uses: orhun/git-cliff-action@v3
with:
config: cliff.toml
- name: Create Release
uses: softprops/action-gh-release@v2
with:
body: ${{ steps.changelog.outputs.content }}
draft: false
prerelease: false
9.3 Conventional Commits
# 커밋 메시지 형식
# type(scope): description
feat(auth): add OAuth2.0 login support
fix(cart): resolve quantity update race condition
docs(api): update REST API documentation
chore(deps): upgrade express to v5.0
perf(db): optimize query for order listing
refactor(payment): extract payment strategy pattern
test(checkout): add E2E tests for checkout flow
# Breaking Change 표기
feat(api)!: change response format for /orders endpoint
BREAKING CHANGE: The orders endpoint now returns
paginated responses instead of a flat array.
9.4 Release Train 모델
┌─────────────────────────────────────────────┐
│ Release Train (격주 릴리스) │
│ │
│ Week 1: 기능 개발 + 코드 동결(Code Freeze) │
│ Week 2: QA + 버그 수정 + 릴리스 │
│ │
│ ──┬──────┬──────┬──────┬──────┬── │
│ v2.1 v2.2 v2.3 v2.4 v2.5 │
│ │
│ 핫픽스는 별도 브랜치에서 즉시 배포 │
└─────────────────────────────────────────────┘
10. 멀티 클라우드 배포
10.1 Terraform을 이용한 멀티 클라우드
# main.tf - 멀티 클라우드 프로바이더
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
google = {
source = "hashicorp/google"
version = "~> 5.0"
}
}
}
# AWS 배포
resource "aws_ecs_service" "primary" {
name = "myapp-primary"
cluster = aws_ecs_cluster.main.id
task_definition = aws_ecs_task_definition.app.arn
desired_count = 3
load_balancer {
target_group_arn = aws_lb_target_group.app.arn
container_name = "myapp"
container_port = 8080
}
}
# GCP 배포 (DR)
resource "google_cloud_run_service" "failover" {
name = "myapp-failover"
location = "asia-northeast3"
template {
spec {
containers {
image = "gcr.io/myproject/myapp:latest"
}
}
}
}
11. 실전 퀴즈
Q1: Blue-Green 배포의 가장 큰 장점은?
A) 인프라 비용이 저렴하다 B) 즉시 롤백이 가능하다 C) 구현이 간단하다 D) 트래픽 분할이 가능하다
정답: B
Blue-Green 배포는 두 개의 동일한 환경을 유지하므로, 문제 발생 시 로드밸런서의 트래픽을 이전 환경(Blue)으로 즉시 전환하여 롤백할 수 있습니다. 다만 2배의 인프라 비용이 단점입니다.
Q2: GitOps에서 Git 저장소의 역할은?
A) 코드 백업 저장소 B) CI/CD 파이프라인 실행기 C) 인프라의 단일 진실의 원천(SSOT) D) 모니터링 대시보드
정답: C
GitOps에서 Git 저장소는 인프라와 애플리케이션 상태의 단일 진실의 원천(Single Source of Truth)입니다. 모든 변경은 Git을 통해 이루어지며, ArgoCD/Flux 같은 도구가 Git 상태와 실제 클러스터 상태를 동기화합니다.
Q3: 모노레포 CI에서 "affected" 빌드의 장점은?
A) 모든 프로젝트를 항상 빌드한다 B) 변경된 프로젝트와 영향받는 프로젝트만 빌드하여 시간을 절약한다 C) 빌드 캐시를 무시한다 D) 테스트를 건너뛴다
정답: B
"affected" 빌드는 Git diff를 분석하여 변경된 프로젝트와 그에 의존하는 프로젝트만 선택적으로 빌드/테스트합니다. 이를 통해 대규모 모노레포에서 CI 시간을 크게 단축할 수 있습니다.
Q4: Feature Flag의 "킬 스위치" 용도는?
A) 서버를 종료한다 B) 문제가 있는 기능을 코드 배포 없이 즉시 비활성화한다 C) 데이터베이스를 삭제한다 D) CI 파이프라인을 중단한다
정답: B
킬 스위치는 프로덕션에서 문제가 발생했을 때, 새로운 코드 배포 없이 피처 플래그를 OFF로 전환하여 해당 기능을 즉시 비활성화하는 패턴입니다. 롤백보다 훨씬 빠르게 대응할 수 있습니다.
Q5: SLSA (Supply-chain Levels for Software Artifacts)의 목적은?
A) 코드 성능 최적화 B) 소프트웨어 공급망의 무결성을 보장한다 C) 데이터베이스 보안 D) UI 디자인 검증
정답: B
SLSA는 소프트웨어 공급망 공격을 방지하기 위한 보안 프레임워크입니다. 빌드 프로세스의 출처 증명, 아티팩트 서명, 빌드 환경의 격리 등을 통해 소프트웨어가 변조되지 않았음을 보장합니다.
12. CI/CD 성숙도 체크리스트
CI/CD 성숙도 체크리스트:
Level 1 - 기본
[ ] 자동 빌드 파이프라인
[ ] 자동 테스트 실행
[ ] 코드 리뷰 프로세스
Level 2 - 표준
[ ] 배포 전략 (Rolling/Blue-Green)
[ ] 환경 분리 (dev/staging/prod)
[ ] 시크릿 관리 (Vault/OIDC)
Level 3 - 고급
[ ] GitOps (ArgoCD/Flux)
[ ] Feature Flags
[ ] Canary 배포
[ ] PR Preview 환경
Level 4 - 엘리트
[ ] 파이프라인 보안 (SLSA)
[ ] 자동 롤백
[ ] 모노레포 최적화
[ ] 릴리스 자동화
13. 참고 자료
- DORA Metrics: dora.dev
- ArgoCD Documentation: argo-cd.readthedocs.io
- Flux Documentation: fluxcd.io
- Turborepo Documentation: turbo.build
- Nx Documentation: nx.dev
- LaunchDarkly Docs: docs.launchdarkly.com
- Unleash Documentation: docs.getunleash.io
- SLSA Framework: slsa.dev
- Sigstore / Cosign: sigstore.dev
- Semgrep: semgrep.dev
- Conventional Commits: conventionalcommits.org
- Git Cliff (Changelog): git-cliff.org
- GitHub Actions Docs: docs.github.com/actions
- Kubernetes Deployment Strategies: kubernetes.io/docs