[Kubernetes in Action] Advanced Scheduling

2022. 2. 28. 10:47DevOps/Docker & Kubernetes

 

 

 

Using taints and tolerations to repel pods from certain nodes

Taint는 "얼룩"이라는 뜻이고, Toleration은 "관용, 허용"등의 뜻을 지니고 있습니다. 따라서 Node에 Taint를 설정한다는 것은 노드에 "얼룩"을 묻힌다는 것을 의미하며 파드에 Toleration을 설정한다는 것은 "이 노드에 얼룩이 있는데 이거 눈감아 줘"라고 이야기하는 것을 의미합니다. 

 

Introducing taints and tolerations

Taint는 key, value, effect로 구성되며 describe에서 <key>=<value>:<effect>의 문법으로 표현됩니다. 실제로 describe node를 하면 description에 "Taints"라는 항목이 나오는데 여기서 어떻게 설정되어 있는지를 확인하였습니다. (실제로 Minikube(위)에서는 Node에 Taints가 설정되어 있지 않아서 다른 클러스터의 Taints가 설정된 노드(아래)도 같이 첨부하였습니다.)

 

 

 

key = maintanence, value = true, effect = NoSchedule의 문법으로 설정되어 있는 것을 확인할 수 있습니다. 이 maintenance Taints가 노드에 묻어있으므로 새로운 파드가 스케줄 되어야 할 때 이 Taints를 허용하지 않는다면, 즉 Tolerate 하고 않는다면 파드는 이 노드에 스케줄 될 수 없게 됩니다. 아래와 같이 describe 명령어를 통해 파드에 설정된 Toleration 값을 확인할 수 있습니다. 

 

 

 

 

Taint의 Effect는 다음과 같이 3가지가 있으며 각각 다음과 같은 기능을 합니다.

 

  • NoSchedule: 해당 Taint를 Tolerate하지 않는 파드들은 노드에 "새롭게" 스케줄링되지 못합니다.

  • PreferNoSchedule: 위의 NoSchedule과 거의 동일하지만 더 이상 해당 파드가 스케줄링 될 수 있는 노드가 없다면 이 노드에 스케줄링이 가능합니다.

  • NoExecute: 위의 2개의 옵션과 다르게 "현재 노드에서 실행중인" 파드들에 영향을 미칠 수 있는 옵션입니다. 해당 Taint를 Tolerate하지 않는 파드들은 전부 삭제됩니다.

 

 

 

Adding custom taints to a node

다음과 같은 커맨드로 노드에 Taint를 추가할 수 있습니다. 실제로 위에서 Taint가 <none>으로 되어 있었던 Minikube 노드에 다음과 같이 taint를 추가할 수 있습니다. 

kubectl taint node <NODE> <key>=<value>:<effect>

 

 

해당 노드에 NoSchedule이라는 Taint가 적용되었으므로 해당 Taint를 Tolerate하지 않는 기존 파드들은 스케줄 되지 않는 것을 확인할 수 있습니다. (다음과 같이 계속 Pending 상태로 머무르게 됩니다)

 

 

 

taint 명령어 제일 마지막에 하이픈(-)을 붙이면 해당 노드에 붙은 taint를 제거할 수 있습니다

 

 

Adding toleration to pods

위에서 minikube 노드에 custom-taint를 추가했더니 해당 노드에 파드가 추가되지 않는 것을 확인할 수 있습니다. (minikube cluster는 노드가 하나뿐이기 때문에 해당 노드에 스케줄 되지 못하면 Pending 상태로 남게 됩니다. 따라서 다음과 같이 파드에 Tolerate를 추가해서 해당 노드에 스케줄 될 수 있도록 변경합니다

 

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
  tolerations:
  - key: "custom-taint"
    value: "true"
    operator: "Equal"
    effect: "NoSchedule"

 

The default value for operator is Equal. - official docs

해당 파드를 다시 생성하면 아래와 같이 잘 스케줄되는 것을 확인할 수 있습니다.

 

 

 

Understanding what taints and tolerations can be used for

기본적으로 Taint와 Toleration은 모두 여러개 가질 수 있습니다. Taint의 경우 Key만 있고 Value는 별도로 없어도 괜찮은데, 공식문서에서 toleration을 설정할 때 operator 옵션을 "Exists"로 주는 경우(Default는 key와 value가 모두 일치하는지를 체크하는 "Equals") 별도로 value에 대한 체크가 없이 해당 Key가 존재하는지만 체크한다고 안내하고 있습니다. 따라서 해당 Taint를 설정할 때 Key값만 설정하고 toleration에서 operator:Exists로 설정해도 의도한 대로 스케줄링을 동작시킬 수 있습니다.

The default value for operator is Equal.
A toleration "matches" a taint if the keys are the same and the effects are the same, and:

the operator is Exists (in which case no value should be specified), or
the operator is Equal and the values are equal.

 

 

 

Using node affinity to attract pods to certain nodes

위에서 Taint와 Toleration이 특정 노드에 파드가 스케줄 되는 것을 "막기" 위해서 사용되었다면(얼룩이 있는 노드에는 스케줄 되지 않도록), 이번에 소개할 node affinity파드가 특정 노드에 스케줄 되도록 하기 위해서 사용하는 옵션입니다. "affinity"라는 뜻 자체가 "친밀도"를 의미하기 때문에 어떤 노드를 "선호할"것인지를 선택하는 옵션이라고 이해할 수 있습니다.

 

 

쿠버네티스 초기버전의 node-selector와 유사한 개념이라고 할 수 있으며 node-selector의 기능들에 추가적인 기능들을 제공하며 앞으로 node-selector는 deprecated 되고 node affinity를 사용해서 파드가 선호하는 노드를 선택할 수 있도록 한다고 합니다.

 

 

Specifying hard node affinity rules

기존에 node selector를 사용하면 다음과 같은 label이 붙어 있는 노드를 선택하도록 pod를 구성할 수 있었습니다. 

apiVersion: v1
kind: Pod
metadata:
  name: kubia-gpu
spec:
  nodeSelector:
    gpu: "true"
...

 

node affinity를 사용하면 다음과 같이 구성할 수 있습니다.

spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: gpu
            operator: In
            values:
            - "true"

 

동일한 기능을 하는데 비해 node affinity의 스펙이 조금 더 복잡한 것은 사실이지만, 이는 더 많은 기능과 커스텀을 제공하기 위한 스펙의 구체화 정도로 생각하시면 됩니다. 몇 가지 용어를 짚고 넘어가자면

 

  • requiredDuringSchedulingIgnoredDuringExecution
    • requiredDuringScheduling + IgnoredDuringExecution의 합으로 이해할 수 있습니다. 스케줄링 시에 필요한 Rule들을 정의하되 노드에서 이미 실행 중인 파드에는 영향을 주지 않는다는 의미입니다.
  • nodeSelectorTerms + matchExpressions
    • 노드의 label에서 일치하는 value를 찾으라는 의미입니다. 

 

이를 정리하면 해당 파드를 위와 같은 node affinity 구성으로 업데이트하면, 기존에 노드에 스케줄링되어있던 파드들을 제외하고 새롭게 노드에 스케줄링되는 파드들에 한해서 gpu = true라는 label이 적용된 노드에 해당 파드를 스케줄 하라는 의미로 이해할 수 있습니다.

 

 

 

 

 

 

 

Prioritizing nodes when scheduling a pod

기존 node selector보다 구성이 다소 복잡해졌지만, 그럼에도 node affinity를 사용하는 가장 큰 장점은 노드에 우선순위를 줄 수 있다는 점입니다. preferredDuringSchedulingIgnoredDuringExecution 필드를 통해서 이를 구성할 수 있습니다.

 

spec:
  affinity:
    nodeAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 80      
        preference:
          matchExpressions:
          - key: availability-zone
            operator: In
            values:
            - zone1
      - weight: 20
        preference:
          matchExpressions:
          - key: share-type
            operator: In
            values:
            - dedicated

 

여러개의 preference를 선언할 수 있고, 각각의 preference마다 weight를 줄 수 있는데, weight가 더 높은 preference에 더 높은 우선순위를 부여한다는 의미입니다. 실제로 위의 예제에서 availability-zone과 share-type의 preference가 있고, availability-zone의 preference가 4배 높은 상태이기 때문에 다음과 같은 두 노드에 대해서 availability-zone만 일치하는 노드가 더 높은 우선순위를 가지게 되는 것입니다.

 

  • aviliability-zone 일치, share-type 불일치

 

  • aviliability-zone 불일치 share-type 일치

 

 

 

 

 

Co-locating pods with pod affinity and anti-affinity

때로는 노드와 파드 사이의 affinity 이외에도 파드 끼리의 affinity가 필요할 때도 있습니다. 이를테면 프런트엔드 파드와 백엔드 파드가 같은 노드에 떠있다면 그렇지 않을 때보다 Network latency를 줄이는 효과를 낳을 수 있습니다. 쿠버네티스에서는 nodeAffinity 이외에도 podAffinity 속성을 지원하며, 이 속성을 통해서 서로 다른 파드를 같은 노드에 스케줄 할 수 있습니다.

 

Using inter-pod affinity to deploy pods on the same node

다음과 같이 backend라는 label을 가진 애플리케이션 파드를 하나 실행시켜보겠습니다.

kubectl run backend -l app=backend --image busybox -- sleep 999999

 

frontend 파드 5개를 배포하기 위해 다음과 같이 Deployment를 구성하겠습니다. 이 때 podAffinity의 labelSelector를 app=backend로 설정하면 해당 레이블을 가지고 있는 파드와 동일한 노드에 frontend 파드가 배포됩니다.

 

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: frontend
spec:
  replicas: 5
  template:
    ... 
    spec:
      affinity:
        podAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - topologyKey: kubernetes.io/hostname
            labelSelector:
              matchLabels:
                app: backend
    ...

 

이렇게 podAffinity를 통해 배포된 파드들은 일종의 참조 관계를 지니기 때문에 위의 상황에서 backend 파드를 지우고 동일한 파드를 다시 생성하면 frontend pod의 affinity설정을 보고 frontend pod의 노드와 동일한 노드에 스케줄 됩니다.

What’s interesting is that if you now delete the backend pod, the Scheduler will sched- ule the pod to node2 even though it doesn’t define any pod affinity rules itself (the rules are only on the frontend pods). This makes sense, because otherwise if the back- end pod were to be deleted by accident and rescheduled to a different node, the fron- tend pods’ affinity rules would be broken.

 

 

 

Deploying pods in the same rack, availability zone, or geographic region

topologyKey의 값들을 적절하게 설정하면 파드들을 동일한 rack, az, geographic region에 스케줄 하는 것이 가능합니다.

 

How topologyKey Works

스케줄러가 스케줄링을 수행할 때 우선적으로 파드의 podAffinity 설정을 확인합니다. 우선 labelSelector를 사용해서 파드의 목록을 조회하고, 해당 파드들이 띄워져 있는 노드를 모두 조사하여 노드의 여러 label들 중에서 topologyKey label에 대한 value가 같은 노드에 스케줄링하는 방식입니다.

 

아래의 예시에서 app=backend라는 Label을 가지고 있는 파드는 Node 12에 스케줄 되어 있습니다. 해당 노드(Node 12)는 topology key "rack"에 대해서 "rack2"라는 value를 가지고 있고, 따라서 rack=rack2 label이 붙어있는 Node 11 ~ Node 20에 Frontend 파드들이 배포될 수 있는 것입니다. 

 

 

Topology Key는 이런 식으로 동작하며 아래와 같이 사전에 약속되어 있는 topology Key를 사용하여 파드를 스케줄 할 수 있습니다.

 

  • failure-domain.beta.kubernetes.io/zone
    • Same Availibility Zone
  • failure-domain.beta.kubernetes.io/region
    • Same geographical region

 

 

Scheduling pods away from each other with pod anti-affinity

podAffinity 이외에 podAntiAffinity를 사용하면 두 파드가 서로 같은 노드에 스케줄링되는 것을 강제로 막을 수 있습니다. 파드에서 실행 중인 애플리케이션이 서로의 Performance에 영향을 주거나 고가용성을 위해 여러 AZ, Region에 파드를 띄우고 싶은 경우 사용할 수 있습니다.

 

spec:
  affinity:
    podAntiAffinity:     
      requiredDuringSchedulingIgnoredDuringExecution:
      - topologyKey: kubernetes.io/hostname
        labelSelector:
          matchLabels:
            app: frontend

 

 

 

 

Reference

https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/

 

Taints and Tolerations

Node affinity is a property of Pods that attracts them to a set of nodes (either as a preference or a hard requirement). Taints are the opposite -- they allow a node to repel a set of pods. Tolerations are applied to pods, and allow (but do not require) th

kubernetes.io

 

 

 

반응형