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과 다른 노드로 할당된다.
- PodAffinity - 집중
- 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에 파드를 할당한다.
- weight라는 속성을 지정해줘야 한다.
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