Workload API vol. 2: ReplicaSet, Deployment

성윤·2026년 2월 25일

[☸️Kubernetes]

목록 보기
8/13
post-thumbnail

들어가며 👋

지난 포스팅에서는 쿠버네티스의 가장 기본 단위인 파드(Pod)에 대해 알아보았습니다.

글 마지막에 "실무에서는 파드를 단독으로 배포하는 일이 거의 없다" 고 말씀드렸었습니다. 파드는 한 번 에러가 나서 죽거나 배정된 노드(서버)가 다운되면 스스로 다시 살아나지 않기 때문입니다.

서비스가 365일 24시간 중단 없이 돌아가려면, 파드가 죽었을 때 누군가 이를 감지하고 즉시 새로운 파드를 채워 넣어주어야 합니다.

오늘은 이러한 '자동화된 복구(Self-Healing)' 를 책임지는 레플리카셋(ReplicaSet) 과, 무중단 배포 및 버전 관리를 총괄하는 쿠버네티스의 핵심 디플로이먼트(Deployment)에 대해 알아보겠습니다. 🚀


1. 레플리카셋 (ReplicaSet): 파드 지켜! 🛡️

레플리카셋은 "내가 설정한 개수만큼의 파드가 항상 클러스터 내에서 실행되도록 유지" 하는 역할을 합니다. 노드의 하드웨어 장애 등으로 파드를 사용할 수 없을 때 다른 노드에 파드를 다시 생성해서 사용자에게 중단 없는 서비스를 제공합니다.

💡참고: 과거에는 ReplicationController라는 리소스를 썼지만, 현재는 기능이 더 강화된 ReplicaSet으로 대체되어 요즘은 웬만하면 ReplicaSet을 사용합니다.


① 레플리카셋은 어떻게 파드를 관리할까? (레이블과 셀렉터)

레플리카셋은 파드의 '이름'이 아니라 '레이블(Label)' 을 기준으로 파드를 감시합니다. 아래 YAML 파일을 통해 구조를 살펴보겠습니다.

# sample-rs.yaml
apiVersion: apps/v1 # Pod와 달리 apps/v1을 사용합니다.
kind: ReplicaSet
metadata:
  name: sample-rs
spec:
  replicas: 3 # 유지할 파드의 개수
  selector: 
    matchLabels:
      app: sample-app # "app: sample-app" 라벨을 가진 파드들을 관리 대상으로 삼겠다는 뜻
  template: # 파드가 죽었을 때 새로 만들 "설계도"
    metadata:
      labels:
        app: sample-app # [중요] selector와 반드시 일치해야 함!
    spec:
      containers:
      - name: nginx-container
        image: nginx

핵심 요약

  • templatelabels: '앞으로 어떤 레이블을 가진 파드를 생성할 것인가'를 정의합니다.

  • selectormatchLabels: '현재 어떤 레이블을 가진 파드를 감시하고 관리할 것인가'를 정의합니다.

이 두 값이 반드시 일치해야 레플리카셋이 자신이 생성한 파드를 정상적으로 추적하고, 설정된 개수(replicas)를 알맞게 유지할 수 있습니다.


이제 리소스를 배포하고 생성된 파드를 확인해 보겠습니다.

$ kubectl apply -f sample-rs.yaml
$ kubectl get pods -l app=sample-app -o wide

NAME              READY   STATUS    RESTARTS   AGE   IP             NODE     
sample-rs-65knz   1/1     Running   0          35s   10.244.1.127   worker2           
sample-rs-8hhb4   1/1     Running   0          35s   10.244.1.128   worker2             
sample-rs-hh7hn   1/1     Running   0          35s   10.244.2.87    worker1    

파드의 이름은 ReplicaSet이름-임의의 문자열 형태로 자동 설정되는 것을 볼 수 있습니다.


🚨 주의사항: 레이블 불일치 에러

spec.selector가 찾는 라벨과 spec.template로 만들어낼 파드의 라벨이 다르면 어떻게 될까요?

쿠버네티스는 이를 문법적 오류로 간주하고 selector does not match template labels라는 에러를 뱉으며 아예 생성을 거부합니다.

그만큼 ReplicaSet을 사용할 때 selector와 template의 레이블을 정확히 맞춰서 써주는 것이 중요합니다.


② 자동화된 복구 (Self-Healing) 실습

위 YAML 파일로 레플리카셋을 생성(kubectl apply -f sample-rs.yaml)하면 3개의 파드가 뜹니다. 이때 파드 하나를 강제로 지워보겠습니다.

# 파드 하나 강제 삭제
$ kubectl delete pods sample-rs-65knz

# 파드 목록 재확인
$ kubectl get pods -l app=sample-app

목록을 다시 확인해 보면, 지워진 파드 대신 새로운 이름의 파드(sample-rs-gbzfv)가 불과 몇 초 만에 새로 생성되어 3개를 유지하고 있는 것을 볼 수 있습니다.

kubectl describe replicaset sample-rs를 쳐보면 이벤트 로그에 새 파드를 언제 다시 만들었는지 이력도 남습니다.


③ 스케일링 (Scaling)

트래픽이 몰려 파드 개수를 3개에서 5개로 늘리고 싶다면 매니페스트를 수정해도 되지만, 명령어로 즉시 스케일 업 할 수도 있습니다.

$ kubectl scale replicaset sample-rs --replicas 5

$ kubectl scale replicaset sample-rs --replicas 5

NAME              READY   STATUS              RESTARTS   AGE
sample-rs-498lk   1/1     Running             0          16m
sample-rs-pwv4b   1/1     Running             0          4s
sample-rs-r8fvx   1/1     Running             0          29m
sample-rs-rjns2   0/1     ContainerCreating   0          4s
sample-rs-wv97j   1/1     Running             0          29m

💡 심화: 집합성 기준 조건 (Set-based Selector)

예전 ReplicationController는 라벨이 정확히 일치(=, !=)해야만 관리할 수 있었지만, ReplicaSet은 in, notin, exists 같은 집합성 조건을 쓸 수 있어 훨씬 유연한 필터링이 가능합니다.


2. 디플로이먼트 (Deployment): 배포와 업데이트의 총괄 매니저 🎩

레플리카셋이 파드의 '개수 유지'에 특화되어 있다면, 디플로이먼트(Deployment)는 레플리카셋을 감싸고 있는 상위 개념으로 '애플리케이션의 배포와 버전 업데이트'를 세분화해서 관리합니다.

쿠버네티스에서 컨테이너를 기동할 때 가장 권장하는 표준 방법입니다. 심지어 하나의 파드만 기동하는 경우에도 디플로이먼트 사용을 권장합니다.

파드나 레플리카셋만으로는 새 버전의 이미지를 무중단으로 배포(Rolling Update)하거나 이전 버전으로 되돌리는(Rollback) 기능을 수행할 수 없기 때문입니다.


① 디플로이먼트의 동작 원리 (해시 기반 레플리카셋 관리)

디플로이먼트는 템플릿(spec.template)의 내용에 변경이 발생하면 해시(Hash)값을 계산하고, 이 값을 사용한 새로운 레이블의 레플리카셋을 신규로 만들어 점진적으로 파드를 옮겨갑니다.

디플로이먼트를 배포할 때 --record=true 옵션을 주면 업데이트 이력을 남길 수 있습니다. (참고: 이 플래그는 추후 K8s에서 삭제될 예정입니다)

$ kubectl apply -f sample-deployment.yaml --record=true
$ kubectl get replicasets -o yaml | head

# 출력 결과에 아래와 같이 업데이트 이력(revision, change-cause)이 주석(annotation)으로 기록됩니다.
# deployment.kubernetes.io/revision: "1"
# kubernetes.io/change-cause: kubectl apply --filename=sample-deployment.yaml

② 업데이트와 롤백 (Rollback) 실습

실제 어떻게 구동되는지 확인하기 위해 이미지 버전을 업데이트(nginx -> nginx:1.17)하고 이력을 확인해 보겠습니다.


우선 이미지를 업데이트하고 업데이트 진행 상태를 확인해보겠습니다.

# 1. 이미지 업데이트 (버전 변경)
$ kubectl set image deployment sample-deployment nginx-container=nginx:1.17 --record

# 2. 업데이트 진행 상태 확인
$ kubectl rollout status deployment sample-deployment

💡출력 결과 해석: 롤링 업데이트의 무중단 배포 과정

위 실행 결과를 보면 쿠버네티스의 기본 배포 방식인 롤링 업데이트(Rolling Update) 가 실제로 어떻게 동작하는지 눈으로 확인할 수 있습니다.

한 번에 3개의 파드를 몽땅 지우고 새로 띄우는 것이 아닙니다.

1 out of 3, 2 out of 3 처럼 새 버전(1.17)의 파드를 하나씩 차례대로 생성하고(new replicas have been updated),

새 파드가 정상적으로 뜰 때마다 구 버전의 파드를 하나씩 순차적으로 종료(old replicas are pending termination)합니다.

마지막에 successfully rolled out이 뜨면, 사용자 입장에서는 단 1초의 서비스 끊김도 없이 안전하게 무중단 배포가 완료된 것입니다!

# 3. 변경 이력(History) 확인
$ kubectl rollout history deployment sample-deployment

어떻게 배포가 되고 변경이 되었는지 확인도 가능합니다.


업데이트 후 치명적인 버그가 발견되었다면, 기존 레플리카셋이 삭제되지 않고 대기 중이므로 즉시 롤백이 가능합니다.

# 직전 버전으로 롤백
$ kubectl rollout undo deployment sample-deployment

# 특정 버전(Revision 2)으로 롤백
$ kubectl rollout undo deployment sample-deployment --to-revision 2

💡실무 팁: CI/CD 파이프라인을 쓰는 환경에서는 rollout undo 명령어보다는, 이전 버전의 YAML 매니페스트 자체를 다시 apply하는 방식을 더 많이 사용합니다. 이력을 코드로 관리하기 위함입니다.


3. 다양한 업데이트 배포 전략 🔄

디플로이먼트의 spec.strategy를 통해 애플리케이션 버전을 교체하는 방식을 세밀하게 제어할 수 있습니다.

① 재생성 (Recreate)

모든 이전 버전의 파드를 한 번에 종료하고 새로운 버전으로 일괄 교체합니다. 업데이트 속도는 빠르지만 다운타임(서비스 중단 시간) 이 발생하며, 새로운 버전에 문제가 생겼을 때 대처가 늦어질 수 있습니다.

spec:
  strategy:
    type: Recreate

💡실무 포인트: 잠시 서비스가 끊겨도 무방한 사내 백오피스 시스템이나, 구버전과 신버전이 혼재되면 데이터 정합성에 심각한 문제가 생기는 경우에 제한적으로 사용합니다.


② 롤링 업데이트 (Rolling Update) - 쿠버네티스 기본 전략

새 버전의 파드를 하나씩 점진적으로 늘려가고, 기존 버전의 파드는 하나씩 줄여나가는 방식입니다. 쿠버네티스에서 아무 설정을 하지 않으면 기본으로 적용되는 표준 배포 방식 입니다.

새로운 버전에 문제가 생기면 즉시 이전 버전의 파드로 서비스를 대체할 수 있어 안정적이지만, 배포가 완료되기까지 시간이 다소 오래 걸린다는 단점이 있습니다.

롤링 업데이트의 속도와 안정성을 조절하기 위해 다음 두 가지 파라미터를 사용합니다. (비율 (%) 또는 개수로 지정 가능하며, 기본값은 둘 다 25%입니다.)

  • maxSurge: 업데이트 과정에서 설정된 레플리카 수 이상으로 추가 생성할 수 있는 최대 파드 개수 입니다. 높게 설정하면 배포 속도가 빨라집니다.

  • maxUnavailable: 업데이트 과정에서 동시에 삭제할 수 있는 최대 파드 개수입니다. 0으로 설정하면 기존 파드를 유지한 채 새 파드를 먼저 띄우므로 무중단을 완벽히 보장할 수 있습니다.

spec:
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 0 # 기존 파드는 하나도 지우지 않고
      maxSurge: 1       # 새 파드를 1개씩 추가하면서 배포 진행

③ 블루/그린 업데이트 (Blue/Green)

2개의 동일한 운영 환경을 구축해 두고, 로드밸런서를 통해 트래픽을 한 번에 전환하는 방식입니다.

  • Blue (기존 버전): 현재 사용자들이 접속해서 사용 중인 운영 환경

  • Green (신규 버전): 새롭게 코드가 배포된 대기 환경

Green 환경에 새 버전을 배포하고 내부 테스트를 완벽하게 마친 뒤, 로드밸런서의 방향만 Green으로 틀어줍니다.

  • 장점: 완벽한 제로 다운타임, 전환 후 문제가 생기면 즉시 Blue로 트래픽을 돌리면 되므로 가장 빠르고 안전한 롤백이 가능합니다.

  • 단점: 기존 환경과 동일한 환경을 하나 더 띄워야 하므로 서버 자원(비용)이 2배로 듭니다. 또한 전환 시점에 데이터베이스에 쌓이는 데이터의 동기화 문제가 복잡해질 수 있습니다.


④ 카나리 업데이트 (Canary Release)

과거 광부들이 유독 가스를 탐지하기 위해 카나리아 새를 데리고 들어갔던 것에서 유래한 방식이라고 합니다.

기존 버전(v1)과 새 버전(v2)을 동시에 띄워두고, 새 버전으로 향하는 트래픽을 1%, 5%, 10% 점진적으로 늘려가며 새로운 기능이나 버그를 테스트합니다.

⚠️주의사항: 두 가지 버전이 동시에 서비스되기 때문에, 한 명의 유저가 새로고침 할 때마다 다른 버전의 화면을 보게 될 수 있습니다. 이를 방지하기 위해 사용자가 한 번 접속한 서버에 계속 연결되도록 하는 스티키 세션(Sticky Session) 설정이 동반되는 경우가 많습니다.


4. 디플로이먼트를 더 섬세하게 다루는 꿀팁 🍯

① 업데이트 일시 정지 및 재개

업데이트 도중 안전성을 위해 잠깐 멈춰서 상태를 확인하고 싶다면 아래 명령어를 씁니다.

  • 중지: kubectl rollout pause deployment sample-deployment

  • 재개: kubectl rollout resume deployment sample-deployment


② 유용한 파라미터 옵션들 (spec 하위)

  • minReadySeconds: 파드가 'Ready' 상태가 된 후, 디플로이먼트가 가동 완료료 판단하기까지 대기하는 최소 시간(초). 애플리케이션이 구동되고 초기화할 시간이 필요할 때 사용합니다.

  • revisionHistoryLimit: 롤백을 위해 남겨둘 레플리카셋의 최대 과거 이력 개수. (기본적으로 과거 이력을 유지하지만, 이를 조절하지 않으면 클러스터에 사용하지 않는 레플리카셋이 불필요하게 쌓이게 됩니다.)

  • progressDeadlineSeconds: 업데이트가 모종의 이유로 꼬여서 진행되지 않을 때, 이 시간이 지나면 타임 아웃(실패)으로 간주하고 자동으로 롤백 처리를 합니다.


③ 명령어를 이용한 파드 스케일링 (Scaling)

트래픽이 몰릴 때 매니페스트 파일(YAML)을 수정하지 않고도 명령어 한 줄로 즉시 파드 개수를 늘리거나 줄일 수 있습니다.

kubectl scale deployment sample-deployment --replicas=5

④ YAML 없이 빠르게 테스트용 배포하기

간단한 테스트를 위해 매니페스트 파일을 작성하는 것이 번거롭다면, CLI 명령어로 즉시 디플로이먼트를 생성할 수 있습니다.

kubectl create deployment sample-deployment-cli --image nginx

마치며 ✍️

정리하자면, 파드가 죽으면 레플리카셋이 즉시 살려내어 개수를 유지하고, 애플리케이션의 버전 업데이트와 롤백 등 배포 전반의 과정은 디플로이먼트가 안전하게 총괄합니다. 이 두 가지 리소스 덕분에 쿠버네티스 환경의 서비스들은 중단 없이 안정적으로 운영될 수 있습니다.

다음 포스팅에서는 클러스터 전역에 파드를 띄우는 DaemonSet, 상태를 유지하는 StatefulSet, 그리고 작업 처리를 위한 Job과 CronJob에 대해 알아보겠습니다.

0개의 댓글