[kubernetes] 컨트롤러(Controller) 종류와 특징

2해승·2024년 10월 14일

쿠버네티스?!

목록 보기
8/16

쿠버네티스에서 컨트롤러는 클러스터의 상태를 관찰한 다음 필요한 경우에 생성 또는 변경을 요청하는 컨트롤 루프로 즉, 파드를 관리하는 역할을 한다.

다양한 목적에 따라 쿠버네티스에서 제공하는 컨트롤러를 사용하면 되는데 컨트롤러의 종류는 다음과 같이 있다.

이번 글에서는 쿠버네티스에서 제공되는 컨트롤러의 종류와 특징, 예제를 한번 살펴보도록 하자.


Replication controller

가장 기본적인 컨트롤러이다. 요구하는 pod의 개수를 보장하며 pod 집합의 실행을 항상 안정적으로 유지하는 것을 목표로 한다.

구성 요소

apiVersion: v1
kind: ReplicationController
metadata:
  name: nginx-rc
spec:
  replicas: 3
  selector:
    app: web
  template:
    metadata:
      name: nginx-pod
      labels:
        app: web
    spec:
      containers:
      - name: nginx-container
        image: nginx:1.14
  • replicas: 단일 포드의 정보를 몇개 복제할지 작성

  • selector: Replication Controller가 담당할 pod의 범위를 지정
    ※ seletor가 가지고 있는 key:value를 pod template에서 라벨로 가지고 있어야함! ※

  • template: 레플리카 컨트롤러에게 어떤 POD 를 기준으로 생성하라고 선언해주어야 함

seletor가 가지고 있는 key:value를 pod template에서 라벨로 가지고 있어야함!

Replication controller 예제

$ kubectl create -f rc-nginx.yaml

$ kubectl get pods -o wide
NAME             READY   STATUS    RESTARTS   AGE   IP           NODE                   NOMINATED NODE   READINESS GATES
nginx-rc-mpwjm   1/1     Running   0          14s   10.0.0.-     test-cluster-worker2   <none>           <none>
nginx-rc-xl2sq   1/1     Running   0          14s   10.0.0.-   test-cluster-worker3   <none>           <none>
nginx-rc-zfbmk   1/1     Running   0          14s   10.0.0.-   test-cluster-worker    <none>           <none>

$ kubectl get replicationcontrollers 혹은 kubectl get rc
NAME       DESIRED   CURRENT   READY   AGE
nginx-rc   3         3         3       3m10s

pod의 이름을 보면 해시값으로 랜덤하게 이름이 생성되고 replicas 3개를 보장하고 있는 것을 확인할 수 있다.

만일 세개의 pod 중 하나를 삭제하면 컨트롤러는 곧바로 새로운 pod를 생성할 것이다.

replicas 변경하기

설정을 변경하는 방법은 두가지 방법이 있는데 2번째 명령어로 곧바로 수정사항을 반영할 수 있다.

  1. kubectl edit 명령어
  2. kubectl scale 명령어
$ kubectl scale rc nginx-rc --replicas=4

$ kubectl get pod
NAME             READY   STATUS    RESTARTS   AGE
nginx-rc-8zl4g   1/1     Running   0          3m35s
nginx-rc-l4nnh   1/1     Running   0          3m35s
nginx-rc-r66w9   1/1     Running   0          3m35s
nginx-rc-sfpjt   1/1     Running   0          4s

Nginx를 1.14 -> 1.15 버전으로 업데이트 하기

만일 kubectl edit 명령어를 사용하여 image: nginx:1.14를 1.15로 변경하고 저장한다면 어떻게 될까?

$ kubectl describe pod nginx-rc-8zl4g
...
Containers:
  nginx-container:
    Container ID:   containerd://5ed323a4dafa55557580280da4c775cb6a8a45-
    Image:          nginx:1.14

pod 정보를 확인하면 이미 동작중이기 때문에 nginx-rc-8zl4g의 nginx 버전이 그대로 1.14임을 알 수 있다. 현재 상태에서 pod를 종료한다면 그 이후부터는 1.15버전으로 동작하는 것을 확인 할 수 있을 것이다.

$ kubectl delete pod nginx-rc-8zl4g

$ kubectl get pod
NAME             READY   STATUS              RESTARTS   AGE
nginx-rc-95nrn   0/1     ContainerCreating   0          4s
nginx-rc-r66w9   1/1     Running             0          24m

$ kubectl describe pod nginx-rc-95nrn
...
Events:
  Type    Reason     Age   From               Message
  ----    ------     ----  ----               -------
  Normal  Scheduled  20s   default-scheduler  Successfully assigned default/nginx-rc-95nrn to test-cluster-worker3
  Normal  Pulling    20s   kubelet            Pulling image "nginx:1.15"
  Normal  Pulled     9s    kubelet            Successfully pulled image "nginx:1.15" in 11.385s (11.385s including waiting). Image size: 41971871 bytes.
  Normal  Created    8s    kubelet            Created container nginx-container
  Normal  Started    8s    kubelet            Started container nginx-container

ReplicaSet

Replication controller와 같은 역할을 하는 컨트롤러로, 보다 풍부한 selector를 지원한다.

구성 요소

selector:
    matchLabels:
      app: web
    matchExpressions:
      - {key: version, operator: In, values: ["1.14", "1.15"]}
  • matchLabels: key:value

  • matchExpressions: vaule의 값이 '1.14', '1.15' 상관이 없으므로 해당되는 버전의 app:web에 맞는 pod를 운영 요청
    ※ Replication controller의 경우 셀렉터를 여러개 쓸경우 and 조건으로 동작한다. ※

    • operator: In / NotIn / Exist / DoesNotExist
    • In: key와 value를 지정하여 key, value가 일치하는 pod만 연결
    • NotIn: key는 일치하고 value는 일치하지 않는 pod만 연결
    • Exist: key에 맞는 label의 pod를 연결
    • DoesNotExist: key와 다른 label의 pod를 연결

ReplicaSet 예제

ReplicaSet 컨트롤러가 동작하고 있다고 할때 replicas를 2개로 변경하고 컨트롤러만 삭제하여 2개의 pod만 남아있다고 가정해보자.

$ kubectl get replicaset 혹은 kubectl get rs

$ kubectl scale rs nginx-rs --replicas=4

$ kubect delete rs nginx-rs --cascade=false

--cascade=false 옵션을 부여한다면 컨트롤러만 삭제되고 pod는 남아있다.

현재 상태에서 기존의 yaml 파일을 통해 컨트롤러를 생성한다면 이미 두개의 pod가 운영중이기 때문에 하나의 pod만 추가되는 것을 확인할 수 있을 것이다.


롤링 업데이트를 위한 Deployment

ReplicaSet을 제어해주는 부모 역할을 하며 pod의 수를 결정한다. 롤링 업데이트 기능을 쓰지 않는다면 ReplicaSet과 동일하다고 보면 된다.

Deployment 예제

$ kubectl create -f deployment-nginx.yaml 
deployment.apps/nginx-deploy created

$ kubectl get deploy,rs,pod
NAME                           READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/nginx-deploy   3/3     3            3           3m27s

NAME                                      DESIRED   CURRENT   READY   AGE
replicaset.apps/nginx-deploy-7db9766f79   3         3         3       3m27s

NAME                                READY   STATUS    RESTARTS   AGE
pod/nginx-deploy-7db9766f79-8ngcf   1/1     Running   0          3m27s
pod/nginx-deploy-7db9766f79-h5rmb   1/1     Running   0          3m27s
pod/nginx-deploy-7db9766f79-lskd8   1/1     Running   0          3m27s

pod의 이름을 보면 컨트롤러를 의미하는 해시값들을 확인할 수 있는데 각각 'pod/nginx-deploy'는 Deployment의 이름을, '7db9766f79'는 컨트롤러 이름, '8ngcf'는 pod의 이름임을 알 수 있다.

Deployment는 ReplicaSet을 삭제해도 새로 생성한다는 특징이 있다.

롤링 업데이트와 롤링 백을 사용해보자!

$ kubectl create -f deployment-nginx.yaml --record
deployment.apps/app-deploy created

$ kubectl get pods
NAME                         READY   STATUS    RESTARTS   AGE
app-deploy-c7fcfbf44-gqj5b   1/1     Running   0          4s
app-deploy-c7fcfbf44-hlkcr   1/1     Running   0          4s
app-deploy-c7fcfbf44-wzmgp   1/1     Running   0          4s

※ 새로운 컨트롤러를 생성할때 --record 옵션을 사용하는 이유? ※

--record 옵션을 사용하지 않으면 history를 조회했을때 다음과 같이 나온다.

$ kubectl rollout history deployment app-deploy
deployment.apps/app-deploy 
REVISION  CHANGE-CAUSE
1         <none>

업데이트된 기록이 나오지 않아 나중에 롤백할때 문제가 될 수 있다. 따라서 레코드 옵션을 항상 잘 적용해주도록 하자.

$ kubectl rollout history deployment app-deploy   
deployment.apps/app-deploy 
REVISION  CHANGE-CAUSE
1         kubectl create --filename=deployment-nginx.yaml --record=true

다시 돌아와서 롤링 업데이트를 진행해보자.

롤링 업데이트

$ kubect set image deployment <deploy_name> <container_name> = <new_version_name>

$ kubectl set image deployment app-deploy app=nginx:1.15 --record

컨테이너를 새로운 버전으로 업데이트 과정은 다음과 같다.

  • 새로운 ReplicaSet을 생성하여 1.15 버전으로 pod를 생성한다. (기존 버전이 1.14 버전)
  • 이후 1.15 버전이 잘 운영되고 있을 때 1.14 버전을 종료하고 1.15 버전을 운영하는 방직으로 업데이트 함

아래 명령어를 통해 업데이트가 성공적으로 완료된 것을 확인할 수 있다.

$ kubectl rollout status deployment app-deploy
deployment "app-deploy" successfully rolled out

// 업데이트 일시정지/재시작 명령어
$ kubectl rollout pause/resume

롤백

히스토리를 조회하여 업데이트 직전의 버전과 특정 버전으로 롤백할 수 있다.

$ kubectl rollout history deployment app-deploy
deployment.apps/app-deploy 
REVISION  CHANGE-CAUSE
1         kubectl create --filename=deployment-nginx.yaml --record=true
2         kubectl set image deployment app-deploy app=nginx:1.15 --record=true

undo 명령어는 히스토리를 기준으로 바로 직전의 단계로 되돌아갈 수 있다.

$ kubectl rollout undo deployment app-deploy
deployment.apps/app-deploy rolled back

만일 특정 버전으로 롤백하고 싶다면 히스토리의 REVISION 번호를 통해 되돌아갈 수 있다.

$ kubectl rollout undo deployment app-deploy —to-revision=2

DaemonSet + RollingUpdate

DaemonSet의 경우 노드당 1개의 pod를 보장해준다. 로그 수집기, 모니터링 에이전트와 같은 프로그램 실행시 사용된다.

또한 이미 노드가 보장되어있기 때문에 replicas 설정이 필요없다.

DaemonSet 예제

$ kubectl get nodes
NAME                         STATUS   ROLES           AGE   VERSION
test-cluster-control-plane   Ready    control-plane   77d   v1.30.0
test-cluster-worker          Ready    <none>          77d   v1.30.0
test-cluster-worker2         Ready    <none>          77d   v1.30.0

$ kubectl create -f daemonset-nginx.yaml 
daemonset.apps/nginx-daemonset created

$ kubectl get pod -o wide
NAME                    READY   STATUS    RESTARTS   AGE   IP            NODE                   NOMINATED NODE   READINESS GATES
nginx-daemonset-8jm8h   1/1     Running   0          16s   10.244.0.-   test-cluster-worker2   <none>           <none>
nginx-daemonset-pjvlc   1/1     Running   0          16s   10.244.0.-    test-cluster-worker    <none>           <none>

$ kubectl get daemonset            
NAME              DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE
nginx-daemonset   2         2         2       2            2           <none>          13m

컨트롤러를 생성 후 pod 정보를 확인해보면 각 노드 별로 한개씩 두개의 pod가 생성된 것을 볼 수 있다.

데몬셋의 롤링 업데이트/ 롤백 예제

kubectl edit 명령어를 통해 nginx의 버전을 1.15 버전으로 변경해보자.

$ kubectl edit daemonset nginx-daemonset

$ kubectl get pods
NAME                    READY   STATUS    RESTARTS   AGE
nginx-daemonset-hc846   1/1     Running   0          4s
nginx-daemonset-pljtl   1/1     Running   0          1s

$ kubectl describe pod nginx-daemonset-hc846
...
Containers:
  nginx:
    Container ID:   containerd://768003dd7ff3045cd8d63be4-
    Image:          nginx:1.15

DaemonSet의 경우 이미지 버전을 변경한 즉시 업데이트를 실행한다.

롤백 명령어는 Deployment와 동일하다.

$ kubectl rollout undo daemonset nginx-daemonset

statefulSet

pod의 상태(pod 이름, 볼륨(스토리지))를 유지해주는 컨트롤러이다.

예를들어 ReplicaSet으로 생성된 pod 이름의 경우 해시값으로 랜덤하게 생성되지만 statefulSet의 경우 스케일 인/아웃시에 pod의 이름에 번호가 부여되어 증감하기 때문에 pod의 이름을 미리 알 수 있다.

구성 요소

metadata:
  name: sf-nginx
spec:
  replicas: 3
  serviceName: sf-service
  podManagementPolicy: Parallel
  • name: sf-nginx-0, sf-nginx-1 …. 이런식으로 부여되는 특징이 있다.

  • podManagementPolicy: OrderedReady(Default) : 순차적으로 증감

  • podManagementPolicy: Parallel : 0, 1, 2번을 동시에 평행하게 사용

statefulSet 예제

statefulSet의 경우 어느 노드에 배치되는지는 보장되지 않기 때문에 랜덤하게 생성된다.

$ kubectl create -f statefulset-nginx.yaml
$ kubectl get pods -o wide 
NAME         READY   STATUS    RESTARTS   AGE   IP            NODE                   NOMINATED NODE   READINESS GATES
sf-nginx-0   1/1     Running   0          9s    10.244.0.-   test-cluster-worker2   <none>           <none>
sf-nginx-1   1/1     Running   0          9s    10.244.0.-   test-cluster-worker2   <none>           <none>
sf-nginx-2   1/1     Running   0          9s    10.244.0.-   test-cluster-worker    <none>           <none>

만일 파드를 삭제할 경우 이름을 보장하기 때문에 동일한 이름으로 생성된다.

$ kubectl delete pod sf-nginx-1

$ kubectl get pods -o wide     
NAME         READY   STATUS    RESTARTS   AGE   IP            NODE                   NOMINATED NODE   READINESS GATES
sf-nginx-0   1/1     Running   0          87s   10.244.0.-   test-cluster-worker2   <none>           <none>
sf-nginx-1   1/1     Running   0          1s    10.244.0.-   test-cluster-worker2   <none>           <none>
sf-nginx-2   1/1     Running   0          87s   10.244.0.-   test-cluster-worker    <none>           <none>

이름이 보장되는 것을 더 확인해보자.

$ kubectl scale statefulset sf-nginx --replicas=4
$ kubectl get pods -o wide                       
NAME         READY   STATUS    RESTARTS   AGE     IP            NODE                   NOMINATED NODE   READINESS GATES
sf-nginx-0   1/1     Running   0          2m57s   10.244.0.-   test-cluster-worker2   <none>           <none>
sf-nginx-1   1/1     Running   0          91s     10.244.0.-   test-cluster-worker2   <none>           <none>
sf-nginx-2   1/1     Running   0          2m57s   10.244.0.-   test-cluster-worker    <none>           <none>
sf-nginx-3   1/1     Running   0          2s      10.244.0.-   test-cluster-worker    <none>           <none>


$ kubectl scale statefulset sf-nginx --replicas=2
$  kubectl get pods -o wide                       
NAME         READY   STATUS    RESTARTS   AGE     IP            NODE                   NOMINATED NODE   READINESS GATES
sf-nginx-0   1/1     Running   0          3m36s   10.244.0.-   test-cluster-worker2   <none>           <none>
sf-nginx-1   1/1     Running   0          2m10s   10.244.0.-   test-cluster-worker2   <none>           <none>

※ statefuleSet의 롤링 업데이트는 순차적으로 종료 후 업데이트를 진행한다.


Job

쿠버네티스는 기본적으로 pod를 항상 러닝중인 상태를 유지하는데 잡 컨트롤러는 pod의 애플리케이션(job) 실행이 완료/종료되는 것에 초점을 맞춘 컨트롤러이다.

레플리케이션, 레플리카셋, 데몬셋의 경우 pod의 애플리케이션이 지속적으로 잘 동작하는 것을 초점에 맞춘것과 대비되는데 애플리케이션이 실행되고 실행이 완료되면 pod의 할 일이 끝난 것으로 간주하고 pod를 종료시킨다.

즉, 잡 컨트롤러를 사용하여 애플리케이션을 실행시켰을때 완료가 정상인지 비정상인지 체크하여 비정상 종료시 재실행, 정상 종료시 완료하는 것을 보장하는 컨트롤러이다.

구성 요소

apiVersion: batch/v1
kind: Job
metadata:
  name: job-example
spec:
  completions: 5
  parallelism: 2
  activeDeadlineSeconds: 15
  template:
    spec:
      containers:
      - name: centos-container
        image: centos:7
        command: ["bash"]
        args:
        - "-c"
        - "echo 'Hello World'; sleep 5; echo 'Bye'"
      restartPolicy: Never / OnFailure
      backoffLimit: 3
  • completions: 실행해야할 job의 수가 몇개인지 설정
  • parallelism: 병렬성, 동시 러닝되는 pod의 수
  • activeDeadlineSeconds: 지정시간 내에 job을 완료
  • restartPolicy: Never : pod 재시작 (RESTART 카운팅X)
  • restartPolicy: OnFailure : pod가 아닌 컨테이너만 재실행
  • backoffLimit: 3 : 3번까지 시도 후 아예 job을 삭제함

job 예제

$ kubectl create -f job-centos.yaml 

$ kubectl describe job job-example
...
Events:
  Type    Reason            Age    From            Message
  ----    ------            ----   ----            -------
  Normal  SuccessfulCreate  2m17s  job-controller  Created pod: job-example-255p7
  Normal  Completed         101s   job-controller  Job completed

job의 이벤트를 확인해보자.

위처럼 ‘Job completed’가 되면 컨트롤러가 종료되어서 이후 삭제되는 파드는 정상적인 것으로 간주하고 정상 삭제된다.
그러나 ‘Job completed’가 아닐 경우 pod를 삭제시키면 비정상 종료라고 판단하여 pod를 새로 생성하게 된다.


그렇다면 backoffLimit:3 에 실패한 pod를 확인해보자.
yaml 설정값을 다음과 같이 설정했다고 가정한다.

restartPolicy: OnFailure
backoffLimit: 3

$ kubectl get pods -o wide
NAME                READY   STATUS              RESTARTS   AGE   IP            NODE                  NOMINATED NODE   READINESS GATES
job-example-688zv   0/1     RunContainerError   0          2s    10.244.0.-   test-cluster-worker   <none>           <none>
 …

최대 3번까지 pod를 재실행한다.

NAME                READY   STATUS             RESTARTS      AGE   IP            NODE                  NOMINATED NODE   READINESS GATES
job-example-688zv   0/1     Terminationg   3 (28s ago)   70s   10.244.0.-   test-cluster-worker   <none>           <none>

전부 실패하면 리소스를 아예 삭제해버린 것을 확인할 수 있다.

$ kubectl get pod
No resources found in default namespace.

CronJob

잡 컨트롤러를 제어해서 사용자가 원하는 시간에 잡이 실행되도록 지원해준다.

설정 값은 평소 우리가 알고있는 cron과 비슷하다고 보면된다.

Cronjob schedule: “0 0 0 0 0”
				  분/시간/일/월/주중(0-6)

쿠버네티스에 원하는 동작을 잡 컨트롤러를 통해 실행한다면 잡 컨트롤러는 동작 수행 후 complite로 종료 시킨다. 잡 컨트롤러가 cronJob에게 스케줄링에 따라 작업해달라고 요청하여 동작하는 것이 cronJob이다.

cronJob schedule 예제

  • 매주 일요일 새벽 3시에 잡을 실행해줘 -> 0 3 * * 0
  • 매월 1일 아침 9시 정각에 잡을 실행해줘 -> 0 9 1 * *
  • 주중 새벽 3시에 잡을 실행해줘 -> 0 3 * * 1-5
  • 주말 새벽 3시에 잡을 실행해줘 -> 0 3 * * 0,6
  • 잡을 5분마다 반복해서 실행해줘 -> /5 * * *

구성 요소

spec:
  schedule: "*/1 * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: hello
            image: busybox
            args:
            - /bin/sh
            - -c
            - date; echo Hello
          restartPolicy: OnFailure
  • schedule: “_ _ _ _ _”
  • startingDeadlineSeconds: 300 : 300초 이내에 잡 애플리케이션을 실행하지 않으면 취소
  • ConcurrencyPolicy: Forbid : 한번에 하나씩만 실행
  • ConcurrencyPolicy: Allow(default) : 동시에 실행되는 Job 을 허용

[참고자료]

controller-definition.yaml 모음
쿠버네티스 컨트롤러 종류

profile
주니어 데브옵스 엔지니어

0개의 댓글