본문 바로가기
Container/Kubernetes

2. Pod, Replication Controller and Replica Set

by wrynn 2021. 11. 28.

Pod

 쿠버네티스를 사용하는 궁극적인 목표는 애플리케이션을 컨테이너 형태로 실행시키고 이를 효율적으로 관리하는 것입니다. 하지만 쿠버네티스는 컨테이너를 그대로 실행시키지는 않으며 Pod라는 별도의 쿠버네티스 오브젝트로 패키징되어 실행됩니다. Pod는 애플리케이션의 단일 인스턴스를 나타내며, 이는 쿠버네티스에서 관리할 수 있는 가장 작은 배포 단위입니다. 

 따라서 사용자 수가 늘어나 애플리케이션의 확장이 필요한 경우에는 Pod 내 컨테이너를 확장시키는 것이 아니라, 새로운 Pod를 생성하여 애플리케이션을 확장하게 됩니다. Pod 내에는 단일 컨테이너를 포함하는 것이 일반적이지만, 애플리케이션 요구사항에 따라 여러개의 컨테이너를 포함할 수도 있습니다. 하나의 Pod 내에 속한 컨테이너끼리는 직접 접근이 가능하며 같은 저장 공간을 사용할 수 있다는 장점이 있습니다. 

 컨테이너 생성 시 사용하는 docker run 명령과 유사하게 kubectl run 명령을 통해 Pod를 생성할 수 있습니다. --image 옵션 다음에 Pod 내에서 생성할 컨테이너 이미지를 지정합니다. 이 이미지는 Docker Hub와 같은 공개 리포지토리에서 가져올 수도 있고, 개인이나 회사에서 사용하는 비공개 리포지토리에서 가져올 수도 있습니다. 이렇게 생성한 Pod는 아직 클러스터 외부로 노출시키지 않았기 때문에, 외부 사용자들이 인터넷을 통해 접근할 수는 없는 상태입니다.

$ kubectl run mypod --image nginx
pod/mypod created

$ kubectl get pods
NAME    READY   STATUS    RESTARTS   AGE
mypod   1/1     Running   0          1s

 생성된 Pod는 kubectl delete 명령으로 삭제할 수 있습니다.

$ kubectl delete po mypod
pod "mypod" deleted

 

yaml 파일

 쿠버네티스에서는 일반적으로 오브젝트를 생성하기 위해 yaml 형식의 파일을 사용합니다. 쿠버네티스에서 사용하는 yaml 파일에는 네가지 최상위 단계 필드를 반드시 포함해야 합니다. 간단한 예제를 통해 각 필드에 대한 설명을 알아보겠습니다. 

apiVersion: v1
kind: Pod
metadata:
  name: mypod
  labels:
    app: nginx
    tier: frontend
spec:
  containers:
  - name: mycontainer
    image: nginx
  • apiVersion: 오브젝트를 생성하는데 사용하는 쿠버네티스 API 버전입니다. 
    ex) v1, apps/v1 
  • kind: 생성하려는 오브젝트의 종류입니다. 
    ex) Pod, ReplicaSet, Deployment
  • metadata: 이름이나 레이블과 같이 오브젝트에 관한 정보를 표현합니다. 위 두가지 필드와 다르게 String 값이 아닌 Dictionary(키-값의 집합) 형태로 값을 저장합니다. labels 아래에도 Dictionary 형태로 값을 저장하게 되는데, metadata 바로 아래에는 지정할 수 있는 필드가 정해져 있는 반면, labels 아래에는 사용자가 정의한 임의의 필드를 추가할 수 있습니다.
  • spec : 오브젝트가 가질 상세 정보를 나타냅니다. 어떤 오브젝트를 생성하는지에 따라 다른 필드를 필요로 합니다.

 이렇게 정의된 yaml 파일로 오브젝트를 생성하려면, kubectl create 명령어를 실행합니다. -f 옵션 다음에는 yaml 파일명을 인자로 전달합니다.

$ kubectl create -f mypod.yaml
pod/mypod created

 kubectl get pods 명령으로 실행 중인 Pod의 목록을 확인할 수 있었습니다. Pod의 상세 정보를 보려면 kubectl describe 명령을 사용합니다. 이를 통해 Pod가 생성된 시점, Pod에 할당된 레이블, Pod 내에서 실행 중인 컨테이너 정보등을 확인할 수 있습니다.

$ kubectl describe pods mypod
Name:         mypod
Namespace:    default
Priority:     0
...output omitted...

 

Replication Controller

 사실 모든 것이 잘 될때는 문제가 되지 않습니다. 그렇다면 알 수 없는 이유로 컨테이너의 작동이 중단된 경우, 쿠버네티스는 어떻게 대응할까요? Replication Controller는 이런 경우에 대비하여 클러스터 내에서 항상 일정한 수의 Pod가 실행되도록 해 주는 쿠버네티스 오브젝트입니다. 이렇게 함으로써 애플리케이션의 고가용성을 보장할 수 있습니다. 하나의 Pod를 단순히 kubectl run 명령으로 실행시키는 것과 Replication Controller를 사용하여 실행시키는 것은 엄연히 다릅니다. 그냥 Pod를 실행시킬 경우 애플리케이션 오류 등으로 Pod가 중단되면 그대로 서비스가 불가능해지지만, 1개의 Pod가 실행되도록 Replication Controller를 생성하면 실행 중인 Pod가 중단된 경우 자동으로 새로운 Pod를 생성하게 됩니다. 

 Replication Controller와 유사한 개념으로 Replica Set이 있습니다. 두 오브젝트는 아주 유사하지만 Replica Set이 약간의 향상된 기능을 제공합니다. Replica Set은 Replication Controller를 대체하기 위해 나타난 새로운 오브젝트로, 쿠버네티스에서는 Replica Set을 사용하는 것을 권장합니다. 

 먼저 Replication Controller의 yaml 파일을 살펴보겠습니다. apiVersion, kind, metadata 필드는 Pod와 큰 차이가 없지만, spec 필드는 Pod와 다소 차이가 있습니다. 예제를 통해 하나씩 살펴보겠습니다.

apiVersion: v1
kind: ReplicationController
metadata:
  name: nginx
spec:
  replicas: 3
  template:
    metadata:
      name: nginx
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
  • .spec.replicas: Replication Controller에 의해 생성할 Pod 수를 나타냅니다. 
  • .spec.template: 생성할 Pod의 metadata 및 spec 정보를 명시합니다.

 .spec.template 아래 필드가 길어져서 복잡해 보일 수 있지만, 자세히 살펴보면 Pod를 생성할 때 사용했던 yaml 파일의 metadata 및 spec 필드를 그대로 가져다놓은 것에 불과하다는 것을 알 수 있습니다. kubectl create 명령을 사용하여 Replication Controller를 생성합니다. 

$ kubectl create -f rc.yaml
replicationcontroller/nginx created

 Replication Controller가 생성된 것도 Pod와 유사하게 kubectl get 명령을 통해 확인할 수 있습니다. (여기서 rc는 Replication Controller의 축약형입니다. 마찬가지로 pods 대신 po를 사용하였습니다.) 그리고 Replication Controller에 의해 생성된 3개의 Pod 또한 확인할 수 있습니다. Replication Controller에 의해 생성된 Pod는 특별한 명명 규칙에 따라, Replication Controller 이름 뒤에 - 기호가 붙고, 그 다음 영문과 숫자가 혼용된 임의의 5자리 문자열이 추가된 형태의 이름을 가집니다. 

$ kubectl get rc
NAME    DESIRED   CURRENT   READY   AGE
nginx   3         3         3       40s

$ kubectl get po
NAME          READY   STATUS    RESTARTS   AGE
nginx-chdvc   1/1     Running   0          41s
nginx-lkb2f   1/1     Running   0          41s
nginx-rmpvj   1/1     Running   0          41s

 Replication Controller가 Pod를 정상적으로 생성했네요. .spec.replicas 에 지정한 개수만큼 3개의 Pod가 생성된 것을 확인할 수 있습니다. 이 상태에서 Pod를 하나 지워보면 어떻게 될까요? kubectl delete 명령으로 Pod를 하나 제거한 뒤, Pod의 상태를 살펴보겠습니다.

$ kubectl delete po nginx-chdvc
pod "nginx-chdvc" deleted

$ kubectl get po
NAME          READY   STATUS    RESTARTS   AGE
nginx-2t74l   1/1     Running   0          11s
nginx-lkb2f   1/1     Running   0          54s
nginx-rmpvj   1/1     Running   0          54s

 nginx-chdvc Pod는 정상적으로 삭제가 되었지만, AGE 컬럼을 통해 nginx-2t74l 이라는 Pod가 방금 전에 새로 생겨난 것을 확인할 수 있습니다. Replication Controller가 Pod 수가 3개가 아닌 것을 감지하고, 새로운 Pod를 생성한 것입니다. Replication Controller 또한 kubectl describe 명령을 통해 상세 정보를 조회할 수 있으며, kubectl delete 명령으로 삭제하면 일정 시간 후에 연결된 Pod가 함께 삭제된 것을 확인할 수 있습니다.

$ kubectl describe rc nginx
apiVersion: v1
kind: ReplicationController
metadata:
...output omitted...

$ kubectl delete rc nginx
replicationcontroller "nginx" deleted

$ kubectl get po
No resources found in default namespace.

 

Replica Set과 Selector

 Replication Controller와 Replica Set은 실제로 특정 Pod의 수를 지속적으로 확인하는 프로세스입니다. 일반적으로 쿠버네티스 클러스터에는 수많은 Pod가 실행 중인데, 이 중에서 감시할 대상이 되는 Pod는 어떻게 확인할까요? 그 해답은 바로 selector 필드에 있습니다. 설명에 앞서 먼저 Replica Set의 yaml 파일을 살펴보겠습니다.

apiVersion: apps/v1		# 1
kind: ReplicaSet		# 2
metadata:
  name: nginx
spec:
  replicas: 3
  template:
    metadata:
      name: nginx
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
  selector:			# 3
    matchLabels:
      app: nginx

 위 yaml을 Replication Controller의 yaml과 비교했을 때, 세가지 부분을 제외하고 동일한 것을 알 수 있습니다.

  1. apiVersion: Replica Set을 생성하기 위한 API 버전이 v1에서 apps/v1 으로 바뀌었습니다.
  2. kind: 생성할 오브젝트 종류는 ReplicationController가 아니라 ReplicaSet 입니다.
  3. spec.selector: 새로운 필드가 추가되었습니다!

 selector 필드에는 labels 정보를 지정하여, 어떤 Pod가 Replica Set 및 Replication Controller의 관리 하에 있는지를 나타내 줍니다. Replication Controller에서도 이 필드를 명시적으로 지정할 수 있습니다만 굳이 지정하지 않아도 쿠버네티스가 자동으로 적절한 selector 필드를 추가해 줍니다. 이전에 실행했던 kubectl describe rc nginx 명령의 결과를 자세히 보면 selector 필드를 지정하지 않았음에도 저절로 추가되어 있는 것을 확인할 수 있습니다.

 위 Replication Controller 예제에서 Pod 하나를 삭제했지만 nginx Pod가 곧바로 생성된 것은 컨트롤러가 레이블이 app: nginx인 Pod 숫자를 지속적으로 확인하다가, 지정된 replicas 수와 차이가 나는 것을 발견했기 때문에 가능한 일이었습니다. 이처럼 Pod의 레이블 정보는 쿠버네티스의 여러 오브젝트가 특정 Pod만을 선택하는 필터로 활용할 수 있게 해줍니다. 

 Replication Controller 역시 selector 필드를 포함하고 있으므로, 여기까지만 보면 Replication Controller와 Replica Set은 별다른 차이가 없어 보입니다. 대체 Replica Set과 Replication Controller 사이에 무슨 차이가 있는 것일까요? 이 둘의 차이는 selector 필드에 있습니다.

  Replication Controller Replica Set
yaml 파일에 selector 필드 포함 선택적 필수적
selector 연산 Equality-based 연산 Set-based 연산

 Equality-based 연산은 일치 및 불일치를 판단하는 연산으로 두가지(일치 및 불일치) 연산이 존재합니다. 아래 예제는 레이블 중 environtment 키의 값이 prod이면서 tier 키 값이 frontend가 아닌 Pod를 선택합니다.

...
spec:
  selector:
    environment: prod
    tier: !frontend
    ...

 Set-based 연산은 집합 기반의 연산으로 네가지 연산(In, NotIn, Exists, DoesNotExists)을 제공합니다. matchLabels는 Set-based 연산을 Equality-based 연산처럼 사용할 수 있게 해 줍니다. matchLabels 아래 component: redis 부분은 matchExpressions 아래에 - {key: component, operator: In, values: [redis]} 와 같이 표현할 수 있습니다.

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

 이제 Replica Set을 생성해봅시다.

$ kubectl create -f rs.yaml
replicaset.apps/nginx created

 실행 중인 Pod 수를 변경하고 싶을 때에는 어떻게 할까요? yaml 파일에서 replicas 필드를 수정한 다음 kubectl replace 명령을 사용할 수도 있고, kubectl scale 명령을 사용하여 yaml 파일을 변경하지 않고 변경할 수도 있습니다. 물론 쿠버네티스에는 이렇게 클러스터 운영자가 직접 변경하지 않고, 부하량에 따라 저절로 Pod 수를 조절할 수도 있습니다.

$ kubectl replace -f rs.yaml
$ kubectl scale -f rs.yaml --replicas=5
$ kubectl scale rs nginx --replicas=5

댓글