kubernetes 核心技术-调度器
在 Kubernetes 集群中,调度器扮演着至关重要的角色。它负责决定将哪些 Pod 放置到哪些节点上运行,以确保集群资源得到高效利用的同时满足各种约束条件。调度器不仅要考虑 CPU 和内存等基本资源的需求,还需要处理诸如亲和性、反亲和性、污点与容忍度等高级特性。本文将深入探讨 Kubernetes 调度器的工作原理及其核心概念,并介绍如何自定义调度策略来优化您的应用部署。
什么是 Kubernetes 调度器?
定义与背景
Kubernetes 调度器是一个控制平面组件,它根据一系列规则和策略选择最合适的节点来运行新的 Pods。其主要职责包括但不限于:
- 资源分配:基于请求的资源量(如 CPU、内存)匹配合适的节点。
- 服务质量保证:支持不同级别的服务质量(QoS),确保关键业务优先获得资源。
- 约束满足:遵守用户指定的各种软硬性约束,比如节点亲和性、Pod 亲和性和反亲和性等。
- 故障恢复:当某个节点出现故障时,重新调度受影响的 Pods 到健康的节点上。
关键特性
- 智能决策:通过复杂的算法评估每个节点的适用性,寻找最优解。
- 扩展能力:允许开发者自定义调度逻辑,满足特定需求。
- 高可用设计:即使面对大规模集群,也能保证高效的调度性能。
调度流程详解
Kubernetes 调度过程大致可以分为两个阶段:过滤(Filtering) 和 评分(Scoring)。
过滤阶段
在此阶段,调度器会遍历集群中的所有节点,剔除那些不符合 Pod 声明要求的节点。这些要求可能包括但不限于:
- 资源需求匹配:确保节点有足够的可用资源(CPU、内存等)来承载 Pod。
- 节点标签筛选:如果 Pod 指定了
nodeSelector
或者使用了节点亲和性规则,则只有符合条件的节点才会被保留。 - 污点与容忍度检查:如果一个节点设置了污点(Taints),那么除非 Pod 明确声明了相应的容忍度(Tolerations),否则该 Pod 将不会被调度到这个节点上。
示例
假设我们有一个 Pod 定义如下:
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
nodeSelector:
disktype: ssd
在这个例子中,除了常规的资源请求外,还指定了 nodeSelector
来限制 Pod 只能部署到带有 disktype=ssd
标签的节点上。这意味着,在过滤阶段,所有未标记为 SSD 的节点都将被排除在外。
评分阶段
经过过滤后剩下的候选节点将进入评分阶段。此时,调度器会对每个节点进行打分,分数越高表示越适合运行目标 Pod。常用的评分依据包括:
- 资源利用率:倾向于选择那些资源使用率较低的节点,以便更好地平衡负载。
- 亲和性偏好:如果有配置 Pod 亲和性或反亲和性规则,则会根据这些规则调整得分。
- 其他自定义规则:开发者可以通过插件机制添加额外的评分标准。
最终,得分最高的节点会被选中作为 Pod 的宿主节点。
自定义调度策略
虽然默认的调度器已经非常强大,但在某些情况下,您可能希望进一步定制化调度行为。Kubernetes 提供了几种方式来实现这一点:
节点亲和性(Node Affinity)
节点亲和性允许您更灵活地指定 Pod 应该部署在哪类节点上。它有两种形式:
- requiredDuringSchedulingIgnoredDuringExecution:必须满足的条件,类似于硬性约束。
- preferredDuringSchedulingIgnoredDuringExecution:优先考虑但非强制性的条件,属于软性约束。
示例
apiVersion: v1
kind: Pod
metadata:
name: with-node-affinity
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/e2e-az-name
operator: In
values:
- e2e-az1
- e2e-az2
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
- key: another-node-label-key
operator: In
values:
- another-node-label-value
containers:
- name: with-node-affinity
image: k8s.gcr.io/pause:2.0
Pod 亲和性与反亲和性(Pod Affinity & Anti-Affinity)
Pod 亲和性和反亲和性使您可以基于现有 Pods 的位置来指导新 Pod 的放置。例如,为了提高性能,您可能想要让某些服务的实例尽可能靠近彼此;相反地,为了避免单点故障,您也可能希望它们分布在不同的节点上。
示例
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: security
operator: In
values:
- S1
topologyKey: failure-domain.beta.kubernetes.io/zone
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: security
operator: In
values:
- S2
topologyKey: kubernetes.io/hostname
自定义调度器
对于需要高度定制化的场景,Kubernetes 还支持创建完全自定义的调度器。这通常涉及到开发一个新的可执行文件,并将其部署到集群中作为一个独立的服务。然后,您可以通过在 Pod 规范中指定 .spec.schedulerName
字段来选择使用哪个调度器。
实战演练
接下来我们将通过几个实际的例子来展示如何运用上述概念优化 Kubernetes 中的应用部署。
使用节点亲和性部署应用
假设我们要部署一个对磁盘 I/O 敏感的应用程序,并且只希望将其部署到具有 SSD 存储的节点上。我们可以这样做:
apiVersion: v1
kind: Pod
metadata:
name: io-sensitive-app
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: disktype
operator: In
values:
- ssd
containers:
- name: app-container
image: myregistry/myapp:v1
这样,只有标记了 disktype=ssd
的节点才有可能成为该 Pod 的宿主。
实现跨区域容错部署
为了增强服务的可靠性,我们可以利用 Pod 反亲和性规则,确保同一服务的不同实例不会同时位于同一个区域内的相同节点上:
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-server
spec:
replicas: 3
selector:
matchLabels:
app: web-server
template:
metadata:
labels:
app: web-server
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- web-server
topologyKey: topology.kubernetes.io/zone
containers:
- name: web-server
image: nginx:latest
这段代码确保了三个副本的 Web 服务器 Pod 不会在同一个区域内运行在同一节点上,从而提高了系统的容错能力。
结语
感谢您的阅读!如果您对调度器或 Kubernetes 有任何疑问或见解,欢迎继续探讨。