Certified Kubernetes Administrator (CKA) #10 Workloads 1: Deployment 깊이, ReplicaSet, rolling update/rollback

#9 RBAC에서 사용자와 ServiceAccount에 최소 권한을 부여하는 법을 다뤘습니다. 이번 글부터는 시험의 Workloads and Scheduling도메인(15%)으로 들어갑니다. 그 첫 주제는 운영자가 클러스터에서 가장 자주 손대는 워크로드인 Deployment입니다.

앱을 띄우고, 트래픽이 늘면 replica를 늘리고, 새 이미지로 무중단 업데이트하고, 문제가 생기면 이전 버전으로 되돌립니다. 이 네 가지가 운영의 일상이고, Deployment 하나가 그 전부를 담당합니다. CKAD에서 매니페스트를 쓰는 관점으로 Deployment를 봤다면, 이번에는 운영자가 kubectl로 빠르게 다루는 관점에서 rollout과 rollback까지 깊이 들여다보겠습니다.

Deployment → ReplicaSet → Pod 계층 #

Deployment를 이해하는 출발점은 그것이 혼자 Pod를 만들지 않는다는 사실입니다. Deployment는 ReplicaSet을 만들고, ReplicaSet이 Pod를 만듭니다. 즉 세 단계의 계층 구조입니다.

Deployment   (원하는 상태 선언 + rollout이력 관리)
   └─ ReplicaSet   (지정된 replica 수만큼 Pod 유지)
        └─ Pod ... Pod   (실제 컨테이너 실행 단위)

각 층의 역할은 분명합니다.

리소스책임
Deployment원하는 상태를 선언하고, 업데이트 시 새 ReplicaSet으로 교체하며, 이전 버전을 revision으로 보관
ReplicaSet자신이 관리하는 라벨의 Pod 수를 항상 지정된 replica 수로 유지
Pod컨테이너가 실제로 도는 최소 단위

업데이트가 일어나면 Deployment는 새 ReplicaSet을 하나 더 만들고, 새 ReplicaSet의 Pod를 늘리면서 이전 ReplicaSet의 Pod를 줄입니다. 이전 ReplicaSet은 삭제되지 않고 replica 0으로 남아 롤백의 발판이 됩니다.

# 한 Deployment가 거느린 ReplicaSet과 Pod를 한눈에
k get deploy,rs,pod -l app=web

라벨 selector로 묶인다 #

계층이 서로를 찾는 방법은 라벨 selector입니다. ReplicaSet은 spec.selector.matchLabels로 자신이 관리할 Pod를 식별하고, 그 selector는 spec.template.metadata.labels와 일치해야 합니다. 이 둘이 어긋나면 API server가 Deployment 생성을 거부합니다.

spec:
  selector:
    matchLabels:
      app: web        # 이 라벨을 가진 Pod를 내 것으로 관리
  template:
    metadata:
      labels:
        app: web      # selector와 반드시 일치

또 하나 기억할 점은 spec.selector생성 후 변경 불가(immutable) 필드라는 것입니다. selector를 바꿔야 한다면 Deployment를 다시 만들어야 하므로, 처음에 정확히 잡는 편이 빠릅니다.

생성과 스케일 #

운영자는 매니페스트를 처음부터 손으로 쓰기보다 kubectl로 골격을 만든 뒤 다듬는 경우가 많습니다. 시험에서도 이쪽이 압도적으로 빠릅니다.

# Deployment 생성 (replica 3, 이미지 지정)
k create deploy web --image=nginx:1.25 --replicas=3

# YAML 골격만 뽑아 편집 후 적용 (do = --dry-run=client -o yaml)
k create deploy web --image=nginx:1.25 $do > deploy.yaml
k apply -f deploy.yaml

생성된 Deployment의 replica 수를 바꾸는 방법은 두 가지입니다.

# 1) kubectl scale: 가장 빠름
k scale deploy web --replicas=5

# 2) 매니페스트 수정 후 재적용: 선언적이라 GitOps와 맞물림
k edit deploy web        # spec.replicas 수정

급할 때는 k scale이 빠르지만, 운영의 정석은 매니페스트의 replicas를 바꿔 재적용하는 선언적 방식입니다. k scale로 바꾼 값은 다음 k apply가 매니페스트의 값으로 되돌리므로, 둘을 섞어 쓰면 replica 수가 의도와 어긋날 수 있습니다.

# 현재 replica와 가용 상태 확인
k get deploy web
# NAME   READY   UP-TO-DATE   AVAILABLE   AGE
# web    5/5     5            5           2m

READY는 준비된/원하는 Pod 수, UP-TO-DATE는 최신 template을 반영한 Pod 수, AVAILABLE은 가용 Pod 수입니다. rollout 중에는 이 세 값이 서로 달라지므로 업데이트 완료 여부를 판단할 때 함께 봅니다.

롤링 업데이트 #

Deployment의 진가는 업데이트 전략에 있습니다. 기본 전략인 rollingUpdate는 이전 Pod를 한꺼번에 내리지 않고, 새 Pod를 조금씩 띄우며 이전 Pod를 조금씩 내려 서비스 중단 없이 버전을 교체합니다.

전략 파라미터: maxSurge와 maxUnavailable #

rollingUpdate의 속도와 안전성은 두 파라미터가 결정합니다.

파라미터의미기본값
maxSurge원하는 replica 수를 초과해 임시로 더 띄울 수 있는 Pod 수(또는 %)25%
maxUnavailablerollout 중 가용하지 않아도 되는 최대 Pod 수(또는 %)25%

maxSurge를 키우면 새 Pod를 더 많이 미리 띄우므로 빠르지만 일시적으로 리소스를 더 씁니다. maxUnavailable을 0으로 두면 항상 원하는 수만큼 가용 Pod가 유지되어 무중단에 가장 안전하지만, 새 Pod가 Ready가 될 때까지 기다리므로 rollout이 느려집니다. 무중단이 절대 조건이면 maxUnavailable: 0 조합이 정석입니다.

spec:
  replicas: 4
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1          # 최대 5개까지 일시 허용
      maxUnavailable: 0    # 가용 Pod는 항상 4개 보장

이미지 교체로 rollout 트리거 #

업데이트는 보통 이미지 태그 변경으로 일어납니다. template의 무언가가 바뀌면 Deployment가 새 ReplicaSet을 만들어 rollout을 시작합니다.

# 컨테이너 이미지를 새 태그로 (가장 흔한 rollout 트리거)
k set image deploy/web nginx=nginx:1.26

# 또는 매니페스트를 직접 편집
k edit deploy web        # spec.template.spec.containers[].image 수정

여기서 nginx=컨테이너 이름입니다. 이미지가 아니라 컨테이너 이름을 적어야 하므로, 컨테이너 이름이 헷갈리면 k get deploy web -o jsonpath='{.spec.template.spec.containers[*].name}'로 먼저 확인합니다.

rollout 상태와 이력 추적 #

업데이트가 진행되는 동안과 끝난 뒤를 kubectl rollout으로 추적합니다.

# rollout이 끝날 때까지 진행 상황을 지켜봄 (끝나면 0으로 종료)
k rollout status deploy/web

# revision이력 (각 revision은 하나의 ReplicaSet에 대응)
k rollout history deploy/web

# 특정 revision의 template 상세
k rollout history deploy/web --revision=2

k rollout status는 rollout이 완료되면 종료 코드 0으로 끝나므로 업데이트 완료를 기다리는 신호로 쓸 수 있습니다. k rollout history의 각 revision은 앞서 본 ReplicaSet 하나에 대응합니다.

CHANGE-CAUSE 기록: –record 대신 annotation #

k rollout historyCHANGE-CAUSE 칸은 각 revision이 왜 만들어졌는지 보여 줍니다. 예전에는 명령 끝에 --record를 붙여 채웠지만, 이 플래그는 deprecated입니다. 지금은 kubernetes.io/change-cause annotation을 직접 다는 방식이 권장됩니다.

# 권장: annotation으로 변경 사유 기록
k annotate deploy/web kubernetes.io/change-cause="update nginx to 1.26"

# 확인
k rollout history deploy/web
# REVISION   CHANGE-CAUSE
# 1          <none>
# 2          update nginx to 1.26

CHANGE-CAUSE를 채워 두면 롤백 대상 revision을 고를 때 각 revision이 어떤 변경이었는지 한눈에 알 수 있어, 운영 사고 대응이 빨라집니다.

롤백 #

새 버전에 문제가 생겼을 때 운영자가 가장 먼저 손대는 것이 롤백입니다. Deployment는 이전 ReplicaSet을 revision으로 보관하므로, 한 줄로 되돌릴 수 있습니다.

# 바로 직전 revision으로 롤백
k rollout undo deploy/web

# 특정 revision으로 롤백
k rollout undo deploy/web --to-revision=2

롤백 역시 rollingUpdate 전략을 따라 무중단으로 진행됩니다. 롤백이 끝난 뒤 k rollout history를 보면, 되돌린 내용이 새 revision으로 추가됩니다. 즉 revision 번호는 줄지 않고 계속 올라갑니다.

pause와 resume: 변경을 모아서 한 번에 #

여러 필드를 연달아 바꿀 때 매 변경마다 rollout이 시작되면 ReplicaSet이 우후죽순 생깁니다. 이때 pause로 rollout을 멈추고, 변경을 모두 적용한 뒤 resume으로 한 번에 rollout 합니다.

# rollout 일시 정지
k rollout pause deploy/web

# 이 사이의 변경은 즉시 rollout 되지 않고 쌓임
k set image deploy/web nginx=nginx:1.26
k set resources deploy/web -c=nginx --limits=cpu=200m,memory=256Mi

# 모은 변경을 한 번의 rollout으로 적용
k rollout resume deploy/web

rollout을 중간에 멈추고 일부만 검증한 뒤 진행을 결정하는 카나리 점검에도 pause/resume이 유용합니다.

recreate vs rollingUpdate #

전략에는 rollingUpdate 외에 Recreate도 있습니다. 둘의 차이를 명확히 알아 두면 무중단이 필요한 곳과 그렇지 않은 곳을 가를 수 있습니다.

항목RollingUpdate (기본)Recreate
교체 방식새 Pod를 띄우며 이전 Pod를 점진 종료이전 Pod를 모두 종료한 뒤 새 Pod 생성
서비스 중단없음(무중단)있음(전환 순간 다운타임 발생)
리소스rollout 중 일시적으로 더 사용(maxSurge)추가 사용 없음
두 버전 공존잠시 공존함공존하지 않음
쓰는 곳대부분의 무상태 웹/API 서비스두 버전이 동시에 뜨면 안 되는 앱(스키마 충돌 등)

Recreate는 구버전과 신버전이 같은 데이터베이스 스키마를 공유할 수 없을 때처럼 두 버전 공존 자체가 문제인 경우에 씁니다. 그 외 일반적인 무상태 서비스는 rollingUpdate가 기본이자 정답입니다.

운영 관점: 무중단 업데이트가 보장되는 조건 #

rollingUpdate 전략만으로 무중단이 자동 보장되지는 않습니다. 새 Pod가 진짜로 트래픽을 받을 준비가 되었는지 쿠버네티스가 알아야 하기 때문입니다. 이 신호가 readinessProbe입니다.

readinessProbe가 없으면 컨테이너가 시작되자마자 Ready로 간주되어, 앱이 아직 초기화 중인데도 Service가 트래픽을 보냅니다. rollingUpdate가 이 Pod를 가용으로 세고 이전 Pod를 내리면, 그 순간 요청이 실패합니다. 따라서 무중단의 실제 조건은 rollingUpdate 전략 + 정확한 readinessProbe + maxUnavailable: 0 의 조합입니다. probe는 #11 이후와 트러블슈팅 편에서 더 다루겠습니다.

시험 포인트 #

  • 계층을 기억하라. Deployment가 ReplicaSet을 만들고 ReplicaSet이 Pod를 만든다. k get deploy,rs,pod로 한 번에 확인한다.
  • selector는 immutable이며 template.labels와 일치해야 한다. 처음에 정확히 잡는다.
  • 생성은 k create deploy --image= --replicas=, 스케일은 k scale deploy --replicas=로 빠르게 처리한다.
  • 이미지 교체는 k set image deploy/<name> <컨테이너이름>=<이미지>. 이미지가 아니라 컨테이너 이름을 적는다.
  • rollout 추적은 k rollout status/history, 롤백은 k rollout undo [--to-revision=N]. revision 번호는 줄지 않고 올라간다.
  • --record는 deprecated. 변경 사유는 kubernetes.io/change-cause annotation으로 기록한다.
  • 무중단이 조건이면 strategy.rollingUpdate.maxUnavailable: 0 조합을 떠올린다.

정리 #

이번 글에서 잡은 것:

  • Deployment → ReplicaSet → Pod 계층과 이를 묶는 라벨 selector
  • k create deploy/k scale로 생성과 스케일, 선언적 방식과 명령형 방식의 차이
  • rollingUpdate 전략(maxSurge/maxUnavailable), k set image/k edit로 rollout 트리거
  • k rollout status/history/undo로 버전을 추적하고 되돌리는 롤백, pause/resume으로 변경을 모아 적용
  • recreate vs rollingUpdate의 차이와 무중단 업데이트가 보장되는 실제 조건(readinessProbe 연계)

Deployment의 동작 원리는 쿠버네티스 워크로드의 기본기입니다. 컨트롤러가 어떻게 원하는 상태를 유지하는지는 #3 Pod 네트워킹 모델 이전에 기초를 다진 k8s 기초 #4에서도 보강할 수 있습니다.

다음: Workloads 2 #

Deployment는 무상태 서비스를 위한 워크로드였습니다. 그러나 클러스터에는 그 틀로 담을 수 없는 워크로드가 더 있습니다.

#11 Workloads 2: DaemonSet, StatefulSet, Job, CronJob에서는 모든 노드에 하나씩 Pod를 띄우는 DaemonSet, 안정적인 식별자와 순서가 필요한 StatefulSet, 한 번 실행하고 끝나는 Job, 그리고 주기적으로 도는 CronJob을 다루겠습니다. 각 워크로드가 어떤 상황을 위해 설계되었는지, kubectl로 어떻게 만들고 운영하는지 차례로 정리하겠습니다.

X