ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 12. 애플리케이션 스케줄링과 라이프사이클 관리 (2)
    개발 스터디/데브옵스(DevOps)를 위한 쿠버네티스 마스터 2021. 8. 20. 20:48
    반응형

    이 문서는 인프런 강의 "데브옵스를 위한 쿠버네티스 마스터"을 듣고 작성되었습니다. 최대한 요약해서 강의 내용을 최소로 하는데 목표를 두고 있어서, 더 친절하고 정확한 내용을 원하신다면 강의를 구매하시는 것을 추천드립니다. => 강의 링크

    컨테이너 리소스 요청과 제한하는 방법

    이번 절은 GCP에서 진행한다. k8s에서 컨테이너 생성 시 CPU, 메모리를 요청 및 제한할 수 있다.

    • request : 최소 스펙 요청 사항
    • limit : 최대 리소스 제한

    CPU는 다음 단위를 사용한다.

    • m (0.1 = 100m)

    메모리는 다음 단위를 사용한다.

    • Ti (1024 ^ 4)
    • Gi (1024 ^ 3)
    • Mi (1024 ^ 2)
    • Ki (1024)
    • T (1000 ^ 4)
    • G (1000 ^ 3)
    • M (1000 ^ 2)
    • K (1000)

    이제 한 번 리소스를 요청/제한에서 컨테이너를 만들어보자. 다음과 같이 만들 것이다.

    • request
      • cpu: 1m
      • memory: 200Mi
    • limit
      • cpu: 2m
      • memory: 400Mi

    다음과 같이 container-limit-request.yaml을 만들어보자.

     

    src/ch12/k8s/container-limit-request.yaml

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: nginx-deployment
      labels:
        app: nginx
    spec:
      replicas: 3
      selector:
        matchLabels:
          app: nginx
      template:
        metadata:
          labels:
            app: nginx
        spec:
          containers:
            - name: nginx
              image: nginx
              ports:
                - containerPort: 80
              resources:
                requests:
                  cpu: "1m"
                  memory: "200Mi"
                limits:
                  cpu: "2m"
                  memory: "400Mi"

     

    요청은 Pod의 다음 필드로 할 수 있다.

    • spec.resources.requests.cpu
    • spec.resources.requests.resource

    제한은 Pod의 다음 필드로 할 수 있다.

    • spec.resources.limits.cpu
    • spec.resources.limits.resource

    이제 다음 명령어로 리소스를 생성한다.

    $ kubectl create -f container-limit-request.yaml
    deployment.apps/nginx-deployment created
    
    $ kubectl get pod -w
    NAME                                READY   STATUS    RESTARTS   AGE
    ...
    nginx-deployment-7fdbdd879d-gtlzp   1/1     Running   0          74s
    nginx-deployment-7fdbdd879d-m55nv   1/1     Running   0          74s
    nginx-deployment-7fdbdd879d-s28ch   1/1     Running   0          74s

     

    이제 describe 명령어를 실행하여 Pod에서 리소스 요청/제한이 잘 되었는지 확인해보자.

    $ kubectl describe pod nginx
    Name:         nginx-deployment-7fdbdd879d-gtlzp
    ...
    Containers:
      nginx:
        ...
        Limits:
          cpu:     2m
          memory:  400Mi
        Requests:
          cpu:        1m
          memory:     200Mi
    ...

     

    그리고 GKE같은 클라우드 프로바이더에서 제공하는 쿠버네티스 클러스터에는 top 명령어를 사용하여 리소스 요청/제한 사항을 확인할 수 있다. (0m이 뜨는 이유는 왜인지 모르겠다..)

    $ kubectl top pod 
    NAME                                CPU(cores)   MEMORY(bytes)
    nginx-deployment-7fdbdd879d-gtlzp   0m           3Mi
    nginx-deployment-7fdbdd879d-m55nv   0m           3Mi
    nginx-deployment-7fdbdd879d-s28ch   0m           3Mi

    LimitRange

    이번 절은 VM에서 진행한다. LimitRangeNamespace에서 Pod 혹은 Container, PersistentVolumeClaim 별로 리소스를 제한하는 "정책"이다. 다음과 같은 제약 조건을 제공한다.

    • Namespace 별 Pod 혹은 Container 별 CPU, Memory 사용량을 지정한다.
    • Namespace 별 StorageClass 별 최소 및 최대 스토리지 요청을 지정한다.
    • Namespace 별 리소스에 대한 request, limit 사이의 비율을 지정한다.
    • Namespace 별 컴퓨팅 리소스에 대한 기본 request/limit 설정하고, 런타임에 있는 컨테이너에 자동으로 설정한다.

    이번 절에서는 Container에 대한 리소스를 제한하는 정책만을 다룬다.

     

    먼저 공통적으로 필요한 부분을 작업해보자. k8s 설정을 변경해주어야 한다. /etc/kubernetes/manifests/kube-apiserver.yaml을 다음과 같이 수정한다.

    apiVersion: v1
    kind: Pod
    metadata:
    annotations:
    kubeadm.kubernetes.io/kube-apiserver.advertise-address.endpoint: 10.0.2.15:6443
    creationTimestamp: null
    labels:
    component: kube-apiserver
    tier: control-plane
    name: kube-apiserver
    namespace: kube-system
    spec:
    containers:
    - command:
        - kube-apiserver
        - --advertise-address=10.0.2.15
        - --allow-privileged=true
        - --authorization-mode=Node,RBAC
        - --client-ca-file=/etc/kubernetes/pki/ca.crt
        # 수정 (, LimitRanger) 추가
        - --enable-admission-plugins=NodeRestriction,LimitRanger
        - --enable-bootstrap-token-auth=true
        - --etcd-cafile=/etc/kubernetes/pki/etcd/ca.crt
        - --etcd-certfile=/etc/kubernetes/pki/apiserver-etcd-client.crt
        - --etcd-keyfile=/etc/kubernetes/pki/apiserver-etcd-client.key
        - --etcd-servers=https://127.0.0.1:2379
        - --insecure-port=0

     

    그 후 얼마 정도 시간이 지나서 get 명령어를 사용해서 결과가 나오면 설정이 잘 변경된 것이다.

    $ kubectl get pod 
    NAME      READY   STATUS    RESTARTS   AGE
    mongodb   1/1     Running   1          8d

     

    이제 Namespace를 생성한다. 다음 명령어를 사용한다.

    $ kubectl create namespace cpu-constraints

     

    아니면 다음과 같이 cpu-constraints-ns.yaml을 만들고 kubectl create -f <file>로 리소스를 생성해도 좋다.

     

    src/ch12/k8s/cpu-constraints-ns.yaml

    apiVersion: v1
    kind: Namespace
    metadata:
      creationTimestamp: null
      name: cpu-constraints
    spec: {}
    status: {}

     

    그 후 이제 LimitRange를 만들어보자. 위에 생성한 Namespace에 CPU를 200m에서 800m까지의 제한 정책을 둘 것이다. 다음과 같이 cpu-constraints-lr.yaml을 생성한다.

     

    src/ch12/k8s/cpu-constraints-lr.yaml

    apiVersion: v1
    kind: LimitRange
    metadata:
      name: cpu-constraints-lr
      namespace: cpu-constraints
    spec:
      limits:
        - max:
            cpu: "800m"
          min:
            cpu: "200m"
          type: Container

     

    그 후 터미널에 다음을 입력하여 리소스를 생성한다.

    $ kubectl create -f cpu-constraints-lr.yaml
    limitrange/cpu-constraints-lr created

     

    이제 다음 명령어를 이용해서 리소스 제한 정책이 잘 만들어졌는지 확인하자.

    $ kubectl get limitranges cpu-constraints-lr --output=yaml -n cpu-constraints 
    apiVersion: v1
    kind: LimitRange
    metadata:
      creationTimestamp: "2021-08-19T11:35:59Z"
      name: cpu-constraints-lr
      namespace: cpu-constraints
      resourceVersion: "55564"
      uid: a69c11c3-b2fc-427a-b19d-948f613ba6f1
    spec:
      limits:
      - default:
          cpu: 800m
        defaultRequest:
          cpu: 800m
        max:
          cpu: 800m
        min:
          cpu: 200m
        type: Container

     

    기본 값을 지정하지 않더라도 자동으로 만들어진다는 것을 확인할 수 있다. 이제 해당 범위에 맞는 Pod을 생성해보자. cpu-constraints-pod-1.yaml을 다음과 같이 생성한다.

     

    src/ch12/k8s/cpu-constraints-pod-1.yaml

    apiVersion: v1
    kind: Pod
    metadata:
      name: constraints-cpu-demo1
      namespace: cpu-constraints
    spec:
      containers:
        - name: constraints-cpu-demo1
          image: nginx
          resources:
            limits:
              cpu: "800m"
            requests:
              cpu: "500m"

     

    이제 리소스를 생성하면 잘 만들어지는 것을 확인할 수 있다.

    $ kubectl create -f cpu-constraints-pod-1.yaml 
    pod/constraints-cpu-demo1 created
    
    $  kubectl get pod -w -n cpu-constraints
    NAME                    READY   STATUS    RESTARTS   AGE
    constraints-cpu-demo1   1/1     Running   0          16s

     

    이번엔 CPU "limit"이 LimitRange의 "max"보다 크게 설정하는 Pod을 만들어본다. cpu-constraints-pod-2.yaml를 다음과 같이 작성한다.

     

    src/ch12/k8s/cpu-constraints-pod-2.yaml

    apiVersion: v1
    kind: Pod
    metadata:
      name: constraints-cpu-demo2
      namespace: cpu-constraints
    spec:
      containers:
        - name: constraints-cpu-demo2
          image: nginx
          resources:
            limits:
              cpu: "1.5"
            requests:
              cpu: "500m"

     

    max가 800인데 1.5를 제한을 걸었다. 리소스를 생성해보자.

    $ kubectl create -f cpu-constraints-pod-2.yaml 
    Error from server (Forbidden): error when creating "cpu-constraints-pod-2.yaml": pods "constraints-cpu-demo2" is forbidden: maximum cpu usage per Container is 800m, but limit is 1500m

     

    limits > max 조건이 성립되기 때문에 생성되지 않는다. 이번엔 Pod의 리소스 "request"가 LimitRange에 설정한 "min"보다 낮은 리소스를 요청하면 어떻게 될까? cpu-constraints-pod-3.yaml를 다음곽 같이 작성한다.

     

    src/ch12/k8s/cpu-constraints-pod-3.yaml

    apiVersion: v1
    kind: Pod
    metadata:
      name: constraints-cpu-demo3
      namespace: cpu-constraints
    spec:
      containers:
        - name: constraints-cpu-demo3
          image: nginx
          resources:
            limits:
              cpu: "800"
            requests:
              cpu: "100m"

     

    다음 명령어를 이용해서 리소스를 생성한다.

    $ kubectl create -f cpu-constraints-pod-3.yaml 
    Error from server (Forbidden): error when creating "cpu-constraints-pod-3.yaml": pods "constraints-cpu-demo3" is forbidden: [minimum cpu usage per Container is 200m, but request is 100m, maximum cpu usage per Container is 800m, but limit is 800]

     

    이것도 만들어지지 않는다. requests < min 조건이 성립하기 때문이다. 즉, min <= requests <= limit <= max라는 조건이 성립되어야 잘 만들어진다는 것을 알 수 있다.

    ResourceQuota

    이번 절은 VM 환경에서 진행된다. ResourceQuotaNamespace 별 만들 수 있는 총 리소스의 양을 제한한다. Namespace당 1개씩 지정할 수 있다. cpu, memory. storage 뿐 아니라 Pod, Deployment 같은 오브젝트 개수까지 제한이 가능하다.

     

    먼저 Namespace를 생성한다. mem-cpu-ns.yaml을 다음과 같이 작성한다.

     

    src/ch12/k8s/mem-cpu-ns.yaml

    apiVersion: v1
    kind: Namespace
    metadata:
      creationTimestamp: null
      name: mem-cpu-demo
    spec: {}
    status: {}

     

    그리고 다음 멸령어로 리소스를 생성한다.

    $ kubectl create -f mem-cpu-ns.yaml
    namespace/mem-cpu-demo created

     

    그 후 ResourceQuota를 만들면 된다. mem-cpu-rq.yaml을 다음과 같이 작성한다.

     

    src/ch12/k8s/mem-cpu-rq.yaml

    apiVersion: v1
    kind: ResourceQuota
    metadata:
      name: mem-cpu-demo-rq
      namespace: mem-cpu-demo
    spec:
      hard:
        requests.cpu: "1"
        requests.memory: 1Gi
        limits.cpu: "2"
        limits.memory: 2Gi

     

    ResourceQuata는 cpu는 request는 1까지, limit은 2까지, memory는 request는 1Gi, limit은 2Gi 까지 생성할 수 있다. 리소스를 생성해보자.

    $ kubectl create -f mem-cpu-rq.yaml
    resourcequota/mem-cpu-demo-rq created

     

    describe 명령어로 한 번 확인해보자.

    $ kubectl describe resourcequota mem-cpu-demo-rq -n mem-cpu-demo
    Name:            mem-cpu-demo-rq
    Namespace:       mem-cpu-demo
    Resource         Used  Hard
    --------         ----  ----
    limits.cpu       0     2
    limits.memory    0     2Gi
    requests.cpu     0     1
    requests.memory  0     1Gi

     

    자 이제 Pod을 1개 만들어보자. mem-cpu-pod-1.yaml을 다음과 같이 작성한다.

     

    src/ch12/k8s/mem-cpu-pod-1.yaml

    apiVersion: v1
    kind: Pod
    metadata:
      name: mem-cpu-demo-1
      namespace: mem-cpu-demo
    spec:
      containers:
        - name: mem-cpu-demo-1
          image: nginx
          resources:
            limits:
              memory: "800Mi"
              cpu: "800m"
            requests:
              memory: "600Mi"
              cpu: "400m"

     리소스 생성을 위해 다음 명령어를 입력한다.

    $ kubectl create -f mem-cpu-pod-1.yaml 
    pod/mem-cpu-demo-1 created

     

    그 후 ResourceQuota를 다시 한 번 확인해서 리소스를 얼마나 사용했는지 확인해보자.

    $ kubectl describe resourcequota mem-cpu-demo-rq -n mem-cpu-demo
    Name:            mem-cpu-demo-rq
    Namespace:       mem-cpu-demo
    Resource         Used   Hard
    --------         ----   ----
    limits.cpu       800m   2
    limits.memory    800Mi  2Gi
    requests.cpu     400m   1
    requests.memory  600Mi  1Gi

     

    리소스를 얼마나 사용한지 확인할 수 있다. 이제 새로운 Pod을 하나 더 만들어보자. mem-cpu-pod-2.yaml을 다음과 같이 작성한다.

     

    src/ch12/k8s/mem-cpu-pod-2.yaml

    apiVersion: v1
    kind: Pod
    metadata:
      name: mem-cpu-demo-2
      namespace: mem-cpu-demo
    spec:
      containers:
        - name: mem-cpu-demo-2
          image: nginx
          resources:
            limits:
              memory: "1Gi"
              cpu: "800m"
            requests:
              memory: "700Mi"
              cpu: "400m"

     

    위의 리소스를 생성하면 cpu request는 0.8, limit은 1.6, memory request는 1.3Gi, limit은 1.8Gi가 된다. 하지만, memory request가 ResourceQuota에 총 제한량보다 커지니까 이 리소스는 만들어지지 않을 것이다. 다음 명령어를 입력해보자.

    $ kubectl create -f mem-cpu-pod-2.yaml 
    Error from server (Forbidden): error when creating "mem-cpu-pod-2.yaml": pods "mem-cpu-demo-2" is forbidden: exceeded quota: mem-cpu-demo-rq, requested: requests.memory=700Mi, used: requests.memory=600Mi, limited: requests.memory=1Gi

     

    역시 예상대로 리소스가 만들어지지 않는 다는 것을 확인할 수 있다.

    DaemonSet

    이번 절에서는 GCP에서 진행한다. DaemonSetReplicaSet과 거의 유사하다. ReplicaSetPod가 임의의 Node에서 생성된다면, DaemonSet은 각 Node에 1개씩 배치된다. 보통은 HostPath로 설정된 Volume과 함께 사용되어 Node의 리소스를 모니터링하는데 사용된다.

     

    자 이제 DaemonSet을 한 번 만들어보자. daemonset.yaml을 다음과 같이 생성한다.

     

    src/ch12/k8s/daemonset.yaml

    apiVersion: apps/v1
    kind: DaemonSet
    metadata:
      name: nginx
      labels:
        name: nginx
    spec:
      selector:
        matchLabels:
          name: nginx
      template:
        metadata:
          labels:
            name: nginx
        spec:
          tolerations:
            - key: node-role.kubernetes.io/master
              operator: Exists
              effect: NoSchedule
          containers:
            - name: nginx
              image: nginx
          terminationGracePeriodSeconds: 30

    ReplicaSet과 거의 유사하다. 몇 가지 추가사항이 있는데 Pod.spec.tolerations 설정이 필요하다. 각 키와 효과는 다음과 같다.

    • node.kubernetes.io/not-ready
      • effect: NoExecute
      • version: 1.13+
      • description: 네트워크 파티션과 같은 노드 문제가 발생해도 데몬셋 파드는 축출되지 않는다.
    • node.kubernetes.io/unreachable
      • effect: NoExecute
      • version: 1.13+
      • description: 네트워크 파티션과 같은 노드 문제가 발생해도 데몬셋 파드는 축출되지 않는다.
    • node.kubernetes.io/disk-pressure
      • effect: NoSchedule
      • version: 1.8+
      • description: 데몬셋 파드는 기본 스케줄러에서 디스크-압박(disk-pressure) 속성을 허용한다.
    • node.kubernetes.io/memory-pressure
      • effect: NoSchedule
      • version: 1.8+
      • description: 데몬셋 파드는 기본 스케줄러에서 메모리-압박(memory-pressure) 속성을 허용한다.
    • node.kubernetes.io/unschedulable
      • effect: NoSchedule
      • version: 1.12+
      • description: 데몬셋 파드는 기본 스케줄러의 스케줄할 수 없는(unschedulable) 속성을 극복한다.
    • node.kubernetes.io/network-unavailable
      • effect: NoSchedule
      • version: 1.12+
      • description: 호스트 네트워크를 사용하는 데몬셋 파드는 기본 스케줄러에 의해 이용할 수 없는 네트워크(network-unavailable) 속성을 극복한다.

    그리고 Pod.spec.terminationGracePeriodSeconds 속성도 추가된다. 이제 리소스를 생성해보자. 다음 명령어를 입력한다.

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

     

    실제 kubectl get pod -o wide 명령어를 이용하면, 어디 노드에 Pod이 구성되었는지 확인할 수 있다. 다음 명령어를 입력해보자.

    $ kubectl get pod -o wide
    NAME          READY   STATUS    RESTARTS   AGE   IP           NODE                                         NOMINATED NODE   READINESS GATES
    # 각 기 다른 노드에 생성된 것을 확인할 수 있다.
    nginx-2v72b   1/1     Running   0          24s   10.20.0.47   gke-gurumee-gke-default-pool-d6d0240d-xvwr   <none>           <none>
    nginx-nhtvq   1/1     Running   0          24s   10.20.2.26   gke-gurumee-gke-default-pool-d6d0240d-sj3x   <none>           <none>
    nginx-pnjq4   1/1     Running   0          24s   10.20.1.10   gke-gurumee-gke-default-pool-d6d0240d-f439   <none>           <none>

     

    각기 다른 노드에 구성됨을 확인할 수 있다.

    Static Pod

    이번 절은 VM에서 진행된다. Static Podkubectl 명령어로 생성하는 것이 아닌 kubelet이 관리하는 Pod을 의미한다. 대표적으로 kube-system에 존재하는 Pod들이 바로 그들인다.

    $ kubectl get pod -n kube-system
    NAME                             READY   STATUS    RESTARTS   AGE
    coredns-558bd4d5db-5z4jv         1/1     Running   13         48d
    coredns-558bd4d5db-z45ck         1/1     Running   10         37d
    etcd-master                      1/1     Running   16         48d
    kube-apiserver-master            1/1     Running   1          23h
    kube-controller-manager-master   1/1     Running   17         48d
    kube-proxy-hvpz5                 1/1     Running   17         48d
    kube-proxy-kdtb5                 1/1     Running   15         48d
    kube-proxy-qthkm                 1/1     Running   13         48d
    kube-scheduler-master            1/1     Running   17         48d
    weave-net-46bsq                  2/2     Running   29         48d
    weave-net-fgxbv                  2/2     Running   29         48d
    weave-net-qr5v6                  2/2     Running   34         48d

     

    또한 이들은 kubectl로 삭제할 수 없다. 한 번 Static Pod을 한 번 만들어보자. kubelet이 실행될 때 옵션으로 경로를 주면 하위 경로에 존재하는 Static Pod 리소스들은 생성된다.

     

    기본 경로는 /etc/kubernetes/manifests/다. 한 번 해당 경로에는 어떤 리소스가 있는지 확인해보자.

    # sudo 권한 획득
    $ ls /etc/kubernetes/manifests/
    etcd.yaml            kube-controller-manager.yaml
    kube-apiserver.yaml  kube-scheduler.yaml

     

    여기다 우리 리소스를 추가하자. 다음 명령어를 입력한다.

    # 파일 생성
    $ sudo tee /etc/kubernetes/manifests/staticpod.yaml <<EOF
    apiVersion: v1
    kind: Pod
    metadata:
      name: static-pod
    spec:
      containers:
        - name: static-pod
          image: nginx
    EOF

     

    소스 코드는 여기 링크 로 확인할 수 있다. 한 번 get pod 명령어를 실행해보자.

    $ kubectl get pod -w
    NAME                READY   STATUS              RESTARTS   AGE
    static-pod-master   0/1     ContainerCreating   0          11s
    static-pod-master   1/1     Running             0          15s

     

    우리는 리소스를 생성하지 않았음에도 Pod이 생성되었다. 주목할 점은 static-pod 뒤에 -master가 붙었다는 것이다. kube-system 네임스페이스 아래에도 이런 Pod들이 있었다.

    $ kubectl get pod -n kube-system | grep master
    etcd-master                      1/1     Running   16         48d
    kube-apiserver-master            1/1     Running   1          23h
    kube-controller-manager-master   1/1     Running   17         48d
    kube-scheduler-master            1/1     Running   17         48d

     

    그렇다 바로 /etc/kubernetes/manifests/에 있던 리소스들로 인해서 위의 Pod들이 생성된 것이다. 이렇게 생성된 Static Podkubectl로 지울 수 없다.

    $ kubectl delete all --all
    pod "static-pod-master" deleted
    service "kubernetes" deleted
    
    $ kubectl get pod -w
    NAME                READY   STATUS    RESTARTS   AGE
    static-pod-master   1/1     Running   0          4s

     

    지우는 방법은 /etc/kubernetes/manifests/ 경로에서 리소스 파일을 삭제하는 것 뿐이다.

    $ sudo rm /etc/kubernetes/manifests/staticpod.yaml
    
    $ kubectl get pod 
    No resources found in default namespace.

    수동 스케줄링

    이번 절은 GCP에서 진행한다. 수동 스케줄링은 자신이 원하는 NodePod들을 배치할 수 있는 기능이다. 2가지 방법이 있다.

    첫 번째 방법은 Node의 이름을 지정하는 것이다. 먼저 scheduling-1.yaml을 다음과 같이 생성한다.

     

    src/ch12/k8s/scheduling-1.yaml

    apiVersion: v1
    kind: Pod
    metadata:
      name: scheduling-1
    spec:
      containers:
        - name: scheduling-1
          image: nginx
        # node 이름
      nodeName: gke-gurumee-gke-default-pool-d6d0240d-sj3x

     

    그 후 다음 명령어를 입력하여 리소스를 생성한다.

    $ kubectl create -f scheduling-1.yaml
    pod/scheduling-1 created

     

    kubectl get pod -o wide 명령어를 입력하면 어떤 노드에 생성되는지 확인할 수 있다. 우리가 nodeName에 지정한 Node에 생성됨을 확인할 수 있다.

    $ kubectl get pod -o wide
    NAME           READY   STATUS    RESTARTS   AGE   IP           NODE                                         NOMINATED NODE   READINESS GATES
    scheduling-1   1/1     Running   0          7s    10.20.2.27   gke-gurumee-gke-default-pool-d6d0240d-sj3x   <none>           <none>

     

    두 번째 방법은 Node의 레이블을 지정하는 것이다. scheduling-2.yaml을 다음과 같이 생성한다.

     

    src/ch12/k8s/scheduling-2.yaml

    apiVersion: v1
    kind: Pod
    metadata:
      name: scheduling-2
    spec:
      containers:
        - name: scheduling-2
          image: nginx
        # node 이름
      nodeSelector:
        gpu: "true"

     

    nodeSelector에 원하는 Node의 레이블 값을 달면 된다. 이제 다음 명령어를 입력하여 리소스를 생성한다.

    $ kubectl create -f scheduling-2.yaml
    pod/scheduling-2 created
    
    $ kubectl get pod -w
    NAME           READY   STATUS    RESTARTS   AGE
    scheduling-1   1/1     Running   0          2m26s
    scheduling-2   0/1     Pending   0          9s

     

    아무리 기다려도 scheduling-2는 생성되지 않는다. 왜냐하면 현재 GKE 클러스터 Nodegpu=true인 레이블이 없기 때문이다.

    $ kubectl get nodes -L gpu
    NAME                                         STATUS   ROLES    AGE   VERSION           GPU
    gke-gurumee-gke-default-pool-d6d0240d-f439   Ready    <none>   9d    v1.20.8-gke.900
    gke-gurumee-gke-default-pool-d6d0240d-sj3x   Ready    <none>   9d    v1.20.8-gke.900
    gke-gurumee-gke-default-pool-d6d0240d-xvwr   Ready    <none>   9d    v1.20.8-gke.900

     

    위 3개의 노드 중 마음 가는 아무거나 레이블을 붙여준다.

    $ kubectl label node gke-gurumee-gke-default-pool-d6d0240d-xvwr gpu="true"
    
    $ kubectl get nodes -L gpu
    NAME                                         STATUS   ROLES    AGE   VERSION           GPU
    gke-gurumee-gke-default-pool-d6d0240d-f439   Ready    <none>   9d    v1.20.8-gke.900
    gke-gurumee-gke-default-pool-d6d0240d-sj3x   Ready    <none>   9d    v1.20.8-gke.900
    gke-gurumee-gke-default-pool-d6d0240d-xvwr   Ready    <none>   9d    v1.20.8-gke.900   true

     

    이제 다시 kubectl get pod -o wide 명령어를 입력하면, gpu=true 레이블을 준 NodePod이 배치됨을 확인할 수 있다.

    $ kubectl get pod -o wide
    NAME           READY   STATUS    RESTARTS   AGE     IP           NODE                                         NOMINATED NODE   READINESS GATES
    scheduling-1   1/1     Running   0          5m41s   10.20.2.27   gke-gurumee-gke-default-pool-d6d0240d-sj3x   <none>           <none>
    scheduling-2   1/1     Running   0          3m24s   10.20.0.48   gke-gurumee-gke-default-pool-d6d0240d-xvwr   <none>           <none>

    오토 스케일링

    k8s에서 오토 스케일링이 필요한 경우가 있다. 총 3가지 유형이 있다.

    • HPA: Pod 자체를 복체하여 처리할 수 있는 Pod의 개수를 늘림.
    • VPA: 리소스를 증가시켜 Pod의 사용 가능한 리소스를 늘림
    • CA: 클러스터를 자체를 늘림

    이 중 HPA만이 기본적으로 제공되는 오토 스케일링 방법이며 나머지 2개는 클라우드 프로바이더 별 제공할 수도 있고 안 할수도 있다. 여기서는 HPA만을 다룬다. 이번 절은 GKE에서 다룬다.

     

    실습을 진행해보자. 먼저 다음과 같이 hpa.yaml을 만든다.

     

    src/ch12/k8s/hpa.yaml

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: php-apache
    spec:
      selector:
        matchLabels:
          run: php-apache
      replicas: 1
      template:
        metadata:
          labels:
            run: php-apache
        spec:
          containers:
            - name: php-apache
              image: k8s.gcr.io/hpa-example
              ports:
                - containerPort: 80
              resources:
                limits:
                  cpu: 500m
                requests:
                  cpu: 200m
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: php-apache
      labels:
        run: php-apache
    spec:
      ports:
        - port: 80
      selector:
        run: php-apache

     

    DeploymentService를 생성하며, php-apache라는 웹 서비스를 구축하게 된다. 다음 명령어를 이용해 리소스를 생성해보자.

    $ kubectl create -f hpa.yaml
    deployment.apps/php-apache created
    service/php-apache created

     

    자 이제 HPA를 생성하자.

    $ kubectl autoscale deployment php-apache --cpu-percent=50 --min=1 --max=10
    horizontalpodautoscaler.autoscaling/php-apache autoscaled
    
    $ kubectl get hpa
    NAME         REFERENCE               TARGETS         MINPODS   MAXPODS   REPLICAS   AGE
    php-apache   Deployment/php-apache   <unknown>/50%   1         10        1          20s

     

    보면 "TARGET"이 50%이고 "MIN"이 1개, "MAX"가 10개, 현재 "REPLICA"는 1개로 되있음을 확인할 수 있다. 이제 무한히 php-apache 서비스를 호출하는 Pod을 만들어보자.

    $ kubectl run -i --tty load-generator --rm --image=busybox --restart=Never -- /bin/sh -c "while sleep 0.01; do wget -q -O- http://php-apache; done"

     

    그리고 HPADeployment를 관찰해보자.

    $ kubectl get hpa -w
    NAME         REFERENCE               TARGETS         MINPODS   MAXPODS   REPLICAS   AGE
    php-apache   Deployment/php-apache   <unknown>/50%   1         10        1          72s
    php-apache   Deployment/php-apache   250%/50%        1         10        1          79s
    php-apache   Deployment/php-apache   250%/50%        1         10        4          80s
    php-apache   Deployment/php-apache   250%/50%        1         10        5          96s
    
    $ kubectl get deployment php-apache
    NAME         READY   UP-TO-DATE   AVAILABLE   AGE
    php-apache   5/5     5            5           115s

     

    "TARGET"이 250%가 되면서 "REPLICA" 개수가 계속 증가한다. 위는 내가 중간에 끊어서 명령어를 실행했기 때문에 5개밖에 안찍혔지만 실제로는 7개까지 Pod이 늘어났다. 이제 호출하는 Pod을 삭제해보자.

    $ kubectl delete pod load-generator
    pod "load-generator" deletedkubectl delete pod load-generator
    pod "load-generator" deleted

     

    이제 다시 HPADeployment를 관찰한다.

    $ kubectl get hpa -w
    NAME         REFERENCE               TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
    php-apache   Deployment/php-apache   60%/50%   1         10        5          2m33s
    php-apache   Deployment/php-apache   67%/50%   1         10        5          2m38s
    php-apache   Deployment/php-apache   67%/50%   1         10        7          2m52s
    php-apache   Deployment/php-apache   14%/50%   1         10        7          3m9s
    php-apache   Deployment/php-apache   0%/50%    1         10        7          3m39s
    php-apache   Deployment/php-apache   0%/50%    1         10        7          8m5s
    php-apache   Deployment/php-apache   0%/50%    1         10        2          8m20s
    php-apache   Deployment/php-apache   0%/50%    1         10        2          8m36s
    php-apache   Deployment/php-apache   0%/50%    1         10        1          8m51s
    
    $ kubectl get deployment php-apache
    NAME         READY   UP-TO-DATE   AVAILABLE   AGE
    php-apache   1/1     1            1           9m13s

     

    증가할 때는 진짜 빠르게 Pod개수를 오토 스케일링했는데 내려갈 때는 매우 천천히 개수가 준다. 아무래도 실제 서비스에는 이런 저런 상황으로 부하가 왔다 갔다 할 수 있으니 그것까지 고려해서 개수를 조정하는 듯 하다.

     

    HPA말고 VPA, CA를 하기 위해서는 각 클라우드 프로바이더에서 제공하는지 확인하고 해당 방법을 래퍼런스를 통해서 찾아야 한다. GKE에서 VPA, CA하는 방법은 아래 명령어들을 참고하라.

     

    vpa

    $ gcloud container clusters create CLUSTER_NAME \
        --enable-vertical-pod-autoscaling --cluster-version=VERSION

    ca

    $ gcloud container clusters create example-cluster \
      --num-nodes 2 \
      --zone us-central1-a \
      --node-locations us-central1-a,us-central1-b,us-central1-f \
      --enable-autoscaling --min-nodes 1 --max-nodes 4
Designed by Tistory.