Certified Kubernetes Application Developer (CKAD) #5 Workloads 1: Deployment, ReplicaSet, rolling update와 rollback
CKAD 시험에서 가장 자주 마주치는 리소스가 Deployment입니다. 실무에서도 애플리케이션 한 벌을 굴리는 표준 단위가 Deployment이고, 시험의 Application Deployment도메인(20%)이 여기서 시작합니다. Pod 하나를 직접 띄우는 일은 거의 없습니다. 운영 환경의 앱은 거의 전부 Deployment로 배포하고, 버전을 올릴 때는 롤링 업데이트로 무중단 교체하며, 새 버전이 문제를 일으키면 한 줄로 롤백합니다.
이번 글에서는 Deployment를 명령형으로 빠르게 생성하는 법부터, Deployment 아래에서 ReplicaSet과 Pod가 어떻게 엮이는지, 그리고 kubectl rollout으로 배포 이력을 관리하고 실패한 버전을 되돌리는 전 과정을 손으로 따라 치며 익히겠습니다. 클러스터 동작의 큰 그림이 흐릿하다면 K8s 실무 트랙 #4 워크로드를 먼저 읽고 오는 편이 도움이 됩니다.
Deployment를 명령형으로 생성한다 #
#1에서 잡아 둔 do alias를 그대로 씁니다. Deployment 뼈대는 k create deploy로 한 줄에 뽑습니다.
# 바로 생성
k create deploy web --image=nginx:1.25 --replicas=3
# 매니페스트 뼈대만 뽑아서 편집 (dry-run)
k create deploy web --image=nginx:1.25 --replicas=3 $do > deploy.yaml--replicas 플래그가 없으면 replicas는 1로 생성됩니다. 시험에서 개수를 지정하라고 하면 생성 시점에 같이 넘기는 편이 빠릅니다. 생성된 매니페스트는 다음 형태입니다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
labels:
app: web
spec:
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: nginx
image: nginx:1.25여기서 핵심은 selector.matchLabels와 template.metadata.labels가 반드시 일치해야 한다는 점입니다. selector는 이 Deployment가 어떤 Pod를 자기 소유로 볼지 정하는 기준이고, template의 라벨이 그 기준에 맞아야 Pod가 정상적으로 관리됩니다. 둘이 어긋나면 생성 자체가 거부됩니다.
Deployment, ReplicaSet, Pod의 관계 #
Deployment를 만들었다고 Deployment가 Pod를 직접 만드는 것은 아닙니다. 그 사이에 ReplicaSet이 한 단계 끼어 있습니다.
Deployment ──소유──> ReplicaSet ──소유──> Pod (×replicas)- Deployment는 “어떤 버전을 몇 개 굴릴지"를 선언하고, 버전이 바뀔 때마다 새 ReplicaSet을 만들어 교체를 지휘합니다.
- ReplicaSet은 “지정된 개수의 Pod를 항상 유지"하는 역할만 합니다. Pod가 죽으면 다시 만들고, 많으면 줄입니다.
- Pod는 실제 컨테이너가 돌아가는 최소 단위입니다.
생성 직후 실제로 무엇이 만들어졌는지 확인해 봅니다.
k get deploy web
k get rs # web-<해시> ReplicaSet이 하나 생김
k get pod # web-<rs해시>-<랜덤> Pod가 replicas만큼 생김k get rs 출력의 이름이 web-7d9c8f6b54처럼 Deployment 이름 뒤에 해시가 붙는 것을 볼 수 있습니다. 이 해시는 Pod 템플릿의 내용으로 계산되므로, 이미지나 환경 변수를 바꾸면 새 해시의 ReplicaSet이 생깁니다. 이 구조가 바로 롤링 업데이트와 롤백의 토대입니다. 새 버전을 새 ReplicaSet으로 만들고, 옛 ReplicaSet은 개수만 0으로 줄여 이력으로 남겨 두기 때문입니다.
스케일: 개수를 바꾼다 #
Pod 개수를 늘리거나 줄이는 일은 k scale로 즉시 처리합니다.
# replicas를 5로
k scale deploy web --replicas=5
# 현재 개수가 3일 때만 5로 변경 (조건부)
k scale deploy web --current-replicas=3 --replicas=5매니페스트를 고쳐서 k apply하는 방법도 있지만, 시험에서 개수만 바꾸라는 작업이면 k scale이 가장 빠릅니다. 변경 후에는 k get deploy web의 READY 열이 5/5가 되는지 확인합니다.
오토스케일이 필요한 작업이라면 k autoscale로 HorizontalPodAutoscaler를 붙입니다.
k autoscale deploy web --min=2 --max=10 --cpu-percent=70롤링 업데이트: 무중단으로 새 버전을 깐다 #
운영 중인 Deployment의 이미지를 새 버전으로 올릴 때, 기본 전략은 RollingUpdate입니다. 기존 Pod를 한꺼번에 죽이지 않고 새 Pod를 조금씩 띄우면서 옛 Pod를 조금씩 내려, 서비스가 끊기지 않게 교체합니다.
maxSurge와 maxUnavailable #
롤링 업데이트의 속도와 안정성은 두 값이 결정합니다.
spec:
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # 원하는 개수보다 최대 몇 개 더 띄울 수 있는가
maxUnavailable: 1 # 교체 중 최대 몇 개까지 부족해도 되는가- maxSurge: 업데이트 도중 desired replicas를 초과해 임시로 더 띄울 수 있는 Pod 수입니다. 값이 크면 새 Pod를 더 많이 미리 띄워 교체가 빨라집니다.
- maxUnavailable: 업데이트 도중 사용 불가 상태여도 허용되는 Pod 수입니다. 값이 0이면 새 Pod가 Ready가 된 다음에야 옛 Pod를 내리므로 가용성이 가장 높습니다.
두 값 모두 정수 또는 백분율(25%)로 줄 수 있습니다. 지정하지 않으면 둘 다 기본값 25%입니다. 가용성을 최우선으로 하라는 작업이면 maxUnavailable: 0으로 두는 패턴을 기억해 둡니다.
이미지 교체로 업데이트를 트리거한다 #
가장 흔한 업데이트는 이미지 버전을 올리는 것입니다. k set image가 한 줄로 끝냅니다.
# 컨테이너 이름이 nginx일 때 v2로 교체
k set image deploy/web nginx=nginx:1.26
# 컨테이너가 여러 개면 각각 지정
k set image deploy/web nginx=nginx:1.26 sidecar=busybox:1.36set image의 nginx= 부분은 컨테이너 이름이지 이미지 이름이 아닙니다. 컨테이너 이름이 헷갈리면 k get deploy web -o jsonpath='{.spec.template.spec.containers[*].name}'로 먼저 확인합니다.
이미지뿐 아니라 다른 필드까지 바꿔야 한다면 k edit으로 매니페스트를 직접 고칩니다.
k edit deploy webk edit으로 template 안의 무언가를 바꾸면 새 ReplicaSet이 생기고 롤링 업데이트가 시작됩니다. 반대로 k scale처럼 template 밖(replicas)만 바꾸면 새 ReplicaSet은 생기지 않습니다. 즉 새 revision은 Pod 템플릿이 바뀔 때만 만들어집니다.
rollout으로 배포를 관리한다 #
업데이트가 잘 진행되는지, 이력이 어떻게 쌓이는지, 문제가 생기면 어떻게 되돌릴지를 모두 kubectl rollout이 담당합니다. CKAD에서 가장 손에 익혀야 할 명령군입니다.
진행 상태 확인 #
# 롤링 업데이트가 끝날 때까지 진행 상황을 보여줌
k rollout status deploy/webset image 직후 이 명령을 걸어 두면 “전부 Ready가 됐는지"를 기다렸다가 확인할 수 있습니다. 작업이 완료됐는지 채점 전에 스스로 검증하는 용도로 유용합니다.
이력 확인 #
# revision 목록
k rollout history deploy/web
# 특정 revision의 상세 (그 시점의 템플릿)
k rollout history deploy/web --revision=2revision은 1부터 올라갑니다. 어떤 변경이 어느 revision이었는지 메모(CHANGE-CAUSE)를 남기고 싶으면, 변경 시 --record를 붙이거나 kubernetes.io/change-cause 애너테이션을 직접 답니다.
k annotate deploy web kubernetes.io/change-cause="update to nginx:1.26"일시 정지와 재개 #
여러 변경을 한꺼번에 묶어 한 번의 롤아웃으로 처리하고 싶을 때는 pause로 멈춰 두고 여러 번 수정한 뒤 resume합니다.
k rollout pause deploy/web
k set image deploy/web nginx=nginx:1.26
k set resources deploy/web -c=nginx --limits=cpu=200m,memory=256Mi
k rollout resume deploy/web # 이때 한 번에 롤아웃pause 상태에서는 template을 바꿔도 새 Pod가 뜨지 않습니다. resume하는 순간 누적된 변경이 한 revision으로 반영됩니다. canary 같은 전략의 토대이며, 자세한 배포 전략은 #8에서 다루겠습니다.
보관할 이력 개수 #
옛 ReplicaSet을 몇 개까지 남길지는 revisionHistoryLimit가 정합니다. 기본값은 10이며, 0으로 두면 롤백할 이력이 사라지므로 주의합니다.
spec:
revisionHistoryLimit: 5롤백 시나리오 실습 #
이제 시험에 자주 나오는 흐름을 처음부터 끝까지 따라 칩니다. 정상 버전을 굴리다가 잘못된 버전을 배포해 실패를 감지하고, 직전 버전으로 되돌리는 시나리오입니다.
# 1) 정상 버전 배포 (revision 1)
k create deploy web --image=nginx:1.25 --replicas=3
k rollout status deploy/web
# 2) 잘못된 이미지로 업데이트 (revision 2), 존재하지 않는 태그
k set image deploy/web nginx=nginx:does-not-exist
# 3) 실패 감지
k rollout status deploy/web # 진행이 멈춰 끝나지 않음
k get pod # 새 Pod가 ImagePullBackoff 상태잘못된 이미지라 새 Pod는 이미지를 받지 못하고 ImagePullBackOff에 빠집니다. 여기서 중요한 점은, maxUnavailable이 25%이므로 옛 Pod의 일부는 여전히 살아 서비스가 완전히 죽지는 않는다는 점입니다. RollingUpdate가 가용성을 지켜 주는 덕분입니다. 그래도 새 버전이 망가졌으니 되돌립니다.
# 4) 직전 revision으로 롤백
k rollout undo deploy/web
k rollout status deploy/web # 다시 정상으로 수렴
# 5) 확인: 다시 1.25로 돌아왔는지
k get deploy web -o jsonpath='{.spec.template.spec.containers[0].image}'특정 revision을 콕 집어 되돌릴 수도 있습니다.
# revision 목록을 보고
k rollout history deploy/web
# 3번 revision으로 되돌리기
k rollout undo deploy/web --to-revision=3undo는 옛 ReplicaSet의 개수를 다시 올리고 망가진 ReplicaSet의 개수를 0으로 내리는 방식으로 동작합니다. 그래서 롤백이 빠르고, 되돌린 결과가 또 하나의 새 revision으로 기록됩니다. revision 번호는 사라지지 않고 계속 누적된다는 점을 기억해 둡니다.
RollingUpdate와 Recreate #
Deployment의 전략에는 RollingUpdate 말고 Recreate도 있습니다. Recreate는 기존 Pod를 전부 내린 뒤 새 Pod를 띄우므로 교체 중 서비스가 잠깐 끊깁니다. 동시에 두 버전이 떠 있으면 안 되는 앱(예: 단일 쓰기 데이터베이스 마이그레이션)에 씁니다.
spec:
strategy:
type: Recreate기본은 무중단 교체인 RollingUpdate이고, Recreate는 다운타임을 감수하는 특수 상황용입니다. 두 전략의 트레이드오프와 blue-green,canary 같은 무중단 고급 전략은 #8 Deployment 전략에서 자세히 다루겠습니다.
시험 포인트 #
- Deployment 생성은
k create deploy <name> --image=<img> --replicas=N이 가장 빠릅니다.--replicas생략 시 1입니다. - selector.matchLabels와 template.labels는 일치해야 합니다. 어긋나면 생성이 거부됩니다.
- 소유 관계는 Deployment → ReplicaSet → Pod입니다. 새 revision은 Pod 템플릿이 바뀔 때만 생깁니다. replicas만 바꾸면 새 ReplicaSet은 생기지 않습니다.
- 스케일은
k scale deploy <name> --replicas=N입니다. - 이미지 교체는
k set image deploy/<name> <컨테이너이름>=<img>입니다. 등호 왼쪽은 이미지가 아니라 컨테이너 이름입니다. - maxSurge는 초과 허용 수, maxUnavailable은 부족 허용 수입니다. 기본값은 둘 다 25%이고, 가용성 최우선이면
maxUnavailable: 0입니다. - rollout 명령군:
status(진행),history(이력),history --revision=N(상세),undo(직전 롤백),undo --to-revision=N(특정 버전),pause,resume(묶어서 배포)을 손에 익혀 둡니다. - 롤백 후에도 revision은 누적됩니다. 보관 개수는
revisionHistoryLimit(기본 10)가 정합니다.
정리 #
이번 글에서 잡은 것:
- Deployment는 앱 배포의 표준 단위이며, Deployment → ReplicaSet → Pod의 3단 구조로 동작합니다.
- 생성,스케일은
k create deploy,k scale로 명령형으로 빠르게 처리합니다. - 롤링 업데이트는 maxSurge,maxUnavailable로 속도와 가용성을 조절하며,
k set image나k edit이 트리거합니다. - **
kubectl rollout**으로 배포 상태를 추적하고 이력을 관리하며,undo로 실패한 버전을 한 줄에 되돌립니다. - Recreate는 다운타임을 감수하는 특수 상황용이고, 기본은 무중단 RollingUpdate입니다.
다음: Workloads 2 #
Deployment로 무상태 앱을 무중단으로 굴리는 법을 잡았습니다. 그런데 모든 워크로드가 “아무 노드에나 N개 띄우면 되는” 형태인 것은 아닙니다.
#6 Workloads 2: DaemonSet, StatefulSet에서는 모든 노드에 정확히 하나씩 Pod를 깔아야 하는 DaemonSet(로그 수집기,모니터링 에이전트), 그리고 안정적인 네트워크 식별자와 순서가 보장된 스토리지가 필요한 StatefulSet(데이터베이스,메시지 큐)을 다루겠습니다. Deployment와 무엇이 다르고 시험에서 어떤 형태로 나오는지 직접 만들어 보며 정리하겠습니다.