简介
调度器通常会从所有符合条件的节点中任选一个。标签和亲和性用于表达工作负载的偏好,而污点则允许节点拒绝缺少匹配容忍度的 Pod。
你将逐步为预先准备好的节点应用这些控制机制,并通过 Pod 事件区分强制调度规则和软性偏好。
可以用下面的思路理解这些机制:标签描述节点,选择器和亲和性表达 Pod 的需求,污点表达节点拒绝的对象,而容忍度则允许 Pod 通过匹配的拒绝规则。它们都不会直接「移动」已经运行中的 Pod。
为节点添加放置标签
在本步骤中,为节点添加描述性标签,并将这些标签显示为表格列。
这些标签特意用于描述工作负载的放置条件,而不是应用身份。workload-zone=local 表示位置,disk-class=fast 表示节点能力。Kubernetes 不会验证这些标签的业务含义;真正赋予它们意义的是你编写的调度规则。
cd /home/labex/project/pod-scheduling
kubectl create namespace practice-scheduling
kubectl label node labex-v135 workload-zone=local disk-class=fast
kubectl get nodes -L workload-zone,disk-class
标签是供选择器和亲和性规则使用的事实信息;除非控制器创建替代 Pod,否则它们不会自动移动已经存在的 Pod。
-L 选项会为每个标签键添加一列。在节点行中看到 local 和 fast,说明调度器能够评估后续步骤中使用的规则。
NAME STATUS ROLES ... VERSION WORKLOAD-ZONE DISK-CLASS
labex-v135 Ready control-plane ... v1.35.5 local fast
这也确认升级后的环境正在运行 Kubernetes v1.35.5。刚刚添加的标签提供了后面两列自定义信息。
使用 nodeSelector 要求节点
在本步骤中,创建一个使用最简单精确匹配规则来指定放置位置的 Pod。
nodeSelector 是一组必需的键值匹配条件。只有当某个节点的每个标签键都具有请求的值时,Pod 才能调度到该节点。对于简单的强制放置需求,这种方式非常合适。
cd /home/labex/project/pod-scheduling
cat <<'EOF' > selected-pod.yaml
apiVersion: v1
kind: Pod
metadata: {name: selected-pod, namespace: practice-scheduling}
spec:
nodeSelector: {disk-class: fast}
containers:
- {name: worker, image: busybox:1.36, imagePullPolicy: IfNotPresent, command: ["sh", "-c", "while true; do sleep 30; done"]}
EOF
应用 Pod,等待其就绪,并查看 NODE 列中的分配节点:
kubectl apply -f selected-pod.yaml
kubectl wait --for=condition=Ready pod/selected-pod -n practice-scheduling --timeout=120s
kubectl get pod selected-pod -n practice-scheduling -o wide
nodeSelector 是强制条件:如果没有节点具备所有请求的标签,Pod 就会保持 Pending 状态。
选中的节点应为 labex-v135。调度成功表明节点上的 disk-class=fast 与 Pod 的要求相匹配。
pod/selected-pod condition met
NAME READY STATUS ... NODE
selected-pod 1/1 Running ... labex-v135
NODE 列显示的是调度结果;清单中的 nodeSelector 则说明该节点为什么符合放置条件。
对比必需亲和性和首选亲和性
在本步骤中,创建一个必须位于本地分区、同时优先使用高速磁盘的 Pod。
节点亲和性比 nodeSelector 更灵活。必需部分会排除不匹配的节点;首选部分则会为匹配的节点增加权重,但不会把这一偏好变成强制条件。
较长的字段名体现了规则的生效时机:规则只在调度期间评估,Pod 运行后不再据此执行驱逐操作。如果标签之后发生变化,Kubernetes 不会仅仅因为这条亲和性规则而驱逐正在运行的 Pod。
cd /home/labex/project/pod-scheduling
cat <<'EOF' > affinity-pod.yaml
apiVersion: v1
kind: Pod
metadata: {name: affinity-pod, namespace: practice-scheduling}
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions: [{key: workload-zone, operator: In, values: [local]}]
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
preference:
matchExpressions: [{key: disk-class, operator: In, values: [fast]}]
containers:
- {name: worker, image: busybox:1.36, imagePullPolicy: IfNotPresent, command: ["sh", "-c", "while true; do sleep 30; done"]}
EOF
应用 Pod 并等待调度完成:
kubectl apply -f affinity-pod.yaml
kubectl wait --for=condition=Ready pod/affinity-pod -n practice-scheduling --timeout=120s
kubectl get pod affinity-pod -n practice-scheduling -o wide
必需规则用于筛选符合条件的节点;首选规则则为筛选后剩余的节点进行评分。IgnoredDuringExecution 表示标签后续发生变化时,不会因此驱逐正在运行的 Pod。
最终结果表中同样会显示 labex-v135。由于这是一个只有一个节点的集群,必需的 workload-zone=local 规则决定了节点是否可用;首选的高速磁盘规则则展示了评分语法,而无需专门增加第二个节点进行比较。
使用污点和容忍度
在本步骤中,为节点添加污点,观察没有容忍度的 Pod 保持 Pending 状态,然后创建一个允许调度的 Pod。
该污点的键为 practice-reserved,值为 true,效果为 NoSchedule。这种效果会阻止没有匹配容忍度的新 Pod 调度到该节点,但不会驱逐已经在节点上运行的 Pod。
kubectl taint node NODE key=value:effect 会将这条规则附加到节点上。下面的 kubectl run 会创建一个没有容忍度的独立 Pod;等待其 PodScheduled 条件时,调度器会在限定的 10 秒内展示预期的失败结果。
cd /home/labex/project/pod-scheduling
kubectl taint node labex-v135 practice-reserved=true:NoSchedule
kubectl run blocked-pod -n practice-scheduling --image=busybox:1.36 --image-pull-policy=IfNotPresent --restart=Never -- sh -c 'sleep 3600'
kubectl wait --for=condition=PodScheduled pod/blocked-pod -n practice-scheduling --timeout=10s || true
kubectl events -n practice-scheduling --for pod/blocked-pod
等待超时是预期结果,|| true 可以让实验继续执行。调度事件中应提到未被容忍的污点,从而说明 Pod 为什么处于 Pending 状态。
error: timed out waiting for the condition on pods/blocked-pod
LAST SEEN TYPE REASON OBJECT MESSAGE
... Warning FailedScheduling Pod/blocked-pod 0/1 nodes are available: 1 node(s) had untolerated taint(s)...
这里的 Pending 是正确的实验结果。调度器已经评估了该 Pod,但找不到允许它使用的节点。
创建一个匹配的容忍度。容忍度允许 Pod 越过污点进行调度,但它本身并不能选择节点。
cat <<'EOF' > tolerated-pod.yaml
apiVersion: v1
kind: Pod
metadata: {name: tolerated-pod, namespace: practice-scheduling}
spec:
nodeSelector: {workload-zone: local}
tolerations:
- {key: practice-reserved, operator: Equal, value: "true", effect: NoSchedule}
containers:
- {name: worker, image: busybox:1.36, imagePullPolicy: IfNotPresent, command: ["sh", "-c", "while true; do sleep 30; done"]}
EOF
kubectl apply -f tolerated-pod.yaml
kubectl wait --for=condition=Ready pod/tolerated-pod -n practice-scheduling --timeout=120s
kubectl delete pod blocked-pod -n practice-scheduling
这个 Pod 同时配置了 nodeSelector,因此一个规则负责选择节点,另一个规则负责允许 Pod 调度到该节点。删除有意创建的阻塞 Pod 后,命名空间会恢复到干净、健康的状态。
pod/tolerated-pod condition met 表明添加匹配的容忍度改变了调度结果。之前的 Pod 仍会继续运行,因为 NoSchedule 只影响新的放置操作,不会驱逐已经运行的 Pod。
总结
你使用标签、nodeSelector、必需和首选亲和性、调度器事件、污点以及容忍度,分别表达了工作负载的放置意图和节点的接纳条件。


