IT Knowledge/DevOps/images/docker-kubernetes-기초-venn.svg

Docker & Kubernetes 기초

📌 핵심 개념

  • Docker: 애플리케이션을 컨테이너로 패키징
  • Kubernetes: 컨테이너 오케스트레이션 플랫폼

🎯 Docker 실전 사례

사례 1: “내 컴퓨터에서는 되는데요?” 문제 해결

Before: 환경 차이로 인한 문제

# 개발자 A (Mac)
node app.js  # ✅ 잘 동작
 
# 개발자 B (Windows)
node app.js  # ❌ 파일 경로 오류
 
# 운영 서버 (Linux)
node app.js  # ❌ Node 버전 불일치

After: Docker 사용

# Dockerfile
FROM node:18-alpine
 
WORKDIR /app
 
# 의존성 설치
COPY package*.json ./
RUN npm ci --only=production
 
# 애플리케이션 코드 복사
COPY . .
 
EXPOSE 3000
 
CMD ["node", "app.js"]
# 모든 환경에서 동일하게 실행
docker build -t myapp .
docker run -p 3000:3000 myapp
 
# 개발, 스테이징, 프로덕션 모두 같은 이미지 사용

사례 2: 멀티 스테이지 빌드로 이미지 크기 최적화

Before: 큰 이미지 (800MB)

FROM node:18
 
WORKDIR /app
COPY . .
RUN npm install  # 개발 의존성도 포함
RUN npm run build
 
CMD ["npm", "start"]

After: 멀티 스테이지 빌드 (150MB)

# 빌드 스테이지
FROM node:18 AS builder
 
WORKDIR /app
COPY package*.json ./
RUN npm install
 
COPY . .
RUN npm run build
 
# 실행 스테이지
FROM node:18-alpine
 
WORKDIR /app
 
# 빌드된 결과물만 복사
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/package*.json ./
 
# 프로덕션 의존성만 설치
RUN npm ci --only=production
 
EXPOSE 3000
CMD ["node", "dist/index.js"]

사례 3: Docker Compose로 개발 환경 구성

# docker-compose.yml
version: '3.8'
 
services:
  # Node.js 애플리케이션
  app:
    build: .
    ports:
      - "3000:3000"
    environment:
      - DATABASE_URL=postgresql://postgres:password@db:5432/myapp
      - REDIS_URL=redis://redis:6379
    volumes:
      - ./src:/app/src  # 코드 수정 시 자동 반영
    depends_on:
      - db
      - redis
 
  # PostgreSQL 데이터베이스
  db:
    image: postgres:14
    environment:
      - POSTGRES_PASSWORD=password
      - POSTGRES_DB=myapp
    volumes:
      - postgres_data:/var/lib/postgresql/data
    ports:
      - "5432:5432"
 
  # Redis
  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
 
  # Nginx (리버스 프록시)
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
    depends_on:
      - app
 
volumes:
  postgres_data:
# 한 명령어로 전체 스택 실행
docker-compose up -d
 
# 로그 확인
docker-compose logs -f app
 
# 종료
docker-compose down

☸️ Kubernetes 실전 사례

사례 1: 트래픽 급증 시 자동 스케일링

문제: 블랙프라이데이 트래픽 폭증

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: shopping-app
spec:
  replicas: 3  # 기본 3개 Pod
  selector:
    matchLabels:
      app: shopping-app
  template:
    metadata:
      labels:
        app: shopping-app
    spec:
      containers:
      - name: app
        image: myapp:v1.0
        ports:
        - containerPort: 3000
        resources:
          requests:  # 최소 리소스
            memory: "128Mi"
            cpu: "250m"
          limits:    # 최대 리소스
            memory: "256Mi"
            cpu: "500m"
 
---
# HPA (Horizontal Pod Autoscaler)
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: shopping-app-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: shopping-app
  minReplicas: 3
  maxReplicas: 50  # 최대 50개까지 자동 확장
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70  # CPU 70% 이상이면 스케일 아웃

결과:

  • 평소: Pod 3개 (비용 절감)
  • 트래픽 급증: 자동으로 50개까지 확장
  • 트래픽 감소: 다시 3개로 축소

사례 2: 무중단 배포 (Rolling Update)

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
spec:
  replicas: 10
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 2        # 추가로 2개까지 생성 가능
      maxUnavailable: 1  # 최대 1개까지 중단 가능
 
  template:
    metadata:
      labels:
        app: myapp
        version: v2.0  # 새 버전
    spec:
      containers:
      - name: app
        image: myapp:v2.0
        readinessProbe:  # 준비 상태 확인
          httpGet:
            path: /health
            port: 3000
          initialDelaySeconds: 5
          periodSeconds: 5
 
        livenessProbe:   # 생존 확인
          httpGet:
            path: /health
            port: 3000
          initialDelaySeconds: 15
          periodSeconds: 10
# 배포 실행
kubectl apply -f deployment.yaml
 
# 배포 상태 확인
kubectl rollout status deployment/myapp
 
# 문제 발생 시 롤백
kubectl rollout undo deployment/myapp

배포 과정:

  1. 새 Pod 2개 생성 (총 12개)
  2. 준비 완료되면 구 Pod 1개 제거
  3. 다시 새 Pod 2개 생성…
  4. 모든 Pod가 v2.0으로 교체될 때까지 반복
  5. 서비스 중단 없음!

사례 3: ConfigMap & Secret으로 설정 관리

# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  APP_ENV: "production"
  LOG_LEVEL: "info"
  API_URL: "https://api.example.com"
 
---
# secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: app-secrets
type: Opaque
data:
  DATABASE_PASSWORD: cGFzc3dvcmQxMjM=  # base64 인코딩
  JWT_SECRET: c2VjcmV0a2V5  # base64 인코딩
 
---
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
spec:
  template:
    spec:
      containers:
      - name: app
        image: myapp:v1.0
        env:
        # ConfigMap에서 환경 변수 주입
        - name: APP_ENV
          valueFrom:
            configMapKeyRef:
              name: app-config
              key: APP_ENV
 
        # Secret에서 환경 변수 주입
        - name: DATABASE_PASSWORD
          valueFrom:
            secretKeyRef:
              name: app-secrets
              key: DATABASE_PASSWORD
 
        # 전체 ConfigMap을 환경 변수로
        envFrom:
        - configMapRef:
            name: app-config
# 설정 변경 (애플리케이션 코드 수정 없이)
kubectl edit configmap app-config
 
# Pod 재시작하여 변경사항 적용
kubectl rollout restart deployment/myapp

사례 4: Ingress로 도메인 라우팅

# ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app-ingress
  annotations:
    kubernetes.io/ingress.class: "nginx"
    cert-manager.io/cluster-issuer: "letsencrypt"  # 자동 SSL
spec:
  tls:
  - hosts:
    - www.example.com
    - api.example.com
    secretName: tls-secret
 
  rules:
  # www.example.com -> frontend
  - host: www.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: frontend-service
            port:
              number: 80
 
  # api.example.com -> backend
  - host: api.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: backend-service
            port:
              number: 3000
 
  # api.example.com/v2 -> backend-v2
  - host: api.example.com
    http:
      paths:
      - path: /v2
        pathType: Prefix
        backend:
          service:
            name: backend-v2-service
            port:
              number: 3000

💡 실전 팁

Docker 최적화

# ✅ 레이어 캐싱 활용
COPY package*.json ./
RUN npm install
COPY . .  # 코드는 나중에 복사 (캐시 활용)
 
# ✅ .dockerignore 사용
# .dockerignore
node_modules
.git
*.md
.env

Kubernetes 디버깅

# Pod 로그 확인
kubectl logs -f pod-name
 
# Pod 내부 접속
kubectl exec -it pod-name -- /bin/sh
 
# 이벤트 확인
kubectl get events --sort-by='.lastTimestamp'
 
# 리소스 사용량 확인
kubectl top pods

🔗 참고 자료