nodeaffinity 必须写在 pod template spec 中,即 spec.template.spec.affinity.nodeaffinity 下;硬约束用 requiredduringschedulingignoredduringexecution,软约束用 preferred;操作符需用 corev1 枚举值;标签须真实存在且节点 ready;go 服务应校验硬件依赖并配合节点标签设计。

nodeAffinity 必须写在 Pod template spec 里,不是 Deployment spec
很多人在写 Deployment YAML 时,把 affinity 放在 spec 顶层,结果调度完全没生效。Kubernetes 只认 spec.template.spec.affinity —— 也就是 Pod 模板内部的 spec。Deployment 控制器会把这个字段透传给它创建的每个 Pod。
常见错误写法:spec.affinity(直接挂在 Deployment 下)→ 被忽略,无报错,但节点亲和不生效。
正确位置是:spec.template.spec.affinity,且必须嵌套在 nodeAffinity 下。
RequiredDuringSchedulingIgnoredDuringExecution 是硬约束,别误用成 preferred
如果你要强制 Pod 只跑在带 disktype=ssd 标签的节点上,就得用 RequiredDuringSchedulingIgnoredDuringExecution。一旦匹配失败,Pod 就卡在 Pending 状态,不会退而求其次。
软约束(PreferredDuringSchedulingIgnoredDuringExecution)只影响打分排序,不保证结果,适合“尽量但不强求”的场景。
- 硬约束:用于隔离环境(如 GPU 节点、安全加固节点)、资源强依赖(如本地 NVMe 盘)
- 软约束:用于性能优化(如优先调度到低负载节点),但不能替代硬隔离
- operator 必须用
corev1.NodeSelectorOpIn、corev1.NodeSelectorOpExists等枚举值,不能写字符串 "In"
标签必须真实存在,且节点已就绪,否则 Pending 无提示
假设你在 nodeAffinity 里写了 key: "region", operator: "In", values: ["cn-north-1"],但集群里没有任何节点有这个标签,或者有标签的节点处于 NotReady 状态,Pod 就会永远卡在 Pending。kubectl describe pod 只会显示 0/5 nodes are available,不会告诉你是因为标签不匹配。
验证方法:
- 先运行
kubectl get nodes --show-labels,确认目标标签确实存在且拼写一致(大小写敏感) - 再查节点状态:
kubectl get nodes -o wide,确保 Ready 状态为True - 如果用 client-go 动态构造,记得调用
clientset.CoreV1().Nodes().List()做预检,而不是只靠 apply 后看结果
Go 服务镜像本身不用改,但启动逻辑要配合亲和性设计
节点亲和性是调度层行为,跟 Go 代码无关。但实际部署中容易踩坑的点在于:你的 Go 服务假设了某些节点特性(比如挂载了特定 hostPath、或依赖某块 GPU),却没在亲和性里声明对应标签,导致 Pod 调度到不兼容节点上,启动就 panic。
建议做法:
- 在
Dockerfile或启动脚本里加简单校验,比如检查/proc/sys/fs/aio-max-nr或nvidia-smi是否存在,失败则 exit 1,触发就绪探针失败和重启 - 把硬件依赖项转为节点标签(如
gpu.present=true、nvme.disk=available),再通过nodeAffinity绑定,比在 Go 里硬编码设备路径更可靠 - 避免在 Go 代码里读取
/sys/class/dmi/id/product_name这类物理信息做分支逻辑——调度器看不见这些,无法提前决策
最常被忽略的是:亲和性规则写对了,但节点标签是手动打的,没随节点生命周期自动同步。比如自动伸缩组扩容的新节点没打标签,就会漏掉调度约束。这类问题只能靠 Operator 或 Cluster Autoscaler 的 node-labeling 插件补全,不是改几行 Go 代码能解决的。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











