사전 레플리카셋
인터페이스

레플리카셋

gabury1

레플리카셋은 같은 파드를 몇 벌 띄워 둘지 쿠버네티스에 적어 내는 리소스입니다. 적어 둔 수보다 파드가 모자라면 새로 만들고 넘치면 지웁니다. 어느 파드를 자기 것으로 셀지는 레이블로 고릅니다.

상세

레플리카셋의 목적은 어느 시점에나 안정된 수의 복제 파드가 돌게 유지하는 것입니다. 쿠버네티스 문서는 그래서 이 리소스가 지정한 개수의 동일한 파드가 가용하도록 보장하는 데 자주 쓰인다고 적습니다.

유지하는 방식은 셈입니다. 레플리카셋은 레이블 셀렉터로 자기가 쥘 파드를 골라냅니다. 그 수를 적어 둔 개수와 견줍니다. 모자라면 파드 템플릿으로 새로 만들고 넘치면 지웁니다. 이 순환을 도는 주체는 레플리카셋 컨트롤러입니다.

다만 쿠버네티스 문서는 보통 디플로이먼트를 정의하라고 적습니다. 그 디플로이먼트가 레플리카셋을 자동으로 관리하게 두라는 것입니다. 디플로이먼트는 레플리카셋을 관리하는 상위 개념입니다. 파드에 선언형 갱신을 제공합니다. 그 밖에도 여러 유용한 기능을 함께 가집니다. 그래서 문서는 레플리카셋을 직접 쓰는 대신 디플로이먼트를 쓰기를 권합니다. 예외로 열어 둔 자리는 둘입니다. 갱신 순서를 직접 짜야 하거나, 갱신이 아예 필요 없는 경우입니다. 문서는 이 말이 곧 레플리카셋 오브젝트를 직접 만질 일이 아예 없을 수도 있다는 뜻이라고 덧붙입니다.

디플로이먼트 쪽 문서도 같은 선을 긋습니다. 디플로이먼트가 파드와 레플리카셋에 대한 선언형 갱신을 제공한다고 정의합니다. 그리고 디플로이먼트가 소유한 레플리카셋은 사람이 관리하지 말라고 못 박습니다.

형태

다른 쿠버네티스 API(Application Programming Interface, 응용 프로그램 인터페이스) 오브젝트와 마찬가지로 레플리카셋에는 apiVersion · kind · metadata 필드가 필요합니다. kind 는 언제나 ReplicaSet 입니다. 여기에 .spec 절이 더 필요합니다.

필드 무엇을 정하나 기본값
.spec.selector 복제 개수에 맞춰 셀 파드를 고르는 레이블 셀렉터입니다. 이 레플리카셋이 제어하려면 반드시 맞아야 하는 레이블 키와 값이고, 파드 템플릿의 레이블과 맞아야 합니다 필수
.spec.template 복제본이 모자란 것으로 판정됐을 때 만들어질 파드를 기술합니다. 레이블이 들어 있어야 합니다 —
.spec.replicas 원하는 파드 개수입니다. 레플리카셋이 이 수에 맞춰 파드를 만들고 지웁니다 1
.spec.minReadySeconds 새로 만든 파드가 컨테이너 크래시 없이 준비 상태로 있어야 가용으로 쳐 주는 최소 초입니다 0. 준비되는 즉시 가용으로 칩니다

.spec.replicas 는 포인터로 둔 필드입니다. 명시적으로 적은 0 과 아예 안 적은 것을 가르려는 설계입니다.

파드 템플릿의 재시작 정책에는 값이 하나뿐입니다. .spec.template.spec.restartPolicy 에 허용되는 값은 Always 입니다. 그것이 기본값입니다.

셀렉터와 파드 템플릿의 짝

.spec.template.metadata.labels 는 spec.selector 와 맞아야 합니다. 맞지 않으면 API 가 거부합니다. 쿠버네티스 문서는 파드 템플릿의 레이블을 다른 컨트롤러의 셀렉터와 겹치지 않게 조심하라고도 적습니다. 겹치면 그 컨트롤러가 이 파드를 데려가려 들 수 있습니다.

셀렉터 문법

셀렉터는 matchLabels 와 matchExpressions 두 갈래로 적습니다.

YAML
selector:
  matchLabels:
    component: redis
  matchExpressions:
    - {key: tier, operator: In, values: [cache]}
    - {key: environment, operator: NotIn, values: [dev]}

matchLabels 는 키와 값 쌍의 맵입니다. 이 맵의 쌍 하나는 matchExpressions 원소 하나와 같습니다. 그 원소는 키 필드가 그 키입니다. 연산자는 In 입니다. 값 배열에는 그 값 하나만 듭니다. matchExpressions 는 파드 셀렉터 요구사항의 목록입니다. 쓸 수 있는 연산자는 In · NotIn · Exists · DoesNotExist 입니다. In 과 NotIn 에서는 값 집합이 비어 있으면 안 됩니다. matchLabels 와 matchExpressions 의 요구사항은 전부 AND 로 묶입니다. 하나라도 어긋나면 그 파드는 안 맞습니다.

동작

레플리카셋은 셀렉터와 복제 개수와 파드 템플릿 세 필드로 정의됩니다. 셀렉터는 자기가 데려갈 수 있는 파드를 어떻게 식별할지 정합니다. 복제 개수는 몇 벌을 유지할지 정합니다. 파드 템플릿은 그 수를 채우려고 새로 만들 파드의 내용을 정합니다. 레플리카셋은 원하는 수에 이르도록 필요한 만큼 파드를 만들고 지우는 방식으로 목적을 이룹니다. 새 파드를 만들어야 할 때는 자기 파드 템플릿을 씁니다.

flowchart TD
    A["셀렉터로 쥘 파드를 식별한다"] --> B["쿠버네티스가 ownerReferences 에 소유를 새긴다"]
    B --> C{"쥔 파드 수와 replicas 를 견준다"}
    C -->|모자람| D["파드 템플릿으로 새로 만든다"]
    C -->|넘침| E["파드를 지운다"]
    C -->|같음| F["그대로 둔다"]

소유가 새겨지는 자리

레플리카셋과 파드를 잇는 자리는 파드의 metadata.ownerReferences 필드입니다. 이 필드는 지금 오브젝트가 어느 리소스에 소유되는지를 적습니다. 레플리카셋이 데려간 파드는 전부 자기를 소유한 레플리카셋의 식별 정보를 이 필드에 가집니다. 레플리카셋은 이 연결을 통해 자기가 유지하는 파드의 상태를 알고 그에 맞춰 계획을 세웁니다.

이 관계는 쿠버네티스 전반의 규칙입니다. 어떤 오브젝트는 다른 오브젝트의 소유자이고, 소유된 쪽은 피소유자입니다. 레플리카셋이 파드 한 벌의 소유자인 것이 그 예입니다. 유효한 소유자 참조는 피소유자와 같은 네임스페이스 안의 오브젝트 이름과 UID(Universally Unique Identifier, 범용 고유 식별자)로 이뤄집니다. 쿠버네티스는 레플리카셋 · DaemonSet · 디플로이먼트 · Job · CronJob · 레플리케이션 컨트롤러의 피소유자에 대해 이 필드 값을 자동으로 세웁니다. 사람이 손으로 바꿀 수도 있지만 대개 그럴 필요가 없습니다.

피소유자에는 ownerReferences.blockOwnerDeletion 필드도 있습니다. 이 불리언 값은 특정 피소유자가 가비지 컬렉션이 소유자를 지우는 것을 막을 수 있는지를 제어합니다. 디플로이먼트 컨트롤러 같은 컨트롤러가 metadata.ownerReferences 값을 세우면 쿠버네티스가 이 필드를 자동으로 true 로 둡니다.

템플릿 밖의 파드를 데려가는 경우

레플리카셋은 셀렉터로 새로 데려갈 파드를 식별합니다. 소유자 참조가 없거나 소유자 참조가 컨트롤러가 아닌 파드가 레플리카셋의 셀렉터에 맞으면, 그 파드는 곧바로 그 레플리카셋에 데려가집니다.

그래서 맨 파드를 만드는 것 자체는 문제가 없지만, 쿠버네티스 문서는 그 맨 파드가 자기 레플리카셋 가운데 하나의 셀렉터와 맞는 레이블을 갖지 않도록 하기를 강하게 권합니다. 레플리카셋이 자기 템플릿으로 지정한 파드만 소유하도록 제한돼 있지 않기 때문입니다.

만드는 순서에 따라 결과가 갈립니다. 레플리카셋이 이미 배포돼 초기 복제본을 다 채운 뒤에 맨 파드를 만들면, 그 새 파드는 레플리카셋에 데려가진 다음 원하는 개수를 넘기 때문에 곧바로 종료됩니다. 반대로 파드를 먼저 만들고 레플리카셋을 나중에 만들면, 레플리카셋이 그 파드를 데려간 뒤 새로 만든 파드와 원래 파드의 수가 원하는 개수에 맞을 때까지만 새로 만듭니다. 이런 식으로 레플리카셋은 균질하지 않은 파드 묶음을 소유할 수 있습니다.

실패

깨지는 자리는 대개 셀렉터가 파드를 쥐고 놓는 언저리입니다.

조건 무엇이 일어나나
.spec.template.metadata.labels 가 spec.selector 와 안 맞는다 API 가 거부합니다
두 레플리카셋이 같은 .spec.selector 를 적고 .spec.template.metadata.labels 와 .spec.template.spec 은 다르게 적었다 각 레플리카셋은 다른 레플리카셋이 만든 파드를 무시합니다
파드의 레이블을 바꿔 레플리카셋에서 떼어냈다 떼어낸 파드는 자동으로 대체됩니다. 복제 개수를 함께 바꾸지 않았다는 전제입니다
kubectl delete --cascade=orphan 으로 레플리카셋만 지웠다 파드는 그대로 남습니다
파드가 삭제나 스케일 다운으로 종료 중이 됐다 종료에 오랜 시간이 걸릴 수 있고 그동안 자원을 더 쓸 수 있습니다. 전체 파드 수가 일시적으로 .spec.replicas 를 넘을 수 있습니다

레이블을 바꿔 파드를 떼어내는 것은 디버깅이나 데이터 복구를 하려고 파드를 서비스에서 빼는 기법으로 쓸 수 있습니다. 뺀 자리는 컨트롤러가 새 파드로 채웁니다.

지우고 다시 만들 때

원본을 지운 뒤에는 그것을 대신할 새 레플리카셋을 만들 수 있습니다. 옛것과 새것의 .spec.selector 가 같기만 하면 새 레플리카셋이 옛 파드를 입양합니다. 다만 기존 파드를 새로운, 다른 파드 템플릿에 맞추려는 노력은 전혀 하지 않습니다. 파드를 새 스펙으로 통제된 방식으로 갱신하려면 디플로이먼트를 써야 합니다. 레플리카셋은 롤링 업데이트를 직접 지원하지 않습니다.

상태 필드

지금 상태는 ReplicaSetStatus 의 필드로 읽습니다.

필드 무엇을 세나
replicas 가장 최근에 관측한, 종료 중이 아닌 파드 수입니다
readyReplicas 이 레플리카셋이 겨냥하는 파드 가운데 Ready 컨디션인, 종료 중이 아닌 파드 수입니다
availableReplicas 이 레플리카셋에서 가용한, 종료 중이 아닌 파드 수입니다. 최소 minReadySeconds 동안 준비 상태였던 파드입니다
fullyLabeledReplicas 레플리카셋 파드 템플릿의 레이블과 맞는 레이블을 가진, 종료 중이 아닌 파드 수입니다
terminatingReplicas 이 레플리카셋에서 종료 중인 파드 수입니다. metadata.deletionTimestamp 가 비어 있지 않고 아직 .status.phase 가 Failed 나 Succeeded 에 이르지 않은 파드입니다
observedGeneration 가장 최근에 관측한 레플리카셋의 제너레이션입니다
conditions 레플리카셋의 현재 상태에 대한 최신 관측을 담습니다

terminatingReplicas 는 베타 필드입니다. 쿠버네티스 v1.35 에서 DeploymentReplicaSetTerminatingReplicas 기능 게이트가 기본 활성으로 들어왔습니다. 이 기능은 API 서버와 kube-controller-manager 양쪽에 그 기능 게이트를 세워 켭니다.

예시

쿠버네티스 문서의 controllers/frontend.yaml 한 벌입니다.

YAML
apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: frontend
  labels:
    app: guestbook
    tier: frontend
spec:
  # modify replicas according to your case
  replicas: 3
  selector:
    matchLabels:
      tier: frontend
  template:
    metadata:
      labels:
        tier: frontend
    spec:
      containers:
      - name: php-redis
        image: us-docker.pkg.dev/google-samples/containers/gke/gb-frontend:v5

replicas: 3 이 파드를 3 벌 유지하라고 정합니다. selector.matchLabels 의 tier: frontend 와 template.metadata.labels 의 tier: frontend 가 짝을 이룹니다. 컨테이너 이름은 php-redis, 이미지는 us-docker.pkg.dev/google-samples/containers/gke/gb-frontend:v5 입니다.

적용하고 상태를 보는 명령

kubectl apply -f https://kubernetes.io/examples/controllers/frontend.yaml
터미널
$ kubectl get rs
NAME       DESIRED   CURRENT   READY   AGE
frontend   3         3         3       6s

DESIRED 는 적어 둔 개수, CURRENT 는 지금 있는 개수, READY 는 준비된 개수입니다.

터미널
$ kubectl describe rs/frontend
Name:         frontend
Namespace:    default
Selector:     tier=frontend
Labels:       app=guestbook
              tier=frontend
Annotations:  <none>
Replicas:     3 current / 3 desired
Pods Status:  3 Running / 0 Waiting / 0 Succeeded / 0 Failed
Pod Template:
  Labels:  tier=frontend
  Containers:
   php-redis:
    Image:        us-docker.pkg.dev/google-samples/containers/gke/gb-frontend:v5
    Port:         <none>
    Host Port:    <none>
    Environment:  <none>
    Mounts:       <none>
  Volumes:  <none>
Events:
  Type    Reason            Age   From                   Message
  ----    ------            ----  ----                   -------
  Normal  SuccessfulCreate  13s   replicaset-controller  Created pod: frontend-gbgfx
  Normal  SuccessfulCreate  13s   replicaset-controller  Created pod: frontend-rwz57
  Normal  SuccessfulCreate  13s   replicaset-controller  Created pod: frontend-wkl7w

이벤트를 낸 주체가 replicaset-controller 로 찍힙니다. 만들어진 파드는 이렇게 보입니다.

터미널
$ kubectl get pods
NAME             READY   STATUS    RESTARTS   AGE
frontend-gbgfx   1/1     Running   0          10m
frontend-rwz57   1/1     Running   0          10m
frontend-wkl7w   1/1     Running   0          10m

파드 하나의 YAML(YAML Ain't Markup Language, YAML 은 마크업 언어가 아니다) 을 뽑으면 소유자 참조가 이 레플리카셋을 가리키는 것을 확인할 수 있습니다.

YAML
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: "2024-02-28T22:30:44Z"
  generateName: frontend-
  labels:
    tier: frontend
  name: frontend-gbgfx
  namespace: default
  ownerReferences:
  - apiVersion: apps/v1
    blockOwnerDeletion: true
    controller: true
    kind: ReplicaSet
    name: frontend
    uid: e129deca-f864-481b-bb16-b27abfd92292

개수를 바꾸는 자리

.spec.replicas 필드를 고치는 것만으로 위아래로 조정됩니다. 레플리카셋 컨트롤러는 맞는 레이블 셀렉터를 가진 파드가 원하는 개수만큼 가용하도록 보장합니다. 그 파드가 동작 상태인 것도 함께 보장합니다.

내릴 때 어느 파드를 지울지는 정해진 순서가 있습니다. 컨트롤러가 가용한 파드를 다음 순서로 정렬해 먼저 내릴 대상을 고릅니다.

  1. 대기 중이면서 스케줄되지 못한 파드가 먼저 내려갑니다
  2. controller.kubernetes.io/pod-deletion-cost 어노테이션이 세워져 있으면 값이 낮은 파드가 먼저 옵니다
  3. 복제본이 더 많은 노드의 파드가 더 적은 노드의 파드보다 먼저 옵니다
  4. 파드의 생성 시각이 다르면 더 최근에 만들어진 파드가 더 오래된 파드보다 먼저 옵니다. 생성 시각은 정수 로그 눈금으로 버킷에 담깁니다

위 조건이 전부 같으면 선택은 무작위입니다.

controller.kubernetes.io/pod-deletion-cost 어노테이션은 파드 쪽에 세우고 범위는 [-2147483648, 2147483647] 입니다. 같은 레플리카셋에 속한 다른 파드와 견준 삭제 비용을 뜻합니다. 값을 안 세운 파드의 암묵값은 0 입니다. 음수도 허용됩니다. 잘못된 값은 API 서버가 거부합니다.

HorizontalPodAutoscaler 의 대상으로 삼는 자리

YAML
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
  name: frontend-scaler
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: ReplicaSet
    name: frontend
  minReplicas: 3
  maxReplicas: 10
  targetCPUUtilizationPercentage: 50

scaleTargetRef 가 앞의 frontend 레플리카셋을 겨냥합니다. 복제된 파드의 CPU(Central Processing Unit, 중앙 처리 장치) 사용률에 따라 3 벌과 10 벌 사이에서 자동 조정합니다. 같은 일을 명령 한 줄로도 적을 수 있습니다.

kubectl autoscale rs frontend --max=10 --min=3 --cpu=50%

관련 항목

레플리카셋을 이루는 필드

파드 템플릿 · 셀렉터 · 레이블

레플리카셋이 파드와 맺는 소유 관계를 이루는 요소

파드 · ownerReference · UID · 네임스페이스 · 가비지 컬렉션

레플리카셋이 속하는 상위 분류

복제 · API · 컨트롤러

레플리카셋을 관리·소유하는 주체

디플로이먼트 · HorizontalPodAutoscaler · 쿠버네티스

같은 자리에서 고르게 되는 워크로드 리소스

레플리케이션 컨트롤러 · Job · CronJob · DaemonSet · StatefulSet · PodDisruptionBudget

레플리카셋을 둘러싼 배포·갱신 맥락

배포 · 롤링 업데이트

레플리카셋이 만든 파드가 맞닿는 노드 실행 계층

노드 · kubelet · CPU

레플리카셋을 적어 부르는 형식과 명령

YAML · kubectl · kubectl apply · kubectl get · kubectl describe · kubectl delete · kubectl autoscale · kubectl scale

다른 이름: ReplicaSet · 쿠버네티스 레플리카셋 · apps/v1 ReplicaSet · rs