Guider/Backend/BackendDevGuide0012
Backend#12

BackendDevGuide0012

클라우드와 컨테이너 완전 정복

☁️
🐳
BackendDevGuide · Episode 12
☁️🐳

클라우드와 컨테이너
완전 정복 A-Z

Docker부터 Kubernetes, AWS까지 — 현업 백엔드의 배포 인프라 완전 가이드

🐳 Docker ⚙️ Kubernetes ☁️ AWS 🔄 CI/CD 📦 IaC 📊 모니터링
📚
14
핵심 챕터
💻
60+
실전 코드/설정
🚀
100%
현업 적용
A-Z
완전 가이드

📋 전체 학습 목차 — 이 글 하나로 클라우드/컨테이너 완전 정복

🐳 Ch01 · Docker란? 컨테이너 기초 완전 이해
📝 Ch02 · Dockerfile 작성 완전 가이드
🔗 Ch03 · Docker Compose — 멀티 컨테이너 관리
⚙️ Ch04 · Kubernetes 기초 — 오케스트레이션
🏗️ Ch05 · Kubernetes 심화 — Deployment, Service
☁️ Ch06 · AWS 핵심 서비스 완전 정복
🗄️ Ch07 · AWS RDS, ElastiCache, S3
🔄 Ch08 · CI/CD 파이프라인 완전 구축
📦 Ch09 · Infrastructure as Code (Terraform)
🔒 Ch10 · 클라우드 보안 완전 정복
📊 Ch11 · 모니터링 — Prometheus + Grafana
💰 Ch12 · 클라우드 비용 최적화 전략
🌐 Ch13 · 서버리스(Lambda)와 컨테이너 선택
🎯 Ch14 · 면접 Q&A + 실무 체크리스트

🐳 Chapter 01

Docker란? — 컨테이너 기초 완전 이해

"내 컴퓨터에서는 되는데..." 이 말이 사라지는 날, Docker를 배운 날입니다

🚢 Docker를 배 여행으로 이해해봅시다

옛날에는 물건을 배에 실을 때 크기/모양이 제각각이라 매번 다르게 포장해야 했습니다. 그러다 표준화된 컨테이너(container)가 등장하면서 어떤 배에든, 어떤 항구에서든 같은 방식으로 처리할 수 있게 됐죠. Docker도 똑같습니다.

😫 Docker 없는 세상

• 개발: Mac에서 Node.js 18 사용
• 서버: Ubuntu에 Node.js 16 설치됨
• 결과: "내 PC에서는 됐는데?!"

• 팀원 A: Python 3.9
• 팀원 B: Python 3.11
• 결과: 라이브러리 충돌 지옥

😊 Docker 있는 세상

• 모든 환경을 컨테이너로 패키징
• "이 컨테이너 이미지 실행해"
• 결과: 어디서나 동일하게 실행!

• Mac, Windows, Linux 무관
• 개발 = 테스트 = 운영 환경 동일

🔑 핵심 개념: 이미지 vs 컨테이너

📄
이미지(Image)
레시피(읽기 전용)
변경 불가능
Docker Hub에 공유
🍕
컨테이너(Container)
레시피로 만든 음식(실행 중)
여러 개 동시 실행 가능
삭제하면 데이터 사라짐
🗂️
볼륨(Volume)
영구 데이터 저장소
컨테이너 삭제 후에도 유지
DB 데이터 보관에 필수

💻 Docker 핵심 명령어 완전 정복

# ━━━ 이미지 관리 ━━━
docker pull nginx:latest            # Docker Hub에서 이미지 다운로드
docker images                       # 로컬 이미지 목록 보기
docker rmi nginx:latest             # 이미지 삭제
docker build -t myapp:1.0 .         # Dockerfile로 이미지 빌드

# ━━━ 컨테이너 실행 ━━━
docker run nginx                    # 기본 실행 (포그라운드)
docker run -d nginx                 # 백그라운드(detached) 실행
docker run -d -p 8080:80 nginx      # 포트 매핑 (호스트:컨테이너)
docker run -d   -p 3000:3000   -e NODE_ENV=production            # 환경변수 설정
  -v /data:/app/data               # 볼륨 마운트
  --name myapp                      # 컨테이너 이름 지정
  --restart unless-stopped          # 자동 재시작
  myapp:1.0

# ━━━ 컨테이너 관리 ━━━
docker ps                           # 실행 중인 컨테이너 목록
docker ps -a                        # 모든 컨테이너 (정지 포함)
docker stop myapp                   # 컨테이너 정지
docker start myapp                  # 컨테이너 시작
docker rm myapp                     # 컨테이너 삭제
docker logs -f myapp                # 실시간 로그 확인
docker exec -it myapp bash          # 실행 중인 컨테이너 내부 접속
docker stats                        # 컨테이너 리소스 사용량 모니터링

# ━━━ 이미지 배포 ━━━
docker tag myapp:1.0 myrepo/myapp:1.0
docker push myrepo/myapp:1.0        # Docker Hub에 업로드

# ━━━ 정리 명령어 ━━━
docker system prune -a              # 미사용 이미지/컨테이너 전체 삭제

📝 Chapter 02

Dockerfile 작성 완전 가이드 — 프로덕션급 이미지 만들기

"Dockerfile은 서버 구축 매뉴얼을 코드로 만든 것입니다"

📋 Dockerfile 명령어 완전 정복

명령어 역할 예시
FROM 베이스 이미지 지정 FROM node:20-alpine
WORKDIR 작업 디렉토리 설정 WORKDIR /app
COPY 호스트 → 컨테이너 파일 복사 COPY . .
RUN 빌드 시 명령어 실행 RUN npm ci --only=production
ENV 환경변수 설정 ENV NODE_ENV=production
EXPOSE 포트 문서화 (실제 개방 아님) EXPOSE 3000
CMD 컨테이너 시작 시 기본 명령 CMD ["node", "dist/main.js"]
ENTRYPOINT 항상 실행되는 명령 (CMD와 결합) ENTRYPOINT ["docker-entrypoint.sh"]
ARG 빌드 시 인자 (런타임엔 없음) ARG NODE_VERSION=20
USER 실행 사용자 (보안상 non-root) USER node

💻 프로덕션급 NestJS Dockerfile — 멀티스테이지 빌드

# ━━━ Stage 1: Builder (빌드 전용) ━━━
FROM node:20-alpine AS builder

WORKDIR /app

# package 파일만 먼저 복사 → 의존성 캐싱 최적화
# 소스코드가 바뀌어도 의존성이 안 바뀌면 npm install 캐시 사용!
COPY package*.json ./
RUN npm ci                   # npm install보다 빠르고 안전 (lock 파일 기반)

COPY . .
RUN npm run build            # TypeScript → JavaScript 빌드

# ━━━ Stage 2: Runner (실행 전용) ━━━
FROM node:20-alpine AS runner

# 보안: 비루트 사용자로 실행
RUN addgroup -g 1001 -S nodejs && adduser -S nestjs -u 1001

WORKDIR /app

# 빌드 결과물만 복사 (node_modules 개발 의존성 제외)
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/package*.json ./
RUN npm ci --only=production && npm cache clean --force

USER nestjs

EXPOSE 3000

# 헬스체크: 컨테이너 정상 여부 확인
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3   CMD wget --quiet --tries=1 --spider http://localhost:3000/health || exit 1

CMD ["node", "dist/main.js"]

# 멀티스테이지 빌드 효과:
# 일반 빌드: ~800MB (빌드 도구 + 개발 의존성 포함)
# 멀티스테이지: ~150MB (5배 이상 작아짐!)

🚫 .dockerignore — 이것도 꼭 만드세요!

# .dockerignore — 빌드 컨텍스트에서 제외
node_modules         # 가장 중요! 로컬 node_modules 제외
.git                 # git 이력 제외
.env                 # 환경변수 파일 절대 포함하지 말 것!
.env.*
dist                 # 빌드 결과물 (컨테이너 내에서 빌드)
coverage             # 테스트 커버리지
*.log
*.md
.DS_Store
docker-compose*.yml  # 컴포즈 파일 제외

🔗 Chapter 03

Docker Compose — 멀티 컨테이너 서비스 한 번에 관리

"앱 서버 + DB + 캐시 + 메시지큐를 명령어 하나로 실행합니다"

🍱 Docker Compose 개념 — 도시락 세트로 이해하기

혼자 먹는 도시락(단일 컨테이너)이 아니라, 밥+반찬+국+음료가 세트(멀티 컨테이너)로 구성된 도시락이 Docker Compose입니다. 각 메뉴는 독립적이지만 하나의 세트로 묶여 함께 서비스됩니다.

# docker-compose.yml — 실전 NestJS 풀스택 구성
version: '3.9'

services:
  # ─── 앱 서버 ───
  api:
    build:
      context: .
      dockerfile: Dockerfile
      target: runner           # 멀티스테이지에서 runner 스테이지만 빌드
    container_name: nestjs-api
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=production
      - DATABASE_URL=postgresql://user:pass@postgres:5432/mydb
      - REDIS_URL=redis://redis:6379
    depends_on:
      postgres:
        condition: service_healthy  # DB 준비 후 시작
      redis:
        condition: service_healthy
    restart: unless-stopped
    networks:
      - app-network

  # ─── PostgreSQL ───
  postgres:
    image: postgres:15-alpine
    container_name: postgres-db
    environment:
      POSTGRES_DB: mydb
      POSTGRES_USER: user
      POSTGRES_PASSWORD: pass
    volumes:
      - postgres-data:/var/lib/postgresql/data  # 데이터 영구 저장
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql  # 초기 SQL 실행
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U user -d mydb"]
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      - app-network

  # ─── Redis ───
  redis:
    image: redis:7-alpine
    container_name: redis-cache
    command: redis-server --requirepass mypassword --maxmemory 256mb --maxmemory-policy allkeys-lru
    volumes:
      - redis-data:/data
    healthcheck:
      test: ["CMD", "redis-cli", "--no-auth-warning", "-a", "mypassword", "ping"]
      interval: 10s
    networks:
      - app-network

  # ─── Nginx (리버스 프록시) ───
  nginx:
    image: nginx:alpine
    container_name: nginx-proxy
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - ./ssl:/etc/ssl/certs:ro
    depends_on:
      - api
    networks:
      - app-network

volumes:
  postgres-data:
  redis-data:

networks:
  app-network:
    driver: bridge
🚀 Docker Compose 핵심 명령어
docker compose up -d          # 전체 서비스 백그라운드 시작
docker compose up -d --build   # 이미지 재빌드 후 시작
docker compose down            # 전체 서비스 중지 + 컨테이너 삭제
docker compose down -v         # 볼륨까지 삭제 (DB 데이터 초기화)
docker compose logs -f api     # api 서비스 실시간 로그
docker compose ps              # 서비스 상태 확인
docker compose exec api bash   # api 컨테이너 내부 접속
docker compose scale api=3     # api 서비스 3개로 스케일업

⚙️ Chapter 04

Kubernetes 기초 — 컨테이너 오케스트레이션의 표준

"Docker가 악기라면, Kubernetes는 오케스트라 지휘자입니다"

🎭 Kubernetes가 필요한 이유

😰 Docker만 쓸 때 문제점

• 컨테이너 하나 죽으면 수동으로 재시작
• 트래픽 몰릴 때 수동으로 컨테이너 추가
• 서버 여러 대에 배포 어떻게?
• 롤링 배포 구현 어떻게?
• 컨테이너 간 로드밸런싱은?

😊 Kubernetes 해결책

• 컨테이너 죽으면 자동 재시작 ✅
• HPA로 트래픽 따라 자동 스케일링 ✅
• 노드 클러스터에 자동 배포 ✅
• 롤링 업데이트/롤백 기본 제공 ✅
• 내장 로드밸런서 + 서비스 디스커버리 ✅

🧩 Kubernetes 핵심 오브젝트 완전 이해

📦
Pod
K8s 최소 배포 단위. 1개 이상의 컨테이너를 포함. 같은 네트워크/스토리지 공유. 직접 만들지 말고 Deployment로 관리
🚀
Deployment
Pod의 목표 상태 선언. "Pod 3개 항상 유지해줘" 롤링 업데이트/롤백 관리
🌐
Service
Pod에 안정적인 IP/DNS 제공. 로드밸런싱. ClusterIP/NodePort/LoadBalancer 타입
🔧
ConfigMap
설정 값 저장 (비밀아닌 것). DB 호스트, 포트 등. 이미지 수정 없이 설정 변경
🔐
Secret
민감 정보 저장 (Base64). DB 비밀번호, API 키. ConfigMap과 비슷하지만 암호화
🚪
Ingress
외부 HTTP/HTTPS 트래픽 라우팅. 도메인 기반 라우팅. SSL 인증서 관리

🏗️ Chapter 05

Kubernetes 심화 — 실전 Manifest 작성과 배포 전략

"YAML 하나로 프로덕션 서비스를 완전히 제어합니다"

💻 실전 Kubernetes Manifest — NestJS 앱 전체 배포

# ━━━ 1. Namespace 분리 ━━━
apiVersion: v1
kind: Namespace
metadata:
  name: production
---
# ━━━ 2. Secret (민감 정보) ━━━
apiVersion: v1
kind: Secret
metadata:
  name: app-secrets
  namespace: production
type: Opaque
stringData:             # stringData는 자동으로 base64 인코딩
  DATABASE_URL: postgresql://user:pass@postgres-svc:5432/mydb
  JWT_SECRET: my-super-secret-jwt-key-here
  REDIS_URL: redis://:password@redis-svc:6379
---
# ━━━ 3. ConfigMap (비민감 설정) ━━━
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
  namespace: production
data:
  NODE_ENV: production
  PORT: "3000"
  LOG_LEVEL: info
---
# ━━━ 4. Deployment (핵심!) ━━━
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nestjs-api
  namespace: production
spec:
  replicas: 3                # Pod 3개 항상 유지
  selector:
    matchLabels:
      app: nestjs-api
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 1       # 배포 중 최대 1개 동시 중지
      maxSurge: 1              # 최대 1개 추가 생성
  template:
    metadata:
      labels:
        app: nestjs-api
    spec:
      containers:
        - name: nestjs-api
          image: myrepo/nestjs-api:v1.2.3
          ports:
            - containerPort: 3000
          envFrom:
            - secretRef:
                name: app-secrets
            - configMapRef:
                name: app-config
          resources:
            requests:
              memory: "256Mi"
              cpu: "250m"
            limits:
              memory: "512Mi"
              cpu: "500m"
          livenessProbe:           # 살아있는지 체크 (실패 시 재시작)
            httpGet:
              path: /health
              port: 3000
            initialDelaySeconds: 30
            periodSeconds: 10
          readinessProbe:          # 트래픽 받을 준비됐는지 체크
            httpGet:
              path: /health/ready
              port: 3000
            initialDelaySeconds: 5
            periodSeconds: 5
---
# ━━━ 5. Service (내부 통신) ━━━
apiVersion: v1
kind: Service
metadata:
  name: nestjs-api-svc
  namespace: production
spec:
  selector:
    app: nestjs-api
  ports:
    - port: 80
      targetPort: 3000
  type: ClusterIP
---
# ━━━ 6. HPA (자동 스케일링) ━━━
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: nestjs-api-hpa
  namespace: production
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: nestjs-api
  minReplicas: 2
  maxReplicas: 10
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 70  # CPU 70% 넘으면 Pod 추가
---
# ━━━ 7. Ingress (외부 트래픽) ━━━
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: api-ingress
  namespace: production
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
    cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
  tls:
    - hosts:
        - api.myapp.com
      secretName: api-tls
  rules:
    - host: api.myapp.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: nestjs-api-svc
                port:
                  number: 80

🔧 kubectl 핵심 명령어 레퍼런스

# 기본 조회
kubectl get pods -n production          # Pod 목록
kubectl get pods -n production -w        # 실시간 상태 감시
kubectl get all -n production           # 모든 리소스 조회
kubectl describe pod nestjs-api-xxx     # Pod 상세 정보 (오류 디버깅)
kubectl logs -f nestjs-api-xxx          # Pod 로그 실시간
kubectl exec -it nestjs-api-xxx -- bash # Pod 내부 접속

# 배포
kubectl apply -f ./k8s/                # 디렉토리 내 모든 yaml 적용
kubectl set image deployment/nestjs-api nestjs-api=myrepo/nestjs-api:v1.3.0
kubectl rollout status deployment/nestjs-api   # 배포 진행 상황
kubectl rollout history deployment/nestjs-api  # 배포 이력
kubectl rollout undo deployment/nestjs-api     # 이전 버전으로 롤백!
kubectl scale deployment nestjs-api --replicas=5  # 수동 스케일링

☁️ Chapter 06

AWS 핵심 서비스 완전 정복 — 백엔드 개발자 필수 서비스

"AWS를 모르면 2024년 백엔드 개발자가 아닙니다"

🗺️ AWS 전체 서비스 지도 — 백엔드 필수 서비스

카테고리 서비스 역할 대응 솔루션
컴퓨팅 EC2 가상 서버 (VM) GCP Compute Engine
ECS/EKS 컨테이너 오케스트레이션 GKE, Azure AKS
Lambda 서버리스 함수 Cloud Functions
데이터베이스 RDS 관리형 관계형 DB Cloud SQL
DynamoDB NoSQL (서버리스) Firestore, CosmosDB
스토리지 S3 객체 스토리지 (파일) GCS, Azure Blob
EFS NFS (공유 파일시스템) Filestore
네트워크 VPC 가상 사설 네트워크 GCP VPC
ALB/NLB 로드 밸런서 Cloud Load Balancing
캐시/메시지 ElastiCache 관리형 Redis/Memcached Cloud Memorystore
SQS/SNS 메시지 큐/알림 Pub/Sub
보안/인증 IAM 권한 관리 IAM (GCP)

💻 EC2 + NestJS 배포 실전 가이드

# ━━━ EC2 초기 설정 스크립트 (User Data) ━━━
#!/bin/bash
set -e

# 시스템 업데이트
apt-get update && apt-get upgrade -y

# Docker 설치
curl -fsSL https://get.docker.com | sh
usermod -aG docker ubuntu

# Docker Compose 설치
curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)"   -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose

# CloudWatch Agent 설치 (로그/메트릭 수집)
wget https://s3.amazonaws.com/amazoncloudwatch-agent/ubuntu/amd64/latest/amazon-cloudwatch-agent.deb
dpkg -i amazon-cloudwatch-agent.deb

# Nginx 설치 (리버스 프록시)
apt-get install -y nginx certbot python3-certbot-nginx

# 방화벽 설정
ufw allow 22    # SSH
ufw allow 80    # HTTP
ufw allow 443   # HTTPS
ufw enable

# Nginx 설정 (리버스 프록시)
cat > /etc/nginx/sites-available/api.myapp.com << 'EOF'
server {
    server_name api.myapp.com;
    
    location / {
        proxy_pass http://localhost:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_cache_bypass $http_upgrade;
    }
    
    # 파일 업로드 크기 제한
    client_max_body_size 50M;
}
EOF

ln -s /etc/nginx/sites-available/api.myapp.com /etc/nginx/sites-enabled/
nginx -t && systemctl reload nginx

# Let's Encrypt SSL 인증서 자동 발급
certbot --nginx -d api.myapp.com --non-interactive --agree-tos -m admin@myapp.com

🗄️ Chapter 07

AWS RDS, ElastiCache, S3 — 데이터 관리 완전 정복

"직접 설치보다 관리형 서비스가 훨씬 편합니다. 실수도 줄어듭니다"

🗄️ AWS RDS vs 직접 설치 비교

😰 EC2에 직접 DB 설치

• 백업: 직접 cron job 설정
• 업그레이드: 직접 진행 (다운타임)
• 장애 복구: 직접 처리
• 모니터링: 별도 구성
• 멀티 AZ: 직접 구성 (매우 복잡)

😊 AWS RDS 사용

• 백업: 자동 (최대 35일 보관) ✅
• 업그레이드: 클릭 한 번 ✅
• 자동 장애 조치 (Multi-AZ) ✅
• CloudWatch 통합 모니터링 ✅
• Read Replica 클릭으로 추가 ✅

# NestJS RDS 연결 설정 (TypeORM)
TypeOrmModule.forRootAsync({
  useFactory: () => ({
    type: 'postgres',
    host: process.env.DB_HOST,           # RDS 엔드포인트
    port: +process.env.DB_PORT || 5432,
    username: process.env.DB_USER,
    password: process.env.DB_PASSWORD,
    database: process.env.DB_NAME,
    ssl: process.env.NODE_ENV === 'production'
      ? { rejectUnauthorized: false }   # RDS SSL 연결
      : false,
    entities: [__dirname + '/**/*.entity{.ts,.js}'],
    migrations: [__dirname + '/migrations/**/*{.ts,.js}'],
    synchronize: process.env.NODE_ENV !== 'production',  # 운영에서 false!
    logging: process.env.NODE_ENV !== 'production',
    # 커넥션 풀 설정 (RDS 동시 연결 제한 주의)
    extra: {
      max: 10,                # 최대 연결 수
      idleTimeoutMillis: 30000,
      connectionTimeoutMillis: 2000,
    },
  }),
})

📁 S3 파일 업로드 — NestJS 실전 구현

// S3 파일 업로드 서비스 (NestJS)
@Injectable()
export class S3Service {
  private s3: S3Client;

  constructor() {
    this.s3 = new S3Client({
      region: process.env.AWS_REGION,
      credentials: {
        accessKeyId: process.env.AWS_ACCESS_KEY_ID,
        secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
      },
    });
  }

  async uploadFile(file: Express.Multer.File, folder: string = 'uploads') {
    const ext = path.extname(file.originalname);
    const key = `${folder}/${Date.now()}-${uuidv4()}${ext}`;

    const command = new PutObjectCommand({
      Bucket: process.env.AWS_S3_BUCKET,
      Key: key,
      Body: file.buffer,
      ContentType: file.mimetype,
      // ACL: 'public-read', // 퍼블릭으로 공개 (CloudFront 사용 시 불필요)
      Metadata: {
        originalName: file.originalname,
        uploadedBy: 'api-server',
      },
    });

    await this.s3.send(command);

    // CloudFront CDN URL 반환 (직접 S3 URL 노출 비권장)
    return {
      key,
      url: `https://${process.env.CLOUDFRONT_DOMAIN}/${key}`,
    };
  }

  // Presigned URL — 클라이언트가 S3에 직접 업로드
  async getPresignedUploadUrl(fileName: string, contentType: string) {
    const key = `uploads/${Date.now()}-${uuidv4()}-${fileName}`;
    const command = new PutObjectCommand({
      Bucket: process.env.AWS_S3_BUCKET,
      Key: key,
      ContentType: contentType,
    });
    const presignedUrl = await getSignedUrl(this.s3, command, { expiresIn: 300 }); // 5분
    return { key, presignedUrl };
  }
}

🔄 Chapter 08

CI/CD 파이프라인 완전 구축 — 자동화된 배포의 모든 것

"코드 push 하나로 운영 서버까지 자동 배포되는 세상을 만들어봅시다"

🔄 CI/CD 개념 완전 이해

🔨 CI (Continuous Integration)

코드 변경 시 자동으로:
1. 코드 체크아웃
2. 의존성 설치
3. 린트/포맷 검사
4. 단위 테스트 실행
5. 통합 테스트 실행
6. 보안 취약점 스캔
7. Docker 이미지 빌드 및 푸시

🚀 CD (Continuous Deployment)

CI 완료 후 자동으로:
1. 스테이징 환경 배포
2. E2E 테스트 실행
3. 승인 단계 (선택)
4. 운영 환경 배포
5. 배포 상태 알림 (Slack)
6. 이상 감지 시 자동 롤백

💻 GitHub Actions — 실전 CI/CD 파이프라인

# .github/workflows/deploy.yml name: CI/CD Pipeline on: push: branches: [main, develop] pull_request: branches: [main, develop] env: REGISTRY: ghcr.io IMAGE_NAME: your-org/your-app jobs: # CI: 테스트 + 빌드 ci: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Setup Node.js 20 uses: actions/setup-node@v4 with: node-version: '20' cache: 'npm' - name: Install run: npm ci - name: Lint run: npm run lint - name: Test (with coverage) run: npm run test:cov - name: Build run: npm run build - name: Docker Build and Push if: github.event_name != 'pull_request' run: | echo "LOGIN_TOKEN" | docker login ghcr.io -u actor --password-stdin docker build -t ghcr.io/your-org/your-app:SHA . docker push ghcr.io/your-org/your-app:SHA # 스테이징 자동 배포 deploy-staging: needs: ci if: github.ref == 'refs/heads/develop' runs-on: ubuntu-latest environment: staging steps: - name: SSH Deploy to Staging uses: appleboy/ssh-action@v1 with: host: STAGING_HOST username: ubuntu key: SSH_PRIVATE_KEY script: | cd /opt/myapp docker compose pull docker compose up -d --no-deps api docker system prune -f # 운영 배포 (main 브랜치) deploy-production: needs: ci if: github.ref == 'refs/heads/main' runs-on: ubuntu-latest environment: production steps: - name: Deploy to EKS run: | aws eks update-kubeconfig --region ap-northeast-2 --name prod-cluster kubectl set image deployment/nestjs-api nestjs-api=IMAGE:SHA -n production kubectl rollout status deployment/nestjs-api -n production - name: Notify Slack on result if: always() run: | curl -X POST SLACK_WEBHOOK -d '{"text":"Production deploy finished"}'

📦 Chapter 09

Infrastructure as Code — Terraform으로 인프라 코드화

"인프라도 코드다. 버전 관리하고, 리뷰하고, 자동화한다"

🏗️ Terraform 기초 — AWS 인프라를 코드로

Terraform은 HashiCorp의 IaC(Infrastructure as Code) 도구입니다. AWS 콘솔에서 클릭클릭 하던 것을 코드로 선언하면, Terraform이 알아서 인프라를 만들어줍니다.

# main.tf — NestJS 운영 인프라 전체 정의 terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 5.0" } } backend "s3" { bucket = "myapp-terraform-state" key = "production/terraform.tfstate" region = "ap-northeast-2" } } provider "aws" { region = var.aws_region } # RDS PostgreSQL resource "aws_db_instance" "postgres" { identifier = "myapp-postgres" engine = "postgres" engine_version = "15.4" instance_class = "db.t3.medium" allocated_storage = 20 storage_encrypted = true db_name = var.db_name username = var.db_username password = var.db_password multi_az = true deletion_protection = true backup_retention_period = 7 skip_final_snapshot = false tags = { Name = "myapp-postgres", Env = "production" } } # ElastiCache Redis resource "aws_elasticache_replication_group" "redis" { replication_group_id = "myapp-redis" description = "MyApp Redis Cluster" node_type = "cache.t3.micro" num_cache_clusters = 2 automatic_failover_enabled = true at_rest_encryption_enabled = true transit_encryption_enabled = true } # Terraform 명령어 # terraform init    → 초기화 # terraform plan    → 변경 미리보기 (dry-run) # terraform apply   → 실제 인프라 생성/변경 # terraform destroy → 전체 인프라 삭제

🔒 Chapter 10

클라우드 보안 완전 정복 — 해킹당하지 않는 인프라

"클라우드 보안 실수 하나가 수백만 원의 요금 폭탄이 될 수 있습니다"

🛡️ AWS IAM 최소 권한 원칙

IAM(Identity and Access Management)은 AWS 서비스 접근 권한을 관리합니다. 최소 권한 원칙(Principle of Least Privilege): 필요한 권한만 부여합니다.

# IAM Policy 예시 — S3 특정 버킷 읽기/쓰기만 허용 { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:GetObject", "s3:PutObject", "s3:DeleteObject" ], "Resource": "arn:aws:s3:::myapp-uploads/*" }, { "Effect": "Allow", "Action": "s3:ListBucket", "Resource": "arn:aws:s3:::myapp-uploads" } ] } # 나쁜 예 (절대 하지 말 것!) # "Action": "*", "Resource": "*" → 모든 서비스 모든 작업 허용! # EC2 인스턴스에 IAM Role 적용 (Access Key 사용 X) resource "aws_iam_role" "ec2_role" { name = "ec2-app-role" assume_role_policy = jsonencode({ Statement = [{ Action = "sts:AssumeRole" Effect = "Allow" Principal = { Service = "ec2.amazonaws.com" } }] }) } # EC2에 Role 적용 시 코드에 AccessKey 하드코딩 불필요! # AWS SDK가 자동으로 인스턴스 메타데이터에서 인증 처리

🚨 절대 하지 말아야 할 보안 실수 TOP 5

❌ AWS Access Key를 코드/GitHub에 커밋 (매우 위험!)
❌ S3 버킷을 퍼블릭으로 설정 (개인정보 유출)
❌ 보안그룹 0.0.0.0/0 인바운드 전체 허용
❌ root 계정 직접 사용 (IAM 사용자 생성 후 사용)
❌ MFA 미설정 (Multi-Factor Authentication 필수)

📊 Chapter 11

모니터링 완전 정복 — Prometheus + Grafana

"장애는 반드시 납니다. 언제, 어디서, 왜 났는지 알아야 합니다"

📊 모니터링 스택 구성

📈
Prometheus
메트릭 수집/저장. Pull 방식. 시계열 DB
📊
Grafana
메트릭 시각화. 대시보드. 알람
📝
Loki
로그 수집/조회. Prometheus의 로그 버전
🔔
AlertManager
알람 라우팅. Slack/PagerDuty 연동
// NestJS Prometheus 메트릭 노출 // npm install @willsoto/nestjs-prometheus prom-client @Module({ imports: [ PrometheusModule.register({ defaultMetrics: { enabled: true }, path: '/metrics', }), ], }) // 커스텀 메트릭 정의 @Injectable() export class AppMetrics { private httpRequestsTotal: Counter; private httpRequestDuration: Histogram; constructor() { this.httpRequestsTotal = new Counter({ name: 'http_requests_total', help: 'Total HTTP requests', labelNames: ['method', 'path', 'status'], }); this.httpRequestDuration = new Histogram({ name: 'http_request_duration_seconds', help: 'HTTP request duration in seconds', labelNames: ['method', 'path'], buckets: [0.1, 0.3, 0.5, 1, 3, 5, 10], }); } recordRequest(method: string, path: string, status: number, duration: number) { this.httpRequestsTotal.labels(method, path, String(status)).inc(); this.httpRequestDuration.labels(method, path).observe(duration); } }

🔔 Grafana 핵심 알람 설정 가이드

메트릭 경고 임계값 위험 임계값 조치
CPU 사용률 70% 90% 스케일 업/아웃
메모리 사용률 75% 90% 메모리 누수 확인
API 응답시간 500ms 2s 쿼리/캐시 최적화
에러율 1% 5% 로그 분석
DB 연결 수 80% 95% 풀 크기 조정

💰 Chapter 12

클라우드 비용 최적화 — 돈 아끼는 기술

"AWS 요금 폭탄 맞아본 사람만 이 챕터의 소중함을 압니다"

💰 AWS 비용 절약 10가지 전략

✅ EC2 절약 전략
Reserved Instance: 1~3년 약정 시 최대 72% 할인
Spot Instance: 경매 방식, 최대 90% 할인 (중단 가능)
Savings Plans: 유연한 약정 할인
Auto Scaling: 트래픽 없을 때 자동 축소
올바른 인스턴스 타입: CPU 최적화 vs 메모리 최적화
✅ 스토리지/데이터 절약
S3 수명주기: 오래된 파일 Glacier로 자동 이동
RDS 스냅샷 관리: 불필요한 스냅샷 삭제
CloudFront: 엣지 캐싱으로 S3 요청 감소
데이터 전송 최소화: 같은 리전 서비스 사용
압축: gzip으로 데이터 전송량 감소

⚠️ 비용 폭탄 방지 필수 설정

1. AWS Budgets: 월 예산 초과 시 이메일/SNS 알림 설정
2. Cost Explorer: 매주 비용 리뷰 습관화
3. 미사용 리소스 정리: 쓰지 않는 EC2, EIP, NAT Gateway 삭제
4. 개발 환경 자동 종료: 업무 시간 외 자동 중지 스케줄러
5. CloudTrail: 누가 어떤 리소스 만들었는지 추적

🌐 Chapter 13

서버리스 vs 컨테이너 — 언제 무엇을 선택할까?

"모든 문제에 맞는 도구가 있습니다"

⚡ Lambda(서버리스) vs ECS(컨테이너) 완전 비교

구분 Lambda (서버리스) ECS/EKS (컨테이너)
과금 요청당 과금 (0원도 가능) 실행 시간 과금 (항상 비용)
시작 시간 Cold Start 100ms~수초 항상 빠름 (이미 실행 중)
실행 시간 최대 15분 제한 없음
스케일링 자동 (동시 10,000개) 수동/자동 (HPA)
상태 관리 Stateless 필수 Stateful 가능
배포 복잡도 낮음 높음
적합한 워크로드 이벤트 기반, 배치, 가끔 실행 항상 실행, 실시간 처리
대표 사용처 이미지 리사이징, 알림, API 백엔드 웹 API, 마이크로서비스
// Lambda 핸들러 — NestJS to Serverless // npm install @vendia/serverless-express import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import serverlessExpress from '@vendia/serverless-express'; let cachedServer: any; async function bootstrap() { if (!cachedServer) { const app = await NestFactory.create(AppModule); await app.init(); const expressApp = app.getHttpAdapter().getInstance(); cachedServer = serverlessExpress({ app: expressApp }); } return cachedServer; } // Lambda 핸들러 export const handler = async (event: any, context: any) => { const server = await bootstrap(); return server(event, context); }; // Cold Start 최소화 팁: // 1. Provisioned Concurrency 설정 (항상 워밍업) // 2. 번들 크기 최소화 (트리쉐이킹) // 3. 불필요한 의존성 제거

🎯 Chapter 14

면접 Q&A + 실무 체크리스트

"이 챕터를 다 읽으면 클라우드/컨테이너 면접이 두렵지 않습니다"

💬 면접 단골 질문 완전 정리

Q. VM(가상머신)과 컨테이너의 차이점은?
VM은 하이퍼바이저 위에 Guest OS 전체를 실행(GB 단위, 부팅 수십 초). 컨테이너는 호스트 OS 커널을 공유하는 격리된 프로세스(MB 단위, 시작 수초 미만). 컨테이너가 가볍고 빠르지만, VM이 보안 격리는 더 강합니다.
Q. Docker 이미지 크기를 줄이는 방법은?
1) alpine 기반 이미지 사용 (node:20-alpine vs node:20 → 10배 차이). 2) 멀티스테이지 빌드로 빌드 도구 제외. 3) .dockerignore로 불필요 파일 제외. 4) RUN 레이어 최소화 (&&로 합치기). 5) 프로덕션 의존성만 설치 (npm ci --only=production).
Q. Kubernetes에서 Liveness Probe와 Readiness Probe의 차이는?
Liveness Probe: 컨테이너가 살아있는지 확인. 실패하면 컨테이너 재시작. (무한 루프, 데드락 감지)
Readiness Probe: 트래픽 받을 준비가 됐는지 확인. 실패하면 Service에서 Pod 제외 (트래픽 차단). 시작 시 초기화 완료 확인에 사용.
Q. AWS에서 고가용성(HA) 아키텍처를 어떻게 구성하나요?
1) Multi-AZ 배포: 서로 다른 가용 영역에 서버 분산
2) ALB + Auto Scaling Group: 로드밸런서 + 자동 스케일링
3) RDS Multi-AZ: 자동 페일오버
4) ElastiCache 클러스터 모드: Redis 다중화
5) Route 53 헬스체크: DNS 레벨 페일오버
Q. CI/CD에서 Blue-Green 배포란?
현재 운영 환경(Blue)과 동일한 새 환경(Green)을 별도로 만든 뒤, 테스트 완료 후 로드밸런서를 Green으로 전환하는 방식입니다. 장점: 다운타임 없음, 즉각 롤백 가능. 단점: 두 배의 인프라 비용. 카나리 배포(일부 트래픽만 새 버전)와 함께 현업에서 많이 사용됩니다.

✅ 클라우드/컨테이너 실무 체크리스트

🐳 Docker 체크리스트

☐ 멀티스테이지 빌드 적용
☐ alpine 이미지 사용
☐ .dockerignore 작성
☐ non-root 사용자 실행
☐ HEALTHCHECK 설정

⚙️ K8s 체크리스트

☐ Resource requests/limits 설정
☐ Liveness/Readiness Probe
☐ HPA 자동 스케일링
☐ Secret으로 민감정보 관리
☐ Namespace로 환경 분리

☁️ AWS 체크리스트

☐ IAM 최소 권한 원칙
☐ RDS Multi-AZ + 자동 백업
☐ S3 버전 관리 + 암호화
☐ CloudWatch 알람 설정
☐ AWS Budgets 예산 알림

🔄 CI/CD 체크리스트

☐ 자동 테스트 + 커버리지 측정
☐ 보안 취약점 스캔 (npm audit)
☐ 이미지 취약점 스캔 (Trivy)
☐ 롤백 전략 수립
☐ Slack/이메일 알림 설정
🎉

BackendDevGuide0012 완성!

클라우드와 컨테이너 완전 정복 A-Z

14
핵심 챕터
60+
실전 코드/설정
A-Z
완전 가이드

Docker부터 Kubernetes, AWS, CI/CD, IaC, 모니터링, 보안까지
현업 백엔드 개발자가 알아야 할 클라우드/인프라 모든 것을 담았습니다 💪

반응형