文章目录
一、Kubernetes 调度器
【集群调度官方文档:https://kubernetes.io/zh/docs/concepts/scheduling-eviction/kube-scheduler/】
在 Kubernetes 中,调度 是指将 Pod 放置到合适的 Node 上,然后对应 Node 上的 Kubelet 才能够运行这些 pod。
调度器通过 kubernetes 的监测(Watch)机制来发现集群中新创建且尚未被调度到 Node 上的 Pod。 调度器会将发现的每一个未调度的 Pod 调度到一个合适的 Node 上来运行。 调度器会依据下文的调度原则来做出调度选择
1.1 kube-scheduler
【kube-scheduler命令官方文档:https://kubernetes.io/zh/docs/reference/command-line-tools-reference/kube-scheduler/】
kube-scheduler 是 Kubernetes 集群的默认调度器,并且是集群 控制面 的一部分。 如果你真的希望或者有这方面的需求,kube-scheduler 在设计上是允许 你自己写一个调度组件并替换原有的 kube-scheduler。
对每一个新创建的 Pod 或者是未被调度的 Pod,kube-scheduler 会选择一个最优的 Node 去运行这个 Pod。然而,Pod 内的每一个容器对资源都有不同的需求,而且 Pod 本身也有不同的资源需求。因此,Pod 在被调度到 Node 上之前, 根据这些特定的资源调度需求,需要对集群中的 Node 进行一次过滤。
在一个集群中,满足一个 Pod 调度请求的所有 Node 称之为 可调度节点。 如果没有任何一个 Node 能满足 Pod 的资源请求,那么这个 Pod 将一直停留在 未调度状态直到调度器能够找到合适的 Node。
调度器先在集群中找到一个 Pod 的所有可调度节点,然后根据一系列函数对这些可调度节点打分, 选出其中得分最高的 Node 来运行 Pod。之后,调度器将这个调度决定通知给 kube-apiserver,这个过程叫做 绑定。
在做调度决定时需要考虑的因素包括:单独和整体的资源请求、硬件/软件/策略限制、 亲和以及反亲和要求、数据局域性、负载间的干扰等等。
二、节点选择约束方式
【约束节点官方文档:https://kubernetes.io/zh/docs/concepts/scheduling-eviction/assign-pod-node/】
master端:server2
node节点:server3、server4
Harbor仓库:server1
2.1 nodeName
nodeName 是节点选择约束的最简单方法,但由于其自身限制,一般不推荐。
如果 nodeName 在 PodSpec
中指定了,则它优先于其他的节点选择方法。
使用 nodeName 来选择节点的一些限制:
- 如果指定的节点不存在。
- 如果指定的节点没有资源来容纳 pod,则pod 调度失败。
- 云环境中的节点名称并非总是可预测或稳定的。
nodeName示例:
此pod将运行在server3上
# pod.yml
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx
nodeName: server3
将 nodeName
的值改为不存在的节点:server5,则一直处于 Pending 状态,一会会后自动取消调度
2.2 nodeSelector
nodeSelector
是节点选择约束的最简单推荐形式
nodeSelector 示例:
1)添加标签 disktype=ssd
到节点
2)添加 nodeSelector 字段到 Pod 配置中
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
env: test
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
nodeSelector:
disktype: ssd
2.3 亲和性与反亲和性
- nodeSelector 提供了一种非常简单的方法来将 pod 约束到具有特定标签的节点上。亲和/反亲和功能极大地扩展了你可以表达约束的类型。
- 你可以发现规则是“软”/“偏好”,而不是硬性要求,因此,如果调度器无法满足该要求,仍然调度该 pod
- 你可以使用节点上的 pod 的标签来约束,而不是使用节点本身的标签,来允许哪些 pod 可以或者不可以被放置在一起。
亲和性功能包含两种类型的亲和性:
- 节点亲和性:节点亲和性就像现有的 nodeSelector(但具有上面列出的前两个好处)
- Pod 间亲和性/反亲和性:约束 Pod 标签而不是节点标签
2.3.1 节点亲和性(nodeAffinity)
两种类型的节点亲和性:
requiredDuringSchedulingIgnoredDuringExecution
:硬需求(必须满足的规则)preferredDuringSchedulingIgnoredDuringExecution
:软需求(将尝试执行但不能保证)
nodeaffinity支持多种规则匹配条件的配置:
- In:label 的值在列表内
- NotIn:label 的值不在列表内
- Gt:label 的值大于设置的值,不支持Pod亲和性
- Lt:label 的值小于设置的值,不支持pod亲和性
- Exists:设置的label 存在
- DoesNotExist:设置的 label 不存在
“IgnoredDuringExecution” 表示如果在Pod运行期间Node的标签发生变化,导致亲和性策略不能满足,则继续运行当前的Pod。
节点亲和性通过 PodSpec 的 affinity
字段下的 nodeAffinity
字段进行指定。
节点亲和性示例: 规定硬需求为 disktype=ssd
或 disktype=sata
,软需求为 roles=nginx
# pod.yml
apiVersion: v1
kind: Pod
metadata:
name: node-affinity
spec:
containers:
- name: nginx
image: nginx
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: disktype
operator: In
values:
- ssd
- sata
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
- key: roles
operator: In
values:
- nginx
1)只有节点server3满足硬需求,调度server3
2)节点server3和server4都满足硬需求,server4满足软需求,调度server4
3)节点server4满足软需求,但两个节点都不满足硬需求,无节点可调度,处于 Pending 状态
2.3.2 pod 间亲和性与反亲和性
Pod 间亲和性与反亲和性需要大量的处理,这可能会显著减慢大规模集群中的调度。 我们不建议在超过数百个节点的集群中使用它们。
通过 PodSpec 中的两个字段进行指定:
podAffinity
:pod间亲和性(主要解决POD可以和哪些POD部署在同一个拓扑域中的问题(拓扑域用主机标签实现,可以是单个主机,也可以是多个主机组成的cluster、zone等)podAntiAffinity
:pod间反亲和性(主要解决POD不能和哪些POD部署在同一个拓扑域中的问题。它们处理的是Kubernetes集群内部POD和POD之间的关系)
与节点亲和性一样,当前有两种类型的 Pod 亲和性与反亲和性,即requiredDuringSchedulingIgnoredDuringExecution
和preferredDuringSchedulingIgnoredDuringExecution
,分别表示“硬性”与“软性”要求。
topologyKey
是节点标签的键以便系统 用来表示这样的拓扑域
亲和性示例: 将pod:myapp必须调度到带有标签 app=nginx
的pod上
cat pod.yml
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
---
apiVersion: v1
kind: Pod
metadata:
name: myapp
labels:
app: myapp
spec:
containers:
- name: myapp
image: myapp:v1
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- nginx
topologyKey: kubernetes.io/hostname
反亲和性示例: 将pod:myapp必须调度到没有标签 app=nginx
的pod上
将上述文件的参数 podAffinity
改为 podAntiAffinity
三、污点和容忍度
NodeAffinity节点亲和性,是Pod上定义的一种属性,使Pod能够按我们的要求调度到某个Node上,而Taints则恰恰相反,它可以让Node拒绝运行Pod,甚至驱逐Pod。
- Taints(污点)是Node的一个属性,设置了Taints后,所以Kubernetes是不会将Pod调度到这个Node上的
- Tolerations(容忍),只要Pod能够容忍Node上的污点,那么Kubernetes就会忽略Node上的污点,就能够(不是必须)把Pod调度过去。
3.1 污点
污点有关命令:
# 增加污点
kubectl taint nodes 节点 key1=v1:NoSchedule
# 查看污点
kubectl describe nodes 节点 | grep Taints
# 删除污点
kubectl taint nodes 节点 key1=v1:NoSchedule-
effect 可取值:
NoSchedule
:POD 不会被调度到标记为 taints 节点,只会影响新创建的pod。PreferNoSchedule
:NoSchedule 的软策略版本。NoExecute
:该选项意味着一旦 Taint 生效,如该节点内正在运行的 POD 没有对应Tolerate 设置,会直接被逐出。
污点示例
1)创建pod
# pod.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-server
spec:
selector:
matchLabels:
app: nginx
replicas: 3
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
2)在节点server3上增加污点 NoExecute
,发现server3的调度被驱离
3)在节点server4上增加污点 NoSchedule
,增加副本数,应用pod文件,发现新创建的pod处于 pending 状态,但以前调度在server4上的pod仍旧Running
3.2 容忍度
一个容忍度和一个污点相“匹配”是指它们有一样的键名和效果,并且:
- operator 是
Exists
(此时容忍度不能指定 value)即这个容忍度能容忍任意 taint。 - operator 是
Equal
(默认):则它们的value
应该相等
容忍示例: 在污点示例文件添加容忍如下
1)添加容忍 Exists
,有任意污点的节点都可以被调度
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-server
spec:
selector:
matchLabels:
app: nginx
replicas: 5
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
spec:
containers:
- name: nginx
image: nginx
tolerations:
- operator: "Exists"
2)增加副本数足够大(这里设为15),master节点也会被调度
3)删除污点
四、其他调度策略
影响Pod调度的指令还有:cordon、drain、delete,后期创建的pod都不会被调度到该节点上,但操作的暴力程度不一样。
4.1 cordon 停止调度
影响最小:
- 只会将节点调为
SchedulingDisabled
- 新创建pod时,不会被调度到该节点
- 节点原有pod不受影响,仍正常对外提供服务。
# 停止调度
kubectl cordon server3
# 恢复
kubectl uncordon server3
cordon 示例:
4.2 drain 驱逐节点
- 首先会驱逐node上的pod,在其他节点重新创建
- 然后将节点调为
SchedulingDisabled
# 驱逐
kubectl drain server3
# 恢复
kubectl uncordon server3
drain 示例:
4.3 delete 删除节点
最暴力的一个:
- 首先驱逐node上的pod,在其他节点重新创建
- 然后,从master节点删除该node,master失去对其控制
如要恢复调度,需进入node节点,重启kubelet服务(基于node的自注册功能,恢复使用)
# 删除节点
kubectl delete node server3
# 基于node的自注册功能,恢复使用
systemctl restart kubelet
delete 示例:
1)在master节点删除server3
2)在node节点重启 kubelet 服务,master端查询已经自注册