Data science/MLOps

[Kubernetes] Controller 이해하기

연금(Pension)술사 2022. 7. 26. 23:19

쿠버네티스의 컨트롤러는 특정 Pod의 수를 보장해주는 역할을 하는 자원을 의미한다. 아래의 쿠버네티스의 마스터노드(컨트롤플레인)가 어플리케이션을 각 노드에서 실행하고, 이를 각 etcd에서 제대로 수행되고 있는지 모니터링하다가 부족한 Pod은 더 실행해주고, 많은 Pod은 종료시켜주는 역할을 한다.

그림. 쿠버네티스 동작원리

 

쿠버네티스의 컨트롤러의 종류


쿠버네티스 컨트롤러는 Replication controller, replicaset, deployment, daemonset등 여러가지가 있어서 헷갈린다. 이 자원들은 모두 다 컨트롤로에 속한다. 다만 각각의 자원들이 쿠버네티스가 발전해나감으로서 생긴 디테일의 차이(대동소이)한 것들이 있어서 몇가지 대표적으로 필요한 것들만 알아보면 좋다. 사용목적에 따른 대표적인 것들은 아래와 같다.

  • 오랜시간 켜두어야할 어플리케이션이 파드로 존재하는 경우: Replication contoller, Replicaset, Deploment
  • 클러스터에 속한 전체 노드에 같은 파드를 실행해야하는 경우: Daemonset
  • 일회성 작업을 해야할 때: Job
  • 주기적인 배치작업을 해야하는 경우: Cronjob

자세히는 아래와 같다. 리플리케이션 컨트롤러 (Replication controller)은 쿠버네티스 초기부터 있던 컨트롤러로, 지정된 숫자만큼 파드의 개수를 항상 보장해줄 수 있도록 제어해주는 컨트롤러이다. 요즘은 이 리플리케이션 컨트롤러보다 리플리카셋(replicaset)을 주로 이용하거나, 앱 배포면 디플로이먼트(deployment)을 이용한다고 한다.

사용방법은 아래와 같다. 1) Kind에 replicationContoller라고 적는다. 2) spec.replicas내에 몇개의 파드를 유지할 것인지를 명시한다. 3) selector은 레이블이 동일한 어떤 파드를 관찰할 것인지를 명시하는 것이다. 여기서는 app: my-ngnix라는 라벨을 가진 파드를 바라보고 관찰한다. 4) template은 명시한 파드가 개수가 부족한 경우, 어떤 스펙으로 다시 만들지를 정의한다. *주의할 것은 selector이하의 속성값과 template.labels의 속성값은 동일해야 에러가 안난다.

아래와 같은 rc의 실행결과를 확인할 수 있다.

PS C:\Users\PC\Documents\repository\kube> kubectl get rc
NAME    DESIRED   CURRENT   READY   AGE
my-rc   3         0         0       24s

 

리플리카셋(Replicaset, RC): ReplicationContorller의 확장판(+POD 검색기능)


ReplicationContoller은 POD의 개수를 보장한다고 했다. 리플리카셋도 마찬가지로 POD의 개수를 보장한다. 하지만, 리플리카셋이 추가적으로 해주는 기능은,

 "="의 selector외에 집합연산자 (in, notin)등을 지원해서 저 쉽게 사용할 수 있다. 리플리카셋의 spec에는 matchLabels과 matchExpressions라는 것을 사용할 수 있는데, matchLabels은 리플리케이션컨트롤러가 해주는 기능과 비슷하다. 다만, matchExpressions으로 좀더 상세하게 POD의 선정을 할 수 있다. 가령 아래와 같이 matchExpression을 써주면 key의 In조건에 맞는 POD들만 선택해서 관리할 수 있다.

# replicaset.yaml

...
spec:
    replicas:3
    selector:
         matchLabels:
             app: my-app
         matchExpression:
             {key: verison, operator: In, value:["3.5", "2.1"]}

 

하지만 matchExpressions을 꼭 써야하는 것은 아니다. 다음과 같이 작성해도 충분히 동작한다.

위와 같이 파드의 개수를 3개로 유지하게끔 선언했는데, 아래와 같이 파드가 1개 생성된 경경우는 2개가 아직 모종의 이유로 생성되지 안음을 확인할 수 있다. cpu 요구량을 못맞춰서 중단된것을 알 수 있다.

> kubectl get pods
NAME                  READY   STATUS              RESTARTS   AGE
my-replicaset-q42qp   0/1     ContainerCreating   0          3s

> kubectl describe replicaset my-replicaset
....
Events:
  Type     Reason            Age                 From                   Message
  ----     ------            ----                ----                   -------
  Normal   SuccessfulCreate  2m7s                replicaset-controller  Created pod: my-replicaset-q42qp
  Warning  FailedCreate      2m7s                replicaset-controller  Error creating: pods "my-replicaset-86kwg" is forbidden: exceeded quota: mem-cpu-demo, requested: requests.cpu=1, used: requests.cpu=1, limited: requests.cpu=1
  Warning  FailedCreate      2m7s                replicaset-controller  Error creating: pods "my-replicaset-n4rkz" is forbidden: exceeded quota: mem-cpu-demo, requested: requests.cpu=1, used: requests.cpu=1, limited: requests.cpu=1
  Warning  FailedCreate      2m7s                replicaset-controller  Error creating: pods "my-replicaset-jg6xc" is forbidden: exceeded quota: mem-cpu-demo, requested: requests.cpu=1, used: requests.cpu=1, limited: requests.cpu=1
  Warning  FailedCreate      2m7s                replicaset-controller  Error creating: pods "my-replicaset-66pht" is forbidden: exceeded quota: mem-cpu-demo, requested: requests.cpu=1, used: requests.cpu=1, limited: requests.cpu=1
  Warning  FailedCreate      2m7s                replicaset-controller  Error creating: pods "my-replicaset-5zg4z" is forbidden: exceeded quota: mem-cpu-demo, requested: requests.cpu=1, used: requests.cpu=1, limited: requests.cpu=1
  Warning  FailedCreate      2m7s                replicaset-controller  Error creating: pods "my-replicaset-8ns6f" is forbidden: exceeded quota: mem-cpu-demo, requested: requests.cpu=1, used: requests.cpu=1, limited: requests.cpu=1
  Warning  FailedCreate      2m7s                replicaset-controller  Error creating: pods "my-replicaset-w5gsb" is forbidden: exceeded quota: mem-cpu-demo, requested: requests.cpu=1, used: requests.cpu=1, limited: requests.cpu=1
  Warning  FailedCreate      2m6s                replicaset-controller  Error creating: pods "my-replicaset-8j4dh" is forbidden: exceeded quota: mem-cpu-demo, requested: requests.cpu=1, used: requests.cpu=1, limited: requests.cpu=1
  Warning  FailedCreate      2m6s                replicaset-controller  Error creating: pods "my-replicaset-vtqgv" is forbidden: exceeded quota: mem-cpu-demo, requested: requests.cpu=1, used: requests.cpu=1, limited: requests.cpu=1
  Warning  FailedCreate      24s (x8 over 2m6s)  replicaset-controller  (combined from similar events): Error creating: pods "my-replicaset-fx7rt" is forbidden: exceeded quota: mem-cpu-demo, requested: requests.cpu=1, used: requests.cpu=1, limited: requests.cpu=1

 

디플로이먼트(Deployment)


컨트롤러기보다는 컨트롤러인 레플리카셋(RS)을 제어해주는 역할이다. 디플로이먼트의 가장 큰 목적은 롤링업데이트(Rolling update) 또는 롤링백(Rolling back)을 하기위함이다. 롤링업데이트의 자세한 설명은 링크와 같다. 소프트웨어를 업데이트를 위한 것인데 요약하면 "롤링 업데이트는 파드가 여러 개 있다고 했을 때, 한 번에 모든 파드를 동시에 버전업하는 것이 아닌, 파드를 하나씩 업데이트해서 무중단 업데이트를 해주는 과정"이라고 생각하면 된다. 핵심은 "무중단 + 업데이트"이다.  

Deployment은 정확히는 컨트롤러는 아니고, 컨트롤러인 Replicaset을 제어하는 자원이다. Replicaset은 POD을 제어한다.

아래와 같이 대화형 튜토리얼을 이용하면 예시를 직접 해볼 수 있다(링크). 아래의 이미지를 보면, 이름이 "kubernetes-bootcamp"인 deployment가 하나가 실행됨을 확인할 수 있다. 위에서도 디플로이먼트가 레플리카셋을 제어한다고하니, 레플리카셋이 하나 있을 것이고, 레플리카셋은 또 파드를 제어하니, 파드의 생성도 함께 확인할 수 있을 것이다.

그러면, 디플로이먼트를 사용하려면 어떻게 yaml파일을 작성해야하나? 일단, deployment의 버전을 확인하기위해서 아래와 같이 확인한다. deployment은 apps/v1의 APIVERSION에 있다.

PS C:\Users\PC> kubectl api-resources
NAME                              SHORTNAMES   APIVERSION                             NAMESPACED   KIND
...
deployments                       deploy       apps/v1                                true         Deployment

그리고 아래와 같이 스펙에 맞춰 yaml파일을 작성한다. 형태는 replciaset과 동일하다.

apiVersion: apps/v1
kind: Deployment
metadata:
    name: my-deployment
spec:
    replicas: 2
    selector:
        matchLabels:
            app: my-nginx
    template:
        metadata:
            name: nginx-pod
            labels: 
                app: my-nginx
        spec:
            containers:
            -   name: nginx-container
                image: nginx:latest
                resources:
                    limits:
                        memory: 500Mi
                        cpu: 1
                    requests:
                        memory: 500Mi
                        cpu: 1

 

그러면 아래와같이 동작하는 것을 확인할 수 있다. (POD 하나는 리소스 이유 때문에 동작은 하지 않았다.)

PS C:\Users\PC\Documents\repository\kube> kubectl get all -o wide
NAME                                READY   STATUS    RESTARTS   AGE   IP          NODE             NOMINATED NODE   READINESS GATES
pod/my-deployment-cff6c8d86-jgb5g   1/1     Running   0          12s   10.1.0.25   docker-desktop   <none>           <none>

NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE   SELECTOR
service/kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   44d   <none>

NAME                            READY   UP-TO-DATE   AVAILABLE   AGE   CONTAINERS        IMAGES         SELECTOR
deployment.apps/my-deployment   1/2     1            1           12s   nginx-container   nginx:latest   app=my-nginx

NAME                                      DESIRED   CURRENT   READY   AGE   CONTAINERS        IMAGES         SELECTOR
replicaset.apps/my-deployment-cff6c8d86   2         1         1       12s   nginx-container   nginx:latest   app=my-nginx,pod-template-hash=cff6c8d86

 

 

Deployment을 이용하여 pod을 스케일업하기: kubectl scale deployment [deployment명] --replicas=3

아래의 그림과 같이 deployment은 Replica Set(RS)을 선언적으로 지정해서 관리한다. 또한, Replica Set은 pod을 선언적으로 관리한다.(선언적= 지정한대로 개수, 자원등을 보장해줌). 따라서, deployment을 생성해놓고, replicaset 또는 pod을 직접 관리하면, 삭제되고 재생산된다. 따라서, pod수나 replicas을 변경하고싶으면 deployment을 이용해서 조작해야한다.

Deployment, Replica Set, Pod의 관계예시: 자료(https://theithollow.com/2019/01/30/kubernetes-deployments/)

아래와같이 쿠버네티스 공식홈페이지에 있는 것과 동일하게 웹서버인 nginx을 서비스하는 deployment을 정의했다.

 

이를 이용해서 Deployment 및 스케일업을 아래와같이 진행해볼 수 있다. 아래의 코드블럭처럼 replicas을 조절하면 replicas에 딸려있는 pod수가 조절되므로 replicas을 조정해도 된다. replicas을조정하기위해서는 deployment을 조정한다. 

// deployment 자원의 생성
$ kubectl create -f nginx-deployment.yaml
deployment.apps/nginx-deployment created

// 자원이 생성되었는지 확인
$ kubectl get deployment
NAME               READY   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   3/3     3            3           18s

// replicas을 5개로 스케일업
$ kubectl scale deployment nginx-deployment --replicas=5
deployment.apps/nginx-deployment scaled


// 스케일업 되었는지 pod수를 확인
$ kubectl get pods
NAME                                READY   STATUS    RESTARTS   AGE
nginx-deployment-7fb96c846b-88ctt   1/1     Running   0          52s
nginx-deployment-7fb96c846b-dqbhk   1/1     Running   0          7s
nginx-deployment-7fb96c846b-jhgq2   1/1     Running   0          52s
nginx-deployment-7fb96c846b-m2bbg   1/1     Running   0          7s
nginx-deployment-7fb96c846b-xj74r   1/1     Running   0          52s

그리고, kubectl descirbde 을 이용하면 아래와같이 스케일업된것을 확인할 수 있다.

 

 

롤링업데이트의 사용은 어떻게 해야하나? 예시로 

# set 명령어 인자
kubectl set image deployment <디플로이먼트 이름> <컨테이너이름>=<새 버전>

# 디플로이먼트 내 컨테이너 ngnix 을 1.9.1로 업데이트
kubectl set image deployment/nginx nginx=nginx:1.9.1

# 모든 디플로이먼트와 RC의 컨테이너 내에 있는 ngnix이미지를 1.9.1로 업데이트
kubectl set image deployments,rc nginx=nginx:1.9.1 --all

*references:https://www.macstadium.com/blog/how-to-k8s-pods-replicasets-and-deployments

 

Deployment의 정지: kubectl delete deployment [deployment명]

// deployment 상태확인
$ kubectl get deployment
NAME                    READY   UP-TO-DATE   AVAILABLE   AGE
simple-app-deployment   3/3     3            3           11m


$ kubectl delete deployment simple-app-deployment
deployment.apps "simple-app-deployment" deleted

 

 

스테이트풀세트(Stateful set)


작성중

 

데몬세트(Daemon set)


작성중

 

잡 컨트롤러(Job contoller)


작성중

 

크론잡(Cronjob)


작성중

 

기타(트러블 슈팅)


이슈1: error: resource mapping not found for name: "my-rc" namespace: "" from ".\\my_rc.yaml": no matches for kind "ReplicationContoller" in version "v1" ensure CRDs are installed first

해결방법: 아래와 같이 쿠버네티스에서 지원하는 자원들과 APIVERSION을 확인한다. 참고로 해당 이슈는 "ReplicationContoller"을 kind에 썼는데, 아래와 같이 "ReplicationController"에 오타가 난 것을 알 수 있다. 즉, 아래의 리스트의 KIND와 동일한 KINDS을 써주어야한다.

$ kubectl api-resources
NAME                              SHORTNAMES   APIVERSION                             NAMESPACED   KIND
bindings                                       v1                                     true         Binding
componentstatuses                 cs           v1                                     false        ComponentStatus
configmaps                        cm           v1                                     true         ConfigMap
endpoints                         ep           v1                                     true         Endpoints
events                            ev           v1                                     true         Event
limitranges                       limits       v1                                     true         LimitRange
namespaces                        ns           v1                                     false        Namespace
nodes                             no           v1                                     false        Node
...

 

이슈 2: 리플리카셋에서 아래와 같이 메시지가 나온 경우(failed quota) Error creating: pods "my-replicaset-vvh6n" is forbidden: failed quota: mem-cpu-demo: must specify limits.cpu for: nginx-container; requests.cpu for: nginx-container

해결방법: 리플리카셋 내 template.spec.container.resources 내에 limit 또는 reqeusts을 명시한다.

반응형