研发工程师玩转Kubernetes——使用Node特性定向调度Pod

文章介绍了如何在Kubernetes中使用nodeSelector和requiredDuringSchedulingIgnoredDuringExecution进行Pod的定向调度。通过设置Node的Labels和调整Deployment的清单文件,确保Pod只部署在具有特定Label的MasterNode上。这有助于优化资源分配和集群管理。

《研发工程师玩转Kubernetes——使用污点(taint)驱逐Pod》中我们提到亲和性(affinity)中的requiredDuringSchedulingIgnoredDuringExecution,它可以定向调度Pod。本节我们将使用相关特性完成定向调度的介绍。

nodeSelector定向调度

我们先模拟Pod被部署在Master Node上的场景。这个时候我们可以使用nodeSelector对Node的Labels做选择。
首先我们查看当前的Node的情况:

查看Node的Labels

get nodes --show-labels
NAME      STATUS   ROLES    AGE   VERSION   LABELS
ubuntuc   Ready    <none>   16m   v1.26.4   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=ubuntuc,kubernetes.io/os=linux,microk8s.io/cluster=true,node.kubernetes.io/microk8s-worker=microk8s-worker
ubuntue   Ready    <none>   15m   v1.26.4   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=ubuntue,kubernetes.io/os=linux,microk8s.io/cluster=true,node.kubernetes.io/microk8s-worker=microk8s-worker
ubuntud   Ready    <none>   15m   v1.26.4   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=ubuntud,kubernetes.io/os=linux,microk8s.io/cluster=true,node.kubernetes.io/microk8s-worker=microk8s-worker
ubuntub   Ready    <none>   16m   v1.26.4   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=ubuntub,kubernetes.io/os=linux,microk8s.io/cluster=true,node.kubernetes.io/microk8s-worker=microk8s-worker
ubuntua   Ready    <none>   20m   v1.27.2   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=ubuntua,kubernetes.io/os=linux,microk8s.io/cluster=true,node.kubernetes.io/microk8s-controlplane=microk8s-controlplane

可以看到只有Master Node(UbuntuA)的Labels含有node.kubernetes.io/microk8s-controlplane=microk8s-controlplane,我们就用这个去做条件选择。

清单文件指定Label

# nginx_deployment.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx-container
        image: nginx
        ports:
        - containerPort: 80
      nodeSelector:
        node.kubernetes.io/microk8s-controlplane: microk8s-controlplane

注意最后两行,即使用nodeSelector让Pod在Labels含有node.kubernetes.io/microk8s-controlplane: microk8s-controlplane的Node上部署。

NodeSelector map[string]string `json:"nodeSelector,omitempty"`

部署

针对上述文件部署Pod

kubectl create -f nginx_deployment.yaml

deployment.apps/nginx-deployment created

kubectl get pods -o wide
NAME                                READY   STATUS    RESTARTS   AGE   IP           NODE      NOMINATED NODE   READINESS GATES
nginx-deployment-5d5bc8fb96-75c9d   1/1     Running   0          67s   10.1.94.67   ubuntua   <none>           <none>

可以看到Pod被部署到Master Node上了。

requiredDuringSchedulingIgnoredDuringExecution定向调度

requiredDuringSchedulingIgnoredDuringExecution是亲和性(affinity)下的一种类型,它表示:只有在规则被满足的时候才能执行调度。
我们修改下清单文件,让Pod只能被调度到Master Node(UbuntuA)上。

清单文件指定亲和性

# nginx_deployment.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: node.kubernetes.io/microk8s-controlplane
                operator: In
                values:
                - microk8s-controlplane
      containers:
      - name: nginx-container
        image: nginx
        ports:
        - containerPort: 80

nodeSelectorTerms只能是一组matchExpressions。

type NodeAffinity struct {
	// If the affinity requirements specified by this field are not met at
	// scheduling time, the pod will not be scheduled onto the node.
	// If the affinity requirements specified by this field cease to be met
	// at some point during pod execution (e.g. due to a node label update),
	// the system will try to eventually evict the pod from its node.
	RequiredDuringSchedulingRequiredDuringExecution *NodeSelector  `json:"requiredDuringSchedulingRequiredDuringExecution,omitempty"`
	// If the affinity requirements specified by this field are not met at
	// scheduling time, the pod will not be scheduled onto the node.
	// If the affinity requirements specified by this field cease to be met
	// at some point during pod execution (e.g. due to a node label update),
	// the system may or may not try to eventually evict the pod from its node.
	RequiredDuringSchedulingIgnoredDuringExecution  *NodeSelector  `json:"requiredDuringSchedulingIgnoredDuringExecution,omitempty"`
	// The scheduler will prefer to schedule pods to nodes that satisfy
	// the affinity expressions specified by this field, but it may choose
	// a node that violates one or more of the expressions. The node that is
	// most preferred is the one with the greatest sum of weights, i.e.
	// for each node that meets all of the scheduling requirements (resource
	// request, RequiredDuringScheduling affinity expressions, etc.),
	// compute a sum by iterating through the elements of this field and adding
	// "weight" to the sum if the node matches the corresponding MatchExpressions; the
	// node(s) with the highest sum are the most preferred.
	PreferredDuringSchedulingIgnoredDuringExecution []PreferredSchedulingTerm  `json:"preferredDuringSchedulingIgnoredDuringExecution,omitempty"`
}

// A node selector represents the union of the results of one or more label queries
// over a set of nodes; that is, it represents the OR of the selectors represented
// by the nodeSelectorTerms.
type NodeSelector struct {
	// nodeSelectorTerms is a list of node selector terms. The terms are ORed.
	NodeSelectorTerms []NodeSelectorTerm `json:"nodeSelectorTerms,omitempty"`
}

// An empty node selector term matches all objects. A null node selector term
// matches no objects.
type NodeSelectorTerm struct {
	// matchExpressions is a list of node selector requirements. The requirements are ANDed.
	MatchExpressions []NodeSelectorRequirement `json:"matchExpressions,omitempty"`
}

// A node selector requirement is a selector that contains values, a key, and an operator
// that relates the key and values.
type NodeSelectorRequirement struct {
	// key is the label key that the selector applies to.
	Key string `json:"key" patchStrategy:"merge" patchMergeKey:"key"`
	// operator represents a key's relationship to a set of values.
	// Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.
	Operator NodeSelectorOperator `json:"operator"`
	// values is an array of string values. If the operator is In or NotIn,
	// the values array must be non-empty. If the operator is Exists or DoesNotExist,
	// the values array must be empty. If the operator is Gt or Lt, the values
	// array must have a single element, which will be interpreted as an integer.
    // This array is replaced during a strategic merge patch.
	Values []string `json:"values,omitempty"`
}

// A node selector operator is the set of operators that can be used in
// a node selector requirement.
type NodeSelectorOperator string

const (
	NodeSelectorOpIn           NodeSelectorOperator = "In"
	NodeSelectorOpNotIn        NodeSelectorOperator = "NotIn"
	NodeSelectorOpExists       NodeSelectorOperator = "Exists"
	NodeSelectorOpDoesNotExist NodeSelectorOperator = "DoesNotExist"
	NodeSelectorOpGt           NodeSelectorOperator = "Gt"
	NodeSelectorOpLt           NodeSelectorOperator = "Lt"
)

观察

get pod --watch -o wide
NAME                                READY   STATUS    RESTARTS   AGE   IP       NODE     NOMINATED NODE   READINESS GATES
nginx-deployment-549db84c68-gk66n   0/1     Pending             0          0s      <none>        <none>    <none>           <none>
nginx-deployment-549db84c68-gk66n   0/1     Pending             0          0s      <none>        ubuntua   <none>           <none>
nginx-deployment-549db84c68-gk66n   0/1     ContainerCreating   0          0s      <none>        ubuntua   <none>           <none>
nginx-deployment-549db84c68-gk66n   0/1     ContainerCreating   0          1s      <none>        ubuntua   <none>           <none>
nginx-deployment-549db84c68-gk66n   1/1     Running             0          4s      10.1.94.80    ubuntua   <none>           <none>

可以看到Pod是在Worker Node(UbuntuA)上部署着。

参考资料

<think>首先,用户的问题是关于Kubernetes nodeSelector配置后节点无法调度的可能原因。用户引用了几个站内引用,这些引用讨论了Kubernetes调度策略,如NodeSelector和NodeAffinity。我需要基于这些引用和我的知识来回答。 关键点: - 用户的问题:nodeSelector配置后节点无法调度。 - 我需要帮助用户逐步排查原因。 - 回答必须结构清晰,使用中文。 - 遵循系统级指令:数学表达式用$...$,独立公式用$$...$$(但在这个上下文中可能不涉及),LaTeX语法正确,生成相关问题。 回顾引用内容: - [^1]: 解释了NodeSelector和NodeAffinity的基础概念。 - [^2]: 讨论了nodeSelector用于定向调度,确保Pod调度到指定节点。 - [^3]: 描述了如何给节点打标签和使用nodeSelector。 - [^4]: 提供了一个YAML示例,使用nodeSelector指定节点标签。 - [^5]: 讨论了nodeSelectorTerms和matchExpressions,强调所有条件必须满足。 可能的原因: 1. **节点标签不存在或错误**:如果节点没有打上指定的标签,或者标签拼写错误,Pod无法调度。 2. **标签值不匹配**:nodeSelector中指定的标签值必须与节点上的标签完全匹配(包括大小写)。 3. **节点资源不足**:即使标签匹配,节点可能没有足够的资源(CPU、内存)来调度Pod。 4. **节点状态问题**:节点可能不是Ready状态,或者有污点(Taint)阻止调度。 5. **Pod配置错误**:YAML文件中nodeSelector部分可能有语法错误或格式问题。 6. **调度器问题**:Kubernetes调度器可能有问题,或者配置了其他调度约束(如亲和性、反亲和性)。 7. **命名空间问题**:Pod和节点在不同命名空间,但标签是集群范围的。 8. **标签未正确应用**:标签可能没有应用到节点上,或者应用后需要时间传播。 9. **其他调度策略干扰**:如果同时配置了nodeSelector和nodeAffinity,可能有冲突。 10. **Pod优先级和抢占**:高优先级Pod可能抢占资源。 基于引用: - 从[^3]:节点必须打上标签,PodnodeSelector必须匹配。 - 从[^4]:YAML示例展示了nodeSelector的使用。 - 从[^5]:强调了所有条件必须满足。 回答结构: 1. **介绍问题**:简要解释nodeSelector的作用和常见问题。 2. **逐步排查原因**:列出可能的原因,并解释如何检查。 3. **提供解决方案建议**:如何修复每个问题。 4. **生成相关问题**:在末尾添加相关问题。 确保回答真实可靠:基于Kubernetes文档和最佳实践。 最终输出格式: - 使用中文。 - 结构清晰:分点列出原因。 - 在相关地方添加引用标识,例如[^1]。 - 末尾添加相关问题。</think>### Kubernetes nodeSelector 配置后节点无法调度的排查指南 在 Kubernetes 中,`nodeSelector` 是一种简单有效的定向调度策略,它允许您通过节点标签(Label)将 Pod 绑定到特定节点[^3]。然而,如果配置后 Pod 无法调度到目标节点,通常是由于标签不匹配、节点状态或配置错误导致的。以下我将基于 Kubernetes 调度机制和常见生产问题,逐步分析可能的原因及排查方法。问题排查需结合 `kubectl` 命令验证,确保回答真实可靠。 #### 可能原因及排查步骤 1. **节点标签不存在或与 nodeSelector 不匹配** - **原因**:`nodeSelector` 要求 Pod 中指定的标签键值必须与节点上的标签完全一致(包括大小写)。如果节点未打标签、标签键值拼写错误,或标签值不匹配,调度器会忽略该节点[^2][^3]。 - **排查方法**: - 检查节点标签:`kubectl get nodes --show-labels`,确认目标节点是否有 `nodeSelector` 中定义的标签。 - 示例:如果您的 Pod 配置了 `nodeSelector: { k8s: node1 }`,但节点标签为 `k8s: Node1`(大小写不一致),则无法匹配[^4]。 - 修复:重新打标签,例如 `kubectl label nodes <node-name> k8s=node1 --overwrite`[^3]。 2. **节点资源不足或状态异常** - **原因**:即使标签匹配,节点可能因资源不足(CPU、内存)、磁盘压力或状态非 Ready 而无法调度 PodKubernetes 调度器会优先考虑节点可用性[^1][^5]。 - **排查方法**: - 查看节点状态:`kubectl describe node <node-name>`,检查 `Conditions` 部分是否显示 `MemoryPressure` 或 `DiskPressure`。 - 验证资源请求:确保 Pod 的 `resources.requests` 不超过节点可用资源(例如,Pod 请求 2Gi 内存,但节点仅剩 1Gi)。 - 修复:扩容节点资源或减少 Pod 资源请求。 3. **节点污点(Taint)阻止调度** - **原因**:节点可能被设置了污点(如 `NoSchedule`),这会阻止未定义容忍(Toleration)的 Pod 调度。污点与 `nodeSelector` 独立作用,可能覆盖标签匹配[^1][^5]。 - **排查方法**: - 检查节点污点:`kubectl describe node <node-name>`,查找 `Taints` 字段。 - 示例:如果节点有 `key=value:NoSchedule`,Pod 必须配置 `tolerations` 才能调度。 - 修复:在 Pod Spec 中添加 `tolerations` 或移除节点污点。 4. **Pod 配置错误或 YAML 语法问题** - **原因**:`nodeSelector` 字段语法错误(如缩进问题、键值格式错误)会导致调度器忽略该配置。此外,如果同时使用了 `nodeName`(直接指定节点名),它会覆盖 `nodeSelector`[^4][^5]。 - **排查方法**: - 检查 Pod YAML:`kubectl get pod <pod-name> -o yaml`,确认 `nodeSelector` 格式正确(例如,键值对需用引号包裹)。 - 参考正确示例: ```yaml spec: nodeSelector: k8s: node1 # 键值对必须与节点标签匹配[^4] containers: - name: nginx image: nginx ``` - 修复:修正 YAML 语法,避免使用 `nodeName` 除非必要。 5. **其他调度策略冲突** - **原因**:如果同时配置了亲和性(Affinity)或反亲和性(Anti-Affinity),这些策略优先级高于 `nodeSelector`,可能导致冲突[^1][^5]。例如,`nodeAffinity` 的 `requiredDuringScheduling` 规则未满足时,Pod 无法调度。 - **排查方法**: - 检查 Pod 配置:`kubectl describe pod <pod-name>`,查看 `Events` 部分是否有调度失败事件(如 "0/3 nodes are available: 3 node(s) didn't match Pod's node affinity")。 - 修复:简化调度策略,或确保 `affinity` 规则与 `nodeSelector` 兼容。 6. **标签未同步或调度器延迟** - **原因**:新打标签后,Kubernetes API 服务器可能需要几秒到几分钟同步。在此期间,调度器可能无法识别新标签[^3]。 - **排查方法**: - 等待并重试:执行 `kubectl label` 后,延迟 1-2 分钟再创建 Pod。 - 验证标签传播:`kubectl get nodes -l k8s=node1`(替换为您的标签),确认节点列表正确。 - 修复:避免频繁修改标签,或在部署脚本中添加等待逻辑。 #### 通用排查工具 - **查看调度事件**:`kubectl describe pod <pod-name>`,关注 `Events` 部分,错误消息如 "no nodes available to schedule pods" 会指明具体原因。 - **检查调度器日志**:`kubectl logs -n kube-system kube-scheduler-<pod-id>`,分析调度决策细节。 - **模拟调度测试**:使用 `kubectl create -f pod.yaml --dry-run=server` 验证配置。 如果以上步骤未解决问题,可能是集群级问题(如 kube-scheduler 故障),建议检查集群组件状态[^1]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

breaksoftware

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值