CKA를 준비해보자 9일차 - Node Selector and Node Affinity

0

CKA

목록 보기
9/43

Node Selector and Node Affinity

node selector와 node affinity 둘 다 pod를 특정 node로 지정하여 스케줄링하는 방법에 대한 것들이다. 먼저 node selector에 대해서 알아보도록 하자.

다음의 pod들과 node들이 있다고 하자. 이들은 서로 자원량이 다르기 때문에 크기에 비레하여 자원량이 다르다고 생각하면된다.

즉, pod는 크면 클수록 더 많은 자원을 요구하고, node는 크면 클수록 더 많은 이용 가능한 자원을 갖고 있는 것이다.

--pod1---   -pod2-
|       |   |    |
|       |   ------
|       |
---------

----Node1----  --Node2--
|           |  |       |
|           |  |       |
|           |  ---------
|           |
-------------

즉 pod1은 pod2보다 더 많은 자원량을 요구하고, node1은 node2보다 더 많은 가용 자원을 가지고 있다. 우리가 생각하기에는 pod1node1로 스케줄링되고 node2pod2가 스케줄링되는 것이 베스트로 보이지만, 실상은 그렇지 않을 수 있다.


----Node1----  --Node2--
|           |  |  pod1 |
|   pod2    |  |       |
|           |  ---------
|           |
-------------

막상 node1에 자원 요구량이 적은 pod2가 배치되어 node1의 자원량이 너무 많이 낭비되고, node2에는 자원 요구량이 많은 pod1이 배치되어 자원이 너무 부족해질 수 있다.

이러한 문제를 해결하기 위해서 pod를 특정 node에 배치되도록 지정할 수 있는데, 이전처럼 직접 nodeName으로 특정 node를 딱 지정하는 것이 아니라, label을 사용하여 node그룹을 지정할 수 있다.

이것이 nodeSelector이다. 가령 pod에서 nodeSelectorsize: Large로 쓰게된다면 size=Large label을 가진 node만이 pod를 스케줄링 할 수 있게 된다.

위의 예시를 들어보면 다음과 같다.

------pod1-------   -pod2-
| nodeSelector: |   |    |
|   size: Large |   |    |
|               |   ------
|               |
-----------------

----Node1----  --Node2--
| size=Large|  |       |
|           |  |       |
|           |  ---------
|           |
-------------

이러한 상태라면 pod1size: Large label을 가진 Node1에 스케줄링된다.

이렇게 nodeSelector를 사용하여 특정 label을 지정한 node에 배치될 수 있도록 만들 수 있다.

그럼 어떻게 pod에 nodeSelector를 지정할 수 있는가? 그건 pod를 생성하는 file에서 설정할 수 있다.

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

이제 data-processor pod는 nodeSelector를 통해서 size: Large label을 가진 node에 스케줄링되는 것이다.

그럼 어떻게 node에 label을 넣을 수 있는가?? node뿐만 아니라, 모든 kubernetes object에 label을 부여하는 방식은 다음과 같다.

kubectl label <object-type> <name> <key>=<value>

가령, nodenode1 node에 size=Large라는 label을 추가하고 싶다면 다음과 같다.

kubectl label nodes node-1 size=Large

node selector에는 몇 가지 한계가 있는데, 가장 큰 한계는 복잡한 node설정을 가질 때, 단순한 label지정 배치 이외에는 여러 기능이 없다는 것이다.

가령, node의 가용 자원량을 label로 표기하여 size: Large, size: Medium, size: Small이 있다고 하자. pod는 Small빼고는 모두 스케줄링이 가능하지만 nodeSelector로 설정할 때는 LargeMedium 둘 중 하나의 label을 선택해야한다.

이러한 문제를 바로 affinity가 해결해주는 것이다.

Node Affinity

node affinity를 사용하는 가장 큰 이유는 pod를 특정 node들에 스케줄링하도록 하기위함이다. 이는 node selector와 같은 목적이지만 node affinity를 사용하면 좀 더 복잡한 방식의 node들을 타겟으로 설정할 수 있다.

이전의 node selector를 쓴 yaml파일을 보도록 하자.

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

이 파일과 동일한 기능을 하는 node affinity가 바로 다음과 같다.

  • pod-definition.yaml
apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod
spec:
  containers:
  - name: data-processor
    image: data-processor

  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: size
            operator: In
            values:
            - Large 

두 yaml파일은 같은 기능을 하는 pod를 만든다. 즉, 둘 다 size=Large label을 가진 node에 해당하는 pod를 스케줄링하라는 것이다.

node affinity에 대해서 좀 더 자세히 알아보도록 하자. 가장 중요한 부분은 key, operator, values이다. 특히 operator: In이라는 것은 label의 key인 size에 해당하는 value로 values 리스트 중 하나만 맞아도 스케줄 후보 node에 들어간다는 것이다. 사실 나머지 부분들은 affinity에서 반복되는 부분이므로, 그냥 이렇게 쓰는 거구나라고 알고만 있으면 된다.

만약 해당 pod를 LargeMedium node에 스케줄링하고 싶다면 다음과 같이 쓸 수 있는 것이다.

...
spec:
  containers:
  - name: data-processor
    image: data-processor

  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: size
            operator: In
            values:
            - Large 
            - Medium

operator에는 In말고도 NotIn도 있어서 Small node말고 Large, Medium node만 가능하게 해주고 싶다면 다음과 같이 쓸 수 있다.

...
spec:
  containers:
  - name: data-processor
    image: data-processor

  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: size
            operator: NotIn
            values:
            - Small

또 다른 operator로는 Exists가 있는데, 이는 key가 있는 node에만 해당 pod를 스케줄링하겠다는 것이다. 즉, values는 뭐든 간에 중요하지 않고 key가 있냐없냐라는 것이다.

...
spec:
  containers:
  - name: data-processor
    image: data-processor

  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: size
            operator: Exists

다음의 경우 keysize가 label로 있는 지 없는 지만 검사하여 해당 pod를 스케줄링할 node를 선택한다. values를 써도되지만 어차피 비교가 없으므로 쓰지 않아도 된다.

이외에도 다양한 operator가 있으니 문서를 참조하여 보도록 하자.

Node Affinity Types

그런데 만약 affinity의 key-value label, operator 조건에 node들이 모두 맞지 않으면, 해당 pod는 아무런 스케줄링도 받지못해 pending되는 것인가??

또한, node affinity에 따라 pod가 특정 node에서 실행되고 있는데, node의 label이 바뀌어 버리면 실행되고 있던 pod는 어떻게되는가??

이러한 모든 상황에 대한 대처는, spec.affinity.nodeAffinity아래의 아주 긴 field에 따라 다르다. 이 부분이 바로 node affinity type인 것이다.

node affinity type은 두 가지 부분으로 나눌 수 있는데, 하나는 스케줄링 될 때(DuringScheduling)과 하나는 pod가 node에서 실행되고 있을 때(DuringExecution)이다. 이 두 가지 부분에 대한 해답은 각 부분의 앞에 적혀있다.

  1. Available

    1. requiredDuringSchedulingIgnoredDuringExecution: 필수적으로 node affinity에 맞게 node를 선택해 pod를 스케줄링하라는 것이다. 따라서, node affinity에 맞는 node가 없다면 pod는 스케줄링되지 않는다. 단, pod가 특정 node에서 실행되고 있을 때, label이 바뀌어 pod의 node affinity를 어겨도, 무시하고 pod는 계속 해당 node에서 실행된다.

    2. preferredDuringSchedulingIgnoredDuringExecution: node affinity에 맞게 node를 선택해 pod를 스케줄링하는 것을 선호하지만, node affinity에 맞는 node가 없다면 pod는 affinity에 맞지 않은 node라도 스케줄링된다. 단, pod가 특정 node에서 실행되고 있을 때, label이 바뀌어 pod의 node affinity를 어겨도, 무시하고 pod는 계속 해당 node에서 실행된다.

  2. Planned

    1. requiredDuringSchedulingRequiredDuringExecution: 필수적으로 node affinity에 맞게 node를 선택해 pod를 스케줄링하라는 것이다. 따라서, node affinity에 맞는 node가 없다면 pod는 스케줄링되지 않는다. 또한, pod가 특정 node에서 실행되고 있을 때, label이 바뀌어 pod의 node affinity를 어기면 실행되고 있던 pod는 evicted된다. 이후 node에서 빠져나와 terminated된다.

표를 그리면 다음과 같다.

DuringSchedulingDuringExecution
Type 1RequiredIgnored
Type 2PreferredIgnored
Type 3RequiredRequired

Type 3를 기준으로 위의 예제를 빌려보면 pod1affinitysize: Large를 가지고 있고, Node1이 label로 size=Large을 가지고 있으면 Node1pod1을 스케줄링 할 수 있다. 만약, Node1size=Large label이 없었다면 pod1은 스케줄링되지 못하고 pending된다.

Node1pod1이 스케줄링되어 실행되고 있다고 하자. 만약 Node1의 label 중 size=Label가 사라지면 Node1에서 실행 중이던 pod1evicted상태가 되고, Node1에서 빠져나와 종료된다.ㅈ

0개의 댓글