Skip to content

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

✨ Learn with Quiz
|

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

도입

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 비교

기능ArgoCDFlux v2
UI 대시보드내장 웹 UIWeave GitOps (별도)
멀티 클러스터지원지원
Helm 지원네이티브HelmRelease CRD
Kustomize 지원네이티브네이티브
이미지 자동 업데이트ArgoCD Image Updater내장
알림ArgoCD NotificationsNotification 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 비교

기능TurborepoNxBazel
학습 곡선낮음중간높음
언어 지원JS/TSJS/TS, Go, Rust 등다중 언어
변경 감지파일 해시프로젝트 그래프의존성 그래프
원격 캐싱Vercel RCNx CloudRemote Execution
설정 복잡도매우 간단중간복잡
대규모 적합성중규모대규모초대규모
코드 생성없음내장 GeneratorRules

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

기능LaunchDarklyUnleashFlagsmith
가격유료 (비쌈)오픈소스 + 엔터프라이즈오픈소스 + 클라우드
타겟팅매우 정교기본정교
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.01.0.1  (버그 수정)
  1.0.11.1.0  (새 기능 추가)
  1.1.02.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. 참고 자료

  1. DORA Metrics: dora.dev
  2. ArgoCD Documentation: argo-cd.readthedocs.io
  3. Flux Documentation: fluxcd.io
  4. Turborepo Documentation: turbo.build
  5. Nx Documentation: nx.dev
  6. LaunchDarkly Docs: docs.launchdarkly.com
  7. Unleash Documentation: docs.getunleash.io
  8. SLSA Framework: slsa.dev
  9. Sigstore / Cosign: sigstore.dev
  10. Semgrep: semgrep.dev
  11. Conventional Commits: conventionalcommits.org
  12. Git Cliff (Changelog): git-cliff.org
  13. GitHub Actions Docs: docs.github.com/actions
  14. Kubernetes Deployment Strategies: kubernetes.io/docs

Advanced CI/CD Pipeline Guide 2025: Deployment Strategies, GitOps, Monorepo, Security Pipeline

Introduction

According to the 2024 DORA (DevOps Research and Assessment) report, elite teams deploy multiple times per day with a change failure rate below 5%. Low-performing teams deploy once a month and take weeks to recover. The key differentiator is CI/CD pipeline maturity.

Beyond simple build-test-deploy, modern CI/CD encompasses deployment strategies (Blue-Green, Canary), GitOps, monorepo optimization, feature flags, and security pipelines. This guide systematically covers advanced CI/CD strategies proven in production environments in 2025.


1. Deployment Strategies

1.1 Rolling Update

The most basic deployment method, replacing instances sequentially.

Time ->

Instance 1:  [v1] [v1] [v2] [v2] [v2]
Instance 2:  [v1] [v1] [v1] [v2] [v2]
Instance 3:  [v1] [v1] [v1] [v1] [v2]

Traffic:     100% v1 -> mixed -> 100% v2
# Kubernetes Rolling Update
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-server
spec:
  replicas: 3
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1        # Max 1 additional Pod
      maxUnavailable: 0  # Zero downtime
  template:
    spec:
      containers:
        - name: api
          image: myapp:v2
          readinessProbe:
            httpGet:
              path: /health
              port: 8080
            initialDelaySeconds: 10
            periodSeconds: 5

Pros: Simple, minimal extra resources, gradual replacement Cons: Mixed v1/v2 traffic during deployment, difficult instant rollback

1.2 Blue-Green Deployment

Maintain two identical environments (Blue/Green) and switch traffic all at once.

┌─────────────────────────────────────────────┐
│                                             │
Load Balancer|v (Before switch: traffic to Blue)│   ┌───────────┐     ┌───────────┐           │
│   │   Blue    │     │   Green   │           │
   (v1)   (v2)    │           │
│   │  Active   │     │  Standby  │           │
│   └───────────┘     └───────────┘           │
│                                             │
After switch: 100% traffic to Green│                                             │
│   ┌───────────┐     ┌───────────┐           │
│   │   Blue    │     │   Green   │           │
   (v1)   (v2)    │           │
│   │  Standby  │     │  Active   │           │
│   └───────────┘     └───────────┘           │
└─────────────────────────────────────────────┘
# Blue-Green with AWS ALB (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 }}

Pros: Instant rollback (switch traffic to Blue), zero downtime Cons: 2x infrastructure cost, complex database migrations

1.3 Canary Deployment

Deploy the new version to a small percentage of traffic first for validation.

Phase 1: 5% traffic to Canary (v2)
┌──────────────────────────────┐
│ v1 v1 v1 v1 v1 v1 v1 v1 v1  │ 95%
│ v2                           │  5%
└──────────────────────────────┘

Phase 2: Expand to 25% after validation
┌──────────────────────────────┐
│ v1 v1 v1 v1 v1 v1 v1        │ 75%
│ v2 v2 v2                     │ 25%
└──────────────────────────────┘

Phase 3: 100% after confirming stability
┌──────────────────────────────┐
│ v2 v2 v2 v2 v2 v2 v2 v2 v2  │ 100%
└──────────────────────────────┘
# Canary Deployment with Istio
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

Pros: Minimized risk, validation with real traffic, progressive rollout Cons: Implementation complexity, requires metrics-based automation

1.4 Deployment Strategy Comparison

StrategyDowntimeRollback SpeedCostComplexityBest For
RollingNoneSlowLowLowGeneral services
Blue-GreenNoneInstantHigh (2x)MediumCritical services
CanaryNoneFastMediumHighHigh-traffic services
A/B TestingNoneFastMediumVery HighFeature experiments

2. GitOps

2.1 GitOps Principles

┌──────────────────────────────────────────────────────┐
GitOps Principles│                                                      │
1. Declarative- System state declared as code                   │
│                                                      │
2. Versioned- Git is the Single Source of Truth (SSOT)│                                                      │
3. Automated- Approved changes auto-applied to system          │
│                                                      │
4. Self-healing                                      │
- Auto-corrects drift between desired/actual state │
└──────────────────────────────────────────────────────┘

2.2 ArgoCD Configuration

# 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         # Clean up orphaned resources
      selfHeal: true      # Auto-revert manual changes
      allowEmpty: false
    syncOptions:
      - CreateNamespace=true
      - PrunePropagationPolicy=foreground
    retry:
      limit: 5
      backoff:
        duration: 5s
        factor: 2
        maxDuration: 3m

2.3 ArgoCD App-of-Apps Pattern

# 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/ directory structure
# apps/
#   myapp/
#     application.yaml
#   api-gateway/
#     application.yaml
#   monitoring/
#     application.yaml
#   cert-manager/
#     application.yaml

2.4 ArgoCD vs Flux Comparison

FeatureArgoCDFlux v2
UI DashboardBuilt-in web UIWeave GitOps (separate)
Multi-clusterSupportedSupported
Helm SupportNativeHelmRelease CRD
KustomizeNativeNative
Image Auto-updateArgoCD Image UpdaterBuilt-in
NotificationsArgoCD NotificationsNotification Controller
RBACBuilt-inUses Kubernetes RBAC
ApproachPull-basedPull-based
Learning CurveMediumMedium-High

2.5 ArgoCD Notification Setup

# 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}} sync succeeded",
          "text": "Deployment complete: {{.app.spec.source.targetRevision}}"
        }]
  template.app-sync-failed: |
    slack:
      attachments: |
        [{
          "color": "#E96D76",
          "title": "{{.app.metadata.name}} sync failed",
          "text": "Error: {{.app.status.conditions}}"
        }]

3. Monorepo CI

3.1 Monorepo Structure

monorepo/
  apps/
    web/           # Next.js frontend
    api/           # Express backend
    mobile/        # React Native app
  packages/
    ui/            # Shared UI components
    utils/         # Shared utilities
    config/        # Shared configuration
  turbo.json       # Turborepo config
  nx.json          # Or Nx config
  package.json

3.2 Turborepo Configuration

{
  "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 Affected Builds

# .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  # Full history needed

      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'npm'

      - run: npm ci

      # Only build/test changed projects
      - 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
  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

FeatureTurborepoNxBazel
Learning CurveLowMediumHigh
Language SupportJS/TSJS/TS, Go, Rust, etc.Multi-language
Change DetectionFile hashesProject graphDependency graph
Remote CachingVercel RCNx CloudRemote Execution
Config ComplexityVery simpleMediumComplex
Scale SuitabilityMediumLargeVery large
Code GenerationNoneBuilt-in GeneratorRules

4. Feature Flags

4.1 Types of Feature Flags

┌──────────────────────────────────────────────────┐
Feature Flag Types├──────────────────────────────────────────────────┤
│                                                  │
1. Release Flag- Hide incomplete features while deploying    │
- Lifespan: Short (days to weeks)│                                                  │
2. Experiment Flag- A/B testing, feature comparison             │
- Lifespan: Medium (weeks to months)│                                                  │
3. Ops Flag- System behavior control (kill switch)- Lifespan: Permanent│                                                  │
4. Permission Flag- Per-user/organization feature access         │
- Lifespan: Permanent└──────────────────────────────────────────────────┘

4.2 Implementation Example

// 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;
}

// Usage in component
function CheckoutPage() {
  const newCheckoutEnabled = useFeatureFlag('new-checkout-flow');

  if (newCheckoutEnabled) {
    return <NewCheckoutFlow />;
  }
  return <LegacyCheckoutFlow />;
}

4.3 Progressive Rollout

// Server-side feature flags (LaunchDarkly style)
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);
}

// Progressive rollout phases:
// Phase 1: Internal employees only (beta-testers group)
// Phase 2: 5% of users
// Phase 3: 25% of users
// Phase 4: 50% of users
// Phase 5: 100% of users

// Kill switch pattern
async function processPayment(order: Order) {
  const newPaymentEnabled = await getFeatureValue(
    'new-payment-gateway',
    { key: order.userId },
    false  // Default: disabled
  );

  if (newPaymentEnabled) {
    return newPaymentGateway.process(order);
  }
  return legacyPaymentGateway.process(order);
}

4.4 LaunchDarkly vs Unleash vs Flagsmith

FeatureLaunchDarklyUnleashFlagsmith
PricingPaid (expensive)Open source + EnterpriseOpen source + Cloud
TargetingVery sophisticatedBasicSophisticated
A/B TestingBuilt-inSeparateBuilt-in
SDK Support25+ languages15+ languages15+ languages
Self-hostingNot availableAvailableAvailable
Audit LogsSupportedSupportedSupported

5. Environment Management

5.1 Environment Hierarchy

┌─────────────────────────────────────────┐
Production- Real user traffic                     │
- Highest level monitoring              │
- Change approval process               │
├─────────────────────────────────────────┤
Staging- Identical to production config        │
- QA team testing                       │
- Integration tests                     │
├─────────────────────────────────────────┤
Development- Latest feature integration            │
- Auto-deploy from main branch          │
├─────────────────────────────────────────┤
PR Preview- Independent environment per PR- Auto-deleted when PR closes           │
└─────────────────────────────────────────┘

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 Management

# GitHub Actions - AWS Auth via OIDC (no long-lived secrets)
name: Secure Deploy

permissions:
  id-token: write
  contents: read

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      # OIDC-based AWS auth (no long-lived secrets needed)
      - 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: us-east-1

      # Secret scanning
      - name: Scan for secrets
        uses: trufflesecurity/trufflehog@v3
        with:
          path: ./
          extra_args: --only-verified

      # Container image scanning
      - 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 - Signed builds + provenance attestation
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

      # Artifact signing with Sigstore
      - name: Sign artifact
        uses: sigstore/cosign-installer@v3

      - name: Sign container image
        run: |
          cosign sign \
            --yes \
            myregistry/myapp:${{ github.sha }}

      # Generate SLSA provenance attestation
      - 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 Security Scanning Pipeline

# .github/workflows/security.yml
name: Security Pipeline

on:
  pull_request:
    branches: [main]
  schedule:
    - cron: '0 6 * * 1'  # Every Monday at 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 Instant Rollback

# Kubernetes rollback
# Immediately rollback to last successful version
# kubectl rollout undo deployment/myapp

# GitHub Actions auto-rollback
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 Database-Aware Rollback

When DB schema changes are part of a deployment:

1. Forward-Compatible Migration (safe)
   ┌─────────────────────────────────────┐
Step 1: Add new column (nullable)Step 2: Deploy app v2 (uses both)Step 3: Migrate data                │
Step 4: Remove old column           │
   └─────────────────────────────────────┘

2. Expand-Contract Pattern
   Phase 1 (Expand): Add new schema, keep old
   Phase 2 (Migrate): Transfer data
   Phase 3 (Contract): Remove old schema

7.3 Forward-Fix vs Rollback Decision Guide

SituationRecommended StrategyReason
Simple bug fixForward-FixFix is faster than rollback
Critical outageInstant RollbackService recovery is top priority
Includes DB migrationForward-FixRisk of data loss on rollback
Performance degradationCanary RollbackAssess impact before deciding

8. Pipeline Performance Optimization

8.1 Caching Strategies

# .github/workflows/optimized-ci.yml
name: Optimized CI

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      # Node.js dependency caching
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'npm'

      # Docker layer caching
      - 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 remote caching
      - name: Build with cache
        run: npx turbo build --cache-dir=.turbo
        env:
          TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
          TURBO_TEAM: myteam

8.2 Parallelization Strategy

# Parallel job execution
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 after all checks pass
  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

# Self-hosted runners (ARM64 Mac, etc.)
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 (e.g., 2.1.3)

MAJOR: Incompatible API changes
MINOR: Backward-compatible new features
PATCH: Backward-compatible bug fixes

Examples:
  1.0.0 -> 1.0.1  (bug fix)
  1.0.1 -> 1.1.0  (new feature)
  1.1.0 -> 2.0.0  (Breaking Change)

9.2 Automated Release Notes

# .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

# Commit message format
# 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 notation
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 Model

┌─────────────────────────────────────────────┐
Release Train (bi-weekly releases)│                                             │
Week 1: Feature development + Code FreezeWeek 2: QA + Bug fixes + Release│                                             │
--+------+------+------+------+--│   v2.1   v2.2   v2.3   v2.4   v2.5│                                             │
Hotfixes deploy immediately from│ separate branch                             │
└─────────────────────────────────────────────┘

10. Multi-Cloud Deployment

10.1 Multi-Cloud with Terraform

# main.tf - Multi-cloud providers
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
    google = {
      source  = "hashicorp/google"
      version = "~> 5.0"
    }
  }
}

# AWS deployment
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 deployment (DR)
resource "google_cloud_run_service" "failover" {
  name     = "myapp-failover"
  location = "us-central1"

  template {
    spec {
      containers {
        image = "gcr.io/myproject/myapp:latest"
      }
    }
  }
}

11. Quiz

Q1: What is the biggest advantage of Blue-Green deployment?

A) Lower infrastructure costs B) Instant rollback capability C) Simple implementation D) Traffic splitting capability

Answer: B

Blue-Green deployment maintains two identical environments, so when issues arise, you can instantly switch the load balancer traffic back to the previous environment (Blue). The tradeoff is 2x infrastructure cost.

Q2: What is the role of the Git repository in GitOps?

A) Code backup storage B) CI/CD pipeline runner C) Single Source of Truth (SSOT) for infrastructure D) Monitoring dashboard

Answer: C

In GitOps, the Git repository serves as the Single Source of Truth (SSOT) for infrastructure and application state. All changes go through Git, and tools like ArgoCD/Flux synchronize Git state with the actual cluster state.

Q3: What is the benefit of "affected" builds in monorepo CI?

A) It always builds all projects B) It only builds changed projects and their dependents, saving time C) It ignores build caches D) It skips tests

Answer: B

Affected builds analyze the Git diff to selectively build and test only changed projects and their dependents. This significantly reduces CI time in large monorepos.

Q4: What is the "kill switch" purpose of Feature Flags?

A) Shuts down the server B) Instantly disables a problematic feature without code deployment C) Deletes the database D) Stops the CI pipeline

Answer: B

A kill switch toggles a feature flag to OFF in production when problems occur, instantly disabling that feature without deploying new code. This is much faster than a rollback.

Q5: What is the purpose of SLSA (Supply-chain Levels for Software Artifacts)?

A) Code performance optimization B) Ensures software supply chain integrity C) Database security D) UI design verification

Answer: B

SLSA is a security framework to prevent software supply chain attacks. It ensures software integrity through build provenance attestation, artifact signing, and build environment isolation.


12. CI/CD Maturity Checklist

CI/CD Maturity Checklist:

Level 1 - Basic
[  ] Automated build pipeline
[  ] Automated test execution
[  ] Code review process

Level 2 - Standard
[  ] Deployment strategy (Rolling/Blue-Green)
[  ] Environment separation (dev/staging/prod)
[  ] Secrets management (Vault/OIDC)

Level 3 - Advanced
[  ] GitOps (ArgoCD/Flux)
[  ] Feature Flags
[  ] Canary deployment
[  ] PR Preview environments

Level 4 - Elite
[  ] Pipeline security (SLSA)
[  ] Auto-rollback
[  ] Monorepo optimization
[  ] Release automation

13. References

  1. DORA Metrics: dora.dev
  2. ArgoCD Documentation: argo-cd.readthedocs.io
  3. Flux Documentation: fluxcd.io
  4. Turborepo Documentation: turbo.build
  5. Nx Documentation: nx.dev
  6. LaunchDarkly Docs: docs.launchdarkly.com
  7. Unleash Documentation: docs.getunleash.io
  8. SLSA Framework: slsa.dev
  9. Sigstore / Cosign: sigstore.dev
  10. Semgrep: semgrep.dev
  11. Conventional Commits: conventionalcommits.org
  12. Git Cliff (Changelog): git-cliff.org
  13. GitHub Actions Docs: docs.github.com/actions
  14. Kubernetes Deployment Strategies: kubernetes.io/docs