Guider/Infra/InfraDevGuide0006
Infra#06· 59분 읽기

InfraDevGuide0006

CI/CD & 자동화 완전 정복

list목차(70)
INFRA DEV GUIDE SERIES · 6단계

🚀 CI/CD & 자동화 완전 정복 A-Z

GitHub Actions · Jenkins · ArgoCD · Terraform · Ansible 현업 실전

수동 배포는 이제 그만 — 코드 푸시 한 번으로 자동 배포까지, A부터 Z까지 한 번에!

🎓 대상: 인프라 입문~중급자⏰ 학습 기간: 3~4주⭐ 난이도: ★★★☆☆💻 실습 예제 50+

🗺️ Infra Dev Guide 시리즈 전체 로드맵

단계 가이드 주제 상태
1단계 InfraDevGuide0001 인프라 개념 완전 정복 ✅ 완료
2단계 InfraDevGuide0002 Linux 기초 완전 정복 ✅ 완료
3단계 InfraDevGuide0003 네트워크 기초 완전 정복 ✅ 완료
4단계 InfraDevGuide0004 클라우드 AWS 완전 정복 ✅ 완료
5단계 InfraDevGuide0005 Docker & 컨테이너 완전 정복 ✅ 완료
6단계 InfraDevGuide0006 CI/CD & 자동화 완전 정복 ← 현재 📖 학습중
7단계 InfraDevGuide0007 실전 프로젝트 & 취업 전략 ⏳ 예정

📋 전체 학습 목차 (12 Chapters)

Ch 챕터명 핵심 내용 난이도
01 CI/CD란? 개념 완전 정복 DevOps 철학, 파이프라인, 자동화 효과 ★☆☆☆☆
02 GitHub Actions 완전 정복 Workflow, Job, Step, 실전 파이프라인 ★★☆☆☆
03 Jenkins 완전 정복 Pipeline as Code, Jenkinsfile, 플러그인 ★★★☆☆
04 ArgoCD & GitOps 완전 정복 GitOps 패턴, K8s 배포 자동화 ★★★☆☆
05 Terraform IaC 완전 정복 인프라 코드화, Plan/Apply, 모듈 ★★★☆☆
06 Ansible 자동화 완전 정복 Playbook, Role, 서버 설정 자동화 ★★★☆☆
07 CI/CD 보안 (DevSecOps) SAST/DAST, 시크릿 관리, 의존성 스캔 ★★★★☆
08 고급 배포 전략 Blue-Green, Canary, Rolling 배포 ★★★★☆
09 모니터링 & 알림 자동화 Slack 알림, PagerDuty, SLO/SLA ★★★★☆
10 완전 자동화 실전 파이프라인 End-to-End CI/CD 구축 실습 ★★★★☆
11 트러블슈팅 & 최적화 파이프라인 디버깅, 속도 최적화 ★★★★★
12 현업 면접 Q&A TOP 15 CI/CD 면접 핵심 질문 & 모범 답안 ★★★★★

💡 Chapter 01. CI/CD란? — DevOps 자동화의 핵심

코드 작성 후 서비스 반영까지의 여정을 자동화하는 현대 개발 문화

❓ CI/CD가 없던 시절에는?

개발자가 코드를 완성하면 ➡️ 직접 서버에 SSH 접속 ➡️ 파일을 수동으로 복사 ➡️ 서버 재시작 ➡️ 서비스 확인 ➡️ 문제 발생 시 롤백 수동 처리

문제점: 배포 실수, 테스트 누락, 긴 배포 주기, 야근 반복 😭

CI/CD 도입 후: 코드 푸시 → 자동 테스트 → 자동 빌드 → 자동 배포 → 알림! 🎉

📊 CI vs CD 개념 비교

구분 CI (Continuous Integration) CD (Continuous Delivery/Deployment)
의미 지속적 통합 지속적 배포/전달
목적 코드 통합 시 문제 조기 발견 검증된 코드를 운영까지 자동화
주요 단계 코드 빌드, 단위/통합 테스트, 코드 품질 검사 스테이징 배포, E2E 테스트, 프로덕션 배포
트리거 코드 Push / PR 생성 시 CI 성공 후 자동 또는 수동 승인
대표 도구 GitHub Actions, Jenkins, GitLab CI ArgoCD, Spinnaker, AWS CodeDeploy

🏗️ CI/CD 파이프라인 전체 흐름

🧑‍💻 개발자 코드 Push
             |
             v
      [1] 📥 소스코드 체크아웃 (git clone)
             |
             v
      [2] 🔧 빌드 (npm build / mvn package / go build)
             |
             v
      [3] 🧪 테스트 실행
           |----+----+----+
           v    v    v    v
          단위  통합  E2E  보안
          테스트 테스트 테스트 스캔
             |
             v (모든 테스트 통과)
      [4] 🐋 Docker 이미지 빌드 & 레지스트리 Push
             |
             v
      [5] 📦 스테이징 환경 자동 배포
             |
             v
      [6]  승인 (자동 또는 수동 Approval)
             |
             v
      [7] 🚀 프로덕션 배포 (Rolling/Blue-Green/Canary)
             |
             v
      [8] 🔔 슬랙/이메일 알림 + 모니터링 확인

📊 CI/CD 도입 전후 비교

지표 CI/CD 도입 전 CI/CD 도입 후
배포 주기 월 1~2회 하루 수십 회 가능
배포 소요시간 수시간 5~15분
장애 복구 시간 수시간 분 단위 롤백
배포 실패율 높음 (수동 오류) 낮음 (자동화)
개발자 야근 배포일 야근 필수 퇴근 후 자동 배포
💡 DevOps 핵심 지표 (DORA Metrics)

구글이 정의한 고성능 DevOps 팀의 4가지 지표: 배포 빈도(Deploy Frequency), 리드 타임(Lead Time for Changes), 변경 실패율(Change Failure Rate), 복구 시간(Mean Time to Restore). Elite 팀은 하루 여러 번 배포, 1시간 이내 복구를 달성합니다.

⚡ Chapter 02. GitHub Actions 완전 정복

GitHub 내장 CI/CD - 코드와 파이프라인을 한 곳에서 관리하는 현대적 자동화

📋 GitHub Actions 핵심 구성요소

구성요소 설명 비유
Workflow 자동화 프로세스 전체 정의 (.yml 파일) 레시피 전체
Event (on:) 워크플로우 트리거 (push, PR, schedule 등) 요리 시작 신호
Job 독립 실행 단위, 병렬/순차 실행 가능 요리 파트 (메인, 사이드)
Step Job 내 순차 실행 명령 레시피 단계
Action 재사용 가능한 Step 묶음 (Marketplace) 즉석 조리식품
Runner 워크플로우 실행 서버 (ubuntu, windows, macos) 주방
Secret 암호화된 환경변수 (API키, 비밀번호 등) 잠금 보관함

🔧 기본 Workflow YAML 구조

# .github/workflows/ci.yml
    name: CI Pipeline
    
    # === 트리거 이벤트 ===
    on:
      push:
        branches: [main, develop]   # main, develop 브랜치 push 시
      pull_request:
        branches: [main]            # main PR 생성/업데이트 시
      schedule:
        - cron: '0 2 * * *'         # 매일 새벽 2시 자동 실행
      workflow_dispatch:             # 수동 트리거 (Actions 탭에서 실행 버튼)
    
    # === 전역 환경변수 ===
    env:
      NODE_VERSION: '18'
      REGISTRY: ghcr.io
      IMAGE_NAME: myorg/myapp
    
    jobs:
      # === Job 1: 테스트 ===
      test:
        runs-on: ubuntu-latest      # GitHub 호스팅 Runner
        strategy:
          matrix:
            node-version: [16, 18, 20]  # 매트릭스: 3개 버전 병렬 테스트
        steps:
          - name: 소스 체크아웃
            uses: actions/checkout@v4
    
          - name: Node.js 설정
            uses: actions/setup-node@v4
            with:
              node-version: ${{ matrix.node-version }}
              cache: 'npm'            # npm 캐시 자동 처리
    
          - name: 의존성 설치
            run: npm ci
    
          - name: 테스트 실행
            run: npm test
    
          - name: 커버리지 업로드
            uses: codecov/codecov-action@v4
            if: matrix.node-version == '18'  # 특정 조건에서만 실행
    
      # === Job 2: 빌드 (test 성공 후 실행) ===
      build:
        needs: test                 # test Job 완료 후 실행
        runs-on: ubuntu-latest
        permissions:
          contents: read
          packages: write
        steps:
          - uses: actions/checkout@v4
    
          - name: GHCR 로그인
            uses: docker/login-action@v3
            with:
              registry: ghcr.io
              username: GITHUB_ACTOR_VAR
              password: GITHUB_TOKEN_VAR
    
          - name: 이미지 메타데이터 추출
            id: meta
            uses: docker/metadata-action@v5
            with:
              images: ghcr.io/myorg/myapp
              tags: |
                type=sha
                type=semver,pattern={{version}}
    
          - name: 이미지 빌드 & Push
            uses: docker/build-push-action@v5
            with:
              context: .
              push: true
              tags: ghcr.io/myorg/myapp:latest
              cache-from: type=gha
              cache-to: type=gha,mode=max

📊 GitHub Actions 고급 기능

🔁 재사용 가능 Workflow
workflow_call 이벤트로 다른 워크플로우에서 호출 가능. 공통 CI 로직을 DRY하게 관리.
📦 Artifact 저장
actions/upload-artifact, download-artifact로 Job 간 파일 공유. 빌드 결과물 보관.
🛡️ Environments & 승인
production 환경에 required reviewers 설정. 특정 담당자 승인 후에만 배포 진행.
⚡ Self-hosted Runner
사내 서버에 Runner 설치. GPU 서버, 내부망 접근, 특수 환경 필요 시 사용.
💾 캐싱 전략
actions/cache로 node_modules, pip, Maven 캐시. 빌드 시간 50~80% 단축 가능.
📝 OIDC 인증
AWS/GCP와 OIDC 연동. Access Key 없이 임시 자격증명으로 안전하게 클라우드 접근.

📋 실전: Node.js 앱 완전 CI/CD 파이프라인

# .github/workflows/deploy.yml
    name: Full CI/CD Pipeline
    
    on:
      push:
        branches: [main]
    
    jobs:
      ci:
        runs-on: ubuntu-latest
        outputs:
          image-tag: steps-meta-outputs-tags  # Job outputs 전달
        steps:
          - uses: actions/checkout@v4
    
          # 의존성 캐시
          - name: Cache node_modules
            uses: actions/cache@v4
            with:
              path: ~/.npm
              key: node-modules-cache
    
          - run: npm ci
          - run: npm run lint         # ESLint 코드 품질 검사
          - run: npm test             # Jest 단위 테스트
          - run: npm run build        # 프로덕션 빌드
    
          # 보안 취약점 스캔
          - name: Trivy 이미지 스캔
            uses: aquasecurity/trivy-action@master
            with:
              scan-type: 'fs'
              exit-code: '1'
              severity: 'CRITICAL'
    
      deploy-staging:
        needs: ci
        runs-on: ubuntu-latest
        environment: staging           # 환경 설정 (secrets 격리)
        steps:
          - uses: actions/checkout@v4
          - name: SSH 스테이징 배포
            uses: appleboy/ssh-action@v1
            with:
              host: staging.myapp.com
              username: deploy
              key: STAGING_SSH_KEY_SECRET
              script: |
                cd /app
                docker compose pull
                docker compose up -d --no-deps app
    
      deploy-prod:
        needs: deploy-staging
        runs-on: ubuntu-latest
        environment: production        # 승인 게이트!
        steps:
          - name: AWS ECS 프로덕션 배포
            uses: aws-actions/amazon-ecs-deploy-task-definition@v1
            with:
              task-definition: task-def.json
              service: myapp-service
              cluster: production
              wait-for-service-stability: true
    
          - name: 슬랙 배포 완료 알림
            uses: 8398a7/action-slack@v3
            with:
              status: custom
              custom_payload: |
                {"text": "Production 배포 완료! v1.2.3 🎉"}
            env:
              SLACK_WEBHOOK_URL: SLACK_WEBHOOK_SECRET
# GitHub Actions 주요 트리거 패턴
    
    # 1. 특정 경로 변경 시만 실행
    on:
      push:
        paths:
          - 'src/**'
          - 'package.json'
        paths-ignore:
          - '**.md'
          - 'docs/**'
    
    # 2. 태그 push 시 릴리즈 배포
    on:
      push:
        tags: ['v*.*.*']  # v1.2.3 형태 태그 push 시
    
    # 3. PR 라벨 기반 실행
    on:
      pull_request:
        types: [labeled]
        # label: deploy-to-staging
    
    # 4. 다른 워크플로우 완료 후 실행
    on:
      workflow_run:
        workflows: ["CI"]
        types: [completed]

👷 Chapter 03. Jenkins 완전 정복

엔터프라이즈 CI/CD의 강자 - Pipeline as Code로 복잡한 빌드 자동화

❓ Jenkins vs GitHub Actions 비교

항목 Jenkins GitHub Actions
호스팅 자체 서버 (온프레미스/클라우드) GitHub 클라우드 (SaaS)
비용 서버 비용 (오픈소스 자체는 무료) 무료 한도 초과 시 유료
커스터마이징 매우 높음 (1800+ 플러그인) 중간 (Marketplace Actions)
학습 곡선 가파름 완만함
내부망 접근 가능 (온프레미스 설치) Self-hosted Runner 필요
추천 환경 대기업, 온프레미스, 복잡한 파이프라인 스타트업, GitHub 중심 팀

🔧 Jenkins Docker로 설치 및 초기 설정

# Jenkins를 Docker로 실행 (공식 권장 방법)
    docker run -d \
      --name jenkins \
      -p 8080:8080 \
      -p 50000:50000 \
      -v jenkins-data:/var/jenkins_home \
      -v /var/run/docker.sock:/var/run/docker.sock \
      jenkins/jenkins:lts-jdk17
    
    # 초기 관리자 비밀번호 확인
    docker logs jenkins | grep -A 5 "initialAdminPassword"
    # 또는
    docker exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword
    
    # 접속: http://localhost:8080
    # 초기 설정 마법사에서:
    # 1. 초기 비밀번호 입력
    # 2. "Install suggested plugins" 선택
    # 3. 관리자 계정 생성

📋 Jenkinsfile (Pipeline as Code) 완전 가이드

// Jenkinsfile (프로젝트 루트에 위치)
    pipeline {
        // 어떤 에이전트(서버)에서 실행할지
        agent any
        // agent { label 'docker-agent' }  // 특정 레이블의 에이전트
    
        // 환경변수
        environment {
            DOCKER_REGISTRY = 'registry.company.com'
            APP_NAME = 'myapp'
            SLACK_CHANNEL = '#deploy'
        }
    
        // 파라미터 (수동 실행 시 입력)
        parameters {
            choice(name: 'ENVIRONMENT', choices: ['staging', 'production'], description: '배포 환경')
            booleanParam(name: 'SKIP_TESTS', defaultValue: false, description: '테스트 건너뛰기')
        }
    
        stages {
            stage('Checkout') {
                steps {
                    checkout scm          // Git 체크아웃
                    sh 'git log -1'       // 최신 커밋 확인
                }
            }
    
            stage('Build & Test') {
                parallel {               // 병렬 실행
                    stage('Unit Test') {
                        when {
                            expression { !params.SKIP_TESTS }
                        }
                        steps {
                            sh 'npm ci && npm test'
                            publishTestResults testResultsPattern: 'test-results/**/*.xml'
                        }
                    }
                    stage('Code Lint') {
                        steps {
                            sh 'npm run lint'
                        }
                    }
                }
            }
    
            stage('Docker Build & Push') {
                steps {
                    script {
                        def imageTag = "${APP_NAME}:${BUILD_NUMBER}"
                        sh "docker build -t ${DOCKER_REGISTRY}/${imageTag} ."
                        // Jenkins Credentials에서 Docker 레지스트리 인증 정보 사용
                        withCredentials([usernamePassword(
                            credentialsId: 'docker-registry-creds',
                            usernameVariable: 'DOCKER_USER',
                            passwordVariable: 'DOCKER_PASS'
                        )]) {
                            sh "echo ${DOCKER_PASS} | docker login -u ${DOCKER_USER} --password-stdin ${DOCKER_REGISTRY}"
                            sh "docker push ${DOCKER_REGISTRY}/${imageTag}"
                        }
                    }
                }
            }
    
            stage('Deploy Staging') {
                steps {
                    sshPublisher(publishers: [
                        sshPublisherDesc(
                            configName: 'staging-server',
                            transfers: [sshTransfer(
                                execCommand: "cd /app && docker compose pull && docker compose up -d"
                            )]
                        )
                    ])
                }
            }
    
            stage('Deploy Production') {
                when { expression { params.ENVIRONMENT == 'production' } }
                input {                  // 수동 승인 게이트
                    message "프로덕션 배포를 진행할까요?"
                    ok "배포 승인"
                    submitter "admin,devops-lead"
                }
                steps {
                    sh './scripts/deploy-prod.sh'
                }
            }
        }
    
        // 항상 실행되는 후처리
        post {
            success {
                slackSend channel: env.SLACK_CHANNEL,
                          color: 'good',
                          message: "✅ 배포 성공: ${env.JOB_NAME} #${env.BUILD_NUMBER}"
            }
            failure {
                slackSend channel: env.SLACK_CHANNEL,
                          color: 'danger',
                          message: "❌ 배포 실패: ${env.JOB_NAME} #${env.BUILD_NUMBER}"
                emailext subject: "Jenkins 빌드 실패 알림",
                         body: "빌드 실패. 로그 확인 필요.",
                         to: "devops@company.com"
            }
            always {
                cleanWs()              // 워크스페이스 정리
            }
        }
    }

🔧 Jenkins 주요 플러그인 목록

플러그인 용도
Pipeline Jenkinsfile로 파이프라인 정의 (필수)
Git / GitHub Git 저장소 연동 및 Webhook 설정
Docker Pipeline Jenkinsfile에서 Docker 명령어 사용
Slack Notification 빌드 결과 Slack 채널 알림
Blue Ocean 시각적 파이프라인 대시보드 (UI 개선)
Credentials Binding API 키, 비밀번호 안전하게 주입
SSH Agent SSH 키로 원격 서버 접근
Kubernetes K8s Pod를 Jenkins Agent로 사용

💫 Chapter 04. ArgoCD & GitOps 완전 정복

Git이 곧 진실의 원천 - Kubernetes 배포를 Git으로 자동 동기화하는 GitOps 패턴

💡 GitOps란?

GitOps는 "Git 저장소가 인프라/애플리케이션의 유일한 진실(Single Source of Truth)"인 운영 방식입니다.

  • 개발자가 Kubernetes YAML을 Git에 커밋하면 ArgoCD가 자동으로 K8s에 동기화
  • 운영 환경을 직접 kubectl로 변경하는 대신, Git PR로 변경 관리
  • 감사 로그, 롤백이 git log 하나로 해결됨

📊 Push 방식 vs Pull(GitOps) 방식 비교

항목 Push 방식 (전통적 CD) Pull 방식 (GitOps / ArgoCD)
배포 트리거 CI가 kubectl apply 직접 실행 ArgoCD가 Git 변경 감지 후 자동 동기화
K8s 접근 권한 CI 서버에 K8s 접근 권한 필요 ArgoCD만 K8s 접근 (CI는 불필요)
드리프트 감지 없음 실시간 감지 후 자동 복원
롤백 파이프라인 재실행 또는 수동 git revert 한 줄로 자동 롤백
감사 추적 CI 로그에 분산 Git 커밋 히스토리가 완전한 감사 로그

🔧 ArgoCD 설치 및 초기 설정

# ArgoCD 설치 (Kubernetes 네임스페이스에 설치)
    kubectl create namespace argocd
    kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
    
    # ArgoCD 서버 외부 접근 (LoadBalancer)
    kubectl patch svc argocd-server -n argocd -p '{"spec": {"type": "LoadBalancer"}}'
    
    # 또는 포트 포워딩 (개발 환경)
    kubectl port-forward svc/argocd-server -n argocd 8080:443
    
    # 초기 admin 비밀번호 조회
    kubectl -n argocd get secret argocd-initial-admin-secret \
      -o jsonpath="{.data.password}" | base64 -d
    
    # ArgoCD CLI 로그인
    argocd login localhost:8080 --username admin --password (위의비밀번호)
    
    # 비밀번호 변경
    argocd account update-password

📋 ArgoCD Application YAML 작성

# argocd-app.yaml - ArgoCD Application 정의
    apiVersion: argoproj.io/v1alpha1
    kind: Application
    metadata:
      name: myapp
      namespace: argocd
    spec:
      project: default
      # 소스: Git 저장소 (K8s 매니페스트 저장소)
      source:
        repoURL: https://github.com/myorg/myapp-manifests.git
        targetRevision: main          # 브랜치 또는 태그
        path: overlays/production     # Kustomize 오버레이 경로
        # helm 사용 시:
        # chart: myapp
        # helm:
        #   values: |
        #     image.tag: 1.2.3
      # 대상: K8s 클러스터 및 네임스페이스
      destination:
        server: https://kubernetes.default.svc
        namespace: production
      # 동기화 정책
      syncPolicy:
        automated:
          prune: true      # Git에 없는 K8s 리소스 자동 삭제
          selfHeal: true   # 클러스터 상태가 Git과 다르면 자동 수정
        syncOptions:
          - CreateNamespace=true  # 네임스페이스 자동 생성
        retry:
          limit: 5
          backoff:
            duration: 5s
            maxDuration: 3m
    ---
    # kubectl apply -f argocd-app.yaml 으로 Application 생성

🏗️ GitOps 실전 레포지토리 구조 (App of Apps 패턴)

# 권장 레포 구조: 애플리케이션 코드와 매니페스트를 분리!
    
    # 레포 1: 애플리케이션 코드 (app-repo)
    app-repo/
    ├── src/
    ├── Dockerfile
    └── .github/workflows/
        └── ci.yml          # 이미지 빌드 후 manifests-repo 업데이트
    
    # 레포 2: K8s 매니페스트 (manifests-repo)
    manifests-repo/
    ├── base/               # 공통 설정
    │   ├── deployment.yaml
    │   ├── service.yaml
    │   └── kustomization.yaml
    ├── overlays/           # 환경별 오버레이
    │   ├── staging/
    │   │   ├── kustomization.yaml  # image.tag: git-sha
    │   │   └── replica-count.yaml  # replicas: 1
    │   └── production/
    │       ├── kustomization.yaml  # image.tag: v1.2.3
    │       └── replica-count.yaml  # replicas: 3
    └── argocd-apps/        # ArgoCD Application 정의
        ├── staging-app.yaml
        └── prod-app.yaml
    
    # CI 파이프라인에서 매니페스트 레포 이미지 태그 자동 업데이트
    # yq e '.images[0].newTag = env(GIT_SHA)' -i overlays/staging/kustomization.yaml
    # git commit -m "Update staging image to $GIT_SHA"

⚡ ArgoCD CLI 주요 명령어

# Application 관리
    argocd app list                          # 모든 앱 목록
    argocd app get myapp                     # 앱 상태 조회
    argocd app sync myapp                    # 수동 동기화
    argocd app sync myapp --dry-run          # 변경사항 미리 보기
    
    # 동기화 상태
    # Synced: Git과 K8s 일치
    # OutOfSync: Git과 K8s 불일치 (변경 감지)
    # Degraded: 헬스체크 실패
    
    # 히스토리 및 롤백
    argocd app history myapp                 # 배포 히스토리
    argocd app rollback myapp 3              # 3번째 배포로 롤백
    
    # 저장소 관리
    argocd repo add https://github.com/myorg/manifests.git \
      --username myuser \
      --password mytoken

🌍 Chapter 05. Terraform IaC 완전 정복

Infrastructure as Code - 클라우드 인프라를 코드로 정의하고 관리하는 현업 필수 기술

💡 Terraform이란?

HashiCorp가 만든 IaC(Infrastructure as Code) 도구입니다. AWS, GCP, Azure, K8s 등 100+ 프로바이더를 지원하며, HCL(HashiCorp Configuration Language)로 인프라를 선언적으로 정의합니다.

  • 선언적(Declarative): "어떻게"가 아닌 "무엇을" 원하는지 기술
  • 멱등성(Idempotent): 여러 번 적용해도 동일한 결과
  • 상태 관리(State): 현재 인프라 상태를 tfstate 파일로 추적

🔧 Terraform 핵심 워크플로우

📁 .tf 파일 작성 (인프라 정의)
           |
           v
    terraform init     # 프로바이더 플러그인 다운로드, 백엔드 초기화
           |
           v
    terraform validate # HCL 문법 검사
           |
           v
    terraform plan     # 변경사항 미리보기 (실제 적용 X) - 매우 중요!
           |
           v (plan 내용 검토 후 승인)
    terraform apply    # 실제 인프라 생성/변경/삭제
           |
           v
    terraform show     # 현재 상태 확인
    
    # 삭제 시
    terraform destroy  # 모든 리소스 삭제 (주의!)

💻 Terraform 기본 문법 & AWS 예시

# main.tf - AWS VPC + EC2 + RDS 기본 인프라
    
    # 1. 테라폼 설정 및 프로바이더
    terraform {
      required_version = ">= 1.0"
      required_providers {
        aws = {
          source  = "hashicorp/aws"
          version = "~> 5.0"
        }
      }
      # 원격 상태 저장 (팀 협업 필수!)
      backend "s3" {
        bucket         = "my-terraform-state"
        key            = "prod/terraform.tfstate"
        region         = "ap-northeast-2"
        dynamodb_table = "terraform-lock"     # 동시 접근 방지 잠금
        encrypt        = true
      }
    }
    
    provider "aws" {
      region = var.aws_region
    }
    
    # 2. 변수 정의 (variables.tf)
    variable "aws_region" {
      description = "AWS 리전"
      type        = string
      default     = "ap-northeast-2"
    }
    
    variable "environment" {
      description = "배포 환경 (dev/staging/prod)"
      type        = string
      validation {
        condition     = contains(["dev", "staging", "prod"], var.environment)
        error_message = "environment는 dev, staging, prod 중 하나여야 합니다."
      }
    }
    
    variable "instance_type" {
      description = "EC2 인스턴스 타입"
      type        = string
      default     = "t3.micro"
    }
    
    # 3. 데이터 소스 (기존 리소스 참조)
    data "aws_ami" "amazon_linux" {
      most_recent = true
      owners      = ["amazon"]
      filter {
        name   = "name"
        values = ["amzn2-ami-hvm-*-x86_64-gp2"]
      }
    }
    
    # 4. 리소스 정의
    resource "aws_vpc" "main" {
      cidr_block = "10.0.0.0/16"
      tags = {
        Name        = "${var.environment}-vpc"
        Environment = var.environment
        ManagedBy   = "terraform"
      }
    }
    
    resource "aws_subnet" "public" {
      count             = 2
      vpc_id            = aws_vpc.main.id
      cidr_block        = "10.0.${count.index}.0/24"
      availability_zone = data.aws_availability_zones.available.names[count.index]
      map_public_ip_on_launch = true
      tags = { Name = "${var.environment}-public-${count.index}" }
    }
    
    resource "aws_instance" "app" {
      ami           = data.aws_ami.amazon_linux.id
      instance_type = var.instance_type
      subnet_id     = aws_subnet.public[0].id
      key_name      = "my-keypair"
    
      user_data = base64encode(templatefile("scripts/user_data.sh", {
        environment = var.environment
        app_version = var.app_version
      }))
    
      tags = {
        Name        = "${var.environment}-app-server"
        Environment = var.environment
      }
    }
    
    # 5. 출력값 (outputs.tf)
    output "instance_public_ip" {
      description = "EC2 인스턴스 공인 IP"
      value       = aws_instance.app.public_ip
    }
    
    output "vpc_id" {
      value = aws_vpc.main.id
    }

📦 Terraform 모듈 (재사용 가능한 코드)

# 디렉토리 구조
    terraform/
    ├── modules/             # 재사용 가능한 모듈
    │   ├── vpc/
    │   │   ├── main.tf
    │   │   ├── variables.tf
    │   │   └── outputs.tf
    │   └── ec2/
    │       ├── main.tf
    │       └── variables.tf
    ├── environments/
    │   ├── dev/
    │   │   ├── main.tf      # 모듈 호출
    │   │   └── terraform.tfvars  # 개발 환경 변수값
    │   └── prod/
    │       ├── main.tf
    │       └── terraform.tfvars  # 프로덕션 변수값
    
    # 모듈 사용 예시 (environments/prod/main.tf)
    module "vpc" {
      source      = "../../modules/vpc"
      environment = "prod"
      cidr_block  = "10.0.0.0/16"
    }
    
    # 공식 모듈 레지스트리 사용
    module "eks" {
      source  = "terraform-aws-modules/eks/aws"
      version = "~> 20.0"
      cluster_name    = "my-eks-cluster"
      cluster_version = "1.29"
      vpc_id          = module.vpc.vpc_id
      subnet_ids      = module.vpc.private_subnets
    }

⚡ Terraform 실전 팁

  • tfstate는 절대 Git에 커밋하지 마세요! 민감 정보가 포함됩니다. S3 백엔드 사용 필수.
  • terraform plan 결과를 반드시 검토하고 approve 후 apply. destroy 표시된 리소스는 특히 주의.
  • tfenv로 Terraform 버전 관리 (팀원 간 버전 통일).
  • Terraform Cloud / Enterprise: 팀 협업, 실행 히스토리, Sentinel 정책 관리.
  • import: 기존 수동 생성 리소스를 Terraform 관리로 편입: terraform import aws_instance.app i-1234567890abcdef0

🤖 Chapter 06. Ansible 자동화 완전 정복

에이전트 없는 서버 자동화 - SSH 하나로 수백 대 서버를 동시에 관리

❓ Terraform vs Ansible 차이점

항목 Terraform Ansible
목적 인프라 프로비저닝 (서버/네트워크 생성) 서버 설정/소프트웨어 설치 (구성 관리)
언어 HCL (선언적) YAML Playbook (절차적)
에이전트 없음 (API 호출) 없음 (SSH 기반)
함께 사용 Terraform으로 서버 생성 후 Ansible로 설정 - 최고의 조합!

📋 Ansible 핵심 구성요소

Inventory (인벤토리)
관리할 서버 목록 정의. 정적(hosts 파일) 또는 동적(AWS EC2 플러그인) 인벤토리 사용.
Playbook (플레이북)
자동화 작업 정의 YAML 파일. "어떤 서버에 어떤 작업을 실행할지" 기술.
Task (태스크)
Playbook 내 개별 작업 단위. 모듈(module)을 호출하여 실행.
Module (모듈)
apt, yum, copy, template, service, docker_container 등 3000+ 내장 모듈.
Role (롤)
재사용 가능한 Playbook 묶음. Ansible Galaxy에서 공식 Role 다운로드 가능.
Vault (볼트)
민감 정보(비밀번호, API키) 암호화 저장. ansible-vault encrypt로 암호화.

🔧 Inventory 및 기본 Playbook 예시

# inventory/hosts.ini
    [webservers]
    web01.company.com ansible_user=ubuntu
    web02.company.com ansible_user=ubuntu
    
    [dbservers]
    db01.company.com ansible_user=ec2-user ansible_port=2222
    
    [production:children]  # 그룹의 그룹
    webservers
    dbservers
    
    [production:vars]      # 그룹 변수
    ansible_ssh_private_key_file=~/.ssh/prod-key.pem
    env=production
    
    # 동적 인벤토리 (AWS EC2)
    # ansible.cfg
    [defaults]
    inventory = aws_ec2.yaml
    
    # aws_ec2.yaml
    plugin: amazon.aws.aws_ec2
    regions: [ap-northeast-2]
    filters:
      tag:Environment: production
      instance-state-name: running
    keyed_groups:
      - key: tags.Role
        prefix: role
# webserver-setup.yml - 웹서버 설정 Playbook
    ---
    - name: 웹서버 초기 설정
      hosts: webservers
      become: yes              # sudo 권한으로 실행
      vars:
        app_port: 8080
        node_version: "18"
      vars_files:
        - vars/secrets.yml     # ansible-vault로 암호화된 변수
    
      tasks:
        - name: 패키지 목록 업데이트
          apt:
            update_cache: yes
            cache_valid_time: 3600  # 1시간 내 캐시면 skip
    
        - name: 필수 패키지 설치
          apt:
            name:
              - curl
              - git
              - nginx
            state: present
    
        - name: Node.js 설치
          shell: |
            curl -fsSL https://deb.nodesource.com/setup_{{ node_version }}.x | bash -
            apt-get install -y nodejs
          args:
            creates: /usr/bin/node  # 이미 있으면 건너뜀 (멱등성)
    
        - name: Nginx 설정 파일 배포
          template:
            src: templates/nginx.conf.j2  # Jinja2 템플릿
            dest: /etc/nginx/sites-available/myapp
            owner: root
            group: root
            mode: '0644'
          notify: nginx 재시작
    
        - name: Nginx 심볼릭 링크 생성
          file:
            src: /etc/nginx/sites-available/myapp
            dest: /etc/nginx/sites-enabled/myapp
            state: link
    
        - name: 방화벽 설정
          ufw:
            rule: allow
            port: "{{ app_port }}"
            proto: tcp
    
      handlers:               # notify 시 실행
        - name: nginx 재시작
          service:
            name: nginx
            state: restarted
    
      post_tasks:
        - name: 서비스 상태 확인
          uri:
            url: "http://localhost:{{ app_port }}/health"
            status_code: 200
          register: health_check
          retries: 3
          delay: 10

📦 Ansible Role 구조 & Galaxy

# Role 디렉토리 구조 (ansible-galaxy role init myrole)
    roles/myrole/
    ├── tasks/main.yml       # 주요 태스크
    ├── handlers/main.yml    # 핸들러
    ├── templates/           # Jinja2 템플릿 (.j2)
    ├── files/               # 정적 파일
    ├── vars/main.yml        # 변수 (높은 우선순위)
    ├── defaults/main.yml    # 기본 변수 (낮은 우선순위)
    ├── meta/main.yml        # Role 메타데이터, 의존성
    └── README.md
    
    # Ansible Galaxy에서 공식 Role 사용
    ansible-galaxy install geerlingguy.nginx     # Nginx Role
    ansible-galaxy install geerlingguy.docker    # Docker Role
    ansible-galaxy collection install amazon.aws # AWS 컬렉션
    
    # requirements.yml로 의존성 관리
    # ansible-galaxy install -r requirements.yml
    
    # Ansible 자주 사용하는 명령어
    ansible all -i inventory/ -m ping           # 연결 테스트
    ansible webservers -i inventory/ -m shell -a "df -h"  # 임시 명령
    ansible-playbook -i inventory/ site.yml     # Playbook 실행
    ansible-playbook -i inventory/ site.yml --check  # 드라이런
    ansible-playbook -i inventory/ site.yml --tags "install"  # 태그 실행
    ansible-vault encrypt vars/secrets.yml      # 암호화
    ansible-vault decrypt vars/secrets.yml      # 복호화
    ansible-vault view vars/secrets.yml         # 내용 확인

🛡️ Chapter 07. CI/CD 보안 (DevSecOps) 완전 정복

보안을 파이프라인에 내장 - Shift Left Security로 배포 전에 취약점 차단

⚠️ DevSecOps란?

"Shift Left" - 보안 검증을 개발 후반이 아닌 초기 단계(왼쪽)에서 수행하는 접근법입니다. 파이프라인에 보안 자동화를 통합하여 취약한 코드가 프로덕션에 배포되는 것을 사전에 차단합니다.

📊 DevSecOps 보안 레이어

단계 보안 도구 대상 예시 도구
코드 작성 SAST 소스코드 취약점 SonarQube, Semgrep, CodeQL
의존성 관리 SCA 오픈소스 라이브러리 취약점 Snyk, Dependabot, OWASP DC
이미지 빌드 컨테이너 스캔 Docker 이미지 CVE Trivy, Grype, Docker Scout
IaC 코드 IaC 스캔 Terraform/K8s 설정 오류 tfsec, checkov, kube-score
시크릿 관리 시크릿 스캔 코드 내 하드코딩 키 GitLeaks, TruffleHog, git-secrets
런타임 DAST 동적 취약점 (OWASP Top 10) OWASP ZAP, Burp Suite

🔧 DevSecOps GitHub Actions 파이프라인

# .github/workflows/devsecops.yml
    name: DevSecOps Security Pipeline
    on: [push, pull_request]
    
    jobs:
      security:
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v4
            with:
              fetch-depth: 0  # 전체 히스토리 (시크릿 스캔용)
    
          # 1. 시크릿 스캔 - 코드에 API 키가 없는지 확인
          - name: GitLeaks 시크릿 스캔
            uses: gitleaks/gitleaks-action@v2
            env:
              GITHUB_TOKEN: GITHUB_TOKEN_VALUE
              GITLEAKS_LICENSE: LICENSE_VALUE  # 유료 버전
    
          # 2. SAST - 소스코드 취약점 스캔
          - name: Semgrep SAST 스캔
            uses: returntocorp/semgrep-action@v1
            with:
              config: >-
                p/owasp-top-ten
                p/javascript
                p/secrets
    
          # 3. 의존성 취약점 스캔 (npm audit)
          - name: npm audit
            run: npm audit --audit-level=high
    
          # 4. Trivy - Docker 이미지 스캔
          - name: Trivy 이미지 스캔
            uses: aquasecurity/trivy-action@master
            with:
              image-ref: myapp:latest
              exit-code: '1'              # CRITICAL 발견 시 빌드 실패
              severity: 'CRITICAL,HIGH'
              format: 'sarif'             # GitHub Security 탭에 결과 표시
              output: 'trivy-results.sarif'
    
          - name: Trivy 결과 GitHub에 업로드
            uses: github/codeql-action/upload-sarif@v3
            with:
              sarif_file: 'trivy-results.sarif'
    
          # 5. Terraform IaC 스캔
          - name: tfsec IaC 스캔
            uses: aquasecurity/tfsec-action@v1.0.0
            with:
              soft_fail: false  # 심각 취약점 발견 시 실패
    
          # 6. OWASP ZAP DAST (스테이징 배포 후 실행)
          - name: ZAP 스캔 (스테이징)
            uses: zaproxy/action-full-scan@v0.9.0
            with:
              target: 'https://staging.myapp.com'

🔑 시크릿 관리 Best Practice

❌ 절대 하지 말아야 할 것
  • 소스코드에 API 키 하드코딩
  • .env 파일 Git 커밋
  • Dockerfile ENV에 비밀번호
  • 공개 저장소에 credentials
✅ 올바른 방법
  • GitHub Actions Secrets 사용
  • AWS Secrets Manager / Vault
  • 환경변수로 런타임 주입
  • OIDC로 임시 자격증명 발급

🎯 Chapter 08. 고급 배포 전략 완전 정복

무중단 배포의 모든 것 - 서비스 다운타임 0을 위한 배포 패턴

📊 배포 전략 비교

전략 원리 다운타임 롤백 속도 비용
Recreate 기존 중지 후 새 버전 배포 있음 느림 낮음
Rolling Update 인스턴스 순차 교체 없음 중간 낮음
Blue-Green 동일 환경 2개, 트래픽 전환 없음 즉시 2배
Canary 일부 트래픽만 새 버전으로 없음 빠름 중간
A/B Testing 사용자 그룹별 다른 버전 없음 중간 중간

🔵 Blue-Green 배포 상세

현재 상태:
      Load Balancer ----> Blue (v1.0, 활성)
                     \ -> Green (v1.1, 배포 준비)
    
    배포 과정:
      1. Green 환경에 v1.1 배포 (사용자에게 보이지 않음)
      2. Green 환경에서 스모크 테스트 실행
      3. LB 트래픽을 Green으로 전환 (순간적, 다운타임 없음)
      4. Blue는 잠시 대기 (이상 시 즉시 롤백 가능)
      5. 안정 확인 후 Blue를 다음 배포를 위해 준비
    
    배포 후 상태:
      Load Balancer ----> Green (v1.1, 활성)
                     \ -> Blue (v1.0, 대기)
# AWS ALB + ECS Blue-Green 배포 예시 (Terraform)
    resource "aws_codedeploy_deployment_group" "main" {
      app_name              = aws_codedeploy_app.main.name
      deployment_group_name = "myapp-deploy-group"
      deployment_config_name = "CodeDeployDefault.ECSAllAtOnce"
    
      ecs_service {
        cluster_name = aws_ecs_cluster.main.name
        service_name = aws_ecs_service.main.name
      }
    
      deployment_style {
        deployment_option = "WITH_TRAFFIC_CONTROL"
        deployment_type   = "BLUE_GREEN"
      }
    
      blue_green_deployment_config {
        deployment_ready_option {
          action_on_timeout = "CONTINUE_DEPLOYMENT"
        }
        terminate_blue_instances_on_deployment_success {
          action                           = "TERMINATE"
          termination_wait_time_in_minutes = 5
        }
      }
    
      load_balancer_info {
        target_group_pair_info {
          prod_traffic_route {
            listener_arns = [aws_alb_listener.main.arn]
          }
          target_group { name = aws_alb_target_group.blue.name }
          target_group { name = aws_alb_target_group.green.name }
        }
      }
    }

🖤 Canary 배포 상세 (Kubernetes + ArgoRollouts)

# Argo Rollouts - Canary 배포 전략
    apiVersion: argoproj.io/v1alpha1
    kind: Rollout
    metadata:
      name: myapp
    spec:
      replicas: 10
      selector:
        matchLabels:
          app: myapp
      template:
        metadata:
          labels:
            app: myapp
        spec:
          containers:
          - name: myapp
            image: myregistry.io/myapp:1.2.3
      strategy:
        canary:
          steps:
          - setWeight: 10    # 1단계: 10% 트래픽만 새 버전으로
          - pause: {duration: 5m}  # 5분 대기 후 메트릭 확인
          - setWeight: 30    # 2단계: 30% 트래픽
          - pause: {duration: 10m}
          - setWeight: 60    # 3단계: 60% 트래픽
          - pause: {}        # 수동 승인 대기
          - setWeight: 100   # 4단계: 완전 전환
          analysis:
            templates:
            - templateName: success-rate  # 에러율 초과 시 자동 롤백
          canaryMetadata:
            labels:
              deployment: canary
    
    # 자동 분석 템플릿
    apiVersion: argoproj.io/v1alpha1
    kind: AnalysisTemplate
    metadata:
      name: success-rate
    spec:
      metrics:
      - name: success-rate
        interval: 1m
        successCondition: result[0] >= 0.95  # 95% 이상 성공률
        failureLimit: 3
        provider:
          prometheus:
            address: http://prometheus:9090
            query: |
              rate(http_requests_total{status!~"5.."}[1m]) /
              rate(http_requests_total[1m])

📋 배포 전략 선택 가이드

  • 🔰 Rolling: 소규모 서비스, 리소스 절약, 빠른 배포 (K8s 기본값)
  • 🔵 Blue-Green: 즉시 롤백이 중요한 금융/결제 서비스
  • 🟡 Canary: 대규모 트래픽, 새 기능 점진적 출시, 실험적 기능
  • 🟠 A/B Testing: 사용자 행동 측정, Feature Flag 기반 실험

🔔 Chapter 09. 모니터링 & 알림 자동화

배포 후가 진짜 시작 - 장애를 사람보다 먼저 감지하는 자동화 알림 시스템

📋 알림 채널 자동화 패턴

# GitHub Actions - 슬랙 알림 통합
    - name: 배포 성공 알림
      uses: slackapi/slack-github-action@v1.26.0
      with:
        channel-id: 'C0XXXXXXX'
        payload: |
          {
            "text": "🎉 *배포 성공!*",
            "attachments": [
              {
                "color": "good",
                "fields": [
                  {"title": "환경", "value": "Production", "short": true},
                  {"title": "버전", "value": "v1.2.3", "short": true},
                  {"title": "배포자", "value": "GITHUB_ACTOR_VAR", "short": true},
                  {"title": "커밋", "value": "GITHUB_SHA_SHORT", "short": true}
                ],
                "actions": [{
                  "type": "button",
                  "text": "🔍 배포 로그 확인",
                  "url": "GITHUB_SERVER_URL_VAR/GITHUB_REPO/actions/runs/GITHUB_RUN_ID"
                }]
              }
            ]
          }
      env:
        SLACK_BOT_TOKEN: SLACK_BOT_TOKEN_SECRET
    
    - name: 배포 실패 알림
      if: failure()
      uses: slackapi/slack-github-action@v1.26.0
      with:
        channel-id: 'C0XXXXXXX'
        payload: |
          {"text": "🚨 *배포 실패* - 즉시 확인 필요!",
           "attachments": [{"color": "danger", "text": "GITHUB_JOB 실패"}]}
      env:
        SLACK_BOT_TOKEN: SLACK_BOT_TOKEN_SECRET

📊 SLO/SLA/SLI 개념 정리

용어 의미 예시
SLI 서비스 수준 지표 (측정값) 현재 가용성 99.95%
SLO 서비스 수준 목표 (내부 목표) 가용성 99.9% 이상 유지
SLA 서비스 수준 계약 (고객과 약속) 가용성 99.5% 보장, 위반 시 환불
Error Budget 허용 가능한 오류 시간 99.9% SLO = 월 43.8분 다운 허용

💡 핵심 모니터링 메트릭 - RED / USE 방법론

RED (서비스 관점): Rate(요청 수), Errors(에러율), Duration(응답 시간)

USE (리소스 관점): Utilization(사용률), Saturation(포화도), Errors(에러)

🏗️ Chapter 10. 완전 자동화 실전 파이프라인 구축

End-to-End CI/CD - 코드 커밋부터 프로덕션 배포까지 완전 자동화

# 완전 자동화 CI/CD 아키텍처 전체 그림
    
    개발자 Push
        |
        v
    [GitHub] ---webhook---> [GitHub Actions CI]
                                  |
                  +---------------+---------------+
                  v               v               v
              lint/test      docker build     trivy scan
                  |               |               |
                  +---------------+---------------+
                                  |
                                  v (모두 통과)
                        [ECR/GHCR Push] (image:git-sha)
                                  |
                                  v
                  [manifests 레포 image tag 자동 업데이트]
                                  |
                                  v
                        [ArgoCD 변경 감지]
                                  |
                  +---------------+
                  v               v
           [Staging 자동 동기화]  [Prod 수동 승인 후]
                  |               |
                  v               v
           [스테이징 E2E 테스트]  [Canary 10%→30%→100%]
                  |               |
                  v               v
           [슬랙 스테이징 알림]  [슬랙 프로덕션 알림]
                                  |
                                  v
                        [Prometheus/Grafana 모니터링]
                        [에러율 급증 시 자동 롤백]

💻 완전 자동화 파이프라인 - 전체 코드

# .github/workflows/full-pipeline.yml
    name: Full Automation Pipeline
    
    on:
      push:
        branches: [main]
    
    env:
      ECR_REGISTRY: 123456789012.dkr.ecr.ap-northeast-2.amazonaws.com
      IMAGE_NAME: myapp
    
    jobs:
      ci:
        name: CI - Build, Test, Security
        runs-on: ubuntu-latest
        outputs:
          image-tag: sha-GITHUB_SHA_SHORT
        steps:
          - uses: actions/checkout@v4
          - uses: actions/setup-node@v4
            with: { node-version: '18', cache: 'npm' }
    
          - run: npm ci
          - run: npm run lint
          - run: npm test -- --coverage
          - run: npm run build
    
          - name: AWS 인증 (OIDC)
            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: ECR 로그인
            uses: aws-actions/amazon-ecr-login@v2
    
          - name: Docker 빌드 & Push
            run: |
              IMAGE_TAG=sha-$(echo GITHUB_SHA | cut -c1-7)
              docker build -t $ECR_REGISTRY/$IMAGE_NAME:$IMAGE_TAG .
              docker push $ECR_REGISTRY/$IMAGE_NAME:$IMAGE_TAG
              echo "IMAGE_TAG=$IMAGE_TAG" >> $GITHUB_OUTPUT
    
          - name: Trivy 보안 스캔
            uses: aquasecurity/trivy-action@master
            with:
              image-ref: myregistry/myapp:latest
              severity: CRITICAL
              exit-code: '1'
    
      update-manifests:
        needs: ci
        runs-on: ubuntu-latest
        steps:
          - name: Manifests 레포 체크아웃
            uses: actions/checkout@v4
            with:
              repository: myorg/k8s-manifests
              token: MANIFESTS_REPO_TOKEN_SECRET
    
          - name: 이미지 태그 업데이트
            run: |
              cd overlays/staging
              # kustomize로 이미지 태그 업데이트
              kustomize edit set image myapp=myregistry/myapp:${{ needs.ci.outputs.image-tag }}
              git config user.email "ci@myorg.com"
              git config user.name "CI Bot"
              git add .
              git commit -m "ci: update staging image to ${{ needs.ci.outputs.image-tag }}"
              git push
          # ArgoCD가 변경 감지 후 자동으로 스테이징에 배포!

🔧 Chapter 11. 트러블슈팅 & 파이프라인 최적화

느린 CI가 개발 속도를 죽인다 - 파이프라인 병목 제거와 실전 디버깅

⏱️ 파이프라인 속도 최적화 10가지

1. 의존성 캐싱
node_modules, pip, Maven ~/.m2 캐싱으로 설치 시간 제거. actions/cache 활용.
2. Docker 레이어 캐싱
BuildKit + cache-from type=gha로 이미지 레이어 캐시. 빌드 70% 단축 가능.
3. Job 병렬화
독립적인 Job은 병렬 실행. lint, test, security scan을 동시에.
4. 테스트 병렬화
Matrix 전략으로 테스트 파일 분할 병렬 실행. 시간 1/N으로 단축.
5. 조건부 실행
paths 필터로 관련 파일 변경 시만 실행. docs 변경엔 CI 불필요.
6. 빠른 실패
Lint를 가장 먼저 실행. 빌드 전에 오류 발견하면 전체 시간 절약.
7. 자체 Runner
Self-hosted Runner로 더 강력한 하드웨어 사용. 특수 GPU/메모리 필요 시.
8. 경량 이미지
Alpine 베이스 이미지로 Pull 시간 단축. node:18 1.1GB → node:18-alpine 55MB.

🚨 파이프라인 트러블슈팅 가이드

증상 원인 해결 방법
빌드가 갑자기 느려짐 캐시 무효화, 의존성 증가 캐시 키 확인, 불필요한 의존성 제거
플래키 테스트 (Flaky Test) 비결정론적 테스트, 타임아웃 retry 메커니즘, 테스트 격리 강화
시크릿 접근 실패 권한 없음, 시크릿 이름 오타 secrets 설정 확인, 환경 범위 확인
Docker Push 실패 인증 만료, 레지스트리 권한 레지스트리 로그인 단계 확인
배포 후 서비스 다운 헬스체크 실패, 설정 오류 롤백 즉시 실행, 로그 분석

📋 GitHub Actions 최적화 전후 비교

# ❌ 느린 파이프라인 (최적화 전)
    jobs:
      build:
        steps:
          - run: npm install          # 매번 설치 (5분)
          - run: npm run lint         # 순차 실행
          - run: npm test             # 순차 실행
          - run: docker build ...     # 캐시 없이 빌드 (10분)
      # 총 소요시간: ~20분
    
    # ✅ 빠른 파이프라인 (최적화 후)
    jobs:
      lint:
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v4
          - uses: actions/setup-node@v4
            with: { node-version: '18', cache: 'npm' }  # 캐시!
          - run: npm ci
          - run: npm run lint
    
      test:
        runs-on: ubuntu-latest
        strategy:
          matrix:
            shard: [1, 2, 3, 4]       # 4개로 분할 병렬 실행
        steps:
          - uses: actions/checkout@v4
          - uses: actions/setup-node@v4
            with: { node-version: '18', cache: 'npm' }
          - run: npm ci
          - run: npm test -- --shard=${{ matrix.shard }}/4
    
      build:
        needs: [lint, test]           # 병렬로 진행된 lint+test 완료 후
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v4
          - uses: docker/setup-buildx-action@v3
          - uses: docker/build-push-action@v5
            with:
              cache-from: type=gha    # GitHub Actions 캐시 활용!
              cache-to: type=gha,mode=max
      # 총 소요시간: ~5분 (4배 단축!)

🎤 Chapter 12. 현업 면접 Q&A TOP 15

DevOps / 인프라 엔지니어 면접에서 실제로 나오는 CI/CD 질문과 모범 답안

Q1. CI와 CD의 차이점을 설명해주세요.

A: CI(지속적 통합)는 개발자들이 자주 코드를 통합하고, 통합 시마다 자동 빌드와 테스트를 실행하여 문제를 조기에 발견하는 프랙티스입니다. CD는 두 가지 의미가 있습니다. Continuous Delivery는 CI를 통과한 코드를 항상 배포 가능한 상태로 유지하는 것이고, Continuous Deployment는 승인 없이 프로덕션까지 자동 배포합니다. 일반적으로 Delivery는 수동 승인 게이트가 있고, Deployment는 완전 자동화됩니다.

Q2. GitOps란 무엇이고 기존 CD와 어떻게 다른가요?

A: GitOps는 Git 저장소를 인프라와 애플리케이션의 Single Source of Truth로 사용하는 운영 방식입니다. 기존 Push 방식 CD는 CI 서버가 직접 kubectl이나 Ansible을 실행하여 배포합니다. GitOps(Pull 방식)에서는 ArgoCD 같은 에이전트가 Git 변경을 지속적으로 감지하고 클러스터 상태를 Git과 동기화합니다. 장점으로는 선언적 인프라, 감사 추적, 드리프트 자동 감지, CI 서버에 K8s 접근권한 불필요 등이 있습니다.

Q3. Blue-Green 배포와 Canary 배포의 차이점은?

A: Blue-Green은 동일한 프로덕션 환경을 두 세트(Blue: 현재, Green: 새 버전) 운영하고 트래픽을 한번에 전환합니다. 즉시 롤백이 가능하지만 리소스 2배가 필요합니다. 금융, 결제처럼 일관성이 중요한 서비스에 적합합니다. Canary는 새 버전에 전체 트래픽의 일부(5~10%)만 보내고 점진적으로 비율을 높입니다. 실제 사용자 대상 검증이 가능하고 리소스 효율적이지만, 두 버전이 동시에 운영되므로 API 하위호환성 관리가 필요합니다.

Q4. Terraform의 state 파일이란 무엇이고 어떻게 관리하나요?

A: Terraform state는 Terraform이 관리하는 실제 인프라와 .tf 파일 간의 매핑 정보를 담은 JSON 파일입니다. 리소스 ID, 속성값 등이 저장됩니다. 팀 환경에서 state를 로컬에 두면 협업이 불가능하므로, S3 + DynamoDB 조합으로 원격 저장합니다. S3에 state 파일을 암호화 저장하고, DynamoDB로 동시 수정을 방지하는 잠금(locking)을 구현합니다. state 파일에는 민감 정보가 포함될 수 있으므로 Git에 절대 커밋하면 안 됩니다.

Q5. CI/CD 파이프라인에서 보안을 어떻게 강화하나요?

A: 여러 레이어에서 보안을 적용합니다. 첫째, SAST(정적 분석)로 코드 취약점을 빌드 전에 발견합니다. 둘째, SCA로 오픈소스 라이브러리 취약점을 스캔합니다. 셋째, Trivy로 Docker 이미지 CVE를 스캔하고 CRITICAL 발견 시 빌드를 실패시킵니다. 넷째, GitLeaks로 코드에 API 키가 하드코딩되어 있지 않은지 확인합니다. 다섯째, tfsec으로 Terraform 코드의 보안 설정 오류를 검사합니다. 여섯째, OIDC로 CI 서버에 장기 Access Key 대신 임시 자격증명을 사용합니다.

Q6. Jenkins와 GitHub Actions 중 어떤 것을 선택하겠습니까?

A: 상황에 따라 다릅니다. GitHub Actions는 GitHub를 이미 사용하는 팀에 자연스럽게 통합되고, 설정이 간단하며, Marketplace의 다양한 Action을 활용할 수 있습니다. 스타트업이나 공개 저장소에 적합합니다. Jenkins는 온프레미스 환경, 내부망 접근이 필요한 경우, 1800개 이상의 플러그인이 필요한 복잡한 파이프라인, 대기업 엔터프라이즈 환경에 적합합니다. 실제 현업에서는 두 가지를 혼용하는 경우도 많습니다. GitHub Actions로 CI를 하고 Jenkins가 배포를 담당하는 패턴도 있습니다.

Q7~Q15. 추가 면접 질문

Q7. 배포 롤백을 어떻게 구현하시겠습니까?

A: K8s Deployment의 경우 kubectl rollout undo로 즉시 이전 버전 롤백. ArgoCD는 git revert + push로 자동 롤백. Blue-Green은 LB를 Blue로 다시 전환. 자동 롤백은 Prometheus 메트릭 기반으로 에러율 5% 초과 시 ArgoRollouts가 자동 실행하도록 구성합니다.

Q8. IaC(Infrastructure as Code)를 사용하는 이유는?

A: 버전 관리(Git), 재현 가능한 인프라, 코드 리뷰를 통한 변경 관리, 문서화 효과, 자동화된 프로비저닝, 드리프트 감지가 주요 이유입니다. 수동 클릭으로 만든 인프라는 "무엇이 왜 만들어졌는지" 추적이 어렵지만 Terraform으로 관리하면 PR 히스토리로 모든 변경이 추적됩니다.

Q9. DORA 메트릭이란 무엇인가요?

A: Google이 정의한 DevOps 성과 측정 지표 4가지입니다. 배포 빈도(Deployment Frequency), 변경 리드 타임(Lead Time for Changes), 변경 실패율(Change Failure Rate), 서비스 복구 시간(MTTR). Elite 팀은 하루 여러 번 배포, 1시간 이내 리드 타임, 0~15% 실패율, 1시간 이내 복구를 달성합니다.

Q10. Feature Flag란 무엇이고 어떻게 활용하나요?

A: Feature Flag(Feature Toggle)는 코드 배포 없이 특정 기능의 활성화 여부를 동적으로 제어하는 기술입니다. 새 기능을 코드에 미리 배포하되 Flag로 비활성화하고, 준비가 되면 Flag만 켜서 활성화합니다. LaunchDarkly, AWS AppConfig, Flagsmith 등의 도구를 사용합니다. A/B 테스트, Canary 배포, Kill Switch(긴급 기능 비활성화)에 활용됩니다.

Q11. Ansible 멱등성(Idempotency)이란?

A: 같은 Playbook을 여러 번 실행해도 항상 동일한 결과가 나오는 성질입니다. 예를 들어 "nginx 설치" 태스크를 이미 설치된 서버에 실행해도 오류 없이 "already installed" 상태로 건너뜁니다. 멱등성이 보장되면 파이프라인에서 안전하게 Playbook을 반복 실행할 수 있습니다.

Q12~Q15. 실전 경험 질문

Q12: CI 파이프라인이 20분 넘게 걸리는데 어떻게 줄이시겠습니까? → 의존성 캐싱, 병렬화, Docker 레이어 캐시, 조건부 실행, 불필요한 단계 제거 순으로 접근합니다.

Q13: 배포 중 프로덕션 DB 마이그레이션은 어떻게 처리하시겠습니까? → Zero-downtime migration: 하위호환 컬럼 추가 먼저 배포 → 앱 배포 → 이전 컬럼 제거 순서로 진행합니다.

Q14: 멀티 클라우드 환경에서 CI/CD를 어떻게 구성하시겠습니까? → Terraform으로 IaC 통합 관리, 공통 Docker 이미지 레지스트리, 환경별 ArgoCD Application 분리.

Q15: 마이크로서비스 환경에서 배포 파이프라인은 어떻게 설계하시겠습니까? → 서비스별 독립 파이프라인, 공통 Library는 Shared Workflow, 통합 테스트는 스테이징에서 실행, ArgoCD App of Apps 패턴으로 관리.

✅ CI/CD & 자동화 현업 투입 체크리스트

이 모든 항목을 이해하고 실습했다면 현업 CI/CD 엔지니어로 준비 완료!

⚡ CI 기초 (Ch01-03)

  • ☐ CI/CD 개념과 DevOps 철학 설명 가능
  • ☐ GitHub Actions Workflow 직접 작성
  • ☐ 테스트 자동화 파이프라인 구현
  • ☐ Docker 빌드 & 레지스트리 Push 자동화
  • ☐ Jenkins Declarative Pipeline 작성
  • ☐ 파이프라인 성공/실패 Slack 알림

🌍 GitOps & IaC (Ch04-05)

  • ☐ ArgoCD 설치 및 Application 생성
  • ☐ GitOps 레포지토리 구조 설계
  • ☐ Kustomize 오버레이 환경 분리
  • ☐ Terraform으로 AWS VPC+EC2 생성
  • ☐ S3 원격 상태 관리 설정
  • ☐ Terraform 모듈 작성 경험

🤖 자동화 & 보안 (Ch06-08)

  • ☐ Ansible Playbook 작성 경험
  • ☐ Ansible Vault로 시크릿 암호화
  • ☐ Trivy 이미지 취약점 스캔 파이프라인
  • ☐ GitLeaks 시크릿 스캔 적용
  • ☐ Blue-Green 배포 구현 경험
  • ☐ Canary 배포 전략 이해

📊 운영 & 최적화 (Ch09-12)

  • ☐ 배포 알림 자동화 (Slack/이메일)
  • ☐ SLO/SLI/SLA 개념 이해
  • ☐ End-to-End 파이프라인 구축 경험
  • ☐ 파이프라인 최적화 (캐싱, 병렬화)
  • ☐ DORA 메트릭 측정 및 개선
  • ☐ 면접 Q&A 15개 자신있게 답변 가능

🎉 CI/CD & 자동화 완전 정복!

이 포스팅을 통해 CI/CD의 A부터 Z까지 학습을 완료했습니다.
다음 단계로 쿠버네티스 완전 정복과 실전 프로젝트로 도전하세요!

🚀 InfraDevGuide0007 예고: 실전 프로젝트 & 취업 전략 완전 정복

🗺️ InfraDevGuide 시리즈 전체 로드맵

단계 제목 상태
InfraDevGuide0001 인프라 개념 완전 정복 A-Z ✅ 완료
InfraDevGuide0002 Linux 기초 완전 정복 A-Z ✅ 완료
InfraDevGuide0003 네트워크 기초 완전 정복 A-Z ✅ 완료
InfraDevGuide0004 클라우드 AWS 완전 정복 A-Z ✅ 완료
InfraDevGuide0005 Docker & 컨테이너 완전 정복 A-Z ✅ 완료
InfraDevGuide0006 CI/CD & 자동화 완전 정복 A-Z ← 현재 📖 학습중
InfraDevGuide0007 실전 프로젝트 & 취업 전략 A-Z ⏳ 예정

📝 InfraDevGuide 시리즈 | 인프라 개발자를 위한 완전 정복 가이드

📅 2025 | CI/CD & 자동화 완전 정복 A-Z