[TOC]
0x01 Scheduler-任务调度器
Q: 什么是Scheduler?
答: Scheduler
美 /ˈskedʒuːlər/
是 kubernetes 中的调度器组件, 其主要任务是把指定的Pod分配到集群工作节点(或者说非污点节点)之上
;
他是作为单独的程序运行的启动之后会一直和APIServer持续连接,当然有 pod 的资源清单中 Pod.Spec.NodeName 为空的时候,对每个pod都会创建一个binding,表明该 pod 应该放到哪个节点上
Tips: 由于Pod调度策略的随机性,其目的是自定义Pod调度到哪一个节点;
Q: 实现 Scheduler 调度器需要考虑的问题?
1.公平: 如何保证每个节点都能被分配.
2.资源高效利用: 集群所有资源最大化被使用.
3.效率: 调度的性能要好,能够尽快地对大批量的pod 完成调度工作.
4.灵活: 允许用户根据自己的需求控制调度的逻辑.
1.调度基础概念
(1)调度过程(构成部分)说明:
- 1.首先是过滤掉不满足条件的节点,这个过程称为
predicate 英 /ˈprɛdɪˌkeɪt/
– v. 使……基于; - 2.然后对通过的节点按照优先级排序,这个过程称为
priority 英 /praɪˈɒrəti/
– n. 优先;优先权; - 3.最后从中选择优先级最高的节点。
Tips: 如果中间任何一步骤有错误就直接返回错误;
- Predicate 系列的算法
- PodFitsResources :节点上剩余的资源是否大于 pod请求的资源
- PodFitsHost :如果pod指定了NodeName,检查节点名称是否和NodeName相匹配
- PodFitsHostPorts :节点上已经使用的port是否和 pod申请的port冲突
- PodSelectorMatches : 过滤掉 和 pod 指定 的label 不匹配的节点
- NoDiskConflict : 已经 mount 的 volume 和 pod 指定的 volume 不冲突,除非它们都是只读
priority 选项
描述: 优先级由一系列键值对组成,键是该优先级项的名称,值是它的权重(非常重要)一般得权重越高即优先级越高
,通过算法对所有的优先级项目和权重进行计算得出最终的结果;
这些优先级选项包括:
LeastRequestedPriority : 通过计算CPU和 Memory 的使用率来决定权重,使用率越低权重越高。这个优先级指标倾向于资源使用比例更低的节点;
BalancedResourceAllocation : 节点上 CPU 和 Memory 使用率越接近,权重越高,该项需要与LeastRequestedPriority
一起使用不能单独使用;
# 其含义示例是表达如下:
Node1:CPU 占用 20% , Memory 占用 60 %;
Node2: CPU 占用 50% , Memory 占用 50 %;
# 此时由于Node2资源分配更加均衡此时我们的Pod将会优先调度到此节点之上;
ImageLocalityPriority : 倾向于已经有要使用镜像的节点,镜像总大小值越大,权重越高
Tips: 如果在 predicate 过程中没有合适的节点,pod 会一直在 pending 状态,不断重试调度直到有节点满足条件(后面将会进行实践
)。之后如果有多个节点满足此条件就继续 priorities 过程按照优先级大小对节点排序
;
2.自定义调度器
描述: 除了 kubernetes 自带的调度器开发者也可以编写自己的调度器, 通过 spec:schedulername
参数指定调度器的名字,可以为 pod 选择某个调度器进行调度;
Kubernetes也允许用户编写自己的调度器组件,并在创建资源的时候引用它。多个调度器可以同时运行和工作,只要名字不冲突, Kubernetes提供的调度器名字是default
。
例如: 使用某个调度器就是在Pod的spec.schedulername
字段中填写上调度器的名字。如果自定义的调度器名字是my-scheduler,那么只有当spec.schedulername字段是my-scheduler才会被调度;
资源清单:
# 资源对象: pod.spec.schedulername
# 通过 spec:schedulername 参数指定调度器的名字,可以为pod 选择某个调度器进行调度
apiVersion: v1
kind: Pod
metadata:
name: annotation-second-scheduler
labels:
name: multischeduler-example
spec:
schedulername: my-scheduler # 关键点 (选择调度器)
containers:
- name: pod-with-second-annotation-container
image: gcr.io/google_containers/pause:2.0
简单调度脚本: 它通过kubectl命令从apiserver获取未调度的Pod(spec.schedulerName 是my-scheduler,并且spec.nodeName 为空
),同样地,用kubectl从apiserver获取nodes的信息,然后随机选择一个node作为调度结果,并写入到apiserver中。我们可通过kubectl describe pod pod_name
查看一个Pod采用的调度器;
#!/bin/bash
SERVER='localhost:8001'
while true;
do
for PODNAME in $(kubectl --server $SERVER get pods -o json | jq '.items[] | select(.spec.schedulerName == "my-scheduler") | select(.spec.nodeName == null) | .metadata.name' | tr -d '"')
;
do
NODES=($(kubectl --server $SERVER get nodes -o json | jq '.items[].metadata.name' | tr -d '"'))
NUMNODES=${#NODES[@]}
CHOSEN=${NODES[$[ $RANDOM % $NUMNODES ]]}
curl --header "Content-Type:application/json" --request POST --data '{"apiVersion":"v1", "kind": "Binding", "metadata": {"name": "'$PODNAME'"}, "target": {"apiVersion": "v1", "kind"
: "Node", "name": "'$CHOSEN'"}}' http://$SERVER/api/v1/namespaces/default/pods/$PODNAME/binding/
echo "Assigned $PODNAME to $CHOSEN"
done
sleep 1
done
3.nodeAffinity – 节点亲和性
资源对象: pod.spec.nodeAffinity
软策略 : ·preferredDuringSchedulingIgnoredDuringExecution: 【我想要去这个节点】 - adj. 优先的;首选的
硬策略 : ·requiredDuringschedulinglgnoredDuringExecution: 【我一定要去这个节点】 - adj. 必需的;(美)必修的
什么是Pod调度的软策略以及硬策略?
1.软策略: 即希望 , 表示想到满足条件的节点上去,当不满足时候就到其它节点上去。
2.硬策略: 即强制 , 表示一定到某一个节点上去, 当不满足条件时候其哪一个节点也不去,注意其优先级高于软策略
;
键值运算关系(即operator的可用值):
- In: label 的值在某个列表中
- NotIn: label 的值不在某个列表中
- Gt: label 的值大于某个值
- Lt: label 的值小于某个值
- Exists: 某个label存在
- DoesNotExist: 某个label不存在
硬策略
资源清单: affinity-strong-demo.yaml
cat > affinity-strong-demo.yaml<<'EOF'
apiVersion: v1
kind: Pod
metadata:
name: strong-affinity
labels:
app: node-strong-affinity-pod
spec:
containers:
- name: with-strong-node-affinity
image: harbor.weiyigeek.top/test/busbox:latest
imagePullPolicy: IfNotPresent
command: ["/bin/sh","-c","sleep 700"]
restartPolicy: Never # 重启策略
affinity: # 亲和性
nodeAffinity: # 节点亲和性
requiredDuringSchedulingIgnoredDuringExecution: # 硬策略(必须得)
nodeSelectorTerms: # 节点选择条件
- matchExpressions: # 匹配表达式
- key: kubernetes.io/hostname # Key 值
operator: NotIn # 表达式 表示 Node主机名称不能是k8s-node-4即表示不能在node-4的节点上运行该Pod;
values:
- k8s-node-4 # Value 值
EOF
操作示例:
# (1) 部署资源清单
~/K8s/Day9/demo1$ kubectl create -f affinity-strong-demo.yaml
# pod/affinity created
# (2) 查看调度器 (此时Pod将会调度到Node0-5之上)
$ kubectl get pod -o wide --show-labels
# NAME READY STATUS RESTARTS AGE IP NODE LABELS
# affinity 1/1 Running 0 64s 10.244.2.17 k8s-node-5 app=node-strong-affinity-pod
# (3) 修改调度器
~/K8s/Day9/demo1$ kubectl delete -f affinity-strong-demo.yaml # 先删除原来的Pod下同
# pod "affinity" deleted
$ vim affinity-strong-demo.yaml
# spec:
# affinity:
# nodeAffinity:
# requiredDuringSchedulingIgnoredDuringExecution:
# nodeSelectorTerms:
# - matchExpressions:
# - key: kubernetes.io/hostname
# operator: In # 修改点 表示 Node主机名称是k8s-node-4即表示只能在 node-4 的节点上运行该Pod
# values:
# - k8s-node-4
~/K8s/Day9/demo1$ kubectl create -f affinity-strong-demo.yaml
# (4) 验证
~/K8s/Day9/demo1$ kubectl get pod -o wide --show-labels
# NAME READY STATUS RESTARTS AGE IP NODE LABELS
# strong-affinity 1/1 Running 0 6s 10.244.1.141 k8s-node-4 app=node-strong-affinity-pod
# (5) 此时设置一个不可满足的条件时(我们根本都没有一个node-6得节点),此时Pod将会一直处于 Pending
# spec:
# affinity:
# nodeAffinity:
# requiredDuringSchedulingIgnoredDuringExecution:
# nodeSelectorTerms:
# - matchExpressions:
# - key: kubernetes.io/hostname # 表示 Node 主机名称
# operator: In # 即表示只能在 node-6 的节点上运行该Pod
# values:
# - k8s-node-6 # 匹配值是 k8s-node-6
~/K8s/Day9/demo1$ kubectl create -f affinity-strong-demo-2.yaml
# pod/strong-affinity created
~/K8s/Day9/demo1$ kubectl get pod -o wide
# NAME READY STATUS RESTARTS AGE IP NODE
# strong-affinity 0/1 Pending (关键点) 0 10s <none> <none>
软策略
资源清单: affinity-soft-demo.yaml
cat > affinity-soft-demo.yaml<<'EOF'
apiVersion: v1
kind: Pod
metadata:
name: soft-affinity
labels:
app: node-soft-affinity-pod
spec:
containers:
- name: with-soft-node-affinity
image: harbor.weiyigeek.top/test/busbox:latest
imagePullPolicy: IfNotPresent
command: ["/bin/sh","-c","sleep 700"]
restartPolicy: Never # 重启策略
affinity: # 亲和性
nodeAffinity: # 节点亲和性
preferredDuringSchedulingIgnoredDuringExecution: # 软策略(优先级首选得)
- weight: 1 # 权重(越高其匹配优先级越高)
preference: # 偏向
matchExpressions: # 匹配表达式
- key: source # Key 值
operator: In # 表达式
values:
- k8s-node-3 # Value 值 表示 最想匹配到node3节点
- weight: 2 # 权重(越高其匹配优先级越高)
preference: # 偏向
matchExpressions: # 匹配表达式
- key: source # Key 值
operator: NotIn # 表达式
values:
- k8s-node-4 # Value 值 表示 不想匹配到node4节点
EOF
操作示例:
# (1) 创建部署节点
~/K8s/Day9/demo1$ kubectl create -f affinity-soft-demo.yaml
# pod/soft-affinity created
~/K8s/Day9/demo1$ kubectl get pod -o wide #此时将会调度到node5上面
# NAME READY STATUS RESTARTS AGE IP NODE
# soft-affinity 1/1 Running 0 8s 10.244.2.19 k8s-node-5
# (2) 修改权重追加如下
# - weight: 3 # 权重(越高其匹配优先级越高)
# preference: # 偏向
# matchExpressions: # 匹配表达式
# - key: source # Key 值
# operator: In # 表达式
# values:
# - k8s-node-4 # Value 值表示想匹配到node4节点
~/K8s/Day9/demo1$ kubectl delete -f affinity-soft-demo-1.yaml && kubectl create -f affinity-soft-demo-1.yaml && sleep 10 && kubectl get pod -o wide
# 此时虽然想到node4节点的亲和性较高然后前面有权重为2的不希望到node4上,此时他任然不会将Pod调度在node4上;
# NAME READY STATUS RESTARTS AGE IP NODE
# soft-affinity 1/1 Running 0 5m38s 10.244.2.22 k8s-node-5
硬软策略联使
描述 : 硬策略与软策略(required & preferred
)可以组合相互进行使用;
资源清单:
cat > requiredpreferred-strategy.yaml<<'END'
apiVersion: v1
kind: Pod
metadata:
name: affinity
labels:
app: node-affinity-pod
spec:
containers:
- name: with-node-affinity
image: harbor.weiyigeek.top/test/busbox:latest
imagePullPolicy: IfNotPresent
command: ["/bin/sh","-c","sleep 700"]
affinity:
nodeAffinity: # 节点亲和性
requiredDuringSchedulingIgnoredDuringExecution: # 硬策略
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: NotIn
values:
- k8s-node02
preferredDuringSchedulingIgnoredDuringExecution: # 软策略
- weight: 1
preference:
matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- k8s-node03
END
Tips : 该Pod一定不能调度到k8s-node02节点
,但如果k8s-node03可以被调度时
便会调度到k8s-node03
节点;
固定节点调度策略(fixed)
描述: 上述讲了亲和性来选择运行的节点,如果想要Pod运行在指定的节点之上就需要按照下述进行设置固定调度的节点;
主要有以下资源对象字段设置Pod运行的固定节点:
- 1)
spec.template.spec.nodeName
- 2)
pod.spec.nodeSelector
方式1.资源清单示例:
cat > fixed-node-test.yaml <<'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
name: fixed-node-test
labels:
app: fixed-node
spec:
replicas: 5
selector: # 注意它是在Deployment控制器中需要依赖的
matchLabels:
app: fixed-node # 匹配的Pod标签非常重要
template:
metadata:
labels:
app: fixed-node
spec:
nodeName: k8s-node-4 # 节点绑定 (关键点)
containers:
- name: fixed-node
image: harbor.weiyigeek.top/test/busbox:latest
imagePullPolicy: IfNotPresent
command: ["/bin/sh","-c","sleep 700"]
ports:
- containerPort: 80
EOF
操作流程:
# (1) 部署`deployment`管理的`fiexd`固定控制器
~/K8s/Day9/demo2$ kubectl create -f fixed-node-test.yaml
# deployment.apps/fixed-node-test created
# (2) 查看Pod部署状态
~/K8s/Day9/demo2$ kubectl get pod
# NAME READY STATUS RESTARTS AGE
# fixed-node-test-5f67b8645d-4c7vd 0/1 ContainerCreating 0 3s
# fixed-node-test-5f67b8645d-bcrj7 0/1 ContainerCreating 0 3s
# fixed-node-test-5f67b8645d-cnb5n 0/1 ContainerCreating 0 3s
# fixed-node-test-5f67b8645d-sh5ld 0/1 ContainerCreating 0 3s
# fixed-node-test-5f67b8645d-v5kfd 0/1 ContainerCreating 0 3s
# taint-tolerations 0/1 Pending 0 24m
~/K8s/Day9/demo2$ kubectl get pod -o wide
# NAME READY STATUS RESTARTS AGE IP NODE
# fixed-node-test-5f67b8645d-4c7vd 1/1 Running 0 7s 10.244.1.148 k8s-node-4
# fixed-node-test-5f67b8645d-bcrj7 1/1 Running 0 7s 10.244.1.146 k8s-node-4
# fixed-node-test-5f67b8645d-cnb5n 1/1 Running 0 7s 10.244.1.145 k8s-node-4
# fixed-node-test-5f67b8645d-sh5ld 1/1 Running 0 7s 10.244.1.147 k8s-node-4
# fixed-node-test-5f67b8645d-v5kfd 1/1 Running 0 7s 10.244.1.149 k8s-node-4
方式2.资源清单示例:
cat > fixed-node-test-1.yaml <<'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
name: fixed-node-test-1
labels:
app: fixed-node-1
spec:
replicas: 5
selector: # 注意它是在Deployment控制器中需要依赖的
matchLabels:
app: fixed-node-1 # 匹配的Pod标签非常重要
template:
metadata:
labels:
app: fixed-node-1
spec:
nodeSelector:
nodetype: fixed # 节点标签 (主要需要给节点打标签然后才能指定)
containers:
- name: fixed-node-1
image: harbor.weiyigeek.top/test/busbox:latest
imagePullPolicy: IfNotPresent
command: ["/bin/sh","-c","sleep 700"]
ports:
- containerPort: 80 # 容器暴露端口
EOF
部署流程:
# (1) 节点打标签(只有打了才能生效)
~/K8s/Day9/demo2$ kubectl label node k8s-node-5 nodetype=fixed
# node/k8s-node-5 labeled
kubectl get node --show-labels # 查看打的标签
# NAME STATUS ROLES AGE VERSION LABELS
# weiyigeek-ubuntu Ready master 18d v1.19.3 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=weiyigeek-ubuntu,kubernetes.io/os=linux,node-role.kubernetes.io/master=
# k8s-node-4 Ready <none> 18d v1.19.3 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-node-4,kubernetes.io/os=linux
# k8s-node-5 Ready <none> 7d v1.19.3 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-node-5,kubernetes.io/os=linux,nodetype=fixed
# (2) 部署
~/K8s/Day9/demo2$ kubectl create -f fixed-node-test-1.yaml
# deployment.apps/fixed-node-test-1 created
~/K8s/Day9/demo2$ kubectl get pod -o wide # 可以看见所有的Pod都运行在k8s-node-5上
# NAME READY STATUS RESTARTS AGE IP NODE
# fixed-node-test-1-6c996c5887-67ldb 1/1 Running 0 37s 10.244.2.30 k8s-node-5
# fixed-node-test-1-6c996c5887-g4gz8 1/1 Running 0 37s 10.244.2.28 k8s-node-5
# fixed-node-test-1-6c996c5887-j9f2k 1/1 Running 0 37s 10.244.2.27 k8s-node-5
# fixed-node-test-1-6c996c5887-msc5g 1/1 Running 0 37s 10.244.2.29 k8s-node-5
# fixed-node-test-1-6c996c5887-xfb6v 1/1 Running 0 37s 10.244.2.26 k8s-node-5
# (3) 更进一步将node4的标签更改一致然后在进行Pod的扩展操作查看有什么效果;
kubectl label node k8s-node-4 nodetype=fixed
# node/k8s-node-4 labeled
# (4) Deployment 进行扩容
~/K8s/Day9/demo2$ kubectl scale --replicas=7 deploy fixed-node-test-1
# deployment.apps/fixed-node-test-1 scaled
# (5) 结果验证
~/K8s/Day9/demo2$ kubectl get pod -o wide | grep "fixed-node-test-1"
# fixed-node-test-1-6c996c5887-67ldb 1/1 Running 0 37s 10.244.2.30 k8s-node-5
# fixed-node-test-1-6c996c5887-g4gz8 1/1 Running 0 37s 10.244.2.28 k8s-node-5
# fixed-node-test-1-6c996c5887-j9f2k 1/1 Running 0 37s 10.244.2.27 k8s-node-5
# fixed-node-test-1-6c996c5887-msc5g 1/1 Running 0 37s 10.244.2.29 k8s-node-5
# fixed-node-test-1-6c996c5887-xfb6v 1/1 Running 0 37s 10.244.2.26 k8s-node-5
# fixed-node-test-1-6c996c5887-tbnf4 1/1 Running 0 61s 10.244.1.152 k8s-node-4 # 此时增加两个Pod运行在K8s-node-4上
# fixed-node-test-1-6c996c5887-xfb6v 1/1 Running 0 10m 10.244.2.26 k8s-node-4
4.podAffinity – Pod 亲和性
描述: 同样PodAffinity
也有软策略和硬策略
其解释与节点亲和性相同;
# (1) 所属对象: pod.spec.affinity.podAffinity (亲和) / podAntiAffinity (反亲和)
软策略 :* preferredDuringSchedulinglgnoredDuringExecution
硬策略 : * requiredDuringSchedulinglgnoredDuringExecution
资源清单示例:
# (1) 根据Pod亲和性匹配该Pod运行的节点,反亲和就是不和满足条件的Pod在同一个节点运行;
cat > pod-affinity-demo-1.yaml <<'EOF'
apiVersion: v1
kind: Pod
metadata:
name: pod-affinity-demo1
labels:
app: pod-affinity
spec:
containers:
- name: pod-affinity
image: harbor.weiyigeek.top/test/busbox:latest
imagePullPolicy: IfNotPresent
command: ["/bin/sh","-c","sleep 700"]
affinity:
podAffinity: # Pod 亲和性
requiredDuringSchedulingIgnoredDuringExecution: # 硬亲和
- labelSelector: # 标签选择器
matchExpressions: # 匹配规则
- key: app
operator: In # 表示 匹配标签为app=node-soft-affinity-pod 的Pod在哪一个节点之上
values:
- node-soft-affinity-pod
topologyKey: kubernetes.io/hostname
podAntiAffinity: # Pod 反亲和
preferredDuringSchedulingIgnoredDuringExecution: # 软亲和
- weight: 1
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In # 表示 匹配标签为`app=node-strong-affinity-pod`的 Pod 在哪一个节点之上
values:
- node-strong-affinity-pod
topologyKey: kubernetes.io/hostname
EOF
温馨提示: 什么是 topologyKey? 个人理解 pod affinity 的调度范围为 topology。
官方解释:如果该X已经在运行一个或多个满足规则Y的Pod,则该Pod应该(或者在非亲和性的情况下不应该)在 X 中运行,Y 表示为LabelSelector规则, X 是一个拓扑域,例如节点,机架,云提供者区域,云提供者区域等。您可以使用topologyKey这是系统用来表示这种拓扑域的节点标签的密钥。
操作流程:
# (1) 部署
~/K8s/Day9/demo1$ kubectl create -f pod-affinity-demo-1.yaml
# pod/pod-affinity-demo1 created
# (2) 验证可以看见运行在 k8s-node-5 节点上,这是由于我们的Pod亲和性的强策略;
~/K8s/Day9/demo1$ kubectl get pod --show-labels -o wide
# NAME READY STATUS RESTARTS AGE IP NODE LABELS
# pod-affinity-demo1 1/1 Running 0 4s 10.244.2.24 k8s-node-5 app=pod-affinity
# soft-affinity 1/1 Running 0 10s 10.244.2.23 k8s-node-5 app=node-soft-affinity-pod
# strong-affinity 0/1 Pending 0 88m <none> <none> app=node-strong-affinity-pod
PS : 在使用Pod亲和性时有一个问题需要非常重视即,与之匹配Pod必须是RUNNING状态,否则认为不满足调度条件则Pod将会被置为Pending状态;
节点与Pod的Affinity亲和性总结:
描述: 节点与Pod亲和性/反亲和性
调度策略比较如下。
# 调度策略 匹配标签 操作符 拓扑域 支持调度目标
nodeAffinity 主机 In, NotIn, Exists,DoesNotExist, Gt, Lt 否 指定主机
podAffinity POD In, NotIn, Exists,DoesNotExist 是 POD与指定POD同一拓扑域
podAnitAffinity POD In, NotIn, Exists,DoesNotExist 是 POD与指定POD不在同一拓扑域
补充说明.解决负载不均衡的问题, 可以给Pod容器设置反亲和,让这些容器平均的分布在各个节点上(不要聚在一起)
# 节点 & Pod 反亲和
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: node
operator: In
values:
- work
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- podAffinityTerm:
labelSelector:
matchExpressions:
- key: app.kubernetes.io/name
operator: In
values:
- ingress-nginx
topologyKey: kubernetes.io/hostname
weight: 100
5.Priority – Pod 优先级
描述: 与前面所讲的调度优选策略中的优先级(Priorities)
不同,前文所讲的优先级指的是节点优先级,而pod priority
指的是Pod的优先级,高优先级的Pod会优先被调度,或者在资源不足低情况牺牲低优先级的Pod以便于重要的Pod能够得到资源部署。
为了定义Pod优先级,需要先定义PriorityClass对象,该对象没有Namespace限制,官网示例:
# (1)
~$ kubectl api-resources | grep "priorityclasses"
priorityclasses pc scheduling.k8s.io false PriorityClass
# (2) 资源清单
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: high-priority
value: 1000000
globalDefault: false
description: "This priority class should be used for XYZ service pods only."
然后通过在Pod的spec. priorityClassName中指定已定义的PriorityClass名称即可:
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
env: test
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
priorityClassName: high-priority
6.Preemption – Pod 抢占
描述: 当节点没有足够的资源供调度器调度Pod、导致Pod处于pending时,抢占(preemption)
逻辑会被触发。Preemption会尝试从一个节点删除低优先级的Pod,从而释放资源使高优先级的Pod得到节点资源进行部署。
0x02 Taints – 污点
描述: 前面我们讲述到节点亲和性是pod的一种属性(偏好或硬性要求), 它可以使得pod运行在满足条件的特定节点之上;
Q: 那什么又是Taint(污点)?
答: Taint 恰恰与之相反, 它
使节点能够排斥一类特定的pod
。注意每个节点上都可以应用一个或多个taint, 如果设置了容忍的Pod将可以容忍污点的存在,可以被调度到存在污点的 Node 上
;
Tips : 使用 kubectl 的 taint 命令可以给某个Node节点设置污点,Node被设置上污点之后就和Pod之间存在了一种相斥的关系,可以让Node拒绝 Pod 的调度执行,甚至将Node已经存在的Pod驱逐出去;
# Syntax : 其中 value 可以为空 effect 描述污点的作用
kubectl taint node [nodeName] key=value:[effect] # 每个污点有一个key和 value作为污点的标签
# 参数值
# 其中 [effect] 可取值 : [ NoSchedule | PreferNoSchedule | NoExecute ]
* NoSchedule : 表示k8s将不会将Pod 调度到具有该污点的 Node 上;
* PreferNoSchedule : 表示k8s将尽量避免将Pod调度到具有该污点的 Node 上;
* NoExecute : 表示k8s将不会将 Pod调度到具有该污点的Node上,同时会将Node上已经存在的 Pod 驱逐出去;
案例演示:
# 0) 查看指定节点taint
$ kubectl describe node master1| grep "Taints" # 查找 Taints 字段
# Taints: node-role.kubernetes.io/master:NoSchedule
# 查看所有污点策略显示三个master节点都是NoSchedule
$ kubectl get no -o yaml | grep taint -A 5
# taints:
# - effect: NoSchedule
# key: node-role.kubernetes.io/master
# status:
# addresses:
# - address: master1的IP
# --
# taints:
# - effect: NoSchedule
# key: node-role.kubernetes.io/master
# status:
# addresses:
# - address: master2的IP
# --
# taints:
# - effect: NoSchedule
# key: node-role.kubernetes.io/master
# status:
# addresses:
# - address: master3的IP
# 1) 手动为master节点设置taints
$ kubectl taint nodes node1 key1=value1:NoSchedule
$ kubectl taint nodes master1 node-role.kubernetes.io/master=:NoSchedule
# 2) 去除污点的设置
kubectl taint nodes node1 key1:NoSchedule- # 这里的key可以不用指定value代表删除指定key所有的effect;
# de1 key1: NoSchedule-
$ kubectl taint nodes k8s-master-1 node-role.kubernetes.io/master=:NoSchedule-
# node/k8s-master-1untainted
# 3) 取消污点后结果
$ kubectl describe node k8s-master-1 | grep "Taints"
# Taints: <none> # 取消污点后
$ kubectl get node --show-labels
# NAME STATUS ROLES AGE VERSION LABELS
# weiyigeek-ubuntu Ready master 18d v1.19.3 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=weiyigeek-ubuntu,kubernetes.io/os=linux,node-role.kubernetes.io/master=
# k8s-node-4 Ready <none> 18d v1.19.3 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-node-4,kubernetes.io/os=linux
# k8s-node-5 Ready <none> 7d v1.19.3 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-node-5,kubernetes.io/os=linux,nodetype=fixed
~/K8s/Day9/demo1$ kubectl get no k8s-node-5 -o yaml | grep "key" -C 3
# - 10.244.2.0/24
# taints:
# - effect: NoExecute
# key: node-role.kubernetes.io/master
# status:
# addresses:
# - address: 10.10.107.215
$ kubectl taint nodes k8s-node-5 node-role.kubernetes.io/master-
# node/k8s-node-5 untainted
补充操作:
# 1) 去除污点允许 master 节点部署 pod;
[root@master1 ~]$ kubectl taint nodes --all node-role.kubernetes.io/master-
# node/master1 untainted
# node/master2 untainted
# node/master3 untainted
# error: taint "node-role.kubernetes.io/master" not found
# 2) 再次查看无显示说明污点去除成功
kubectl get no -o yaml | grep taint -A 5
Tips : 为 master 设置的这个 taint 中node-role.kubernetes.io/master
为 key/value 为空 effect 为NoSchedule;
Tips : 如果输入命令时你丢掉了= 符号, 写成了node-role.kubernetes.io/master:NoSchedule
, 会报 error: at least one taint update is required
错误;
0x03 Toleration – 容忍
Q: 什么是Toleration容忍?
答: 即表示这些 pod 可以(但不要求)被调度到具有匹配 taint 的节点上, 简单的说
Tips: Taint 和 Toleration 相互配合可以用来避免pod被分配到不合适的节点上。表示对于那些不能容忍这些 taint 的 pod 是不会被该节点接受的, 但是针对 Pod
没有节点运行时可以选一台污点的节点运行Pod;
在 pod 的 spec 中设置 tolerations 字段:
# 1.当不指定key值时表示容忍所有的污点key:
tolerations:
- operator: "Exists"
# 2.当不指定 effect 值时表示容忍所有的污点作用
tolerations:
- key: "key"
operator: "Exists"
# 3.查看容忍
~$ kubectl describe pod
Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Tolerations 资源清单演示:
# pod.spec.tolerations
tolerationstolerations:
- key: "key1"
operator: "Equal" # 其中key, vaule, effect 要与Node上设置的 taint保持一致
value: "value1"
effect: "NoSchedule"
tolerationSeconds: 3600 # 用于描述当Pod需要被驱逐时可以在 Pod上继续保留运行的时间
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoExecute"
- key: "key2"
operator: "Exists" # 为Exists将会忽略value值
effect: "NoSchedule"
- key: "node-role.kubernetes.io/master" # 容忍 tolerations 主节点的 taints
operator: "Equal"
value: ""
effect: "PreferNoSchedule" # 尽可能不调度Pod到该节点之上
操作实例:
# (0) 有多个Master 存在时防止资源浪费,可以如下设置
kubectl taint nodes Node-Name node-role.kubernetes.io/master=:PreferNoSchedule
# (1) 当节点有故障需要退出时,需要将Pod从指定Node节点进行驱逐后,方可删除节点
kubectl taint nodes k8s-master-4 node-role.kubernetes.io/master=:NoExecute
# node/k8s-node-5 tainted
# (2) 查看Pod运行节点
$ kubectl get pod -o wide
# NAME READY STATUS RESTARTS AGE IP NODE
# pod-affinity-demo1 1/1 Terminating 335 2d17h 10.244.2.24 k8s-node-5
~/K8s/Day9/demo1$ kubectl get no k8s-node-4 -o yaml | grep "key" -C 3
- 10.244.1.0/24
taints:
- effect: NoSchedule
key: node-role.kubernetes.io/master # 关键点
status:
addresses:
- address: 10.10.107.214
# (3) 演示案例
cat > taint-Tolerations-mode.yaml <<'EOF'
apiVersion: v1
kind: Pod
metadata:
name: taint-tolerations
labels:
app: taint-tolerations-mode
spec:
containers:
- name: with-soft-node-affinity
image: harbor.weiyigeek.top/test/busbox:latest
imagePullPolicy: IfNotPresent
command: ["/bin/sh","-c","sleep 700"]
restartPolicy: Never # 重启策略(绝不除非移除退出)
tolerations:
- key: "kubernetes.io/hostname"
operator: "Equal" # supported values: "Equal", "Exists"
value: "k8s-node-5"
effect: "NoExecute"
EOF
# (4) 此时Pod不会运行在k8s-node-5主机上
~/K8s/Day9/demo1$ kubectl create -f taint-Tolerations-mode.yaml
# pod/taint-tolerations created
~/K8s/Day9/demo1$ kubectl get pod -o wide
# NAME READY STATUS RESTARTS AGE IP NODE
# taint-tolerations 1/1 Running 0 33s 10.244.1.143 k8s-node-4
# (5) 此时如果将node-4、5主机都设置成为污点则删除重建Pod将会运行k8s-node-5有污点的这台机器,这是因为上面 tolerations 容忍配置的原因;
~/K8s/Day9/demo1$ kubectl taint nodes k8s-node-4 node-role.kubernetes.io/master=:NoSchedule
~/K8s/Day9/demo1$ kubectl taint nodes k8s-node-5 node-role.kubernetes.io/master=:NoSchedule
~/K8s/Day9/demo1$ kubectl delete -f taint-Tolerations-mode.yaml
~/K8s/Day9/demo1$ kubectl get pod -o wide
# NAME READY STATUS RESTARTS AGE IP NODE
# taint-tolerations 1/1 Running 0 10s 10.244.2.33 k8s-node-4 <none> <none>
0x04 Cordon – Node 隔离与恢复
描述: 在某些场景下需要对Node进行隔离,比如硬件升级或者维护,目前隔离的Node有两种方式。
# (1) 使用 kubectl cordon 命令可以对某一个Node进行隔离,在隔离后就不会向该Node节点调度Pod。
$ kubectl get node | grep "224"
# weiyigeek-224 Ready <none> 72d v1.19.6
$ kubectl cordon weiyigeek-224
# node/weiyigeek-224 cordoned
$ kubectl get node # Tips :隔离操作并不会停止或者删除正在运行的Pod需要人工进行干预,所以尽管已经对224进行隔离,但是其在上面运行的Pod状态并没有受到任何影响。
# NAME STATUS ROLES AGE VERSION
# weiyigeek-224 Ready,SchedulingDisabled <none> 72d v1.19.6
$ ansible weiyigeek-224 -m shell -a "docker ps"
# weiyigeek-224 | CHANGED | rc=0 >>
# CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
# 558fa21e1993 caa6655434a5 "/conf/update-node.s…" 2 months ago Up 2 months k8s_redis_redis-cluster-3_database_db60bf69-de70-4aa4-b8e6-84ebbb37992b_0
# a515551a4f71 registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.2 "/pause" 2 months ago Up 2 months k8s_POD_redis-cluster-3_database_db60bf69-de70-4aa4-b8e6-84ebbb37992b_0
# ffe033537640 183b53858d7d "start_runit" 2 months ago Up 2 months k8s_calico-node_calico-node-7jcwh_kube-system_5c66e41b-f6d8-4ed8-a5b6-d08a5e734214_0
# 6dbb52c66413 registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.2 "/pause" 2 months ago Up 2 months k8s_POD_calico-node-7jcwh_kube-system_5c66e41b-f6d8-4ed8-a5b6-d08a5e734214_0
# 22bad44b05f4 dbcc366449b0 "/usr/local/bin/kube…" 2 months ago Up 2 months k8s_kube-proxy_kube-proxy-25tts_kube-system_89870e87-ecb3-48fd-b42d-2b7662631d51_0
# d0619946b54e registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.2 "/pause" 2 months ago Up 2 months k8s_POD_kube-proxy-25tts_kube-system_89870e87-ecb3-48fd-b42d-2b7662631d51_0
# (2) 统一的恢复方式:
~$ kubectl uncordon weiyigeek-224
# node/weiyigeek-224 uncordoned
~$ kubectl get node | grep "224"
# weiyigeek-224 Ready <none> 72d v1.19.6
FAQ – 知识扩展
Q:普通用户有自定义Pod优先级的权限吗?
A:可以,Pod优先级定义与创建普通Pod类似,并没有特别权限控制。定义Pod优先级,需要先定义kind为PriorityClass类型的资源对象,然后通过在Pod的spec. priorityClassName中指定已定义的PriorityClass名称即可。
Q:Kubernetes scheduler extender能介绍一下么?
A:extender可理解为Kubernetes调度策略和算法的扩展,属于自定义调度器的一种方式,与Kubernetes默认调度器的过程类似,主要是针对一些不算受集群本身控制的资源(比如网络),需要通过外部调用来进行调度的情况。
Q:用户使用了NodeSelector指定了Pod调度的node节点后,如果node不可用,那么scheduler会采用别的策略吗?
A:nodeSelector是目前最为简单的一种pod运行时调度限制,目前在Kubernetes 1.7.x及以下版本可用。Pod.spec.nodeSelector通过kubernetes的label-selector机制选择节点,由调度器调度策略匹配label,而后调度Pod到目标节点,该匹配规则属于强制约束,如果node不可用,Pod会一直处于pending状态。nodeAffinity具备nodeSelector的全部功能,所以未来Kubernetes会将nodeSelector废除。
Q: 如何让多个Pod均匀部署到各个节点上?
描述: 通过前面学习我们知道在K8S中kube-scheduler组件负责Pod的调度对每一个新创建的 Pod 或者是未被调度的 Pod,kube-scheduler 会选择一个最优的节点去运行这个 Pod,那我们如何将Pod部署到各个节点上呢?
通常两种方式,首先考虑使用工作负载反亲和特性让Pod之间尽量“互斥”,其次是可以使用daemonsets.apps
资源控制器管理Pod资源,这样就能尽量均匀的分布在各节点上。
# 亲和性
affinity:
podAntiAffinity: # 工作负载反亲和
preferredDuringSchedulingIgnoredDuringExecution: # 尽量满足如下条件
- podAffinityTerm:
labelSelector: # 选择Pod的标签,与工作负载本身反亲和
matchExpressions:
- key: app
operator: In
values:
- nginx
namespaces:
- default
topologyKey: kubernetes.io/hostname # 在指定节点上起作用