Kubernetes集群调度全面解析:从组件协作到实战(含亲和性、污点容忍与故障排查)
前言
在Kubernetes(简称K8s)集群中,调度是实现资源高效利用与业务稳定运行的核心环节。它负责将Pod合理分配到集群节点上,既要满足业务的资源需求,又要保证集群整体的负载均衡。本文将从K8s组件协作机制出发,详细解析Pod的创建流程、调度器的工作原理,以及节点选择、亲和性调度、污点与容忍等关键技术,并结合实践案例说明节点维护与故障排查方法,帮助读者全面掌握K8s集群调度的核心知识。
一、Kubernetes集群调度基础
1.1 Kubernetes组件协作机制
Kubernetes各组件通过List-Watch机制实现协同工作与数据同步,这种机制确保了组件间的解耦与集群状态的实时一致性。
1.1.1 关键组件及职责
| 组件 | 职责描述 |
|---|---|
| kubectl / API客户端 | 向APIServer发起资源创建、查询、更新等管理请求 |
| APIServer | 集群控制的核心入口,负责API调用处理、权限校验、与etcd交互 |
| etcd | 分布式键值存储,保存集群所有状态信息(如Pod、Node、配置等) |
| Controller Manager | 运行各类控制器(如ReplicaSet、Deployment),维持资源期望状态(如副本数、自愈逻辑) |
| Scheduler | 调度器,为未分配节点的Pod选择最合适的Node节点 |
| kubelet | 节点代理程序,负责本节点上Pod的生命周期管理(创建、启停、状态上报) |
1.1.2 组件协作流程简述
用户通过kubectl或其他客户端向APIServer发送请求(如创建Pod)后,APIServer会完成权限校验并将数据存入etcd。etcd存储数据时会触发事件(如Create事件)并同步给APIServer;其他组件(Controller Manager、Scheduler、kubelet)通过监听APIServer的事件变化,触发各自的业务逻辑(如Controller Manager维持副本数、Scheduler调度Pod、kubelet运行容器),最终完成整个资源的部署与管理。
1.2 Pod创建与工作机制流程
Pod的完整生命周期需多个组件协同完成,其核心依托于List-Watch模型实现动态响应。以下是Pod创建的典型流程:
1.2.1 初始化监听(List-Watch启动)
Controller Manager、Scheduler、kubelet启动后会分别通过Watch API Server(HTTPS 6443端口)监听集群资源事件变化。
- Controller Manager:监听副本控制类对象(如ReplicaSet、Deployment),确保资源符合期望状态;
- Scheduler:监听未被调度的Pod(
spec.nodeName为空),准备为其分配节点; - kubelet:监听分配到本节点的Pod,负责后续容器的创建与管理。
1.2.2 用户发起创建Pod对象请求
用户通过kubectl apply -f pod.yaml等命令,向APIServer发送创建Pod的请求。配置文件pod.yaml可能包含Pod的元数据、容器镜像、资源需求等信息。
1.2.3 etcd数据存储与事件触发
APIServer校验请求合法性后,将Pod元数据写入etcd;etcd写入成功后,向APIServer返回确认,并触发Create事件;APIServer将事件广播给所有监听的组件。
1.2.4 副本控制与调度
- Controller Manager响应:若Pod关联ReplicaSet/Deployment,Controller Manager会检查当前副本数是否符合期望,不足则创建新副本;
- APIServer更新数据:Controller Manager创建完Pod副本后,API Server会将Pod的详细信息更新写入etcd,触发更新事件;
- Scheduler调度:监听至未调度的Pod(Pending状态),通过调度算法选择合适节点,并将节点信息写入APIServer;
- 数据同步:APIServer更新etcd中Pod的节点绑定信息,完成调度确认。
1.2.5 容器运行与状态维护
目标节点的kubelet监听到新分配的Pod,调用容器运行时(如Docker、containerd)拉取镜像、创建并启动容器;kubelet将Pod状态(如Running、Failed)上报给APIServer;APIServer更新etcd中Pod的状态信息,etcd确认写入成功后,集群状态同步完成,Pod进入稳定运行(Running)阶段。
说明:kubelet会持续监听Pod事件,以应对副本数调整、镜像更新等动态变化,确保容器状态与期望一致。
二、Scheduler工作原理
2.1 调度器核心任务与目标
2.1.1 核心任务
Scheduler的核心职责是为未绑定节点的Pod(spec.nodeName == "")分配合适的Node节点,通过创建binding对象记录调度结果。
2.1.2 调度目标
- 公平性:确保节点间资源分配均衡,避免个别节点负载过高;
- 高效性:最大化集群资源利用率,减少资源浪费;
- 效率:快速完成大批量Pod的调度,降低调度延迟;
- 灵活性:支持自定义调度策略与插件,适配复杂业务场景。
2.2 调度流程详解
调度过程分为过滤(Predicate)和优选(Priorities)两个阶段,最终选择最优节点绑定Pod。
2.2.1 过滤阶段(Predicate)
过滤阶段通过一系列算法排除不满足Pod需求的节点,确保剩余节点均为“可行节点”。常见过滤算法如下:
| 算法名 | 功能描述 |
|---|---|
| PodFitsResources | 检查节点剩余CPU、内存等资源是否满足Pod需求 |
| PodFitsHost | 检查Pod指定的nodeName是否匹配当前节点 |
| PodFitsHostPorts | 检查Pod所需端口是否与节点已使用端口冲突 |
| PodSelectorMatches | 检查节点标签是否匹配Pod的标签选择器 |
| NoDiskConflict | 检查Pod挂载的Volume是否与节点已有挂载冲突 |
注意:若过滤后无可行节点,Pod将保持Pending状态,调度器会不断重试直至有节点满足条件;如果有多个节点满足条件,则进入优选阶段。
2.2.2 优选阶段(Priorities)
优选阶段对过滤后的可行节点进行打分排序,选择得分最高的节点作为目标。常见优选算法如下:
| 优先级项 | 描述 |
|---|---|
| LeastRequestedPriority | 节点资源使用率越低,得分越高(优先分配资源充足的节点) |
| BalancedResourceAllocation | 优先选择CPU与内存使用率更均衡的节点(如20%CPU+30%内存优于10%CPU+50%内存) |
| ImageLocalityPriority | 优先选择已缓存Pod所需镜像的节点(减少镜像拉取时间) |
最终,调度器将得分最高的节点作为Pod的绑定目标,并更新APIServer中的Pod信息。
三、指定调度节点的常用方式
3.1 nodeName:强制绑定节点
pod.spec.nodeName直接将Pod调度到指定节点,跳过Scheduler的调度逻辑,属于强制绑定。
3.1.1 配置示例
vim myapp.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
nodeName: node01 # 强制调度到node01
containers:
- name: myapp
image: soscscs/myapp:v1
ports:
- containerPort: 80
执行命令创建Deployment并验证:
# 执行YAML文件创建Deployment
kubectl apply -f myapp.yaml
# 查看Pod详细信息,全部调度到了node01
kubectl get pods -o wide
# 查看详细事件(确认未经过scheduler调度分配)
kubectl describe pod myapp-699655c7fd-c7shz
3.1.2 特点
- 直接由kubelet负责创建容器,无需Scheduler参与;
- 若指定节点不存在或不可用,Pod会一直处于Pending状态。
3.2 nodeSelector:基于标签匹配
pod.spec.nodeSelector通过标签选择器label-selector匹配节点,由Scheduler根据标签策略调度Pod到目标节点,属于强制约束。
3.2.1 操作流程
- 获取节点名称:
kubectl get node
- 为节点添加标签:
# 给对应的node设置标签分别为yjs=a和yjs=b
kubectl label nodes node01 yjs=a
kubectl label nodes node02 yjs=b
# 查看标签
kubectl get nodes --show-labels
- 配置Pod的nodeSelector:
vim myapp1.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp1
spec:
replicas: 3
selector:
matchLabels:
app: myapp1
template:
metadata:
labels:
app: myapp1
spec:
nodeSelector:
yjs: a
containers:
- name: myapp1
image: soscscs/myapp:v1
ports:
- containerPort: 80
- 执行配置并验证:
kubectl apply -f myapp1.yaml
# 查看Pod运行节点(所有Pod均运行在node01)
kubectl get pods -o wide
# 查看详细事件(确认经过scheduler调度分配)
kubectl describe pod myapp1-64c58784f9-4mgc6
3.2.2 标签管理命令
- 查看节点标签:
kubectl get nodes --show-labels - 筛选指定标签的节点:
kubectl get nodes -l yjs=a - 修改标签(需加
--overwrite):kubectl label nodes node01 yjs=b --overwrite - 删除标签:
kubectl label nodes node01 yjs-
四、亲和性与反亲和性调度
亲和性调度用于更灵活地控制Pod与节点、Pod与Pod之间的调度关系,分为节点亲和性、Pod亲和性与Pod反亲和性。
4.1 节点亲和性(NodeAffinity)
节点亲和性定义Pod对节点的“偏好”或“硬性要求”,类似“想去哪个班”(软策略)或“必须去哪个班”(硬策略)。
4.1.1 配置结构
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution: # 硬策略(必须满足)
nodeSelectorTerms:
- matchExpressions: # 匹配规则
- key: <节点标签键>
operator: <操作符>
values: [<值列表>]
preferredDuringSchedulingIgnoredDuringExecution: # 软策略(优先满足)
- weight: <权重,0-100> # 多个软策略时,权重越高优先级越高
preference:
matchExpressions:
- key: <节点标签键>
operator: <操作符>
values: [<值列表>]
4.1.2 支持的操作符
| 操作符 | 含义 | 示例 |
|---|---|---|
| In | 标签值在列表中 | env In (dev, test) |
| NotIn | 标签值不在列表中 | env NotIn (prod) |
| Gt | 标签值大于指定数值 | version Gt 3 |
| Lt | 标签值小于指定数值 | version Lt 3 |
| Exists | 标签存在(忽略值) | Exists zone |
| DoesNotExist | 标签不存在 | DoesNotExist debug |
4.1.3 实践案例
案例1:硬策略(必须排除node02)
# 创建目录并编写配置文件
mkdir /opt/affinity
cd /opt/affinity
vim pod1.yaml
apiVersion: v1
kind: Pod
metadata:
name: affinity
labels:
app: node-affinity-pod
spec:
containers:
- name: with-node-affinity
image: soscscs/myapp:v1
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname # 指定node的标签
operator: NotIn # 设置Pod安装到标签值不在values列表中的node上
values:
- node02
执行配置并验证:
# 创建Pod
kubectl apply -f pod1.yaml
# 查看Pod调度节点(确认不在node02上)
kubectl get pods -o wide
# 重新部署并验证(可选)
kubectl delete pod --all && kubectl apply -f pod1.yaml && kubectl get pods -o wide
注意:如果硬策略不满足条件,Pod状态会一直处于Pending状态。
案例2:软策略
vim pod2.yaml
apiVersion: v1
kind: Pod
metadata:
name: affinity01
labels:
app: node-affinity-pod
spec:
containers:
- name: with-node-affinity
image: soscscs/myapp:v1
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 5 # 多个软策略时,权重越大优先级越高
preference:
matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- node01
- weight: 10 # 权重更高,优先调度到node02
preference:
matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- node02
执行配置并验证:
kubectl apply -f pod2.yaml
# 查看Pod状态及调度节点(优先调度到权重高的node02)
kubectl get pods -o wide
案例3:软硬策略
如果同时使用硬策略和软策略,需先满足硬策略再满足软策略:
vim pod3.yaml
apiVersion: v1
kind: Pod
metadata:
name: affinity03
labels:
app: node-affinity-pod
spec:
containers:
- name: with-node-affinity
image: soscscs/myapp:v1
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution: # 先满足硬策略,排除node02
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: NotIn
values:
- node02
preferredDuringSchedulingIgnoredDuringExecution: # 再满足软策略,优先选择yjs=a的节点
- weight: 1
preference:
matchExpressions:
- key: yjs
operator: In
values:
- a
执行配置并验证:
kubectl apply -f pod3.yaml
# 查看Pod调度节点(不在node02且优先在yjs=a的节点)
kubectl get pods -o wide
4.2 Pod亲和性与反亲和性
Pod亲和性/反亲和性基于已有Pod的标签控制调度,用于实现“Pod与目标Pod同节点/同拓扑域”或“Pod与目标Pod分离”的需求。
4.2.1 核心区别
| 调度策略 | 匹配对象 | 核心作用 | 拓扑域支持 |
|---|---|---|---|
| nodeAffinity | 主机 | 根据节点标签控制Pod调度到指定节点(硬/软策略) | 不支持 |
| podAffinity | 其他Pod | 使当前Pod与目标Pod在同一拓扑域 | 支持 |
| podAntiAffinity | 其他Pod | 使当前Pod与目标Pod在不同拓扑域 | 支持 |
拓扑域:通过topologyKey指定节点标签,标签值相同的节点属于同一拓扑域(如同一机房、同一机架)。
4.2.2 实践案例
案例1:Pod亲和性(与myapp01同拓扑域)
- 为节点添加标签并创建目标Pod:
kubectl label nodes node01 yjs=a
kubectl label nodes node02 yjs=b
vim pod4.yaml
apiVersion: v1
kind: Pod
metadata:
name: myapp01
labels:
app: myapp01
spec:
containers:
- name: with-node-affinity
image: soscscs/myapp:v1
kubectl apply -f pod4.yaml
# 查看目标Pod状态及标签
kubectl get pods --show-labels -o wide
- 配置Pod亲和性调度:
vim pod5.yaml
apiVersion: v1
kind: Pod
metadata:
name: myapp02
labels:
app: myapp02
spec:
containers:
- name: myapp02
image: soscscs/myapp:v1
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- myapp01 # 匹配标签app=myapp01的Pod
topologyKey: yjs # 拓扑域为标签yjs,值相同的节点为同一域
- 执行配置并验证:
kubectl apply -f pod5.yaml
# 查看Pod调度节点(与myapp01同属yjs=a域)
kubectl get pods --show-labels -o wide
案例2:Pod反亲和性(与myapp01不同节点)
示例1:软策略反亲和
vim pod6.yaml
apiVersion: v1
kind: Pod
metadata:
name: myapp10
labels:
app: myapp10
spec:
containers:
- name: myapp10
image: soscscs/myapp:v1
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- myapp01 # 匹配标签app=myapp01的Pod
topologyKey: kubernetes.io/hostname # 拓扑域为节点主机名(单个节点)
执行配置并验证:
kubectl apply -f pod6.yaml
# 查看Pod调度节点(与myapp01不同节点)
kubectl get pods --show-labels -o wide
示例2:硬策略反亲和
前提:node01和node02都有yjs=a标签
vim pod7.yaml
apiVersion: v1
kind: Pod
metadata:
name: myapp20
labels:
app: myapp20
spec:
containers:
- name: myapp20
image: soscscs/myapp:v1
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- myapp01
topologyKey: yjs
执行配置并验证:
kubectl apply -f pod7.yaml
# 查看Pod状态(因无可用拓扑域,处于Pending状态)
kubectl get pod --show-labels -o wide
说明:node01和node02同属yjs=a拓扑域,反亲和要求新Pod与myapp01不在同一拓扑域,导致无可用节点。
五、污点(Taint)与容忍(Toleration)
节点亲和性是Pod的属性,使Pod被吸引到特定节点;而Taint(污点)则相反,使节点能够排斥特定Pod。污点与容忍机制用于实现节点对Pod的“排斥”与“例外”,节点通过污点拒绝不匹配的Pod,Pod通过容忍突破排斥。
5.1 污点(Taint)
5.1.1 污点格式与作用
污点格式:key=value:effect,其中effect定义排斥行为,支持以下三种类型:
| effect类型 | 描述 |
|---|---|
| NoSchedule | 拒绝调度新Pod到该节点(已运行的Pod不受影响) |
| PreferNoSchedule | 尽量不调度新Pod到该节点(非强制) |
| NoExecute | 拒绝新Pod调度,且驱逐已运行的不匹配Pod |
5.1.2 污点管理命令
- 添加污点:
kubectl taint node node01 key=value:NoSchedule - 查看节点污点:
kubectl describe node node01 | grep Taint - 删除污点:
kubectl taint node node01 key:NoSchedule-
示例:
# 查看节点列表
kubectl get nodes
# 查看master节点污点(默认有NoSchedule污点,不调度Pod)
kubectl describe node master | grep Taint
# 为node02添加NoExecute污点,驱逐现有不匹配Pod
kubectl taint node node02 check=mycheck:NoExecute
# 查看Pod状态(node02上的Pod被驱逐)
kubectl get pods -o wide
5.2 容忍(Toleration)
Pod通过容忍配置可突破节点的污点限制,需与节点污点的key、value、effect匹配。
5.2.1 配置示例
- 为节点添加污点并尝试创建Pod:
kubectl taint node node01 check=mycheck:NoExecute
# 创建无容忍配置的Pod(创建失败)
kubectl apply -f pod3.yaml
kubectl get pods -o wide
- 配置Pod容忍:
vim pod3.yaml
apiVersion: v1
kind: Pod
metadata:
name: myapp01
labels:
app: myapp01
spec:
containers:
- name: with-node-affinity
image: soscscs/myapp:v1
tolerations:
- key: "check"
operator: "Equal" # Equal需匹配value,Exists忽略value
value: "mycheck" # 与污点的value匹配
effect: "NoExecute" # 与污点的effect匹配
tolerationSeconds: 3600 # 被驱逐前可在节点上保留的时间(仅对NoExecute有效)
- 执行配置并验证:
kubectl apply -f pod3.yaml
# 查看Pod状态(创建成功)
kubectl get pods -o wide
5.2.2 特殊配置
- 容忍所有污点:
tolerations:
- operator: "Exists" # 忽略key和effect,容忍所有污点
- 容忍指定key的所有effect:
tolerations:
- key: "key"
operator: "Exists" # 忽略effect,容忍key=key的所有effect
- 多Master节点资源优化:
kubectl taint node Master-Name node-role.kubernetes.io/master=:PreferNoSchedule
5.2.3 污点在Node节点更新组件时的作用
# 节点更新前,添加NoExecute污点驱逐Pod
kubectl taint node node01 check=mycheck:NoExecute
# 资源不足时,临时允许Pod调度到Master
kubectl taint node master node-role.kubernetes.io/master=:PreferNoSchedule
# 节点更新完成后,去除污点恢复正常
kubectl taint node node01 check=mycheck:NoExecute-
注意:key、value、effect必须与Node上的污点匹配;operator: Exists表示存在即可,无需匹配value。
六、节点维护与Pod生命周期
6.1 节点维护操作
当需要对节点进行升级、检修时,可通过以下命令暂时隔离节点并迁移Pod:
| 命令 | 功能描述 |
|---|---|
kubectl cordon <node> |
标记节点为“不可调度”,阻止新Pod调度至此 |
kubectl drain <node> --ignore-daemonsets --delete-emptydir-data |
驱逐节点上的Pod(忽略DaemonSet,删除本地数据) |
kubectl uncordon <node> |
恢复节点为“可调度”状态 |
说明:drain命令等价于“cordon + 驱逐”,适用于节点离线维护场景。
示例:
# 标记node01为不可调度
kubectl cordon node01
# 查看节点污点(确认添加不可调度标记)
kubectl describe nodes node01 | grep Taint
# 驱逐node01上的Pod
kubectl drain node01 --ignore-daemonsets --delete-emptydir-data --force
# 查看Pod状态(已迁移至其他节点)
kubectl get pods -o wide
# 恢复node01为可调度状态
kubectl uncordon node01
# 验证污点已移除
kubectl describe nodes node01 | grep Taint
Kubernetes中的驱逐(Eviction)机制:
- 触发场景:节点维护/升级、节点资源不足;
- 决策依据:节点负载、Pod优先级;
- 终止流程:发送SIGTERM信号→预留终止时间窗口→超时强制终止。
6.2 Pod生命周期阶段(Phase)
Pod在生命周期中会经历以下阶段,反映其当前状态:
| 阶段 | 说明 |
|---|---|
| Pending | 已创建但未调度,或正在拉取镜像 |
| Running | 至少一个容器运行中(或启动/重启中) |
| Succeeded | 所有容器正常终止(如Job类任务完成) |
| Failed | 至少一个容器异常终止(非0退出码或被强制终止) |
| Unknown | 无法获取状态(通常为通信故障) |
阶段详解:
- Pending:APIServer已创建Pod并存入etcd,但未完成调度或正在下载镜像;
- Running:Pod已调度到节点,所有容器已创建,至少一个容器运行/启动/重启中(不一定可提供服务);
- Succeeded:适用于非持久运行Pod,所有容器成功终止且不重启;
- Failed:所有容器终止,至少一个容器故障退出;
- Unknown:kube-controller-manager与Pod通信异常。
七、故障排查常用命令
| 操作需求 | 命令示例 |
|---|---|
| 查看Pod事件(调度失败原因) | kubectl describe pod <pod-name> |
| 查看Pod日志 | kubectl logs <pod-name> [-c <container-name>] |
| 进入容器执行命令 | kubectl exec -it <pod-name> -- bash |
| 查看集群状态 | kubectl cluster-info |
| 查看节点状态 | kubectl get nodes |
| 查看kubelet日志 | journalctl -xefu kubelet |
总结
Kubernetes调度是集群资源管理的核心,通过组件协作、调度算法、亲和性策略、污点与容忍等机制,实现了Pod的智能分配。本文从基础机制到实践操作,详细介绍了:
- 组件通过List-Watch机制协同工作,完成Pod从创建到运行的全流程;
- 调度器通过过滤与优选阶段选择最优节点,平衡资源利用率与业务需求;
- 节点选择方式从简单的强制绑定(nodeName)到灵活的标签匹配(nodeSelector)、亲和性调度,满足不同场景需求;
- 污点与容忍机制实现节点对Pod的精细控制,配合节点维护命令保障集群稳定性。
更多推荐


所有评论(0)