DevOps/Kubernetes

[Kubernetes] Pod Scheduling ( + kube-scheduler, nodeName, nodeSelector)

TTOII 2022. 6. 5. 10:00
728x90

🚀 kube-scheduler

쿠버네티스 스케줄러 | Kubernetes

kube-scheduler는 파드나 컨트롤러를 만들었을 때 또는 스케줄링을 할 때 파드를 어디에 배치시킬 것인지 결정한다.

kube-scheduler는 각 노드를 필터링하고 스코어링한다.

 

필터링은 어떤 노드의 어떤 리소스를 볼 것인가 하는 기준을 정하는 것이고

스코어링은 해당 기준에 따라서 가중치를 두며 이에 기반해 점수를 매긴다.


즉, 노드가 여러개 있는 경우 각 노드마다 점수를 매겨서 파드를 만들었을 때

파드를 어디에 배치할 것인지 결정하는 것이 kube-scheduler가 하는 역할이다.

필터링, 스코어링을 어떻게 할 것인지 구성할 수 있다.

 

🚀 nodeName

노드에 파드 할당하기 | Kubernetes
nodeName은 가능하면 사용하지 않는 것을 권장한다.
nodeName은 실질적으로 스케줄링을 사용하지 않는다.

kube-scheduler의 영향을 받지 않고 특정 노드에 강제로 배치할 수 있다.

 

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - name: nginx
    image: nginx
  nodeName: kube-01

파드 선언시 spec.nodeName이라는 필드가 있다.
이 필드를 지정하면 해당 파드는 지정한 노드에만 배치된다.
즉, 스케줄링의 필터링, 스코어링을 무시하고 강제로 배치시킬 수 있다.

 

vagrant@k8s-node1  ~  kubectl explain pod.spec.nodeName  
KIND:     Pod
VERSION:  v1

FIELD:    nodeName <string>

 vagrant@k8s-node1  ~  kubectl explain rs.spec.template.spec.nodeName
KIND:     ReplicaSet
VERSION:  apps/v1

FIELD:    nodeName <string>

 vagrant@k8s-node1  ~  kubectl explain deploy.spec.template.spec.nodeName
KIND:     Deployment
VERSION:  apps/v1

FIELD:    nodeName <string>

Pod, RS, Deploy, STS에 모두 nodeName 필드가 존재한다.

 

🚀 실습

myweb-rs-nn.yaml

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: myweb-rs-nn
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      nodeName: node2
      containers:
        - name: myweb
          image: ghcr.io/c1t1d0s7/go-myweb
 vagrant@k8s-node1  ~/schedule/nodename  kubectl get po -o wide
NAME                                      READY   STATUS    RESTARTS   AGE     IP              NODE    NOMINATED NODE   READINESS GATES
myweb-rs-nn-82lxr                         1/1     Running   0          33s     10.233.96.137   node2   <none>           <none>
myweb-rs-nn-mcgxp                         1/1     Running   0          33s     10.233.96.135   node2   <none>           <none>
myweb-rs-nn-szphk                         1/1     Running   0          33s     10.233.96.136   node2   <none>           <none>
nfs-client-provisioner-758f8cd4d6-wpjbt   1/1     Running   0          2d19h   10.233.92.110   node3   <none>           <none>

고가용성을 위해 복제본을 구성했지만 nodeName설정 때문에 노드 3개 모두가 node2에 배치되었다.

노드 장애가 발생하면 해당 App은 더이상 실행할 수 없다.

replicas가 1개라면 어떤 노드에 배치시킬 것인지를 결정할 때 사용할 수는 있지만

기본적으로 여러개의 복제본을 구성하는 경우 장점이 없다.

 

🚀 nodeSelector

필요한 특정 구성이 있는 경우 특정 노드 그룹을 만들어서 배치시킬 수 있다.
노드에 레이블을 적절하게 붙여서 특정 파드를 원하는 위치에만 배치시킬 수 있다.

 vagrant@k8s-node1  ~/cont/ds  kubectl get ds                  
NAME       DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE
myweb-ds   3         3         2       3            2           <none>          4s

NODE SELECTOR 항목을 볼 수 있다.
데몬셋의 경우 NODE SELECTOR를 많이 사용하는 워크로드 중 하나이다.
그렇지만 데몬셋만이 NODE SELECTOR를 사용할 수 있는 것은 아니다.

 

 vagrant@k8s-node1  ~/schedule/nodename  kubectl explain pod.spec.nodeSelector 
KIND:     Pod
VERSION:  v1

FIELD:    nodeSelector <map[string]string>

 vagrant@k8s-node1  ~/schedule/nodename  kubectl explain deploy.spec.template.spec.nodeSelector
KIND:     Deployment
VERSION:  apps/v1

FIELD:    nodeSelector <map[string]string>

 vagrant@k8s-node1  ~/schedule/nodename  kubectl explain sts.spec.template.spec.nodeSelector
KIND:     StatefulSet
VERSION:  apps/v1

FIELD:    nodeSelector <map[string]string>

 vagrant@k8s-node1  ~/schedule/nodename  kubectl explain rc.spec.template.spec.nodeSelector
KIND:     ReplicationController
VERSION:  v1

FIELD:    nodeSelector <map[string]string>

모든 컨트롤러는 nodeSelector라는 구성이 존재한다. 노드에 부여된 노드의 레이블과 매칭시킨다.

 

 

📌 노드 레이블 확인하기

kubectl get nodes --show-labels

 

node1

beta.kubernetes.io/arch=amd64
beta.kubernetes.io/os=linux
kubernetes.io/arch=amd64
kubernetes.io/hostname=node1
kubernetes.io/os=linux
node-role.kubernetes.io/control-plane=
node-role.kubernetes.io/master=
node.kubernetes.io/exclude-from-external-load-balancers=
node-role.kubernetes.io/control-plane=
node-role.kubernetes.io/master=
node.kubernetes.io/exclude-from-external-load-balancers=

해당 내용은 컨트롤 플레인에서만 확인할 수 있다.
클라우드 환경에서는 각 클라우드에서 필요한 노드 레이블이 기본적으로 붙어있다.

 

node2

beta.kubernetes.io/arch=amd64
beta.kubernetes.io/os=linux
kubernetes.io/arch=amd64
kubernetes.io/hostname=node2
kubernetes.io/os=linux

 

node3

beta.kubernetes.io/arch=amd64
beta.kubernetes.io/os=linux
kubernetes.io/arch=amd64
kubernetes.io/hostname=node3
kubernetes.io/os=linux

 

📌 노드에 레이블 부여하기 

 vagrant@k8s-node1  ~/schedule/nodename  kubectl label node node1 gpu=highend 
node/node1 labeled
 vagrant@k8s-node1  ~/schedule/nodename  kubectl label node node2 gpu=midrange
node/node2 labeled
 vagrant@k8s-node1  ~/schedule/nodename  kubectl label node node3 gpu=lowend  
node/node3 labeled

 vagrant@k8s-node1  ~/schedule/nodename  kubectl get nodes -L gpu           
NAME    STATUS   ROLES                  AGE     VERSION   GPU
node1   Ready    control-plane,master   10d     v1.22.8   highend
node2   Ready    <none>                 10d     v1.22.8   midrange
node3   Ready    <none>                 7d20h   v1.22.8   lowend

명령형 커맨드를 이용해 노드에 레이블을 부여할 수 있다.
-L : key를 지정해서 필드로 볼 수 있다.

 

myweb-rs-ns.yaml

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: myweb-rs-ns
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      nodeSelector:
        gpu: lowend
      containers:
        - name: myweb
          image: ghcr.io/c1t1d0s7/go-myweb
 vagrant@k8s-node1  ~/schedule/nodeselector  kubectl get po -o wide            
NAME                                      READY   STATUS    RESTARTS   AGE     IP              NODE    NOMINATED NODE   READINESS GATES
myweb-rs-ns-gzbwx                         1/1     Running   0          4s      10.233.92.157   node3   <none>           <none>
myweb-rs-ns-hcc5c                         1/1     Running   0          4s      10.233.92.156   node3   <none>           <none>
myweb-rs-ns-v28cd                         1/1     Running   0          4s      10.233.92.158   node3   <none>           <none>
nfs-client-provisioner-758f8cd4d6-wpjbt   1/1     Running   0          2d19h   10.233.92.110   node3   <none>           <none>

모두 node3에 배치된 것을 볼 수 있다. 노드에 셀렉팅되는 것이 1개밖에 없기 때문이다.

 

 vagrant@k8s-node1  ~/schedule/nodeselector  kubectl label node node2 gpu=lowend --overwrite
node/node2 labeled

 vagrant@k8s-node1  ~/schedule/nodeselector  kubectl get nodes -L gpu                       
NAME    STATUS   ROLES                  AGE     VERSION   GPU
node1   Ready    control-plane,master   10d     v1.22.8   highend
node2   Ready    <none>                 10d     v1.22.8   lowend
node3   Ready    <none>                 7d20h   v1.22.8   lowend

 vagrant@k8s-node1  ~/schedule/nodeselector  kubectl delete po -l app=web 
pod "myweb-rs-ns-gzbwx" deleted
pod "myweb-rs-ns-hcc5c" deleted
pod "myweb-rs-ns-v28cd" deleted

 vagrant@k8s-node1  ~/schedule/nodeselector  kubectl get po -o wide      
NAME                                      READY   STATUS    RESTARTS   AGE     IP              NODE    NOMINATED NODE   READINESS GATES
myweb-rs-ns-m2cjh                         1/1     Running   0          8s      10.233.96.140   node2   <none>           <none>
myweb-rs-ns-pf6dj                         1/1     Running   0          8s      10.233.96.139   node2   <none>           <none>
myweb-rs-ns-vq44h                         1/1     Running   0          8s      10.233.92.159   node3   <none>           <none>
nfs-client-provisioner-758f8cd4d6-wpjbt   1/1     Running   0          2d19h   10.233.92.110   node3   <none>           <none>

node2의 gpu를 lowend로 바꾸고 app이 web인 파드를 전부 삭제하면 파드가 재생성되고 다시 스케줄링이 된다.

node2번과 node3번에 걸쳐 파드가 배치되는 것을 볼 수 있다.

 

스케줄러는 우리가 지정한 노드의 레이블을 확인하고 어떻게 분산할 것인지 결정한다.

따라서 gpu가 lowend가 아닌 node1은 배치의 대상이 아니다.

 

 

📌 사용 사례

상용환경에서 4개의 노드 중 2개에만 gpu 인스턴스가 있다고 하자

 

인공지능 모델링 같은 고성능 컴퓨팅을 요구하는 작업을 할 때

nodeSelector를 지정하지 않고 그래픽카드가 없는 다른 노드에 배치시키면 작업이 매우 느리게 진행된다.

 

gpu 인스턴스는 매우 비싸기 때문에 gpu:on 같은 레이블을 달아놓고

리소스를 만들 때 nodeSelector를 이용하여 해당 레이블이 있는 노드에만 배치시킬 수 있다.

 

ARM CPU를 가진 노드 2개와 X86 CPU를 가진 노드 2개가 있을 때

사용할 이미지가 특정 CPU에서만 돌아가는 이미지라면 ARM CPU에 배치시켰을 때 실행 자체가 안된다.


kubernetes.io/arch=amd64라고 하는 셀렉터를 부여해서 적절하게 배치될 수 있도록 구성할 수 있다.

AWS의 경우 nodeSelector를 사용해 원하는 가용영역에 배치시킬 수 있다.

728x90