infra
Platform

모듈 맵

[Kubernetes] Deployment를 이용한 안정적인 서비스 배포와 롤백 전략

0 / 29 완료

펼치기
0 / 29 완료0%

Kubernetes · 05 / 29

[Kubernetes] Deployment를 이용한 안정적인 서비스 배포와 롤백 전략

Deployment → ReplicaSet → Pod 계층 구조를 이해하고, 롤링 업데이트와 롤백을 실습합니다

🚨INCIDENT ALERT
HIGH

금요일 오후 새 버전을 배포했더니 일부 사용자가 502를 보기 시작했습니다. 운영팀은 파드를 직접 지우는 대신 Deployment가 ReplicaSet과 롤링 업데이트를 어떻게 관리하는지 알아야 안전하게 되돌릴 수 있습니다. Deployment는 Kubernetes에서 무중단 배포를 다루는 기본 단위입니다.

Deployment 기본

배포 당일 오전, 팀장이 말했다. "새 버전 올려주세요." 긴장한 채 kubectl set image를 입력했다. 롤링 업데이트가 시작됐다. 그런데 모니터에 502 에러가 치솟기 시작했다. 새 Pod들이 아직 DB 연결을 맺는 중인데, K8s가 이미 트래픽을 보내고 있었다. readinessProbe가 없었던 것이다. 이 사고는 Deployment가 어떻게 동작하는지 정확히 몰랐기 때문에 발생했다. Deployment는 단순히 Pod를 여러 개 실행하는 것이 아니다. 버전 관리, 롤링 업데이트, 자동 롤백까지 내포하는 K8s의 핵심 리소스다. 구조를 이해하면 배포는 두렵지 않다.


이번 챕터에서 배울 것

Deployment를 사용해 Pod를 선언적으로 관리하고, 롤링 업데이트와 롤백을 안전하게 수행하는 방법을 마스터합니다.

  • 1Deployment → ReplicaSet → Pod 3계층 구조와 각자의 역할
  • 2롤링 업데이트 동작 원리: maxUnavailable, maxSurge
  • 3kubectl rollout: status, history, undo로 배포 상태 확인 및 롤백
  • 4readinessProbe와 롤링 업데이트의 관계 (무중단 배포의 조건)
  • 5Deployment 스케일링: kubectl scale
실습 환경 준비

실습은 deploy-lab 네임스페이스에서 진행합니다. 마지막에 kubectl config set-context --current --namespace=default로 원복하세요.

클러스터 연결 확인
kubectl cluster-info
실습용 네임스페이스 생성
kubectl create namespace deploy-lab 2>/dev/null || echo 'exists'
현재 네임스페이스 설정 (선택)
kubectl config set-context --current --namespace=deploy-lab
💡개념

Deployment → ReplicaSet → Pod 계층 구조

실무에서 Pod를 직접 만드는 경우는 거의 없습니다. Deployment를 정의하면 K8s가 자동으로 ReplicaSet을 생성하고, ReplicaSet이 Pod를 관리합니다.

Deployment → ReplicaSet → Pod 계층 구조

왜 Deployment가 필요한가?

Kubernetes
# Pod를 직접 생성했을 때의 문제
kubectl run my-pod --image=nginx:alpine
# Pod가 죽으면? → 자동 복구 없음
# 새 버전 배포? → 삭제 후 재생성 (다운타임)
# 여러 개 필요? → 일일이 kubectl run 반복

# Deployment를 쓰면
kubectl create deployment my-app --image=nginx:alpine --replicas=3
# → Pod 3개 자동 생성 + 자동 복구
# → 이미지 변경 시 롤링 업데이트 자동
# → 실패 시 자동 롤백 가능

3계층 관계 확인

Kubernetes
# Deployment 생성
kubectl create deployment my-app \
  --image=nginx:alpine \
  --replicas=3 \
  -n deploy-lab

# 계층 구조 확인
kubectl get deployment,replicaset,pod -n deploy-lab
# NAME                    READY   UP-TO-DATE   AVAILABLE
# deployment.apps/my-app  3/3     3            3
#
# NAME                              DESIRED   CURRENT   READY
# replicaset.apps/my-app-5d85b4bdc  3         3         3
#
# NAME                        READY   STATUS    RESTARTS
# pod/my-app-5d85b4bdc-abc12  1/1     Running   0
# pod/my-app-5d85b4bdc-def34  1/1     Running   0
# pod/my-app-5d85b4bdc-ghi56  1/1     Running   0
🔍실행 후 확인할 것
  • NAME조회 대상 리소스 이름이 예상한 대상과 일치하는지 확인합니다.
  • STATUS/READYRunning, Ready, Available처럼 정상 상태를 나타내는 필드가 있는지 봅니다.
  • RESTARTS/EVENTS재시작 횟수나 Warning 이벤트가 증가하지 않는지 확인합니다.

ReplicaSet의 역할

Kubernetes
# Pod 하나를 수동으로 삭제해 보자
kubectl delete pod my-app-5d85b4bdc-abc12 -n deploy-lab

# 즉시 새 Pod가 생성됨
kubectl get pods -n deploy-lab
# NAME                        READY   STATUS    RESTARTS   AGE
# my-app-5d85b4bdc-def34      1/1     Running   0          5m
# my-app-5d85b4bdc-ghi56      1/1     Running   0          5m
# my-app-5d85b4bdc-xyz99      1/1     Running   0          5s  ← 새로 생성됨

ReplicaSet은 "항상 3개가 실행 중이어야 한다"는 원하는 상태를 유지합니다.

Deployment YAML 구조

YAML
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
  namespace: deploy-lab
spec:
  replicas: 3                    # 원하는 Pod 수
  selector:
    matchLabels:
      app: my-app                # 어느 Pod를 관리할지
  strategy:
    type: RollingUpdate          # 배포 전략
    rollingUpdate:
      maxUnavailable: 1          # 업데이트 중 최대 불가 Pod 수
      maxSurge: 1               # 추가로 생성 가능한 최대 Pod 수
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
      - name: nginx
        image: nginx:1.24        # 이미지 버전 관리의 핵심
        ports:
        - containerPort: 80
        readinessProbe:          # 롤링 업데이트의 안전 장치
          httpGet:
            path: /
            port: 80
          initialDelaySeconds: 5
          periodSeconds: 5
        resources:
          requests:
            memory: "64Mi"
            cpu: "50m"
          limits:
            memory: "128Mi"
            cpu: "100m"
💡개념

롤링 업데이트 — 무중단 배포의 원리

새 버전을 배포할 때 기존 파드를 모두 내리고 새 파드를 올리는 방식은 짧은 다운타임이 생깁니다. 트래픽이 있는 서비스에서 다운타임은 사용자 경험과 SLA에 직접 영향을 줍니다. 롤링 업데이트는 기존 파드를 하나씩 교체하면서 항상 일정 수 이상이 요청을 처리하도록 유지합니다. readinessProbe가 없으면 새 파드가 아직 초기화 중인데 트래픽이 들어와 502가 발생하므로, 롤링 업데이트와 readinessProbe는 함께 설정해야 진정한 무중단이 됩니다. maxUnavailable과 maxSurge 수치는 배포 속도와 가용성 사이의 트레이드오프를 결정하는 운영 파라미터입니다.

롤링 업데이트 — 무중단 배포의 원리

롤링 업데이트 동작 과정

초기 상태: Pod v1.0 × 3개

1단계: 새 ReplicaSet 생성 (v2.0)
       Pod v1.0 × 3, Pod v2.0 × 1 (maxSurge: 1)

2단계: v2.0 Pod가 Ready → v1.0 Pod 1개 종료
       Pod v1.0 × 2, Pod v2.0 × 1

3단계: v2.0 Pod 하나 더 생성
       Pod v1.0 × 2, Pod v2.0 × 2

4단계: v2.0 Pod Ready → v1.0 Pod 1개 종료
       Pod v1.0 × 1, Pod v2.0 × 2

... 반복 ...

최종: Pod v2.0 × 3, 구 ReplicaSet(v1.0) replicas=0으로 보관

이미지 업데이트 방법

Kubernetes
# 방법 1: kubectl set image (빠른 업데이트)
kubectl set image deployment/my-app nginx=nginx:1.25 -n deploy-lab

# 방법 2: kubectl edit (YAML 직접 편집)
kubectl edit deployment/my-app -n deploy-lab
# → image: nginx:1.24 를 nginx:1.25 로 변경

# 방법 3: YAML 파일 수정 후 apply (권장 — GitOps 방식)
# deployment.yaml의 image 필드 수정 후
kubectl apply -f deployment.yaml -n deploy-lab

롤아웃 상태 모니터링

Kubernetes
# 롤아웃 진행 상황 실시간 확인
kubectl rollout status deployment/my-app -n deploy-lab
# Waiting for deployment "my-app" rollout to finish: 1 out of 3 new replicas have been updated...
# Waiting for deployment "my-app" rollout to finish: 2 out of 3 new replicas have been updated...
# Waiting for deployment "my-app" rollout to finish: 1 old replicas are pending termination...
# deployment "my-app" successfully rolled out

# 배포 이력 확인
kubectl rollout history deployment/my-app -n deploy-lab
# REVISION  CHANGE-CAUSE
# 1         <none>
# 2         <none>

# 특정 revision 상세 확인
kubectl rollout history deployment/my-app --revision=2 -n deploy-lab

maxUnavailable과 maxSurge 전략

YAML
# 전략 1: 기본 (빠른 배포)
rollingUpdate:
  maxUnavailable: 1   # 교체 중 최대 1개 다운 허용
  maxSurge: 1         # 최대 1개 추가 생성

# 전략 2: 무중단 (가용성 최우선)
rollingUpdate:
  maxUnavailable: 0   # 다운 없이 교체
  maxSurge: 1         # 새 Pod 먼저 생성 후 교체

# 전략 3: 빠른 교체 (속도 최우선)
rollingUpdate:
  maxUnavailable: 2   # 2개까지 동시 교체
  maxSurge: 2         # 2개 추가 생성 허용

실습: Deployment 생성 및 롤링 업데이트

실습 1: Deployment 생성 및 계층 구조 확인

로컬 터미널
cat <<EOF > /tmp/my-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
  namespace: deploy-lab
  annotations:
    kubernetes.io/change-cause: "Initial deployment with nginx 1.24"
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web-app
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 1
      maxSurge: 1
  template:
    metadata:
      labels:
        app: web-app
    spec:
      containers:
      - name: nginx
        image: nginx:1.24-alpine
        ports:
        - containerPort: 80
        readinessProbe:
          httpGet:
            path: /
            port: 80
          initialDelaySeconds: 5
          periodSeconds: 3
        resources:
          requests:
            memory: "32Mi"
            cpu: "10m"
EOF

kubectl apply -f /tmp/my-deployment.yaml

# 생성 확인
kubectl get deployment web-app -n deploy-lab
kubectl get replicaset -n deploy-lab
kubectl get pods -n deploy-lab -l app=web-app

실습 2: 롤링 업데이트

Kubernetes
# 이미지 버전 변경
kubectl set image deployment/web-app nginx=nginx:1.25-alpine -n deploy-lab

# 또는 annotation 추가하면 history에 reason이 기록됨
kubectl annotate deployment/web-app kubernetes.io/change-cause="Update nginx to 1.25" -n deploy-lab

# 실시간 모니터링 (다른 터미널에서)
kubectl rollout status deployment/web-app -n deploy-lab

# Pod 교체 과정 확인 (-w로 watch)
kubectl get pods -n deploy-lab -l app=web-app -w
# NAME                      READY   STATUS              RESTARTS
# web-app-abc-xxx           1/1     Running             0
# web-app-abc-yyy           1/1     Running             0
# web-app-abc-zzz           1/1     Running             0
# web-app-def-new1          0/1     ContainerCreating   0      ← 새 Pod 시작
# web-app-def-new1          1/1     Running             0      ← Ready
# web-app-abc-xxx           1/1     Terminating         0      ← 구 Pod 종료

실습 3: 롤백

Kubernetes
# 배포 이력 확인
kubectl rollout history deployment/web-app -n deploy-lab
# REVISION  CHANGE-CAUSE
# 1         Initial deployment with nginx 1.24
# 2         Update nginx to 1.25

# 이전 버전으로 롤백
kubectl rollout undo deployment/web-app -n deploy-lab
# deployment.apps/web-app rolled back

# 롤백 확인
kubectl rollout status deployment/web-app -n deploy-lab
kubectl get replicasets -n deploy-lab
# NAME              DESIRED   CURRENT   READY   AGE
# web-app-abc-...   3         3         3       10m  ← 이전 RS가 복구됨
# web-app-def-...   0         0         0       5m   ← 새 RS가 0으로

# 특정 revision으로 롤백
kubectl rollout undo deployment/web-app --to-revision=1 -n deploy-lab

실습 4: 스케일링

Kubernetes
# 스케일 아웃
kubectl scale deployment/web-app --replicas=5 -n deploy-lab

# 확인
kubectl get pods -n deploy-lab -l app=web-app
# 5개의 Pod가 실행 중

# 스케일 인
kubectl scale deployment/web-app --replicas=2 -n deploy-lab

# YAML 파일 수정 방식 (GitOps 권장)
# deployment.yaml에서 replicas: 2로 변경 후
kubectl apply -f /tmp/my-deployment.yaml -n deploy-lab

상황

Kubernetes
kubectl set image deployment/api-server app=api-server:v2 -n production
# 업데이트 시작

# 모니터링 대시보드: 502 에러율 30% → 갑자기 폭증
# 사용자 신고: "사이트가 안 됩니다"

원인 분석

Kubernetes
# 롤링 업데이트 확인
kubectl get pods -n production -l app=api-server
# NAME              READY   STATUS    RESTARTS   AGE
# api-server-v2-1   1/1     Running   0          10s  ← v2, Running 상태지만...
# api-server-v1-2   1/1     Running   0          5m
# api-server-v1-3   1/1     Running   0          5m

# Deployment 확인
kubectl describe deployment api-server -n production | grep -A5 "Ready Probe"
# (readinessProbe 없음)

readinessProbe가 없으니, v2 Pod가 Running이 되는 즉시 Service 엔드포인트에 추가됩니다. 하지만 앱은 아직 DB 연결과 캐시 초기화 중입니다. 이 시간 동안 들어오는 요청이 502를 냅니다.

즉각 대응: 롤백

Kubernetes
# 즉시 롤백
kubectl rollout undo deployment/api-server -n production

# 롤백 완료 확인
kubectl rollout status deployment/api-server -n production
# deployment "api-server" successfully rolled out

근본 해결: readinessProbe 추가

YAML
containers:
- name: app
  image: api-server:v2
  readinessProbe:
    httpGet:
      path: /health/ready    # 앱이 준비됐을 때 200을 반환하는 엔드포인트
      port: 8080
    initialDelaySeconds: 10  # 앱 시작 후 첫 체크까지 대기
    periodSeconds: 5
    failureThreshold: 3
    successThreshold: 1
로컬 터미널
# 앱 코드에서 /health/ready 구현 (Node.js 예시)
app.get('/health/ready', async (req, res) => {
  try {
    await db.ping()          # DB 연결 확인
    await cache.ping()       # 캐시 연결 확인
    res.status(200).json({ status: 'ready' })
  } catch (err) {
    res.status(503).json({ status: 'not ready', error: err.message })
  }
})

결과: readinessProbe 추가 후 재배포 → v2 Pod가 DB/캐시 연결 완료 후에만 트래픽 수신 → 502 에러 없는 무중단 배포.

핵심 교훈: 롤링 업데이트에서 무중단을 보장하려면 readinessProbe가 필수입니다. 이 설정 없이는 maxUnavailable: 0으로 설정해도 의미가 없습니다.

💼
실무 맥락
현업 패턴

실무 시나리오: 프로덕션 배포 체크리스트

시니어가 공유한 배포 전 체크리스트:

Kubernetes
# 1. Dry-run으로 변경사항 확인
kubectl apply -f deployment.yaml --dry-run=server -n production
# "deployment.apps/my-app configured (server dry run)"

# 2. diff로 변경 내용 비교
kubectl diff -f deployment.yaml -n production
# -   image: nginx:1.24-alpine
# +   image: nginx:1.25-alpine

# 3. 배포 실행
kubectl apply -f deployment.yaml -n production

# 4. 롤아웃 모니터링
kubectl rollout status deployment/my-app -n production --timeout=5m
# 5분 내 완료 안 되면 문제 상황

# 5. 배포 후 검증
kubectl get pods -n production -l app=my-app
# 모든 Pod가 1/1 Running인지 확인

kubectl logs -n production -l app=my-app --tail=20
# 에러 로그 없는지 확인

배포 실패 시 즉각 롤백 원칙:

"배포 후 5분 내 에러율 상승 시 즉시 롤백, 원인 파악은 롤백 후."

Kubernetes
# 에러 감지 → 즉시 롤백 (원인 파악은 나중에)
kubectl rollout undo deployment/my-app -n production

# 롤백 완료 확인
kubectl rollout status deployment/my-app -n production

# 이후 staging에서 원인 파악

revision 관리 팁:

Kubernetes
# CHANGE-CAUSE annotation으로 배포 이력 문서화
kubectl annotate deployment/my-app \
  kubernetes.io/change-cause="Fix: DB connection pool size increase, JIRA-1234" \
  -n production

# 이력 확인
kubectl rollout history deployment/my-app -n production
# REVISION  CHANGE-CAUSE
# 1         Initial deployment
# 2         Fix: DB connection pool size increase, JIRA-1234

Kubernetes 입문 트랙 5개 모듈을 완료했습니다. 이제 컨테이너 오케스트레이션의 필요성부터 시작해, 클러스터 아키텍처, kubectl 기본 명령어, Pod 생명주기, 그리고 Deployment를 통한 선언적 관리까지 K8s 운영의 핵심 기반을 갖췄습니다.

다음 단계 추천:

  • kubernetes/service-networking — ClusterIP, NodePort, LoadBalancer 서비스 타입
  • kubernetes/configmap-secret — 설정 및 민감 정보 관리
  • kubernetes/persistent-volume — 스테이트풀 워크로드를 위한 볼륨 관리

지식 확인

퀴즈 — 4문제

Q1

Deployment, ReplicaSet, Pod의 관계를 가장 잘 설명한 것은?

Q2

롤링 업데이트 중 maxUnavailable: 0, maxSurge: 1 설정의 의미는?

Q3

`kubectl rollout undo deployment/my-app`을 실행하면 어떻게 되는가?

Q4

readinessProbe 없이 롤링 업데이트를 했을 때 발생할 수 있는 문제는?

0 / 4 답변

🧪 실습으로 확인하기

K8s 기초 — Pod/Deployment/Service 생성

초급

kubectl로 nginx Pod를 생성하고 Deployment와 Service를 차례로 만들어 클러스터 외부에서 접근 가능한 상태까지 구성한다. K8s 3대 리소스의 역할과 관계를 직접 손으로 익힌다.

40📋 5단계💻 직접 환경
실습 시작하기 →

이것도 배워보세요

kubernetes중급 · 70
[Kubernetes] Ingress Controller 경로 기반 포워딩과 SSL/TLS 설정
Kubernetes 트랙 계속
docker입문 · 30
[Docker] 백엔드 개발자에게 Docker와 컨테이너 가상화가 필수인 이유
Docker 트랙 시작점