쌩로그

[Kubernetes] 쿠버네티스 레플리카셋(ReplicaSet) 본문

Deploy/Kubernetes

[Kubernetes] 쿠버네티스 레플리카셋(ReplicaSet)

.쌩수. 2025. 2. 25. 21:53
반응형

목록

  1. 포스팅 개요
  2. 본론
      2-1. 레플리카셋을 사용하는 이유
      2-2. 레플리카셋 사용하기
      2-3. 레플리카셋의 동작 원리
      2-3. 레플리케이션 컨트롤러 vs. 레플리카셋
  3. 요약

1. 포스팅 개요

이 포스팅은 위키북스 출판사의 '시작하세요! 도커/쿠버네티스'의 제 6장 쿠버네티스 시작하기를 학습하며 기록한 포스팅이다.

그 중 일정 개수의 파드를 유지하는 컨트롤러인 Replica Set에 대한 내용이다.

2. 본론

2-1. 레플리카셋을 사용하는 이유

쿠버네티스의 기본 단위인 파드는 여러 개의 컨테이너를 추상화해서 하나의 애플리케이션으로 동작하도록 만드는 컨테이너 묶음이었다.

그런데 YAML에 파드만 정의해 생성하게 되면 이 파드의 생애 주기(Lifecycle)는 어떻게 될까??

예를 들어 앞서 생성했던, 2개의 컨테이너가 담겨 있는 Nginx 파드는 다음의 방법으로 삭제할 수 있다.

$ kubectl get pods
NAME           READY   STATUS    RESTARTS   AGE
my-nginx-pod   2/2     Running   0          89m


$ kubectl delete -f nginx-pod-with-ubuntu.yaml
pod "my-nginx-pod" deleted

kubectl delete 명령어로 파드를 삭제하면 그 파드의 컨테이너 또한 삭제된 뒤 쿠버네티스에서 영원히 사라지게 된다.
이처럼 YAML 파일에 파드만 정의해 생성할 경우 해당 파드는 오직 쿠버네티스 사용자에 의해 관리된다.

단순히 파드의 기능을 테스트하는 등의 간단한 용도로는 이렇게 파드를 사용할 수 있을수도 있다.
그렇지만 실제로 외부 사용자의 요청을 처리해야하는 마이크로 서비스 구조의 파드라면 이러한 방식을 사용하기 어렵다.
스웜 모드에서 다뤘던 것처럼 마이크로서비스에서는 여러 개의 동일한 컨테이너를 생성한 뒤 외부 요청이 각 컨테이너에 적절히 분배될 수 있어야 한다.

쿠버네티스에서는 기본 단위가 파드이기 때문에 동일한 여러 개의 파드를 생성해 외부 요청을 각 파드에 분배하는 방식을 사용해야 할 것이다.

그렇다면 동일한 여러 개의 파드를 어떻게 생성할 수 있을까??
아마 가장 간단한 방법은 다른 이름을 가지는 여러 개의 파드를 직접 만드는 방식일 것이다.

apiVersion: v1 
kind: Pod 
metadata: 
  name: my-nginx-pod-a 
spec:
    containers:
     - name: my-nginx-container 
       image: nginx:latest 
       ports: 
        - containerPort: 80 
          protocol: TCP 

---
apiVersion: v1 
kind: Pod 
metadata: 
    name: my-nginx-pod-b 
spec: 
    containers: 
    - name: my-nginx-container 
      image: nginx:latest 
      ports: 
      - containerPort: 80 
        protocol: TCP

YAML 파일은 --- 구분자로 사용해 여러 개의 리소스를 정의할 수 있다.

그러나 여러 개의 파드를 직접 생성하는 방법은 여러 가지 이유로 적절하지 않다.
우선 동일한 파드의 개수가 많아질수록 이처럼 일일이 정의하는 것은 매우 비효율적인 작업이다.
또한 파드가 어떠한 이유로든지 삭제되거나, 파드가 위치한 노드에 장애가 발생해 더 이상 파드에 접근하지 못 하게 됐을 때, 직접 파드를 삭제하고 다시 생성하지 않는 한 해당 파드는 다시 복구되지 않는다.

아래의 예시에서는 kubectl get pods-o wide 옵션을 붙여 파드가 실행 중인 워커 노드를 확인 한 뒤, 직접 워커 노드 서버를 종료했다.
파드가 생성된 노드에 장애가 발생하더라도 파드는 다른 노드에서 다시 생성되지 않으며, 파드는 단지 종료된 상태로 남아있을 뿐이다.

$ kubectl get pods -o wide
NAME           READY   STATUS        RESTARTS   AGE   IP               NODE          NOMINATED NODE   READINESS GATES
my-nginx-pod   1/1     Terminating   0          30m   192.168.11.130   kuber-work1   <none>           <none>

이처럼 파드만 YAML 파일에 정의해 사용하는 방식은 여러 가지 한계점이 있다.
따라서 쿠버네티스에서 파드만 정의해 사용하는 경우는 거의 없다.
그래서 이런 한계점을 해결해주는 레플리카셋(replicaset)이라는 쿠버네티스 오브젝트를 함께 사용하는 것이 일반적이다.

레플리카셋이 수행하는 역할

  • 정해진 수의 동일한 파드가 항상 실행되도록 관리한다.
  • 노드 장애 등의 이유로 파드를 사용할 수 없다면 다른 노드에서 파드를 다시 생성한다.

따라서 동일한 Nginx 파드를 안정적으로 여러 개 실행할 수도 있고,
워커 노드에 장애가 생기더라도 정해진 개수의 파드를 유지할 수도 있다.

참고

워커 노드를 종료하고, 다시 켰을 때 알아서 연결이 된다.
잠시 연결이 안 돼서 확인했더니 swap 이 활성화 되어있었다.
swap만 비활성화 시켜주면 다시 node들이 알아서 연결된다.

$ swapoff -a

2-2. 레플리카셋 사용하기

레플리카셋이 어떠한 역할을 하는지 대략적으로 이해했다면 이번에는 Nginx 파드를 생성하는 레플리카셋을 만들어 보자.
다음과 같은 내용으로 replicaset-nginx.yaml 파일을 작성하자.

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: replicaset-nginx
spec:
  replicas: 3
  selector:
    matchLabels:
     app: my-nginx-pods-label
  template:
    metadata:
     name: my-nginx-pod
     labels:
       app: my-nginx-pods-label
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        ports:
        - containerPort: 80
  • 리소스의 고유한 이름은 파드뿐만 아니라 모든 쿠버네티스 오브젝트에서 설정할 수 있다.
  • 이 레플리카셋의 이름은 metadataname 항목에서 replicaset-nginx로 설정했다.
  • 그런데 이전의 파드를 생성할 때 사용한 YAML 파일과 비교해보면 새로운 항목이 몇 가지 추가된 것을 알 수 있을 것이다.

참고

앞으로 YAML 파일에서 들여쓰기(indent) 된 항목의 이름을 표현하기 위해 상위 항목부터 이름을 표시하는 방법을 사용한다.

예를 들어, 다음 예시에서는 name 항목을 metadata.name 이라고 표현할 수 있다.
즉, 'metadata.name의 값이 replicaset-nginx 로 설정돼 있다' 라고 말할 수 있다.

metadata:
  name: replicaset-nginx
  • `spec.replicas
    • 동일한 파드를 몇 개 유지시킬 것인지 설정한다.
    • 위 예시에서는 파드의 개수를 3으로 설정했기 때문에 레플리카셋은 3개의 파드를 새롭게 생성할 것이다.
  • spec.template 아래의 내용들
    • 파드를 생성할 때 사용할 템플릿을 정의한다.
    • template 아래의 내용을 자세히 들여다보면 이전에 작성했던 nginx-pod.yaml 파일의 내용과 거의 다르지 않음을 알 수 있다.
    • 즉, 파드를 사용했던 내용을 동일하게 레플리카셋에도 정의함으로써 어떠한 파드를 어떻게 생성할 것인지 명시하는 것이다. 이를 보통 파드 스펙. 또는 파드 템플릿이라고 말한다.

파일을 작성했다면 위 내용으로 레플리카셋을 직접 생성해 보자.
파드를 생성했던 방식과 동일하게 kubectl apply -f 명령어로 YAML 파일을 읽어와 생성한다.

$ kubectl apply -f replicaset-nginx.yaml
replicaset.apps/replicaset-nginx created

kubectl get 명령어로 레플리카셋과 파드의 목록을 확인해보자.

$ kubectl get po
NAME                     READY   STATUS    RESTARTS   AGE
replicaset-nginx-72tk7   1/1     Running   0          6s
replicaset-nginx-frbd9   1/1     Running   0          6s
replicaset-nginx-frq58   1/1     Running   0          6s
$ kubectl get rs
NAME               DESIRED   CURRENT   READY   AGE
replicaset-nginx   3         3         3       22s
  • 3개의 파드가 정상적으로 생성됐다.
  • 이 3개의 파드는 위에서 생성한 레플리카셋에 의해 생성된 것이다.
  • 따라서 레플리카셋을 삭제하거나 수정하면 파드에 변경사항이 반영된다.

이번에는 레플리카셋에 정의된 파드의 개수를 늘려 4개의 파드가 실행되게 해보자.
레플리카셋의 파드 개수를 변경하기 위해서 이미 생성된 레플리카셋을 삭제하고 다시 생성할 필요는 없다.
쿠버네티스에서는 이미 생성된 리소스의 속성을 변경하는 기능을 제공하고 있다.
이를 위해 kubectl edit, kubectl patch 등 여러 방법을 사용할 수 있지만, 지금은 간단히 YAML 파일에서 숫자만 바꿔 사용해보자.

$ cat replicaset-nginx-4pods.yaml
apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: replicaset-nginx
spec:
  replicas: 4 # 변경
  selector:
    matchLabels:
     app: my-nginx-pods-label
  template:
    metadata:
     name: my-nginx-pod
     labels:
       app: my-nginx-pods-label
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        ports:
        - containerPort: 80

kubectl apply -f 명령어로 변경한 YAML 파일을 쿠버네티스에 적용하자.

$ kubectl apply -f replicaset-nginx-4pods.yaml
replicaset.apps/replicaset-nginx configured

이번에는 created가 아닌 configured라는 문구가 출력됐다.
새로운 리소스를 생성한 것이 아닌 기존의 리소스를 수정한 것이기 때문이다.
파드의 개수를 4개로 조정했으니 1개의 파드가 추가로 생성됐을 것이다.

$ kubectl get pods
NAME                     READY   STATUS    RESTARTS   AGE
replicaset-nginx-5r7dn   1/1     Running   0          58s
replicaset-nginx-72tk7   1/1     Running   0          4m32s
replicaset-nginx-frbd9   1/1     Running   0          4m32s
replicaset-nginx-frq58   1/1     Running   0          4m32s

레플리카셋을 삭제하려면 마찬가지로 kubectl delete -f 명령어를 사용하거나, kubectl delete rs 명령어를 사용하면 된다.
레플리카셋에 의해 생성된 파드 또한 함께 삭제될 것이다.

$ kubectl delete rs replicaset-nginx
replicaset.apps "replicaset-nginx" deleted

$ kubectl get po
No resources found in default namespace.

2-3. 레플리카셋의 동작 원리

앞서 살펴본 예시만 놓고 생각해 본다면 마치 레플리카셋은 파드와 연결된 것처럼 보일 수도 있다.
왜냐하면 레플리카셋을 생성하면 파드가 생성되고, 레플리카셋을 삭제하면 파드 또한 삭제된다.

그러나 실제로는 레플리카셋은 파드와 연결돼 있지 않다.
오히려 느슨한 연결(loosely coupled)을 유지하고 있으며, 이러한 느슨한 연결은 파드와 레플리카셋의 정의 중 라벨 셀렉터(Label Selector) 를 이용해 이뤄진다.
라벨 셀렉터를 이해하기 위해 레플리카셋을 생성했을 때 사용한 YAML 파일 내용의 일부를 다시 살펴보자.

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: replicaset-nginx
spec:
  replicas: 3
  selector:
    matchLabels:
     app: my-nginx-pods-label
-----------------------이 구분선 위로는 레플리카셋 정의-----------------------------
--------------------------이 구분선 아래로는 파드 정의------------------------------
  template:
    metadata:
     name: my-nginx-pod
     labels:
       app: my-nginx-pods-label
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        ports:
        - containerPort: 80

이전에 파드를 생성할 때, metadata 항목에서는 리소스의 부가적인 정보를 설정할 수 있다고 설명했다.
그 부가 정보 중에는 리소스의 고유한 이름뿐만 아니라 주석(애노테이션 : Annotation),라벨 등도 포함된다.
특히 라벨은 파드 등의 쿠버네티스 리소스를 분류할 때 유용하게 사용할 수 있는 메타데이터다.

라벨은 쿠버네티스 리소스의 부가적인 정보를 표현할 수 있을 뿐만 아니라, 서로 다른 오브젝트가 서로를 찾아야 할 때 사용되기도 한다.

예를 들어 레플리카셋은 spec.selector.matchLabel에 정의된 라벨을 통해 생성해야 하는 파드를 찾는다.
즉, app: my-nginx-pods-label 라벨을 가지는 파드의 개수가 replicas 항목에 정의된 숫자인 3개와 일치하지 않으면 파드를 정의하는 파드 템플릿(template) 항목의 내용으로 파드를 생성한다.

레플리카셋을 처음 생성했을 때는 app: my-nginx-pods-label 라벨을 가지는 파드가 존재하지 않기 때문에 template에 정의된 파드 설정으로 3개의 파드를 생성한다.
그다음 새롭게 생성된 3개의 파드는 app: my-nginx-pods-label 을 가지고 있기 때문에 레플리카셋은 더 이상 파드를 생성하지 않는다.

그렇다면 app: my-nginx-pods-label 라벨을 가지는 파드를 미리 생성해 놓은 다음, 위의 레플리카셋을 생성하면 어떻게 될까??

다음과 같이 라벨을 가지는 파드를 수동으로 생성해보자.

apiVersion: v1
kind: Pod
metadata:
  name: my-nginx-pod
  labels:
    app: my-nginx-pods-label
spec:
  containers:
  - name: my-nginx-container
    image: nginx:latest
    ports:
    - containerPort: 80
$ kubectl apply -f nginx-pod-without-rs.yaml
pod/my-nginx-pod created

kubectl get pods 명령어에서 --show-labels 사용하면 라벨을 함께 출력할 수 있다.
위의 YAML 파일에서 설정했던 라벨이 파드에 잘 설정된 것을 확인할 수 있다.
설정된 라벨은 다음과 같이 -l 옵션으로 원하는 리소스만을 출력하는 용도로 쓰일 수도 있다.

$ kubectl get pods --show-labels
NAME           READY   STATUS    RESTARTS   AGE   LABELS
my-nginx-pod   1/1     Running   0          77s   app=my-nginx-pods-label

$ kubectl get pods -l app
NAME           READY   STATUS    RESTARTS   AGE
my-nginx-pod   1/1     Running   0          105s

$ kubectl get pods -l app=my-nginx-pods-label
NAME           READY   STATUS    RESTARTS   AGE
my-nginx-pod   1/1     Running   0          2m9s

이 상태에서 레플리카셋을 생성해 보자.

$ kubectl apply -f replicaset-nginx.yaml
replicaset.apps/replicaset-nginx created

$ kubectl get pods
NAME                     READY   STATUS    RESTARTS   AGE
my-nginx-pod             1/1     Running   0          3m40s
replicaset-nginx-br52b   1/1     Running   0          66s
replicaset-nginx-kk2qt   1/1     Running   0          66s

레플리카셋의 selector.matchLabel 에 정의된 app: my-nginx-pods-label 라벨을 가지는 파드 가 이미 1 개 존재하기 때문에 template에 정의된 파드 설정을 통해 2개의 파드만이 생성됐다.

그렇다면 수동으로 생성한 파드를 삭제하면 어떻게 될까???

$ kubectl delete pods my-nginx-pod
pod "my-nginx-pod" deleted


$ kubectl get pods
NAME                     READY   STATUS    RESTARTS   AGE
replicaset-nginx-78g9l   1/1     Running   0          10s
replicaset-nginx-br52b   1/1     Running   0          96s
replicaset-nginx-kk2qt   1/1     Running   0          96s

레플리카셋의 selector.matchLabel 에 정의된 값인 app: my-nginx-pods-label 라벨을 가지는 파드의 개수가 2개로 줄어들었기 때문에 새로운 파드를 생성한 것을 알 수 있다.

이를 그림으로 나타내보면 다음과 같다.


참고

레플리카셋과 파드의 라벨은 고유한 키-값 쌍이어야만 한다.
위의 예사는 이해를 돕기 위한 것일 뿐 레플리카셋과 동일한 라벨을 가지는 파드를 직접 생성하는 것은 바람직하지 않다.


그렇다면 레플리카셋이 생성해 놓은 파드의 라벨을 삭제한다면 어떻게 될까????

이번에는 kubectl edit 명령어로 파드 중 하나의 라벨을 삭제해보자.
kubectl edit 명령어는 리소스의 속성을 변경할 수 있도록 텍스트 편집기를 실행하며, 변경 사항을 적용하려면 파일을 저장한 뒤 빠져나오면 된다.

아래 내용 중에서 진하게 표시된 부분인 두 줄(labels와 app)을 삭제한 다음, 저장하고 빠져나온다.

$ kubectl edit pods replicaset-nginx-78g9
...
  5 apiVersion: v1
  6 kind: Pod
  7 metadata:
  8   annotations:
...
 # 삭제 및 주석 14   labels:
 # 삭제 및 주석 15     app: my-nginx-pods-label
 16   name: replicaset-nginx-78g9l
 17   namespace: default
 18   ownerReferences:
 19   - apiVersion: apps/v1
 20     blockOwnerDeletion: true

# wq! 하고 나오면 아래의 변경사항 적용 내용이 나온다.(참고 밑에 글)

참고
kubectl edit 명령어는 파드뿐만 아니라 모든 종류의 쿠버네티스 오브젝트에 사용할 수 있으며, YAML에서 설정하지 않았던 상세한 리소스의 설정까지 확인할 수 있다.

예를 들어, 레플리카셋으로부터 생성된 파드는 레플리카셋의 이름을 ownerReferences 라는 항목에 저장하고 있다.


변경 사항을 적용했다면 파드의 목록을 다시 확인해 보자.

$ kubectl edit pods replicaset-nginx-5sx29
pod/replicaset-nginx-5sx29 edited

$ kubectl get pods --show-labels
NAME                     READY   STATUS    RESTARTS   AGE     LABELS
replicaset-nginx-5sx29   1/1     Running   0          112s    <none>
replicaset-nginx-6wpkv   1/1     Running   0          55s     app=my-nginx-pods-label
replicaset-nginx-8bfjm   1/1     Running   0          2m12s   app=my-nginx-pods-label
replicaset-nginx-mx76c   1/1     Running   0          2m12s   app=my-nginx-pods-label

app: my-nginx-pods-label 라벨을 가지는 파드가 2개가 됐으므로, 레플리카셋은 새로운 파드를 새롭게 하나 더 생성한다.
위 예시에서 라벨을 삭제한 파드는 레플리카셋의 selector, matchLabel 항목의 값과 더 이상 일치하지 않으므로 레플리카셋에 의해 관리되지 않으며, 직접 수동으로 생성한 파드와 동일한 상태가 된다.
따라서 레플리카셋을 삭제해도 이 파드는 삭제되지 않으며, 직접 수동으로 삭제해야 한다.

$ kubectl delete rs replicaset-nginx
replicaset.apps "replicaset-nginx" deleted

$ kubectl get pods # 라벨이 삭제된 파드는 레플리카셋에 의해 삭제되지 않는다.
NAME                     READY   STATUS    RESTARTS   AGE
replicaset-nginx-5sx29   1/1     Running   0          3m34s

# 남아있는 파드를 직접 삭제해줘야 한다.
$ kubectl delete pods replicaset-nginx-5sx29
pod "replicaset-nginx-5sx29" deleted
  • 여기서 한 가지 오해하지 말아야 할 점은 레플리카셋의 목적이 '파드를 생성하는 것'이 아닌, '일정 개수의 파드를 유지하는 것이라는 점이다.
  • 현재 파드의 개수가 replicas 에 설정된 값보다 적으면 레플리카셋은 파드를 더 생성해 replicas 와 동일한 개수를 유지하려 시도한다.
  • 그러나 파드가 너무 많으면 역으로 파드를 삭제해 replicas에 설정된 파드 개수에 맞추려고 할 것이다.
    • 따라서 레 플리카셋의 목적은 replicas설정된 숫자만큼 동일한 파드를 유지해 바람직한 상태로 만드는 것임을 잊지 말아야 한다..!!

참고

레플리카셋의 selector.matchLabel 항목처럼 두 리소스 간의 라벨을 일치시킴으로써 쿠버네티스의 기능을 사용하는 경우가 앞으로 자주 등장할 것이다.
따라서 라벨이라는 단어와 사용 방법에 대해서 친숙해지는 것이 좋다.


2-4. 레플리케이션 컨트롤러 vs. 레플리카셋

  • 이전 버전의 쿠버네티스에서는 레플리카셋이 아닌 레플리케이션 컨트롤러(Replication Controller)라는 오브젝트를 통해 파드의 개수를 유지했었다.
  • 그러나 쿠버네티스 버전이 올라감에 따라 레플리케이션 컨트롤러는 더 이상 사용되지 않으며(deprecated), 그 대신 레플리카셋이 사용되고 있다.
  • 그렇기 때문에 레플리케이션 컨트롤러에 대해서는 자세히 알 필요가 없다.
  • 레플리카셋이 레플리케이션 컨트롤러와 다른 점 중 하나는 "표현식(matchExpression)" 기반의 라벨 셀렉터를 사용할 수 있다는 것이다.
    • 예를 들어 레플리카셋의 YAML 파일에서 selector 항목은 다음과 같이 표현식으로 정의할 수도 있다.
...
selector:
    matchExpressions:
        - key: app
          values:
            - my-nginx-pods-label
            - your-nginx-pods-label
          operator: In
    template:

위 예시는 키가 app인 라벨을 가지고 있는 파드들 중에서 values 항목에 정의된 값들이 존재(In)하는 파드들을 대상으로 하겠다는 의미이다.
따라서 app: your-nginx-pods-label 이라는 라벨을 가지는 파드뿐만 아니라 app: your-nginx-pods-label 이라는 라벨을 가지는 파드 또한 레플리카셋의 관리하에 놓이게 된다.

3. 요약

  • 레플리카셋이 수행하는 역할
    • 정해진 수의 동일한 파드가 항상 실행되도록 관리한다.
    • 노드 장애 등의 이유로 파드를 사용할 수 없다면 다른 노드에서 파드를 다시 생성한다.
  • 레플리카셋의 목적
    • replicas설정된 숫자만큼 동일한 파드를 유지해 바람직한 상태로 만드는 것이다.
728x90
Comments