目录
一、调度简介
kube-scheduler是Kubernetes中的关键模块,扮演管家的角色遵从一套机制为Pod提供调度服务,例如基于资源的公平调度、调度Pod到指定节点、或者通信频繁的Pod调度到同一节点等。容器调度本身是一件比较复杂的事,因为要确保以下几个目标:
公平性:在调度Pod时需要公平的进行决策,每个节点都有被分配资源的机会,调度器需要对不同节点的使用作出平衡决策。
资源高效利用:最大化群集所有资源的利用率,使有限的CPU、内存等资源服务尽可能更多的Pod。
效率问题:能快速的完成对大批量Pod的调度工作,在集群规模扩增的情况下,依然保证调度过程的性能。
灵活性:在实际运作中,用户往往希望Pod的调度策略是可控的,从而处理大量复杂的实际问题。因此平台要允许多个调度器并行工作,同时支持自定义调度器
设计相关概念:nodeName、nodeSelector、亲和性、污点、容忍、删除节点等,下面依次进行讲解与实验。
nodename
nodeName 是节点选择约束的最简单方法,但一般不推荐。如果 nodeName 在 PodSpec 中指定了,则它优先于其他的节点选择方法。直接就指定在哪个节点调度。
使用 nodeName 来选择节点的一些限制:
(1)如果指定的节点不存在。
(2)如果指定的节点没有资源来容纳 pod,则pod 调度失败。
(3)云环境中的节点名称并非总是可预测或稳定的。
编辑nodename.yaml文件
[root@k8s2 node]# vim nodename.yaml
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
nodeName: k8s4
若找不到节点pod会出现pending
[root@k8s2 node]# kubectl apply -f nodename.yaml
回收
[root@k8s2 node]# kubectl delete -f nodename.yaml
nodeselector
nodeSelector 是节点选择约束的实用形式,它的原理是匹配某些特殊的标签,满足该条件的节点就可以被调度过去。标签是可以为不同节点加的。
编辑nodeselector.yaml文件
[root@k8s2 node]# vim nodeselector.yaml
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
nodeSelector:
disktype: ssd #选择标签disktype=ssd的node
不给节点加标签,pod会一直处于pending状态,因为节点都没有ssd这个标签。
给节点加标签
[root@k8s2 node]# kubectl label nodes k8s4 disktype=ssd
[root@k8s2 node]# kubectl label nodes k8s3 disktype=ssd
运行,发现通过标签匹配到k8s4上了
[root@k8s2 node]# kubectl apply -f nodeselector.yaml
回收
[root@k8s2 node]# kubectl delete -f nodeselector.yaml
nodeaffinity
节点亲和性,用于替换nodeSelector的全新调度策略。
目前有两种节点亲和性表达:
1.Required During Scheduling Ignored During Execution:
必须满足制定的规则才可以调度pod到Node上。相当于硬限制
2.Preferred During Scheduling Ignore During Execution:
强调优先满足制定规则,调度器会尝试调度pod到Node上,但并不强求,相当于软限制。
多个优先级规则还可以设置权重值,以定义执行的先后顺序。
IgnoredDuringExecution
的意思是:
如果一个pod所在的节点在pod运行期间标签发生了变更,不在符合该pod的节点亲和性需求,则系统将忽略node上lable的变化,该pod能机选在该节点运行。
NodeAffinity 语法支持的操作符包括:
- In:label 的值在某个列表中
- NotIn:label 的值不在某个列表中
- Exists:某个 label 存在
- DoesNotExit:某个 label 不存在
- Gt:label 的值大于某个值
- Lt:label 的值小于某个值
nodeAffinity规则设置的注意事项如下:
1.如果同时定义了nodeSelector和nodeAffinity,name必须两个条件都得到满足,pod才能最终运行在指定的node上。
2.如果nodeAffinity指定了多个nodeSelectorTerms,那么其中一个能够匹配成功即可。
3.如果在nodeSelectorTerms中有多个matchExpressions,则一个节点必须满足所有matchExpressions才能运行该pod。
编辑nodeaffinity.yaml文件
[root@k8s2 node]# vim nodeaffinity.yaml
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
- fc
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1 #倾向权限可设置
preference:
matchExpressions:
- key: kubernetes.io/hostname
operator: NotIn #不要往这上调度
values:
- k8s3
运行并查看,require为必须满足,3和4标签都有,即必须在3和4之间,preference为倾向,尽量不往3上面,则最后pod运行在了4上面
[root@k8s2 node]# kubectl apply -f nodeaffinity.yaml
[root@k8s2 node]# kubectl describe pod node-affinity
回收
[root@k8s2 node]# kubectl delete -f nodeaffinity.yaml
pod 亲和性和反亲和性
除了上面的节点的亲和性,也可以使用 pod 的标签来约束,而不是使用节点本身的标签,来允许哪些 pod 可以或者不可以被放置在一起。
pod 亲和性和反亲和性:
(1)podAffinity 主要解决pod可以和哪些pod部署在同一个拓扑域中的问题(拓扑域用主机标签实现,可以是单个主机,也可以是多个主机组成的cluster、zone等)。即pod2想和某些特殊的pod1在同一个节点工作
(2)podAntiAffinity主要解决pod不能和哪些pod部署在同一个拓扑域中的问题。它们处理的是Kubernetes集群内部pod和pod之间的关系。即pod2想避开某些特殊的pod1所在的那个节点
podaffinity
pod亲和性
先创建一个pod到k8s4上,标签为nginx
编辑yaml文件,让他跟随标签为nginx的pod,生产环境中也确实有这种需要,nginx服务和mysql数据库要放在一起
[root@k8s2 node]# vim podaffinity.yaml
apiVersion: v1
kind: Pod
metadata:
name: mysql
labels: #mysql这个pod的标签是app=mysql
app: mysql
spec:
containers:
- name: mysql
image: mysql:5.7
env: 环境变量
- name: "MYSQL_ROOT_PASSWORD"
value: "westos"
affinity:
podAffinity: #pod亲和性
requiredDuringSchedulingIgnoredDuringExecution: #必须满足以下条件
- labelSelector:
matchExpressions:
- key: app #标签app=nginx
operator: In
values:
- nginx
topologyKey: kubernetes.io/hostname
运行,发现跟随nginx到4上面了
[root@k8s2 node]# kubectl apply -f podaffinity.yaml
回收
[root@k8s2 node]# kubectl delete -f podaffinity.yaml
podantiaffinity
同样的,改为反亲和
[root@k8s2 node]# vim podantiaffinity.yaml
...
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
...
创建pod,可以看到,mysql和nginx专门不在同一个节点上,起到互斥效果
[root@k8s2 node]# kubectl apply -f podantiaffinity.yaml
回收
[root@k8s2 node]# kubectl delete -f podantiaffinity.yaml
Taints
节点亲和性,是Pod的一种属性(偏好或硬性要求),它使Pod被吸引到一类特定的节点
Taint 则相反,它使节点能够排斥一类特定的 Pod
Taint 和 Toleration 相互配合,可以用来避免 Pod 被分配到不合适的节点上。每个节点上都可以应用一个或多个 taint ,这表示对于那些不能容忍这些 taint 的 Pod,是不会被该节点接受的。如果将 toleration 应用于 Pod 上,则表示这些 Pod 可以(但不一定)被调度到具有匹配 taint 的节点上
污点的设置和去除
# 设置污点
kubectl taint nodes node1 xtz=value1:NoSchedule# 去除污点
kubectl taint nodes node1 xtz:NoSchedule-#节点说明中,查找 Taints 字段
kubectl describe node node-name
NoSchedule:POD 不会被调度到标记为 taints 的节点
PreferNoSchedule:NoSchedule 的软策略版本(最好不要调度到标记为 taints 的节点)
NoExecute:该选项意味着一旦 Taint 生效,如该节点内正在运行的 POD 没有对应 Tolerate 设置,会直接被逐出
NoSchedule+标签选择
k8s2上面就有污点,所以不会调度到2上面。
[root@k8s2 node]# vim taint.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: web
name: web
spec:
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- image: nginx
name: nginx
[root@k8s2 node]# kubectl apply -f taint.yaml
设置taint,给3加个污点
[root@k8s2 pod]# kubectl taint node k8s3 k1=v1:NoSchedule
查询说明
[root@k8s2 pod]# kubectl describe nodes k8s3 |grep Tain
Taints: k1=v1:NoSchedule
将副本数扩大到6个
[root@k8s2 pod]# kubectl scale deployment web --replicas 6
新的pod都在4,没有往3调度
修改参数,正在运行的pod都被驱逐
[root@k8s2 pod]# kubectl taint node k8s3 k1=v1:NoExecute
node/k8s3 tainted
[root@k8s2 pod]# kubectl describe nodes k8s3 |grep Tain
Taints: k1=v1:NoExecute
可查看到之前的都是4上的了
回收
[root@k8s2 node]# kubectl delete -f taint.yaml
设置 tolerations
[root@k8s2 node]# vim taint.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: web
name: web
spec:
replicas: 6
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
tolerations:
- operator: Exists
effect: NoSchedule
containers:
- image: nginx
name: nginx
运行,容忍NoSchedul,发现2上也可以了
[root@k8s2 node]# kubectl apply -f taint.yaml
回收
[root@k8s2 node]# kubectl delete -f taint.yaml
容忍所有taints
[root@k8s2 node]# vim taint.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: web
name: web
spec:
replicas: 6
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
tolerations:
- operator: Exists
containers:
- image: nginx
name: nginx
容忍Exists,这下都可以了,3上也有了
[root@k8s2 node]# kubectl apply -f taint.yaml
回收
[root@k8s2 node]# kubectl delete -f taint.yaml
删除taints
[root@k8s2 node]# kubectl taint node k8s3 k1-
cordon、drain、delete
影响Pod调度的指令还有:cordon、drain、delete,后期创建的pod都不会被调度到该节点上,但操作的暴力程度不一样,逐渐递增。
创建三个副本
[root@k8s2 node]# kubectl create deployment demo --image nginx --replicas 3
cordon
停止调度3
[root@k8s2 node]# kubectl cordon k8s3
将node调为SchedulingDisabled
[root@k8s2 node]# kubectl get node
NAME STATUS ROLES AGE VERSION
k8s2 Ready control-plane 22d v1.24.0
k8s3 Ready,SchedulingDisabled <none> 22d v1.24.0
k8s4 Ready <none> 22d v1.24.0
隔离server3后,状态为SchedulingDisabled,新的pod不会被调度到server3
[root@k8s2 node]# kubectl scale deployment demo --replicas 6
drain
首先驱逐node上的已有pod,在其他节点重新创建,然后将节点调为SchedulingDisabled。
[root@k8s2 node]# kubectl drain k8s3 --ignore-daemonsets
delete
delete 删除节点。最暴力的一个,首先驱逐node上的pod,在其他节点重新创建,然后,从master节点删除该node,master失去对其控制
[root@k8s2 node]# kubectl delete nodes k8s3
[root@k8s2 node]# kubectl get node
NAME STATUS ROLES AGE VERSION
k8s2 Ready control-plane 22d v1.24.0
k8s4 Ready <none> 22d v1.24.0
k8s3节点重启kubelet服务重新加入集群
[root@k8s3 ~]# systemctl restart kubelet
[root@k8s2 node]# kubectl get node
NAME STATUS ROLES AGE VERSION
k8s2 Ready control-plane 22d v1.24.0
k8s3 Ready <none> 17s v1.24.0
k8s4 Ready <none> 22d v1.24.0