スケジューリングを制御する
한국어 원문으로 표시합니다.
목표
파드가 어느 노드에 뜰지 결정하는 일곱 가지 수단을 전부 써 보고, 마지막에는 앞 단계에서 만든 클러스터 상태(cordon 된 노드, 테인트된 노드)를 고려해 배치를 설계합니다.
왜 중요한가
스케줄링은 CKA 에서 배점이 크지 않지만 트러블슈팅 문제의 절반이 사실 스케줄링 문제입니다. Pending 파드를 보고 filter 단계의 어느 조건에서 걸렸는지 읽어 낼 수 있어야 합니다.
특히 topologySpreadConstraints 는 오해가 많습니다. 편차를 계산할 때 쓰는 후보 도메인은 파드의 nodeAffinity/nodeSelector 를 통과하는 노드들입니다. 테인트는 기본적으로 무시하고 셉니다. 그래서 파드가 쓸 수 없는 노드가 0개짜리 도메인으로 남아 편차를 키우고, DoNotSchedule 이면 나머지 파드가 전부 Pending 이 되는 일이 흔합니다. 후보를 좁히는 열쇠가 노드 어피니티라는 걸 아는 것이 이 실습의 핵심입니다.
단계
- 네임스페이스
cka-sched를 만든다.lab-node-0에topology.kubernetes.io/zone=zone-a,lab-node-1에zone-b,lab-node-2에zone-c라벨을 붙이고,lab-node-0에 추가로disktype=ssd를 붙인다. 그 다음 파드ssd-pod(이미지nginx:1.27)를 nodeSelectordisktype=ssd로 만들어lab-node-0에 뜨게 한다. lab-node-2에disktype=hdd라벨을 붙이고, 파드affinity-required를 required 노드 어피니티(disktype In [hdd])만으로lab-node-2에 배치한다. nodeSelector 는 쓰지 않는다.- Deployment
pref(레플리카 2, 이미지nginx:1.27)를 만들고 preferred 노드 어피니티를 붙인다. weight 는 50, 조건은disktype In [ssd]. - Deployment
spread(레플리카 3, 파드 라벨app=spread)를 만들고, required 파드 안티어피니티를 붙인다. labelSelector 는app=spread, topologyKey 는topology.kubernetes.io/zone. - Deployment
drain-demo(레플리카 2, 이미지nginx:1.27)를 만든 뒤,lab-node-1을 cordon 하고 drain 해서 완전히 비운다.drain-demo는 다른 노드에서 2/2 로 돌아와야 한다. lab-node-2에 테인트workload=batch:NoSchedule을 건다. 파드batch-pod에 그 테인트를 견디는 톨러레이션과 nodeSelectordisktype=hdd를 주어lab-node-2에 배치한다.- PriorityClass
cka-high를 만든다. value 100000, globalDefault false, preemptionPolicyPreemptLowerPriority. 파드important에 이 클래스를 지정한다. - Deployment
evenly(레플리카 4, 파드 라벨app=evenly)를 만든다. topologySpreadConstraints 는 maxSkew 1, topologyKeytopology.kubernetes.io/zone, whenUnsatisfiableDoNotSchedule, labelSelectorapp=evenly. 여기에 required 노드 어피니티disktype In [ssd, hdd]와workload=batch:NoSchedule톨러레이션을 함께 붙여, 4개 파드가 두 존에 2:2 로 배치되게 한다.
참고
kubectl get pods -n cka-sched -o wide로 어느 노드에 떴는지 바로 볼 수 있습니다.- drain 은
--ignore-daemonsets --delete-emptydir-data --force를 붙여야 대개 끝까지 진행됩니다. - 흔한 실수 1: 5단계에서 cordon 만 하고 끝내는 것. cordon 은 새 파드만 막습니다.
- 흔한 실수 2: 8단계에서 노드 어피니티를 빼먹는 것. 그러면 cordon 된
lab-node-1이 파드 0개인 존으로 남아 편차가 2가 되고 절반이 Pending 에 머뭅니다.
노드 라벨과 nodeSelector
네임스페이스 cka-sched 를 만든다. lab-node-0 에 topology.kubernetes.io/zone=zone-a, lab-node-1 에 zone-b, lab-node-2 에 zone-c 라벨을 붙이고, lab-node-0 에 추가로 disktype=ssd 를 붙인다. 그 다음 파드 ssd-pod (이미지 nginx:1.27)를 nodeSelector disktype=ssd 로 만들어 lab-node-0 에 뜨게 한다.
nodeSelector 는 파드 스펙 최상위에 오는 맵입니다. 라벨이 정확히 일치해야 하며 표현식은 쓸 수 없습니다.
required 노드 어피니티
lab-node-2 에 disktype=hdd 라벨을 붙이고, 파드 affinity-required 를 required 노드 어피니티(disktype In [hdd])만으로 lab-node-2 에 배치한다. nodeSelector 는 쓰지 않는다.
affinity.nodeAffinity 아래 requiredDuringSchedulingIgnoredDuringExecution 안에는 nodeSelectorTerms 배열이 들어갑니다. matchExpressions 의 operator 는 In 입니다. nodeSelector 는 쓰지 마세요.
preferred 노드 어피니티
Deployment pref (레플리카 2, 이미지 nginx:1.27)를 만들고 preferred 노드 어피니티를 붙인다. weight 는 50, 조건은 disktype In [ssd].
preferred 쪽은 배열이고 각 항목이 weight 와 preference 를 갖습니다. 못 맞춰도 배치되므로 어디에 떴는지는 채점하지 않습니다.
파드 안티어피니티로 흩뿌리기
Deployment spread (레플리카 3, 파드 라벨 app=spread)를 만들고, required 파드 안티어피니티를 붙인다. labelSelector 는 app=spread, topologyKey 는 topology.kubernetes.io/zone.
podAntiAffinity 의 항목은 labelSelector 와 topologyKey 를 갖습니다. 노드마다 존 라벨이 다르니 존 단위로 흩으면 결과적으로 노드가 갈립니다.
노드 비우기
Deployment drain-demo (레플리카 2, 이미지 nginx:1.27)를 만든 뒤, lab-node-1 을 cordon 하고 drain 해서 완전히 비운다. drain-demo 는 다른 노드에서 2/2 로 돌아와야 한다.
cordon 은 새 파드만 막고 이미 뜬 파드는 그대로 둡니다. 실제로 비우려면 drain 이 필요하고, 데몬셋과 emptyDir 관련 옵션을 붙여야 진행됩니다.
테인트와 톨러레이션
lab-node-2 에 테인트 workload=batch:NoSchedule 을 건다. 파드 batch-pod 에 그 테인트를 견디는 톨러레이션과 nodeSelector disktype=hdd 를 주어 lab-node-2 에 배치한다.
테인트는 key=value:effect 형식입니다. 톨러레이션은 operator 를 Equal 로 두고 value 까지 맞춰야 합니다. 어느 노드로 갈지는 별도로 지정해야 합니다.
PriorityClass 붙이기
PriorityClass cka-high 를 만든다. value 100000, globalDefault false, preemptionPolicy PreemptLowerPriority. 파드 important 에 이 클래스를 지정한다.
PriorityClass 는 클러스터 스코프입니다. globalDefault 를 true 로 두면 클러스터 전체 파드에 영향을 주니 주의하세요. 파드에 이름만 적으면 spec.priority 는 자동으로 채워집니다.
종합: 남은 노드에 고르게 흩뿌리기
Deployment evenly (레플리카 4, 파드 라벨 app=evenly)를 만든다. topologySpreadConstraints 는 maxSkew 1, topologyKey topology.kubernetes.io/zone, whenUnsatisfiable DoNotSchedule, labelSelector app=evenly. 여기에 required 노드 어피니티 disktype In [ssd, hdd] 와 workload=batch:NoSchedule 톨러레이션을 함께 붙여, 4개 파드가 두 존에 2:2 로 배치되게 한다.
지금 이 클러스터에서 파드를 받을 수 있는 노드가 몇 개인지 먼저 세어 보세요. cordon 된 노드는 후보에서 빼야 하고, 테인트된 노드는 톨러레이션이 있어야 씁니다. 후보를 줄이는 열쇠는 노드 어피니티입니다.