k8sday16调度器
目录
一、调度器概念
1、调度器的主要功能
①、过滤 (Filtering)
②、评分 (Scoring)
③、绑定 (Binding)
2、调度流程
①、Pod 创建
②、调度队列
③、调度周期
④、绑定
⑤、kubelet 执行
3、调度策略
①、预选策略 (Predicates)
②、优选策略 (Priorities)
4、自定义调度器
二、亲和性
1、节点亲和性的类型
①、硬性约束
Ⅰ、配置文件
Ⅱ、实操效果
②、软性约束
Ⅰ、配置文件
Ⅱ、实操效果
2、Pod亲和/反亲和性的类型
三、容忍和污点
1、污点
①、污点语法
②、小细节
③、操作语法
2、容忍
①、容忍语法
②、特殊写法
四、固定节点调度
1、直接指定节点名称 (nodeName)
2、 节点选择器 (nodeSelector)
一、调度器概念
Kubernetes 调度器 Scheduler 是 Kubernetes 控制平面的核心组件之一,通过 API Server 监听尚未被绑定到具体节点的 Pod(即 Pod 的 spec 中的 NodeName 为空的 Pod),最终实现将 Pod 分配到集群中合适的 Node 上运行。
1、调度器的主要功能
①、过滤 (Filtering)
根据 Pod 的资源需求和 Node 的资源容量,筛选出可调度的 Node
②、评分 (Scoring)
对通过过滤的 Node 进行评分,选择最优的 Node
③、绑定 (Binding)
将 Pod 绑定到选定的 Node 上
2、调度流程
①、Pod 创建
用户通过 kubectl 或其他方式创建 Pod
②、调度队列
Pod 进入调度器的待调度队列
③、调度周期
-
过滤阶段:排除不满足条件的 Node(预选策略)
-
评分阶段:对剩余 Node 进行评分(优选策略)
-
选择阶段:选择分数最高的 Node
④、绑定
将 Pod 绑定到选定的 Node
⑤、kubelet 执行
相应 Node 上的 kubelet 创建并运行 Pod 容器
3、调度策略
首先“预选",排除掉不满足条件的节点,然后”优选“,将满足条件的节点按照优先级排序,最后选出优先级最高的节点。当其中有一步发生错误,就会直接返回错误。
预选策略必须每一个都通过,否则Pod会一直处于“Pending”状态,不断重试调度,直到找到满足条件的节点。
有多个节点通过预选后会进行优选进行排序,选择最高优先级的节点绑定Pod。
①、预选策略 (Predicates)
-
PodFitsResources:检查 Node 是否有足够的资源,即当前节点剩余资源是否大于Pod请求资源
-
PodFitsHostPorts:检查 Node 是否有可用的端口,即已使用的端口是否和Pod请求的端口冲突
-
HostName:检查 Pod 是否指定了特定 Node
-
MatchNodeSelector:检查 Node 标签是否匹配 Pod 的 nodeSelector
-
NoDiskConflict:检查存储卷是否冲突,即已挂载的卷是否和Pod请求的卷冲突
注意,每个版本的K8S其对应的预选策略有部分差别
②、优选策略 (Priorities)
-
LeastRequestedPriority:选择资源利用率最低的 Node
-
BalancedResourceAllocation:平衡 CPU 和内存的使用
-
NodeAffinityPriority:根据 Node 亲和性规则评分
-
TaintTolerationPriority:根据污点和容忍度评分
-
ImageLocalityPriority:优先选择已经有所需镜像的 Node
注意,每个版本的K8S其对应的优选策略有部分差别
4、自定义调度器
自定义调度器是 Kubernetes 中一个强大的功能,允许我们实现自己的调度逻辑来替代或补充默认调度器。
官方的默认调度器已经很优秀了,要使用自定义的调度器多为要我们的特殊调度需求,针对某些特定领域等等。这里就不给大家演示了,有兴趣的可以自己去搜索创建。
二、亲和性
节点亲和性(Node Affinity) 是一种调度约束,用于指定 Pod 应该或不应该调度到哪些节点 上运行。它基于节点的 标签(Labels) 进行匹配,比传统的 nodeSelector
更灵活,支持 软约束(偏好) 和 硬约束(要求)。
1、节点亲和性的类型
节点亲和性通过 nodeAffinity 字段定义,分为两种规则:
类型 | 字段 | 说明 | 类比 |
---|---|---|---|
必需(硬约束) | requiredDuringSchedulingIgnoredDuringExecution | 必须满足,否则 Pod 无法调度 | nodeSelector 的增强版 |
偏好(软约束) | preferredDuringSchedulingIgnoredDuringExecution | 尽量满足,但不强制 | 类似“优先选择” |
-
硬性约束即必须满足,带有强制性,达不到则Pod一直处于"Pending"
-
软性约束即我的期望,不带有强制性,达不到则Pod找其他节点绑定
①、硬性约束
Ⅰ、配置文件
apiVersion: v1kind: Podmetadata:name: nginx-ssdspec:containers:- name: nginximage: nginx:1.24.0affinity: # 亲和性nodeAffinity: # 节点亲和性requiredDuringSchedulingIgnoredDuringExecution: # 表明是硬性约束nodeSelectorTerms: # 选择的节点- matchExpressions: # 匹配运算符- key: disktype # 匹配的节点标签的键operator: In # 在列表values: ["ssd"] # 匹配的节点标签的值# 即该 Pod 只能 调度到带有 disktype=ssd 标签的节点,没有则一直"Pending"。
Ⅱ、实操效果
刚开始我的所有节点都没有目标标签,这时候创建Pod,查看nginx-ssd的状态
现在我向我的节点my-multi-node-cluster1-worker2添加标签disktype=ssd,观察nginx-ssd的状态:
# 给节点添加标签格式如下:# kubectl label nodes <node-name> <key>=<value>kubectl label nodes my-multi-node-cluster1-worker2 disktype=ssd# 查看是否添加成功kubectl get nodes --show-labels# 再次查看nginx-ssd的状态kubectl get po -o wide# 结束后删除特定标签格式: kubectl label nodes <node-name> <key>- (可选)
可见该 Pod 只能调度到带有 disktype=ssd 标签的节点,没有则一直"Pending"。
②、软性约束
Ⅰ、配置文件
apiVersion: v1kind: Podmetadata:name: nginx-testlabelspec:containers:- name: nginximage: nginx:1.24.0affinity: nodeAffinity: # 亲和性preferredDuringSchedulingIgnoredDuringExecution: # 表明是软性约束# 注意权重只在软性约束出现,硬性约束没有- weight: 50 # 权重 1-100,越高越优先preference: # 亲和类型matchExpressions: # 匹配运算符- key: testlabel # 匹配的节点标签的键operator: Invalues: ["a"] # 匹配的节点标签的值# 调度器会优先选择 testlabel=a 的节点,但如果没有,也可以调度到其他节点。
Ⅱ、实操效果
刚开始我的所有节点都没有目标标签,这时候创建Pod,查看nginx-testlabel的状态
可见即使没有 testlabel=a 的节点,该 Pod 也可以调度到其他节点。
现在我向我的节点my-multi-node-cluster1-worker添加标签 testlabel=a,观察nginx-testlabel的状态:
# 给节点添加标签格式如下:# kubectl label nodes <node-name> <key>=<value>kubectl label nodes my-multi-node-cluster1-worker testlabel=a# 查看是否添加成功kubectl get nodes --show-labels# 再次查看nginx-ssd的状态kubectl get po -o wide# 结束后删除特定标签格式: kubectl label nodes <node-name> <key>- (可选)
可见调度器会优先选择 testlabel=a 的节点,但如果没有,也可以调度到其他节点。
2、Pod亲和/反亲和性的类型
Pod 亲和性(Pod Affinity)与反亲和性(Pod Anti-Affinity)用来控制 Pod 之间 的“吸引”或“排斥”关系——也就是「这个 Pod 想/不想和哪些 Pod 跑在一起」。它们同样通过节点上的 topologyKey(通常是主机名、机架、可用区等)来划定“拓扑域”,然后在该域内按标签匹配其它 Pod,即拓扑域就是通过节点标签来规定范围。
语义 | 字段 | 场景示例 |
---|---|---|
required…(硬约束) | requiredDuringSchedulingIgnoredDuringExecution | “必须”和某类 Pod 同域/异域 |
preferred…(软约束) | preferredDuringSchedulingIgnoredDuringExecution | “尽量”和某类 Pod 同域/异域 |
Pod的亲和性和反亲和性都有两种类型,即硬性约束和软性约束。
-
亲和性软性约束:最好和哪个Pod在一起运行
-
亲和性硬性约束:必须和哪个Pod在一起运行
-
反亲和性软性约束:最好不和哪个Pod在一起运行
-
反亲和性硬性约束:必须不和哪个Pod在一起运行
和节点亲和性的配置文件类似,以下给出配置文件大致模板:
affinity:podAffinity: # Pod 亲和性(想在一起)<required|preferred>DuringSchedulingIgnoredDuringExecution: # 硬性约束/软性约束- topologyKey: <key> # 划定“拓扑域”的节点标签labelSelector: # 匹配其它 Pod 的标签matchExpressions:- {key: app, operator: In, values: [backend]}namespaces: [ns1, ns2] # 默认当前命名空间# 注意:权重只在软性约束时出现,硬性约束没有weight: 50 # 权重 1-100,越高越优先podAntiAffinity: # Pod 反亲和性(不想在一起)<required|preferred>DuringSchedulingIgnoredDuringExecution: # 硬性约束/软性约束- topologyKey: kubernetes.io/hostnamelabelSelector:matchLabels:app: nginx
给出一个Pod亲和软性约束+一个Pod反亲和硬性约束的配置文件:
apiVersion: apps/v1kind: Deploymentmetadata:name: cachespec:replicas: 1selector:matchLabels: { app: nginx } # 和下方 Pod 模板中的标签相同template:metadata:labels: { app: nginx } # 和上方的匹配标签相同spec:containers:- name: nginximage: nginx:1.24.0affinity:podAffinity: # Pod 亲和性(想在一起)preferredDuringSchedulingIgnoredDuringExecution: # 软性约束# 注意:权重只在软性约束中出现,硬性约束没有- weight: 80 # 权重越高,优先级越高topologyKey: kubernetes.io/hostname # 拓扑域labelSelector: # 匹配的 Pod 标签matchLabels: { app: backend }# 即该配置文件创建的 Pod 最好和在 kubernetes.io/hostname 这一拓扑域中含有的带有 app=backend 标签的 Pod 在一起运行,如果没有则 Pod 随便选取要绑定的节点---apiVersion: apps/v1kind: Deploymentmetadata:name: nginxspec:replicas: 3selector:matchLabels: { app: nginx }template:metadata:labels: { app: nginx }spec:containers:- name: nginximage: nginx:1.24.0affinity:podAntiAffinity: # Pod 反亲和性(不想在一起)requiredDuringSchedulingIgnoredDuringExecution: # 硬性约束- topologyKey: kubernetes.io/hostname # 拓扑域labelSelector: # 匹配的 Pod 标签matchLabels: { app: nginx }# 即该配置文件创建的 Pod 必须不能和在 kubernetes.io/hostname 拓扑域中含有的带有 app=nginx 标签的 Pod 在一起运行,如果没有则 Pod 一直 "Pending",不绑定节点
三、容忍和污点
污点和容忍(Taints & Tolerations)是 Kubernetes 用来 “排斥” Pod 与节点 的强耦合机制:
-
Taint(污点) 打在节点上 → “拒绝” 没有对应容忍度的 Pod。
-
Toleration(容忍度) 写在 Pod 上 → “允许” 被调度到带有对应污点的节点。
精简总结:只有可容忍节点的污点的Pod才有可能被绑定,注意是有可能,不是一定
1、污点
在node节点设置污点,可以一定程度上根据对应effect策略和Pod之间产生排斥,Pod一定程度上不会被调度到含污点的节点上。
①、污点语法
key=value:effect
每个污点都有匹配的key和value,value可为空,effect描述污点的作用
effect当前包含三个选项:
Effect | 调度阶段 | 已运行 Pod 是否驱逐 |
---|---|---|
NoSchedule | 新 Pod 不可调度 | 不驱逐 |
PreferNoSchedule | 软排斥(尽量不去) | 不驱逐 |
NoExecute | 新 Pod 不可调度 + 已运行且无容忍的 Pod 立即驱逐 | 驱逐 |
精简总结:NoSchedule 是不会将 Pod 调度到含该污点的节点上,PreferNoSchedule是尽量不将 Pod 调度到含该污点的节点上,NoExecute 是不会将 Pod 调度到含该污点的节点上且原有的 Pod 会进行驱逐
②、小细节
我们在之前不管是创建DeployMent还是StatefulSet等等,他们创建出来的Pod都是运行在副节点,也就是worker节点上的,没有运行在主节点(master节点)上的,这是为什么呢?答案就是因为主节点有污点,不参与调度。如图:
但真的所有Pod都不能在主节点运行吗,实际不然,设置了容忍的Pod可以在主节点运行,如图:
③、操作语法
# 添加污点kubectl taint nodes <node-name> key=value:NoSchedule# 例:让 master 节点只接受控制面 Podkubectl taint nodes master-0 node-role.kubernetes.io/master:NoSchedule# 查看污点kubectl describe nodes <node-name># 删除污点kubectl taint nodes <node-name> key=value:NoSchedule-# 或kubectl taint nodes <node-name> key:NoSchedule-
2、容忍
在node节点设置污点,可以一定程度上根据对应effect策略和Pod之间产生排斥,Pod一定程度上不会被调度到含污点的节点上。但也只是一定程度,我们如果在Pod上设置了容忍度(Toleration),就代表着我的这个Pod容忍这个带污点的node节点,那么就可以在这个节点上运行。
①、容忍语法
tolerations: # 容忍度- key: "key1" # 污点的键operator: "Equal" # 比较操作符(Equal 或 Exists)value: "value1" # 污点的值effect: "NoSchedule" # 污点效果作用 tolerationSeconds: 3600 # 仅对 NoExecute 有效,表示容忍时间(秒),超过这个时间驱离所有 Pod
effect的三个污点作用就是上面的NoSchedule, PreferNoSchedule, NoExecute
②、特殊写法
tolerations: # 容忍度- key: "key1" # 污点的键operator: "Exists" # 比较操作符(Equal 或 Exists)effect: "NoSchedule" # 污点效果作用 # 即不写污点的值,比较操作符写为Exists
即只要是key1污点内所有设置为驱离(NoSchedule)的污点我都容忍
tolerations: # 容忍度- key: "key1" # 污点的键operator: "Exists" # 比较操作符(Equal 或 Exists)# 即不写污点的值和污点效果,比较操作符写为Exists
即容忍所有key1污点
tolerations: # 容忍度- operator: "Exists" # 比较操作符(Equal 或 Exists)# 即不写污点的键、值和污点效果,比较操作符写为Exists
即容忍所有污点
四、固定节点调度
1、直接指定节点名称 (nodeName)
使用 spec.nodeName 直接指定节点名称后,会直接跳过调度器(Scheduler)的调度,而是直接匹配到你指定的node节点。
apiVersion: v1kind: Podmetadata:name: nginxspec:nodeName: node-1 # 直接指定节点名称containers:- name: nginximage: nginx:1.24.0
-
最直接、最高优先级的调度方式
-
绕过调度器,直接绑定到指定节点
-
如果节点不存在或资源不足,Pod 会保持 Pending 状态
-
适用于开发和测试环境,生产环境不推荐
2、 节点选择器 (nodeSelector)
使用 spec.nodeSelector 直接指定节点标签后,由调度器(Scheduler)按照调度策略进行调度,之后匹配Pod到你指定的node节点,这是强制约束,类似硬性约束。
apiVersion: v1kind: Podmetadata:name: nginxspec:containers:- name: nginximage: nginx:1.24.0nodeSelector:disktype: ssd # 只调度到有 disktype=ssd 标签的节点
-
简单易用
-
只能简单匹配,不支持复杂表达式
-
适用于简单的节点选择需求
-
无节点满足则一直"Pending"