Infra/Kubernetes

Pod - Node Scheduling

최-코드 2025. 6. 19. 01:18
파드는 기본적으로 스케줄러에 의해서 노드에 할당이 되지만, 사용자의 의도에 의해서 내가 노드를 지정할 수도 있고 운용자가 특정 노드를 사용하지 못하도록 관리할 수 있다.

NodeName, NodeSelector, NodeAffinity, PodAffiniy, PodAntiAfinity, Taint, Toleration 모두 파드가 최초 노드를 선택할 때만 고려되는 조건이다.  즉, 이미 Pod가 노드에 안착되어 있는 상태에서 라벨을 바꾸거나 노드에 taint를 단다고 해서 파드가 삭제되거나 하지 않는다.

Node Scheduling 개요1

  • NodeName, NodeSelector, NodeAffinity는 파드를 특정 노드에 할당되도록 선택하기 위한 용도로 사용된다.
  • 1번의 경우 노드를 지정하지 않는 파드로, 스케줄러에 의해 CPU 가용 자원이 가장 많은 노드1에 파드를 할당시킨다.
  • 2번의 경우 NodeName을 지정했기 때문에, 스케줄러와는 상관없이 바로 해당 노드에 할당된다.
    • 노드가 추가되고 삭제되면서 노드 이름이 계속 변경되기 때문에 사용하지 않는 방식이다.
  • 따라서 특정 노드를 지정할 때 권장되는 건 NodeSelector이다. 3번 파드와 같이 key:value를 달아서 해당 key:value 라벨을 가진 노드에 할당된다.
    • 해당 라벨을 가진 노드가 하나라면 스케줄러를 거치지 않는다.
    • 같은 라벨을 가진 노드가 많이 있으면 스케줄러를 통해 가용 자원량이 많은 노드에 할당된다.
    • 문제점으로는, key:value에 해당하는 라벨을 가진 노드가 없으면 결국 어느 노드에도 할당이 안 되어서 에러가 발생한다.
  • NodeSelector의 문제점을 보완한 것이 NodeAffinity이다.
    • 파드에 key만 설정해도 해당 key를 가진 노드에 대해서 스케줄러를 통해 가용 자원이 많은 노드에 할당된다.
    • 파드에 지정한 key에 대해 노드가 존재할지라도, 스케줄러가 가용 자원이 많은 노드에 할당이 되도록 옵션을 줄 수 있다.

Node Scheduling 개요2

  • PodAffinity, PodAntiAnffinity는 여러 파드들을 한 노드에 집중해서 할당하거나 파드들 간에 겹치는 노드 없이 분산하도록 할당해줄 수 있다.
    • PodAffinity - 집중
      • PV가 hostPath를 쓴다고 했을 때, 파드1과 파드2는 같은 노드 상에 있어야만 문제가 없게 된다. 따라서 이 두 파드를 같은 노드에 할당하려면 PodAffinity를 사용해서 Pod간 집중을 해야 한다.
      • 자세한 동작은 아래와 같다.
        • 파드1이 스케줄러에 의해서 특정 노드(Node3)에 할당이 된다.
        • Node3에는 PV가 생긴다.
        • 파드2가 파드1에 있는 노드에 들어가게 하려면 파드2를 만들 때 PodAffinity 속성을 넣고 파드1에 있는 라벨을 지정해준다.
        • 파드2가 파드1이 있는 노드로 할당된다.
    •  PodAntiAffinity - 분산
      • Master 파드, 파드3이 죽으면 Slave 파드, 파드4가 백업을 해줘야 하는 관계일 때 이 두 파드가 같은 노드로 들어갈 경우 해당 노드가 다운되면 둘 다 장애가 나기 때문에 서로 다른 노드의 스케줄링되어야 한다.
      • 자세한 동작은 아래와 같다.
        • 파드3이 스케줄러에 의해서 특정 노드(Node3)에 할당이 된다.
        • 파드4가 파드3과 다른 노드에 들어가게 하려면 파드4를 만들 때 PodAntiAffinity 속성을 넣고 파드3에 있는 라벨을 지정해준다.
        • 파드4가 파드3과 다른 노드로 할당된다.
  • Toleration / Taint는 특정 노드에는 아무 파드나 할당이 되지 않도록 제한하기 위해서 사용하는 것이다.
    • 노드5는 높은 사양의 그래픽 카드를 요규하는 앱을 돌리는 용도로 GPU를 설정했을 때, 운영자는 Taint라는 것을 설정한다.
    • 일반적인 파드들은 스케줄러가 Taint가 설정된 노드로 할당시키지 않고, NodeName과 같이 직접 노드에 할당하려고 해도 안 된다.
    • Taint가 설정된 노드에 할당이 되려면 파드는 Toleration을 달고 와야지 할당이 된다.

NodeName, NodeSelector는 간단하므로 생략

NodeAffinity

  • matchExpressions
    • https://rosoa0475.tistory.com/497 - Select 참고 부분 참고
    • Gt, Lt가 추가 되었고, 단어 그대로 Gt는 values의 값보다 큰 노드를, Lt는 values의 값보다 작은 노드를 선택한다.
  • required vs preferred
    • required 속성을 가진 파드는 matchExpressions에 맞는 노드가 없다면, 어떤 노드라도 절대 스케줄링되지 않는다.
    • preferred 속성을 가진 파드는 matchExpressions에 맞는 노드가 있으면 선호를 할 뿐이지 그 노드에 할당되어야 하는 건 아니다.
      • weight라는 속성을 지정해줘야 한다.
        • 위와 같이 파드에 두 개의 preferred 옵션을 가진 NodeAffinity를 설정했을 때 1번은 10 weigth의 값을, 2번은 50 weight의 값을 할당했다고 가정하자.
        • 스케줄러는 두 노드에게 CPU 가용 자원량에 대해 각각 50점과 30점을 부여하고, weight의 값을 더해주어서 더 큰 값을 보이는 Node2에 파드를 할당한다.
apiVersion: v1
kind: Pod
metadata:
 name: pod-match-expressions1
spec:
 affinity:
  nodeAffinity:
   requiredDuringSchedulingIgnoredDuringExecution:   
    nodeSelectorTerms:
    - matchExpressions:
      -  {key: kr, operator: Exists}
 containers:
 - name: container
   image: kubetm/app
 terminationGracePeriodSeconds: 0
  • requiredDuringSchedulingIgnoredDuringExecution = required 옵션
apiVersion: v1
kind: Pod
metadata:
 name: pod-preferred
spec:
 affinity:
  nodeAffinity:
   preferredDuringSchedulingIgnoredDuringExecution:
    - weight: 1
      preference:
       matchExpressions:
       - {key: ch, operator: Exists}
 containers:
 - name: container
   image: kubetm/app
 terminationGracePeriodSeconds: 0

 

  • preferredDuringSchedulingIgnoredDuringExecution = preferred 옵션

PodAffinity & PodAntiAffinity

  • NodeAffinity와 다르게 matchExpressions에 들어가는 key, value는 파드의 라벨이다. + matchExpressions에 대한 쓰임과 형태는 동일하다.
  • PodAffinity & PodAntiAffinity에서도 NodeAffinity에서의 required와 preferred 옵션이 적용될 수 있다.
  • PodAffinity
    • web 파드가 Node1에 들어갔을 때, server 파드를 같은 노드에 넣으려면 podAffinity라는 속성의 matchExpressions에 web 파드의 key와 value를 넣어줘야 한다.
    • topologyKey에는 노드의 key를 지정해줘야 한다. 이는 해당 키를 가진 노드에만 이 파드를 할당하겠다는 의미이다.
    • 즉, topologyKey에서 지정한 key를 가진 노드 내에서만 matchExpressions와 일치하는 파드를 찾아서 파드를 할당하겠다는 내용이다.
    • topologyKey가 a-team의 값을 가진 상태에서 만약 web 파드가  b-team 키를 가진 노드에 할당되면 server 파드는 pending 상태가 되고 podAffinity 조건을 만족할 때까지 노드에 할당되지 않는다.
apiVersion: v1
kind: Pod
metadata:
 name: server1
spec:
 affinity:
  podAffinity:
   requiredDuringSchedulingIgnoredDuringExecution:   
   - topologyKey: a-team
     labelSelector:
      matchExpressions:
      -  {key: type, operator: In, values: [web1]}
 containers:
 - name: container
   image: kubetm/app
 terminationGracePeriodSeconds: 0
  • PodAntiAffinity
    • master 파드가 노드4에 할당됐을 때 이와 다른 노드에 할당하기 위해서 slave 파드에서는 podAntiAffinity를 달고 matchExpressions에 mater 파드의 key와 value를 넣어주면 된다.
    • PodAffinity와 마찬가지로 동일한 역할을 하는 topologyKey를 지정할 수 있다.  
apiVersion: v1
kind: Pod
metadata:
 name: slave
spec:
 affinity:
  podAntiAffinity:
   requiredDuringSchedulingIgnoredDuringExecution:   
   - topologyKey: a-team
     labelSelector:
      matchExpressions:
      -  {key: type, operator: In, values: [master]}
 containers:
 - name: container
   image: kubetm/app
 terminationGracePeriodSeconds: 0

Taint & Toleration

  • Taint
    • Taint는 파드가 아닌 노드에 다는 속성을 말한다.
    • Taint를 식별하는 라벨인 key와 value가 존재한다.
    • effect라는 옵션에 NoSchedule 값을 주면 타 파드들이 이 노드에 할당이 되지 않는다.
      • preferNoSchedule: 가급적 스케줄링 안 되도록 하는 값으로, 파드가 정 다른 노드에 배치될 수 없는 상황이면 Taint가 붙은 노드라도 할당을 허용해준다.
      • noExecute: noSchedule과 달리 파드1이 노드2에 할당된 상태에서 taint를 달면 맨 위에서 말했듯이 삭제되지 않는다. 하지만 effect의 값이 noExecute이면 이미 Pod가 안착되어 있는 상태이어도 삭제시킨다. 
    • 마스터 노드에는 Taint가 자동으로 설정되고, 이때 effect는 NoSchedule이다.
    • 노드에 장애가 발생하면 쿠버네티스는 해당 노드에 있는 파드들이 정상적으로 동작하지 않을 수도 있기 때문에 noExecute 옵션을 가진 Taint를 장애가 발생한 노드에 달아준다. 그러면 파드는 삭제되고, 만약 replicaSet을 사용하여 파드를 관리할 때 새로운 노드에 파드가 할당된다.
      • 노드가 정상 상태가 되면 NodeLifecycleController에 의해 Taint가 제거된다.
kubectl taint nodes k8s-node1 hw=gpu:NoSchedule
#key = hw, value = gpu, effect = NoSchedule
  •  Toleration
    • Taint가 붙은 노드에 할당이 되도록 파드에 설정해줘야 하는 것이다.
    • key, operator, value, effect가 존재하고, 이 조건이 모두 Taint의 내용과 맞아야 된다.
      • operator에는 Equal과 Exists만 있다.
      • effect 중 noExecute를 설정할 때 tolerationSeconds를 설정하지 않으면 노드에서 파드가 제거되는 일은 없다. 하지만 tolerationSeconds를 설정하면 설정한 값만큼 노드내에서 존재하다가 삭제된다. ( Taint가 할당된 시점부터 카운트)
    • Toleration이 붙은 파드는 Taint가 달린 노드를 찾아서 스케줄링되는 게 아니다.
      • 그저 Taint가 붙은 노드에 스케줄링되었을 때 이 노드에 할당될 수 있는지에 대한 조건이 부합되는 것 뿐이다. 
      • 따라서 다른 노드에도 스케줄링이 될 수 있다.
      • 이를 방지하기 위해서는 nodeSelector를 달아서 파드가 해당 노드에만 배치될 수 있도록 해줘야 한다.
apiVersion: v1
kind: Pod
metadata:
 name: pod-with-toleration
spec:
 nodeSelector:
  gpu: no1
 tolerations:
 - effect: NoSchedule
   key: hw
   operator: Equal
   value: gpu
 containers:
 - name: container
   image: kubetm/app
 terminationGracePeriodSeconds: 0