当前位置: 首页 > news >正文

k8s(四)Kubernetes 集群调度

文章目录

  • 前言
  • 一、Kubernetes 集群调度
    • 1.1 Kubernetes 组件协作机制
      • 1.1.1 关键组件关系
    • 1.2 Pod 创建与工作机制流程
      • 1.2.1 典型创建过程(List-Watch 模型)
        • 1.2.1.1 三大组件启动监听(List-Watch)
        • 1.2.1.2 用户创建 Pod 对象
        • 1.2.1.3 API Server 将 Pod 信息写入 etcd
        • 1.2.1.4 etcd 通知事件(Create)
        • 1.2.1.5 Controller Manager 监听到 Pod 创建事件
        • 1.2.1.6 Replication Controller(RC)/ ReplicaSet 保证副本数
        • 1.2.1.7 API Server 更新 etcd(记录详细信息)
        • 1.2.1.8 etcd 触发 Pod 信息更新事件
        • 1.2.1.9 Scheduler 监听到待调度的 Pod
        • 1.2.1.10 Scheduler 更新调度结果
        • 1.2.1.11 etcd 确认更新 & API Server 同步结果
        • 1.2.1.12 kubelet 在 Node 上拉取并运行容器
        • 1.2.1.13 API Server 更新 Pod 状态
    • 1.3 Scheduler 过程(调度器)
      • 1.3.1 核心任务
      • 1.3.2 调度目标
    • 1.4 调度流程
      • 1.4.1 过滤阶段(Predicate)
      • 1.4.2 优选阶段(Priorities)
    • 1.5 指定调度节点方式
      • 1.5.1 nodeName(强制绑定)
      • 1.5.2 nodeSelector(基于标签匹配)
        • 1.5.2.1 管理 Node 标签命令
    • 1.6 节点亲和性与 Pod 亲和性
      • 1.6.1 节点亲和性(NodeAffinity)
        • 1.6.1.1 案例1 硬策略
        • 1.6.1.2 案例2 软策略
        • 1.6.1.3 案例3 软硬策略结合
      • 1.6.2 Pod 亲和性与反亲和性
        • 1.6.2.1 案例1 Pod 亲和性调度
        • 1.6.2.2 案例2 Pod 反亲和性调度
    • 1.7 污点(Taint) 和 容忍(Tolerations)
      • 1.7.1 污点(Taint)
      • 1.7.2 容忍 (Tolerations)
    • 1.8 节点维护操作
      • 1.8.1 cordon & drain
    • 1.9 Pod 生命周期(Phase)
    • 1.10 故障排查命令
    • 1.11 总结
      • 1.11.1 核心组件协作关系图
      • 1.11.2 核心调度策略层次
      • 1.11.3 思维总结
  • 总结
    • 1. 底层基石:组件协作与 List-Watch 机制
    • 2. 调度策略:从“强制绑定”到“柔性偏好”的分层设计
    • 3. 运维保障:从状态监控到节点维护


前言

在 Kubernetes(简称 K8s)集群管理中,“调度”是决定 Pod 如何合理分配到节点、保障集群资源高效利用与业务稳定运行的核心机制。无论是小规模测试集群还是大规模生产集群,能否通过调度策略实现“Pod 到节点”的精准匹配,直接影响集群的资源利用率、业务可用性与运维效率。

例如,如何让核心业务 Pod 优先调度到性能更强的节点?如何避免不同业务的 Pod 因资源争抢导致故障?如何在节点维护时安全迁移 Pod 而不中断服务?这些问题的答案,都隐藏在 K8s 调度体系的设计逻辑中。

本文聚焦 K8s 集群调度核心内容,从组件协作的底层机制(List-Watch)切入,逐步拆解 Pod 创建的完整流程、Scheduler 调度算法的过滤与优选逻辑,再到节点绑定、亲和性/反亲和性、污点与容忍等实用策略,最后补充节点维护与故障排查方法。文中包含大量可直接复用的配置示例与命令,旨在帮助读者从“理解原理”到“落地实践”,全面掌握 K8s 调度的核心能力,为集群资源管理与业务部署提供清晰指引。

一、Kubernetes 集群调度

1.1 Kubernetes 组件协作机制

Kubernetes 通过 List-Watch 机制使各个组件协作、数据同步,从而实现解耦与实时一致性。

1.1.1 关键组件关系

组件职责
kubectl / API 客户端向 APIServer 发起资源创建或管理请求
APIServer负责 API 调用、权限校验、存储交互,是集群控制的核心入口
etcd存储集群所有状态信息
Controller Manager维持副本数、执行自愈逻辑(扩容、重建等)
Scheduler调度器,将未分配节点的 Pod 分配到合适的 Node
kubelet节点代理,负责 Pod 生命周期管理和容器运行状态上报

用户通过 kubectl 根据配置文件,向 APIServer 发送命令,在 Node 节点上面建立 Pod 和 Container。APIServer 经过 API 调用,权限控制,调用资源和存储资源的过程,实际上还没有真正开始部署应用。这里需要 Controller Manager、Scheduler 和 kubelet 的协助才能完成整个部署过程。

在 Kubernetes 中,所有部署的信息都会写到 etcd 中保存。实际上 etcd 在存储部署信息的时候,会发送 Create 事件给 APIServer,而 APIServer 会通过监听(Watch)etcd 发过来的事件。其他组件也会监听(Watch)APIServer 发出来的事件。

1.2 Pod 创建与工作机制流程

Pod 创建的整个生命周期由多组件配合完成:

1.2.1 典型创建过程(List-Watch 模型)

在这里插入图片描述

1.2.1.1 三大组件启动监听(List-Watch)
  • Controller Manager、Scheduler、kubelet 启动后会分别通过 Watch API Server(HTTPS 6443 端口)监听集群资源事件变化。
    • Controller Manager:监听副本控制类对象(如 ReplicaSet、Deployment)
    • Scheduler:监听未调度的 Pod
    • kubelet:监听分配到本节点的 Pod
1.2.1.2 用户创建 Pod 对象
  • 用户通过 kubectl 或其他 API 客户端发送创建 Pod 的请求给 API Server
    示例:
    kubectl apply -f pod.yaml
    
1.2.1.3 API Server 将 Pod 信息写入 etcd
  • API Server 校验请求后,将 Pod 的元数据存入 etcd
  • 写入成功后返回确认给客户端。
1.2.1.4 etcd 通知事件(Create)
  • etcd 接受到 Pod 信息后,触发 Create 事件。
  • 该事件被发送给 API Server
1.2.1.5 Controller Manager 监听到 Pod 创建事件
  • Controller Manager 通过 Watch 机制收到 API Server 发出的 Pod 创建事件。
1.2.1.6 Replication Controller(RC)/ ReplicaSet 保证副本数
  • Controller Manager 在接到 Create 事件以后,如果发现副本数量不足,则由 RC/ReplicaSet 创建所需副本。
  • 扩容、缩容操作也都由此机制控制。
1.2.1.7 API Server 更新 etcd(记录详细信息)
  • Controller Manager 创建完 Pod 副本后,API Server 会将 Pod 的详细信息更新写入 etcd。
    • 包括副本数量、副本模板、容器规格等。
1.2.1.8 etcd 触发 Pod 信息更新事件
  • etcd 再次发送更新事件给 API Server。
1.2.1.9 Scheduler 监听到待调度的 Pod
  • Scheduler Watch 到新创建的 Pod 处于 “Pending” 状态(尚未分配节点)。
  • 通过调度算法(资源、亲和性、污点等)为其选择一个合适的 Node。
1.2.1.10 Scheduler 更新调度结果
  • Scheduler 将选定的 Node 信息写回到 API Server
  • API Server 更新 etcd 中该 Pod 的 Node 绑定信息。
1.2.1.11 etcd 确认更新 & API Server 同步结果
  • etcd 更新成功后向 API Server 返回确认。
  • API Server 同步 Pod 的最新状态(包括 Node 绑定结果)。
1.2.1.12 kubelet 在 Node 上拉取并运行容器
  • kubelet 监听到分配给自己的新 Pod。
  • 调用容器运行时(Docker/containerd):
    1. 拉取镜像
    2. 创建容器
    3. 启动容器
  • 启动成功后将 Pod 状态(Running、Failed 等)上报给 API Server。
1.2.1.13 API Server 更新 Pod 状态
  • API Server 将 kubelet 上报的状态写入 etcd。
  • etcd 确认写入成功后,集群状态完成同步,Pod 正式进入 Running 状态

kubelet 持续监听 Pod 事件,是为了应对副本数变化、镜像更新等动态操作。

注意:在创建 Pod 的工作就已经完成了后,为什么 kubelet 还要一直监听呢?原因很简单,假设这个时候 kubectl 发命令,要扩充 Pod 副本数量,那么上面的流程又会触发一遍,kubelet 会根据最新的 Pod 的部署情况调整 Node 的资源。又或者 Pod 副本数量没有发生变化,但是其中的镜像文件升级了,kubelet 也会自动获取最新的镜像文件并且加载。

1.3 Scheduler 过程(调度器)

1.3.1 核心任务

未绑定 Node 的 Podspec.nodeName == "") 分配到合适的节点。

1.3.2 调度目标

  • 公平性:节点间资源分配均衡
  • 高效性:集群所有资源最大化被使用
  • 效率:调度的性能要好,能够尽快地对大批量的 pod 完成调度工作
  • 灵活性:允许自定义策略(调度策略、插件)

Sheduler 是作为单独的程序运行的,启动之后会一直监听 APIServer,获取 spec.nodeName 为空的 pod,对每个 pod 都会创建一个 binding,表明该 pod 应该放到哪个节点上。

调度分为几个部分:首先是过滤掉不满足条件的节点,这个过程称为预算策略(predicate);然后对通过的节点按照优先级排序,这个是优选策略(priorities);最后从中选择优先级最高的节点。如果中间任何一步骤有错误,就直接返回错误。

1.4 调度流程

1.4.1 过滤阶段(Predicate)

过滤掉不满足条件的节点。

常见过滤算法:

算法名功能描述
PodFitsResources检查节点剩余资源是否满足 Pod 需求
PodFitsHost检查 NodeName 是否匹配
PodFitsHostPorts检查端口冲突
PodSelectorMatcheslabel 匹配
NoDiskConflictVolume 挂载冲突检测

若无节点满足条件,Pod 进入 Pending 状态,不断重试。
如果在 predicate 过程中没有合适的节点,pod 会一直在 pending 状态,不断重试调度,直到有节点满足条件。 经过这个步骤,如果有多个节点满足条件,就继续 priorities 过程:按照优先级大小对节点排序。

1.4.2 优选阶段(Priorities)

对可行节点进行打分排序。

常见算法:

优先级项描述
LeastRequestedPriority资源使用率越低,权重越高
BalancedResourceAllocationCPU 与内存使用率越接近越好(这个一般和上面的一起使用,不单独使用。比如 node01 的 CPU 和 Memory 使用率 20:60,node02 的 CPU 和 Memory 使用率 50:50,虽然 node01 的总使用率比 node02 低,但 node02 的 CPU 和 Memory 使用率更接近,从而调度时会优选 node02。)
ImageLocalityPriority优先节点上已有目标镜像的节点

最终选出权重最高的节点进行调度。

1.5 指定调度节点方式

1.5.1 nodeName(强制绑定)

pod.spec.nodeName 将 Pod 直接调度到指定的 Node 节点上,会跳过 Scheduler 的调度策略,该匹配规则是强制匹配

spec:nodeName: node01
# vim myapp.yaml
apiVersion: apps/v1  
kind: Deployment  
metadata:name: myapp
spec:replicas: 3selector:matchLabels:app: myapptemplate:metadata:labels:app: myappspec:nodeName: node01containers:- name: myappimage: soscscs/myapp:v1ports:- containerPort: 80
kubectl apply -f myapp.yaml
kubectl get pods -o wide
# 查看详细事件(发现未经过 scheduler 调度分配)
kubectl describe pod myapp-699655c7fd-9zgsk

在这里插入图片描述
在这里插入图片描述

直接跳过 Scheduler,由 kubelet 负责启动。

1.5.2 nodeSelector(基于标签匹配)

pod.spec.nodeSelector:通过 kubernetes 的 label-selector 机制选择节点,由调度器调度策略匹配 label,然后调度 Pod 到目标节点,该匹配规则属于强制约束

spec:nodeSelector:yjs: a
# 获取标签帮助
kubectl label --help
Usage:kubectl label [--overwrite] (-f FILENAME | TYPE NAME) KEY_1=VAL_1 ... KEY_N=VAL_N [--resource-version=version] [options]# 需要获取 node 上的 NAME 名称
kubectl get node
NAME     STATUS   ROLES    AGE   VERSION
master   Ready    master   30h   v1.20.11
node01   Ready    <none>   30h   v1.20.11
node02   Ready    <none>   30h   v1.20.11# 给对应的 node 设置标签分别为 yjs=a 和 yjs=b
kubectl label nodes node01 yjs=akubectl label nodes node02 yjs=b# 查看标签
kubectl get nodes --show-labels
NAME     STATUS   ROLES    AGE   VERSION   LABELS
master   Ready    master   30h   v1.20.11   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=master,kubernetes.io/os=linux,node-role.kubernetes.io/master=
node01   Ready    <none>   30h   v1.20.11   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kgc=a,kubernetes.io/arch=amd64,kubernetes.io/hostname=node01,kubernetes.io/os=linux
node02   Ready    <none>   30h   v1.20.11   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kgc=b,kubernetes.io/arch=amd64,kubernetes.io/hostname=node02,kubernetes.io/os=linux

在这里插入图片描述

# vim myapp1.yaml
apiVersion: apps/v1
kind: Deployment  
metadata:name: myapp1
spec:replicas: 3selector:matchLabels:app: myapp1template:metadata:labels:app: myapp1spec:nodeSelector:yjs: acontainers:- name: myapp1image: soscscs/myapp:v1ports:- containerPort: 80 
kubectl apply -f myapp1.yaml kubectl get pods -o wide
NAME                     READY   STATUS    RESTARTS   AGE   IP            NODE     NOMINATED NODE   READINESS GATES
myapp1-58cff4d75-52xm5   1/1     Running   0          24s   10.244.1.29   node01   <none>           <none>
myapp1-58cff4d75-f747q   1/1     Running   0          24s   10.244.1.27   node01   <none>           <none>
myapp1-58cff4d75-kn8gk   1/1     Running   0          24s   10.244.1.28   node01   <none>           <none># 查看详细事件(通过事件可以发现要先经过 scheduler 调度分配)
kubectl describe pod myapp1-58cff4d75-52xm5
Events:Type    Reason     Age   From               Message----    ------     ----  ----               -------Normal  Scheduled  57s   default-scheduler  Successfully assigned default/myapp1-58cff4d75-52xm5 to node01Normal  Pulled     57s   kubelet, node01    Container image "soscscs/myapp:v1" already present on machineNormal  Created    56s   kubelet, node01    Created container myapp1Normal  Started    56s   kubelet, node01    Started container myapp1

在这里插入图片描述

Scheduler 会调度匹配此标签的节点。

1.5.2.1 管理 Node 标签命令
kubectl label nodes node01 yjs=a
kubectl get nodes --show-labels# 指定标签查询 node 节点
kubectl get node -l yjs=a  # 修改一个 label 的值,需要加上 --overwrite 参数
kubectl label nodes node02 yjs=b --overwrite# 删除一个 label,只需在命令行最后指定 label 的 key 名并与一个减号相连即可:
kubectl label nodes node02 yjs-

1.6 节点亲和性与 Pod 亲和性

参考文档:https://kubernetes.io/zh/docs/concepts/scheduling-eviction/assign-pod-node/

1.6.1 节点亲和性(NodeAffinity)

affinity:nodeAffinity:requiredDuringSchedulingIgnoredDuringExecution:   # 硬策略preferredDuringSchedulingIgnoredDuringExecution:  # 软策略

🧠 类比理解:

你更“想去”做什么(软策略),还是“必须去”做什么(硬策略)。

操作符支持InNotInExistsDoesNotExistGtLt

Kubernetes 中 label selector 的键值运算关系:在 Kubernetes 中,很多资源(比如 Pod、Deployment、Service)都会用 label selector(标签选择器) 来筛选对象。运算符决定“选谁”或“不选谁”。

运算符含义通俗理解示例
Inlabel 的值在列表中“只要它的值在我列的名单里,就选上它”env In (dev, test) → 选出 env=devenv=test 的 Pod
NotInlabel 的值不在列表中“黑名单里的不要”env NotIn (prod) → 除了生产环境都选上
Gtlabel 的值大于某个数“比这个数字大的才要”version Gt 3 → 选出 version=4,5,6… 的 Pod
Ltlabel 的值小于某个数“比这个数字小的才要”version Lt 3 → 选出 version=1,2 的 Pod
Existslabel 存在即可“只要有这个标签就算数,不管值是啥”Exists zone → 只要有 zone 这个标签
DoesNotExistlabel 不存在“没有这个标签的才要”DoesNotExist debug → 没有 debug 标签的 Pod

简单理解

In / NotIn 看“名单”,Gt / Lt 看“数值”,Exists / DoesNotExist 看“有没有”。

1.6.1.1 案例1 硬策略

先查看节点标签:

kubectl get nodes --show-labels

在这里插入图片描述

# vim pod1.yaml
apiVersion: v1
kind: Pod
metadata:name: affinitylabels:app: node-affinity-pod
spec:containers:- name: with-node-affinityimage: soscscs/myapp:v1affinity:nodeAffinity:requiredDuringSchedulingIgnoredDuringExecution:nodeSelectorTerms:- matchExpressions:- key: kubernetes.io/hostname    # 指定node的标签operator: NotIn # 设置Pod安装到kubernetes.io/hostname的标签值不在values列表中的node上values:- node02
kubectl apply -f pod1.yaml
kubectl delete pod --all && kubectl apply -f pod1.yaml && kubectl get pods -o wide# 如果硬策略不满足条件,Pod 状态一直会处于 Pending 状态。

在这里插入图片描述

1.6.1.2 案例2 软策略
# preferredDuringSchedulingIgnoredDuringExecution:软策略
# vim pod2.yaml
apiVersion: v1
kind: Pod
metadata:name: affinitylabels:app: node-affinity-pod
spec:containers:- name: with-node-affinityimage: soscscs/myapp:v1affinity:nodeAffinity:preferredDuringSchedulingIgnoredDuringExecution:- weight: 1   # 如果有多个软策略选项的话,权重越大,优先级越高preference:matchExpressions:- key: kubernetes.io/hostnameoperator: Invalues:- node03
kubectl apply -f pod2.yamlkubectl get pods -o wide
NAME       READY   STATUS    RESTARTS   AGE   IP            NODE     NOMINATED NODE   READINESS GATES
affinity   1/1     Running   0          5s    10.244.2.35   node02   <none>           <none># 把values:的值改成node01,则会优先在node01上创建Pod
kubectl delete pod --all && kubectl apply -f pod2.yaml && kubectl get pods -o wide

在这里插入图片描述

软策略无法满足时,并不会阻碍pod的创建,而是会满足硬策略后,继续创建

1.6.1.3 案例3 软硬策略结合
# 如果把硬策略和软策略合在一起使用,则要先满足硬策略之后才会满足软策略
apiVersion: v1
kind: Pod
metadata:name: affinitylabels:app: node-affinity-pod
spec:containers:- name: with-node-affinityimage: soscscs/myapp:v1affinity:nodeAffinity:requiredDuringSchedulingIgnoredDuringExecution:   # 先满足硬策略,排除有kubernetes.io/hostname=node02标签的节点nodeSelectorTerms:- matchExpressions:- key: kubernetes.io/hostnameoperator: NotInvalues:- node02preferredDuringSchedulingIgnoredDuringExecution:  # 再满足软策略,优先选择有yjs=a标签的节点- weight: 1preference:matchExpressions:- key: yjsoperator: Invalues:- akubectl delete pod --all && kubectl apply -f pod3.yaml && kubectl get pods -o wide

在这里插入图片描述

软硬策略有冲突时,优先满足硬策略再考虑软策略。

1.6.2 Pod 亲和性与反亲和性

调度策略匹配标签操作符拓扑域支持调度目标
nodeAffinity主机In, NotIn, Exists, DoesNotExist, Gt, Lt指定主机
podAffinityPodIn, NotIn, Exists, DoesNotExist与指定 Pod 同域
podAntiAffinityPodIn, NotIn, Exists, DoesNotExist与指定 Pod 不同域
1.6.2.1 案例1 Pod 亲和性调度
kubectl label nodes node01 yjs=a    # pod1 所在节点标签
kubectl label nodes node02 yjs=b    # pod2 所在节点标签
# 创建一个标签为 app=myapp01 的 Pod
# vim pod3.yaml
apiVersion: v1
kind: Pod
metadata:name: myapp01labels:app: myapp01
spec:containers:- name: with-node-affinityimage: soscscs/myapp:v1
kubectl apply -f pod3.yamlkubectl get pods --show-labels -o wide

在这里插入图片描述

# 使用 Pod 亲和性调度,创建多个 Pod 资源
# vim pod4.yaml
apiVersion: v1
kind: Pod
metadata:name: myapp02labels:app: myapp02
spec:containers:- name: myapp02image: soscscs/myapp:v1affinity:podAffinity:requiredDuringSchedulingIgnoredDuringExecution:- labelSelector:matchExpressions:- key: appoperator: Invalues:- myapp01topologyKey: yjs

仅当节点和至少一个已运行且有键为“app”且值为“myapp01”的标签的 Pod 处于同一拓扑域时,才可以将该 Pod 调度到节点上。(更确切的说,如果节点 N 具有带有键 yjs 和某个值 V 的标签,则 Pod 有资格在节点 N 上运行,以便集群中至少有一个具有键 yjs 和值为 V 的节点正在运行具有键“app”和值 “myapp01”的标签的 pod。)
topologyKey 是节点标签的键。如果两个节点使用此键标记并且具有相同的标签值,则调度器会将这两个节点视为处于同一拓扑域中。调度器试图在每个拓扑域中放置数量均衡的 Pod。
如果 yjs 对应的值不一样就是不同的拓扑域。比如 Pod1 在 yjs=a 的 Node 上,Pod2 在 yjs=b 的 Node 上,Pod3 在 yjs=a 的 Node 上,则 Pod2 和 Pod1、Pod3 不在同一个拓扑域,而 Pod1 和 Pod3 在同一个拓扑域。

kubectl apply -f pod4.yamlkubectl get pods --show-labels -o wide

在这里插入图片描述

1.6.2.2 案例2 Pod 反亲和性调度

示例1:

# vim pod5.yaml
apiVersion: v1
kind: Pod
metadata:name: myapp10labels:app: myapp10
spec:containers:- name: myapp10image: soscscs/myapp:v1affinity:podAntiAffinity:preferredDuringSchedulingIgnoredDuringExecution:- weight: 100podAffinityTerm:labelSelector:matchExpressions:- key: appoperator: Invalues:- myapp01topologyKey: kubernetes.io/hostname #表示了区分拓扑域的依据,以hostname为依据

如果节点处于 Pod 所在的同一拓扑域且具有键“app”和值“myapp01”的标签,则该 pod 不应将其调度到该节点上。(如果 topologyKey 为 kubernetes.io/hostname,则意味着当节点和具有键 “app”和值“myapp01”的 Pod 处于相同的拓扑域,Pod 不能被调度到该节点上。)

kubectl apply -f pod5.yamlkubectl get pods --show-labels -o wide

在这里插入图片描述

示例2:

# vim pod6.yaml
apiVersion: v1
kind: Pod
metadata:name: myapp20labels:app: myapp20
spec:containers:- name: myapp20image: soscscs/myapp:v1affinity:podAntiAffinity:requiredDuringSchedulingIgnoredDuringExecution:- labelSelector:matchExpressions:- key: appoperator: Invalues:- myapp01topologyKey: yjs #识别同一拓扑域的关键为yjs键的数值,如果全部的yjs键的值都是一个,则无法满足这个反亲和的硬策略
# 由于指定 Pod 所在的 node01 节点上具有带有键 yjs 和标签值 a 的标签,node02 也有这个yjs=a的标签,所以 node01 和 node02 是在一个拓扑域中,反亲和要求新 Pod 与指定 Pod 不在同一拓扑域,所以新 Pod 没有可用的 node 节点,即为 Pending 状态。
kubectl get pod --show-labels -owide

在这里插入图片描述

kubectl label nodes node02 yjs=b --overwritekubectl get pod --show-labels -o wide

在这里插入图片描述

1.7 污点(Taint) 和 容忍(Tolerations)

1.7.1 污点(Taint)

节点亲和性,是 Pod 的一种属性(偏好或硬性要求),它使 Pod 被吸引到一类特定的节点。Taint 则相反,它使节点能够排斥一类特定的 Pod。Taint 和 Toleration 相互配合,可以用来避免 Pod 被分配到不合适的节点上。每个节点上都可以应用一个或多个 taint ,这表示对于那些不能容忍这些 taint 的 Pod,是不会被该节点接受的。如果将 toleration 应用于 Pod 上,则表示这些 Pod 可以(但不一定)被调度到具有匹配 taint 的节点上。

使用 kubectl taint 命令可以给某个 Node 节点设置污点,Node 被设置上污点之后就和 Pod 之间存在了一种相斥的关系,可以让 Node 拒绝 Pod 的调度执行,甚至将 Node 已经存在的 Pod 驱逐出去。

污点格式

key=value:effect

每个污点有一个 key 和 value 作为污点的标签,其中 value 可以为空,effect 描述污点的作用。

当前 taint effect 支持如下三个选项

类型描述
NoSchedule不调度到此节点
PreferNoSchedule尽量避免调度具有该污点的 Node 上
NoExecute不调度 + 驱逐已存在 Pod

命令示例

kubectl get nodes
# master 就是因为有 NoSchedule 污点,k8s 才不会将 Pod 调度到 master 节点上
kubectl describe node master
......
Taints:             node-role.kubernetes.io/master:NoSchedule

在这里插入图片描述

# 设置污点
kubectl taint node node01 key1=value1:NoSchedule# 节点说明中,查找 Taints 字段
kubectl describe node node-name  # 去除污点
kubectl taint node node01 key1:NoSchedule-

在这里插入图片描述
在这里插入图片描述

kubectl get pods -o wide
NAME      READY   STATUS    RESTARTS   AGE     IP           NODE     NOMINATED NODE   READINESS GATES
myapp01   1/1     Running   0          4h28m   10.244.2.3   node02   <none>           <none>
myapp02   1/1     Running   0          4h13m   10.244.2.4   node02   <none>           <none>
myapp03   1/1     Running   0          3h45m   10.244.1.4   node01   <none>           <none>kubectl taint node node02 check=mycheck:NoExecute# 查看 Pod 状态,会发现 node02 上的 Pod 已经被全部驱逐(注:如果是 Deployment 或者 StatefulSet 资源类型,为了维持副本数量则会在别的 Node 上再创建新的 Pod)
kubectl get pods -o wide
NAME      READY   STATUS    RESTARTS   AGE     IP           NODE     NOMINATED NODE   READINESS GATES
myapp03   1/1     Running   0          3h48m   10.244.1.4   node01   <none>           <none>

在这里插入图片描述

1.7.2 容忍 (Tolerations)

设置了污点的 Node 将根据 taint 的 effect:NoSchedule、PreferNoSchedule、NoExecute 和 Pod 之间产生互斥的关系,Pod 将在一定程度上不会被调度到 Node 上。但我们可以在 Pod 上设置容忍(Tolerations),意思是设置了容忍的 Pod 将可以容忍污点的存在,可以被调度到存在污点的 Node 上。

kubectl taint node node01 check=mycheck:NoExecutevim pod3.yamlkubectl apply -f pod3.yaml# 在两个 Node 上都设置了污点后,此时 Pod 将无法创建成功
kubectl get pods -o wide
NAME      READY   STATUS    RESTARTS   AGE   IP       NODE     NOMINATED NODE   READINESS GATES
myapp01   0/1     Pending   0          17s   <none>   <none>   <none>           <none>

在这里插入图片描述

# vim pod3.yaml
apiVersion: v1
kind: Pod
metadata:name: myapp01labels:app: myapp01
spec:containers:- name: with-node-affinityimage: soscscs/myapp:v1tolerations:- key: "check"operator: "Equal"  # operator: "Equal" 意味着这个值等于value,如果是Exists,则不需要填写value,只要有这个key就容忍value: "mycheck"effect: "NoExecute"tolerationSeconds: 3600 #当需要被驱逐前在node上停留的时间

当节点添加了带有 NoExecute 效果的污点(例如节点故障、资源不足等)时:
tolerationSeconds: 3600 表示 Pod 会在 3600 秒内保持运行,超过这个时间后才会被驱逐。
但如果节点的污点触发了立即驱逐条件(如节点不可用、健康检查失败),Kubernetes 可能会忽略这个容忍时间,直接驱逐 Pod 以保证集群稳定性。

其中的 key、vaule、effect 都要与 Node 上设置的 taint 保持一致
operator 的值为 Exists 将会忽略 value 值,即存在即可

kubectl apply -f pod3.yaml# 在设置了容忍之后,Pod 创建成功
kubectl get pods -o wide
NAME       READY   STATUS    RESTARTS   AGE   IP           NODE     NOMINATED NODE   READINESS GATES
myapp01   1/1     Running   0          10m   10.244.1.5   node01   <none>           <none>

在这里插入图片描述

# 其它注意事项
(1)当不指定 key 值时,表示容忍所有的污点 keytolerations:- operator: "Exists"(2)当不指定 effect 值时,表示容忍所有的污点作用tolerations:- key: "key"operator: "Exists"(3)有多个 Master 存在时,防止资源浪费,可以如下设置
kubectl taint node Master-Name node-role.kubernetes.io/master=:PreferNoSchedule# 如果某个 Node 更新升级系统组件,为了防止业务长时间中断,可以先在该 Node 设置 NoExecute 污点,把该 Node 上的 Pod 都驱逐出去
kubectl taint node node01 check=mycheck:NoExecute# 此时如果别的 Node 资源不够用,可临时给 Master 设置 PreferNoSchedule 污点,让 Pod 可在 Master 上临时创建
kubectl taint node master node-role.kubernetes.io/master=:PreferNoSchedule# 待所有 Node 的更新操作都完成后,再去除污点
kubectl taint node node01 check=mycheck:NoExecute-

key、value、effect 必须与 Node 上的污点匹配。operator: Exists 表示存在即可。

1.8 节点维护操作

1.8.1 cordon & drain

命令功能
kubectl cordon <node>标记为不可调度
kubectl drain <node> --ignore-daemonsets --delete-local-data --force驱逐 Pod
kubectl uncordon <node>恢复可调度状态

drain 等价于 “cordon + 驱逐”。
在 Kubernetes 中,驱逐(Eviction)是指将运行中的 Pod 从一个节点上迁移到另一个节点的过程,同时释放原节点上的资源以供其他使用。驱逐通常发生在以下情况:

  • 需要将 Pod 迁移到其他节点以维护节点或进行软硬件升级。
  • 某个节点的资源不足以容纳新的 Pod,需要将旧的 Pod 驱逐以释放资源。
    Kubernetes 可以使用自动驱逐机制来管理 Pod 的迁移。它可以基于节点负载、Pod 优先级等条件来自动选择需要驱逐的 Pod,并将它们迁移到其他节点上。在执行驱逐时,Kubernetes 会首先将 SIGTERM 信号发送给 Pod,然后等待一段时间,让 Pod 自行终止。如果自我终止未成功,则 Kubernetes 会强制终止 Pod 并进行清理工作。
    驱逐是 Kubernetes 集群中必不可少的机制之一,它可以确保系统的高冗余性和高可用性,并优化集群中的资源利用率。
    在这里插入图片描述

1.9 Pod 生命周期(Phase)

阶段说明
Pending已创建但未调度或拉取镜像中
Running容器已启动运行中
Succeeded所有容器成功终止(Job 类)
Failed容器失败退出(非0或异常)
Unknown无法获取状态(通信异常)

详解 phase 的可能状态有:

  • Pending:表示 APIServer 创建了 Pod 资源对象并已经存入了 etcd 中,但是它并未被调度完成(比如还没有调度到某台 node 上),或者仍然处于从仓库下载镜像的过程中。
  • Running:Pod 已经被调度到某节点之上,并且 Pod 中所有容器都已经被 kubelet 创建。至少有一个容器正在运行,或者正处于启动或者重启状态(也就是说 Running 状态下的 Pod 不一定能被正常访问)。
  • Succeeded:有些 pod 不是长久运行的,比如 job、cronjob,一段时间后 Pod 中的所有容器都被成功终止,并且不会再重启。需要反馈任务执行的结果。
  • Failed:Pod 中的所有容器都已终止了,并且至少有一个容器是因为失败终止。也就是说,容器以非0状态退出或者被系统终止,比如 command 写的有问题。
  • Unknown:表示无法读取 Pod 状态,通常是 kube-controller-manager 无法与 Pod 通信。

1.10 故障排查命令

操作命令
查看事件kubectl describe pod <POD>
查看日志kubectl logs <POD> [-c 容器名]
进入容器kubectl exec -it <POD> bash
查看集群状态kubectl cluster-info
查看节点状态kubectl get nodes
查看 kubelet 日志journalctl -xefu kubelet

1.11 总结

1.11.1 核心组件协作关系图

kubectl → APIServer → etcd↓    ↑
Controller Manager ←→ Scheduler ←→ kubelet(Node)

1.11.2 核心调度策略层次

  1. nodeName(直接绑定)
  2. nodeSelector(Label 匹配)
  3. nodeAffinity / podAffinity(亲和性调度)
  4. Taint & Toleration(污点/容忍机制)
  5. cordon & drain(节点维护与 Pod 迁移)

1.11.3 思维总结

“调度是吸引,污点是排斥,亲和性是偏好,容忍是例外。”

总结

K8s 集群调度是一个“由组件协作驱动、由策略定义规则”的动态过程,其核心目标是在“资源高效利用”与“业务需求匹配”之间找到最优平衡。通过本文的梳理,可将调度体系的核心逻辑归纳为以下三点:

1. 底层基石:组件协作与 List-Watch 机制

K8s 调度的实现依赖于 APIServer、Controller Manager、Scheduler、kubelet 与 etcd 的紧密协作,而 List-Watch 机制则是组件间“实时同步状态、解耦交互逻辑”的关键。从用户通过 kubectl 创建 Pod,到 APIServer 写入 etcd、Scheduler 完成节点匹配、kubelet 启动容器,整个流程环环相扣,确保 Pod 从“创建请求”到“Running 状态”的稳定过渡。

2. 调度策略:从“强制绑定”到“柔性偏好”的分层设计

K8s 提供了多层次的调度策略,以满足不同场景的需求:

  • 基础层:nodeName 直接绑定节点(跳过调度器,适合特殊场景)、nodeSelector 基于标签匹配(简单强制约束);
  • 灵活层:节点亲和性(NodeAffinity)与 Pod 亲和性/反亲和性(PodAffinity/PodAntiAffinity),通过“硬策略(必须满足)”与“软策略(优先满足)”实现精细化调度,例如让关联业务 Pod 同节点部署、避免同类 Pod 集中在单一节点;
  • 排斥层:污点(Taint)与容忍(Toleration)机制,让节点主动“排斥”不匹配的 Pod,同时允许特定 Pod 以“容忍”突破排斥,适合节点隔离(如 Master 节点禁止普通 Pod 调度)、节点维护等场景。

3. 运维保障:从状态监控到节点维护

理解 Pod 生命周期的 5 种核心状态(Pending、Running、Succeeded、Failed、Unknown),是排查调度故障的基础;而 kubectl describe podjournalctl -xefu kubelet 等命令,则是定位“调度失败”“容器启动异常”等问题的关键工具。此外,cordon(标记不可调度)、drain(驱逐 Pod)等节点维护命令,能在不中断业务的前提下完成节点升级、故障修复,进一步保障集群稳定性。

简言之,K8s 调度的本质是“用规则定义 Pod 与节点的关系”——调度策略是“吸引”Pod 到合适节点的拉力,污点是“排斥”Pod 的推力,而容忍则是应对特殊需求的“例外条款”。掌握这些机制,不仅能解决当前集群的调度问题,更能为后续大规模集群、复杂业务场景的调度优化打下基础。

http://www.dtcms.com/a/482501.html

相关文章:

  • 如何从 iPhone 中导出视频
  • 关于网站开发的开题报告郑州制作平台网站
  • 吐鲁番做网站godaddy服务器做网站
  • 特色专业建设验收网站如何做网络营销方案策划
  • iBizModel 日历部件(PSSYSCALENDAR)模型体系详解
  • Goroutine间的“灵魂管道”:Channel如何实现数据同步与因果传递?
  • 一个网站上能不能放两个域名 两个备案号沧县做网站价格
  • GPIO 也是一个接口,还有 QEMU GPIODEV 和 GUSE
  • 网站制作公司电话山东专业网站建设公司
  • 第 8 篇:更广阔的世界 - 加载 3D 模型
  • C/C++---_access 和 access 函数 文件/目录状态判断
  • Linux内存管理-缓存系统中的Major和Minor详解
  • 8 读写分离-实战
  • 手机网站建设西安检查网站是否做301
  • 网站Favicon图标:小图标背后的大作用 引言
  • 什么是GEO生成式引擎优化?GEO科普:定义、原理与应用指南
  • 使用 Gensim 进行主题建模(LDA)与词向量训练(Word2Vec)的完整指南
  • 诺奖解码外周免疫耐受,泰克生物以抗体工具链加速机制研究突破
  • 虚幻引擎5 GAS开发俯视角RPG游戏 P05-07 广播效果资产标签
  • 南阳专业做网站抖音代运营平台
  • 网站公司怎么做的好天津海外seo
  • 二级网站建设方案模板做ppt的网站叫什么名字
  • Java优选算法——位运算
  • Linux编辑器vim
  • 大模型-去噪扩散概率模型(DDPM)采样算法详解
  • LeetCode 398:随机数索引
  • 通过公网STUN服务器实现UDP打洞
  • 手机怎样设计网站建设哪个网站有做兼职的
  • 分布式专题——44 ElasticSearch安装
  • Java HTTP编程深度解析:从基础到微服务通信的完整架构实践