본문 바로가기
K8S/Scheduling

Node Selectors & Node Affinity⭐⭐⭐

by HH_g 2024. 1. 19.

Node Selector 와 Node Affinity를 사용하여 파드가 특정 노드에만 실행되도록 제한을 설정할 수 있습니다.

특정한 노드들에서만 동작하거나 특정한 노드 집합에서 동작하는 것을 선호하도록 제한 설정이 가능합니다.

1. Node Selector

파드의 spec 섹션node selector라는 새 필드를 추가하여 간단하고 쉽게 설정이 가능합니다.

node selector에서 레이블을 사용하므로 파드 생성 전에 우선 노드에 레이블을 지정해 줘야 한다.

설정한 노드 이름을 key-value 형식으로 사용하여 node selector에서 설정할 수 있습니다.

apiVersion: v1
kind: Pod
metadata:
	name: myapp-pod
spec:
	containers:
		- name: data-processor
			image: data-processor
	nodeSelector:
		size: Large

2. Node Affinity

node selector와 비슷하게 노드의 레이블을 기반으로 파드를 스케쥴링합니다.

node selector와 함께 설정할 수 있으며, 이 때는 둘 모두의 조건을 만족하는 노드에 파드를 스케쥴링합니다.

쿠버네티스에서 affinity는 일반적으로 파드가 특정 유형의 노드와 더 많이 조우하도록 만드는 데 사용되는 메커니즘을 나타냄.

Node Affinity 는 더 세세하게 노드에 제한을 설정해 줄 수 있습니다.

파드가 어떤 노드에 배치되기를 선호하는지를 정의하는 데 사용되며 특정 노드에 스케쥴링 되도록 도와줍니다.

apiVersion: v1
kind: Pod
metadata:
	name: myapp-pod
spec:
	containers:
		- name: data-processor
			image: data-processor
	affinity:
		nodeAffinity:
			requireDuringSchedulingIgnoreDuringExecution:
				nodeSelectorTerms:
					- matchExpressions:
						- key: size
							operator: In
							values:
								- Large

Affinity type - 두 가지 필드 존재

Availbale

Pod 가 처음 만들어질 때를 생각해 봅시다.

matching label의 노드가 존재하지 않을 때, required/preferred 에 따라 동작이 달라집니다.

  1. requiredDuringSchedulingIgnoredDuringExecution 
    • 스케쥴링하는 동안 꼭 필요한 조건입니다.
    • 해당 조건에 만족하지 못하면 파드 배정이 되지 않고 pending 상태에 머무릅니다.
    • 파드의 위치가 중요한 경우에 사용합니다.
    ...중략...
    affinity:
      nodeAffinity:
        **requiredDuringSchedulingIgnoredDuringExecution:**
          nodeSelectorTerms:
          - matchExpressions:
            - key: disktype
              operator: In
              values:
              - ssd
    

preferredDuringSchedulingIgnoredDuringExecution 

  • 지정한 조건을 가장 잘 만족시키는 노드에 스케쥴링을 시도합니다.
  • 조건을 만족하지 않은 노드에도 스케쥴링 가능합니다.
    • 이런 경우 스케쥴러는 node affinity rule을 무시하고 가능한 아무 노드에 파드를 배정하므로, 파드의 위치가 별로 중요하지 않은 경우에 사용한다.
...생략
affinity:
  nodeAffinity:
		preferredDuringSchedulingIgnoredDuringExecution:
		- weight: 10
		  preference:
		  - matchExpressions:
		    - key: disktype
		      operator: In
		      values:
		      - hdd

requiredDuringSchedulingIgnoredDuringExecution 필드와 설정이 비슷합니다.

다른 점은 weight 필드가 있다는 점과 nodeSelectorTerms 대신 preference 필드를 사용한다는 점.

 

  • weight 필드는 1 - 100 까지의 값 설정 가능. 여러 개 matchExpressions 필드 안 설정 각각이 노드의 설정과 맞을 때마다 weight 필드 값을 더한다. 그리고 모든 노드 중에서 weight 필드 값의 합계가 가장 큰 노드를 선택함.
  • preference 필드의 조건에 따라 맞는 노드를 우선해서 선택하지만 없다면 없는 대로 조건에 맞지 않는 노드에 파드를 스케쥴링하여 실행함.

ignored during execution : 파드가 배정되어서 running 중일 때 변경사항이 생겨도 영향을 받지 않음.

 

Planned

  1. requiredDuringScheduliingRequiredDuringExecution

만약 running 중에 변경이 생기면 해당 pod는 terminated 혹은 evicted(제거) 됩니다.

Operator

operator는 key가 만족할 조건입니다.

node selector나 noce affinity 등에서 사용되는 비교 연산자를 나타냅니다.

이 연산자를 사용하여 라벨 또는 필드의 값을 비교하여 해당 조건이 참인지 여부를 결정합니다.

  • Equal: 값이 정확하게 일치하는지 확인합니다.
  • NotEqual: 값이 일치하지 않는지 확인합니다.
  • Exists: 지정된 키 또는 필드가 존재하는지 확인합니다. 존재하는 경우에만 파드가 스케쥴링됩니다.
  • DoesNotExist: 지정된 키 또는 필드가 존재하지 않는지 확인합니다.
  • Gt (Greater Than): 값이 지정된 값보다 큰지 확인합니다.
  • Lt (Less Than): 값이 지정된 값보다 작은지 확인합니다.
  • In: 값이 지정된 값 목록 중 하나와 일치하는지 확인합니다.
  • NotIn: 값이 지정된 값 목록 중 어느 것과도 일치하지 않는지 확인합니다.

 

 

예시

Node Affinity 예시 - requiredDuringSchedulingIgnoredDuringExecution

[k8s] 파드 스케쥴링 - Node Affinity(노드 어피니티) (tistory.com)

 

1) 노드의 레이블 확인

kubectl get nodes --show-labels

 

2) 노드에 레이블 추가

kubectl label nodes k8s-node1 disktype==ssd

 

3) 노드에 레이블 추가 확인

kubectl get nodes --show-labels

 

4) 노드 어피니티를 사용하여 파드 스케쥴링

nginx 이미지를 disktype==ssd 라는 label을 가진 노드에 스케쥴링하도록 하는 manifest 파일 정의

# pod-nginx-required-affinity.yaml
apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  # affinity
  affinity:
    nodeAffinity:
      # required
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        # disktype이 ssd인 node에 파드 생성
        - matchExpressions:
          - key: disktype
            operator: In
            values:
            - ssd            
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
kubectl apply -f pod-nginx-required-affinity.yaml

 

 

k8s-node1에 disktype=ssd라는 레이블을 추가하였기 때문에 nginx pod가 k8x-node1에 할당됩니다.

 

5) 만약 disktype=hdd라는 레이블이 있는 노드에 추가하도록 변경한다면?

affinity:
    nodeAffinity:
      # required
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        # disktype이 ssd인 node에 파드 생성
        - matchExpressions:
          - key: disktype
            operator: In
            values:
            - hdd

 

 

pod가 pending 상태에서 시작되지 못하는 것을 확인할 수 있습니다.

 

describe명령어로 파드의 상태를 출력하면,

 

FailedScheduling 이라는 reason으로 파드가 스케쥴링되지 못했다는 것을 알 수 있다.

오류 메세지: 0/3 nodes are available: 1 node(s) had taint {node-role.kubernetes.io/master: }, that the pod didn't tolerate, 2 node(s) didn't match Pod's node affinity/selector

 

해석하자면 노드가 3개 있는데 1개는 taint로 허용되지 않는 노드(master node) 2개는 pod의 affinity selector와 일치하지 않아서 파드가 생성되지 않았다는 뜻입니다.

 

6) requiredDuringSchedulingIgnoredDuringExecution 필드에서 preferredDuringSchedulingIgnoredDuringExecution으로 변경

# pod-nginx-required-affinity.yaml
apiVersion: v1
kind: Pod
metadata:
  name: nginx2
spec:
  # affinity
  affinity:
    nodeAffinity:
      # preferred
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 10
        preference:
          matchExpressions:
          - key: disktype
            operator: In
            values:
            - hdd
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent

 

 

적용 후 확인하면 두 개의 파드 모두 생성되는 것을 확인할 수 있습니다.

 

Set Node Affinity to the deployment to place the pods on node01 only.

edit 명령어로 deployment 에 node affinity 필드를 추가합니다.

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: blue
spec:
  replicas: 3
  selector:
    matchLabels:
      run: nginx
  template:
    metadata:
      labels:
        run: nginx
    spec:
      containers:
      - image: nginx
        imagePullPolicy: Always
        name: nginx
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: color
                operator: In
                values:
                - blue

중요) spec.template.spec 에 존재해야합니다.

 

어떤 리소스 던지 affinity는 pod의 스펙 정의하는 곳에 넣어야 함.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: red
spec:
  replicas: 2
  selector:
    matchLabels:
      run: nginx
  template:
    metadata:
      labels:
        run: nginx
    spec:
      containers:
      - image: nginx
        imagePullPolicy: Always
        name: nginx
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: node-role.kubernetes.io/control-plane
                operator: **Exists**

여기서 operator가 equal이 아니고 Exists 인 이유:

해당 노드에 다양한 라벨이 있을 수 있기 때문입니다.

Taints and Tolerations VS Node Affinity ⭐⭐⭐

Taints and Toleration만 사용하여 각각의 색깔에 맞는 노드에 파드를 배정하려고 했을 때 Toleration을 가진 파드는 taints 가 없는 노드에도 배정될 수 있는 위험이 존재하고,

Node Affinity 만을 사용할 때는 노드에 원하지 않는 파드가 배정될 수 있는 위험이 존재합니다.

 

이 때 두 가지 방법을 모두 사용하여 원하는 노드에 원하는 파드만 배정할 수 있습니다.

먼저 노드에 taint 를 부여하여 해당하는 색(레이블)만 배정될 수 있게 하고 파드에 node affinity를 정해주면 해당하는 색의 노드에 해당하는 색의 파드만 배정할 수 있을 것이다.

 

 

 

참고

Certified Kubernetes Adiminstrtor(CKA) with Practice Tests by Mumshad Mannambeth, KodeKloud Training

'K8S > Scheduling' 카테고리의 다른 글

Daemon Sets  (0) 2024.01.19
[Scheduling] Resource Requirements and Limits  (0) 2024.01.19
Taints and Tolerations⭐⭐⭐  (0) 2024.01.18
[Scheduling] Pod Scheduling Process, nodeName  (0) 2024.01.18
Labels & Selectors  (1) 2024.01.18