K8s集群调度
K8s集群调度
K8s集群调度
1 集群调度
2 调度简介
- Scheduler是kubernetes的调度器,主要任务是把定义的pod分配到集群的节点上。听起来非常简单,但有很多要考虑的问题
¤ 公平: 如何保证每个节点都能被分配资源
¤ 资源高效利用:集群所有资源最大化被使用
¤ 效率:调度的性能要好,能够尽快地对大批量的pod完成调度工作
¤ 灵活: 允许用户根据自己的需求控制调度的逻辑 - Schduler是最为单独的程序运行的,启动之后会一直坚挺API Server,获取PodSpec.NodeName为空的pod,对每个pod都会创建一个binding,表明该pod应该放到那个节点上。
- 这里的PodSpec.NodeName不为空的pod,说明我们手动指定了这个pod应该部署在哪个node上,所以这种情况Sheduler就不需要参与进来了
3 调度过程
-
调度过程分为两部分,如果中间任何一步骤有错误,直接返回错误:
1)predicate(预选): 首先是过滤掉不满足条件的节点
2)priority(优选): 然后从中选择优先级最高的节点 -
Predicate(预选)有一系列的算法可以使用:
1)PodFitsResources: 节点上剩余的资源是否大于pod请求的资源
2)Podfitshost: 如果pod指定了NodeName,检查节点名称是否和NodeName相匹配
3)PodFfitsHostPorts: 节点上已经使用的port是否和 pod申请的port冲突
4)PodSelectorMatches: 过滤掉和 pod指定的label不匹配的节点
5)NoDiskConflict: 已经mount的volume和 pod指定的volume不冲突,除非它们都是只读 -
注意:如果在predicate过程中没有合适的节点。pod会一直在pending状态,不断重试调度,直到有节点满足条件。经过这个步骤,如果有多个节点满足条件,就继续priorities过程
-
Priorities(优选)是按照优先级大小对节点排序
-
优先级由一系列键值对组成,键是该优先级项的名称,值是它的权重(该项的重要性)。这些优先级选项包括:
1)LeastRequestedPriority:通过计算CPU和 Memory的使用率来决定权重,使用率越低权重越高。换句话说,这个优先级指标倾向于资源使用比例更低的节点
2)BalancedResourceA1location:节点上CPU和Memory 使用率越接近,权重越高。这个应该和上面的一起使用,不应该单独使用
3)ImageLocalityPriority:倾向于已经有要使用镜像的节点,镜像总大小值越大,权重越高。 -
通过算法对所有的优先级项目和权重进行计算,得出最终的结果。上面只是常见的算法,还有很多算法可以到官网查阅。
4 自定义调度器
除了kubernetes自带的调度器,你也可以编写自己的调度器。通过spec:schedulername参数指定调度器的名字,可以为pod选择某个调度器进行调整。比如下面的pod选择my-scheduler 进行调度,而不是默认的 default-scheduler:
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: nginx:latest
5 调度的亲和性
5.1 节点亲和性
- node节点亲和性: 简单来理解就是,指定调度到的node,nodeAffinity分为两种pod.spec.nodeAffinity:
1)preferredDuringSchedulinglgnoredDuringExecution:软策略【我想要去这个节点】
2)requiredDuringschedulinglgnoredDuringExecution:硬策略【我一定要去这个节点】 - node节点亲和性硬策略示例:requiredDuringSchedulingIgnoredDuringExecution
kubectl get pod -o wide
apiVersion: v1
kind: Pod
metadata:
name: affinity
labels:
app: node-affinity-pod
spec:
containers:
- name: with-node-affinity
image: hub.qnhyn.com/library/myapp:v1
affinity:
# 指定亲和性为node亲和性
nodeAffinity:
# 指定为硬策略
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
# key就是node的label
# 这句话代表当前pod一定不能分配到k8s-node02节点上
- matchExpressions:
- key: kubernetes.io/hostname # 标签的键名kubectl get node --show-labels查看
operator: NotIn
values:
- k8s-node2
- node亲和性软策略示例:preferredDuringSchedulingIgnoredDuringExecution。
kubectl get pod -o wide
apiVersion: v1
kind: Pod
metadata:
name: affinity
labels:
app: node-affinity-pod
spec:
containers:
- name: with-node-affinity
image: hub.qnhyn.com/library/myapp:v1
affinity:
# 声明节点亲和性为软策略
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
# 当前策略权重为1
- weight: 1
preference:
# [最好]能分配到label为source=k8s-node03的节点上
matchExpressions:
- key: source
operator: In
values:
- k8s-node03
4.合体
5. 若软策略和硬策略同时存在。要先满足我们的硬策略在满足软策略才行。
6. 键值运算关系
In: label的值在某个列表中
Notin: label 的值不在某个列表中
Gt: label的值大于某个值
Lt: label的值小于某个值
Exists:某个label存在
DoesNotExist: 某个label不存在
5.2 Pod亲和性
1)pod亲和性主要解决pod可以和哪些pod部署在同一个拓扑域中的问题
2)拓扑域: 用主机标签实现,可以是单个主机,或者具有同个label的多个主机,也可以是多个主机组成的 cluster、zone 等等
3)所以简单来说: 比如一个 pod 在一个节点上了,那么我这个也得在这个节点,或者你这个 pod 在节点上了,那么我就不想和你待在同一个节点上
4)pod亲和性/反亲和性又分为两种:pod.spec.affinity.podAffinity/podAntiAffinity:
preferredDuringSchedulinglgnoredDuringExecution:软策略
requiredDuringSchedulinglgnoredDuringExecution:硬策略
apiVersion: v1
kind: Pod
metadata:
name: pod-3
labels:
app: pod-3
spec:
containers:
- name: pod-3
image: hub.qnhyn.com/library/myapp:v1
affinity:
# 配置一条pod亲和性策略
podAffinity:
# 配置为硬策略 kubectl get pod --show-labels labels是app=pod1的pod同一拓扑域
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- pod-1
topologyKey: kubernetes.io/hostname
# 配置一条pod反亲和性策略
podAntiAffinity:
# 配置为软策略
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- pod-2
topologyKey: kubernetes.io/hostname
5)亲和性/反亲和性调度策略比较
调度策略 | 匹配标签 | 操作符 | 拓扑域支持 | 调度目标 |
---|---|---|---|---|
nodeAffinity | 主机 | IN, NotIn, Exists, DoesNotExist, Gt, Lt | 否 | 指定主机 |
podAffinity | POD | IN, NotIn, Exists, DoesNotExist | 是 | POD与指定POD同一拓扑域 |
podAntiAffinity | POD | IN, NotIn, Exists, DoesNotExist | 是 | POD与指定POD不在同一拓扑域 |
5.3 Taint(污点)介绍
- 节点亲和性是pod的一种属性(偏好或硬性要求),它使pod被吸引到一类特定的节点。Taint则相反,它使节点能够排斥一类特定的pod。
- Taint和toleration相互配合,可以用来避免pod被分配到不合适的节点上。每个节点上都可以应用一个或多个taint,这表示对于那些不能容忍这些taint的pod,是不会被该节点接受的。如果将toleration 应用于pod上,则表示这些pod 可以(但不要求)被调度到具有匹配 taint 的节点上。
- 如果没有特别配置toleration,默认是不容忍所有污点的
- 使用kubectl taint 命令可以给某个Node节点设置污点,Node被设置上污点之后就和Pod之间存在了一种相斥的关系,可以让Node拒绝Pod的调度执行,甚至将Node已经存在的Pod驱逐出去
5.污点的value是可选项,即污点有两种组成形式:
key=value:effect
key:effect
- 每个污点有一个key和value作为污点的标签,其中value可以为空,effect 描述污点的作用。当前taint effect支持如下三个选项:
NoSchedule:表示k8s将不会将Pod调度到具有该污点的Node上
PreferNoschedulel: 表示k8s将尽量避免将Pod调度到具有该污点的Node上
NoExecute: 表示k8s将不会将Pod调度到具有该污点的Node上,同时会将Node上已经存在的Pod驱逐出去 - kubectl describe node k8s-master 主节点上本身就有一个NoSchedule的污点,默认不在上面创建Pod。
# 设置污点
kubectl taint nodes node1 key1=value1:NoSchedule
# 例子
kubectl taint nodes k8s-node1 check=qnhyn:NoExecute
# 节点说明中,查找Taints字段
kubectl describe pod pod-name
# 去除污点 通过describe查看污点,然后把污点复制出来,按照如下格式在最后加一个-就好了
kubectl taint nodes node1 key1:NoSchedule-
# 例子:
kubectl taint nodes k8s-node1 check=qnhyn:NoExecute-
5.4 Toleration(容忍)
- 设置了污点的Node将根据taint的effect:NoSchedule、PreferNoSchedule、NoExecute 和 Pod之间产生互斥的关系,Pod将在一定程度上不会被调度到Node上
- 但我们可以在Pod上设置容忍(Toleration)。意思是设置了容忍的Pod将可以容忍污点的存在,可以被调度到存在污点的Node上
- 可以被调度不代表一定会被调度,只是保存了可能性
- Toleration的资源清单配置:
tolerations:
# 容忍key1-value1:NoSchedule的污点
# 且需要被驱逐时,可以再呆3600秒
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoSchedule"
# 用于描述当Pod需要被驱逐时可以在 Pod上继续保留运行的时间
tolerationSeconds: 3600
# 容忍key1-value1:NoExecute的污点
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoExecute"
# 容忍key2:NoSchedule的污点
- key:"key2"
operator: "Exists"
effect: "NoSchedule"
- 注意点
1.key,value, effect要与Node上设置的 taint保持一致
2.operator的值为Exists将会忽略value值,如不指定operator,则默认为equal
3.tolerationSeconds用于描述当Pod需要被驱逐时可以在 Pod上继续保留运行的时间 - 当不指定key值时,表示容忍所有的污点key
tolerations:
- operator: "Exists"
- 当不指定effect时,表示容忍所有的污点作用
tolerations:
- key: "key"
operator: "Exists"
- 有多个Master存在时,防止资源浪费,可以如下设置(尽可能不在master上运行)
kubectl taint nodes Node-Name node-role.kubernetes.io/master=:PreferNoSchedule
5.5 指定调度节点
- 通过指定Pod.spec.nodeName将Pod直接调度到指定的Node节点上(根据节点的名称选择)
1.会跳过Scheduler的调度策略
2.该匹配规则是强制匹配
apiVersion: apps/v1
kind: Deployment
metadata:
name: myweb
spec:
replicas: 7
selector:
matchLabels:
app: myweb
template:
metadata:
labels:
app: myweb
spec:
# 直接指定node名称 七个Pod全在k8s-node1上
nodeName: k8s-node1
containers:
- name: myweb
image: hub.qnhyn.com/library/myapp:v1
ports:
- containerPort: 80
- Pod.spec.nodeSplector: 通过kubernetes的label-selector机制选择节点,由调度器调度策略
匹配label,而后调度Pod到目标节点,该匹配规则属于强制约束(根据标签选择)
1.kubectl label node k8s-node1 disk=ssd 给节点打个标签
apiVersion: apps/v1
kind: Deployment
metadata:
name: myweb
spec:
replicas: 7
selector:
matchLabels:
app: myweb
template:
metadata:
labels:
app: myweb
spec:
# nodeSplector
nodeSplector:
disk: ssd # 标签为硬盘类型为ssd的
containers:
- name: myweb
image: hub.qnhyn.com/library/myapp:v1
ports:
- containerPort: 80
6 总结
k8s提供了四大类调度方式:
1.自动调度:运行在哪个节点上完全由scheduler经过一系列的算法得出
2.定向调度:nodename、nodeselector
3.亲和性调度:nodeaffinity、podaffinity、podantiaffinity
4.污点(容忍)调度:Taints、toleration
6.1 定向调度
定向调度,指的是利用在pod上声明nodename或者nodeselector,以此将pod调度到期望的node节点上。注意,这里的调度是强制的,这就意味着即使要调度目标node不存在,也会向上面进行调度,只不过pod运行失败而已。
2. nodename用于强制约束将pod调度到指定的name的pod节点上。这种方式,其实是直接跳过scheduler的调度逻辑,直接写入podlist表
vim pod-nodename.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-nodename
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
nodeName: n2
kubectl create -f pod-nodename.yaml
- nodeselector
nodeselector用于将pod调度到添加了指定标签的node节点上,它是通过k8s的label-selector机制实现的,也即是说,在pod创建之前,会由
scheduler使用matchnodeselector调度策略进行label匹配,找出目标node,然后将pod调度到目标节点,该匹配规则是强制约束
接下来,实验一下:
kubectl label nodes n1 nodeenv=pro
kubectl label nodes n2 nodeenv=test
apiVersion: v1
kind: Pod
metadata:
name: pod-nodeselector
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
nodeSelector:
nodeenv: pro #指定调度到具有nodeenv=pro标签的节点上
kubectl get pod pod-nodeselector -n dev -o wide
6.2 亲和性调度
上面两种定向调度的方式使用起来非常方便,但是也有一定的问题,那就是如果没有满足条件的node,那么pod将不会被运行,即使在集群中还有可用的node列表也不行,这就限制了它的使用场景。
基于上面的问题,k8s还提供了一种亲和性调度,它在nodeselector的基础之上进行了扩展,可以通过配置的方式,实现优先选择满足条件的node进行调度,如果没有,也可以调度到不满足条件的节点上,使调度更加灵活。
affinity主要分为三类:
nodeaffinity(node亲和性):以node为目标,解决node可以调度到哪些node的问题
podaffinity(pod亲和性):以pod为目标,解决pod可以和哪些已存在的pod部署到同一个拓扑域中的问题
podantiaffinity(pod反亲和性):以pod为目标,解决pod不能和哪些已存在的pod部署到同一个拓扑域中的问题
关于亲和性(反亲和性)使用场景的说明:
亲和性:如果两个应用频繁交互,那就有必要利用亲和性让两个应用尽可能地靠近,这样可以减少网络通信而带来的性能损耗。
反亲和性:当应用采用副本部署时,有必要采用反亲和性让各个应用实例打散分布在各个node上,这样可以提高服务的高可用性。
nodeaffinity
关系符的使用说明
- matchExpressions:
- key: nodeenv #匹配存在标签的key为nodeenv的节点
operator: Exists - key: nodeenv #匹配存在标签的key为nodeenv,且value是"xxx"或"yyy"的节点
operator: In
values: [“xxx”,“yyy”] - key: nodeenv #匹配存在标签的key为nodeenv,且value大于"xxx"的节点
operator: Gt
values: “xxx”
- key: nodeenv #匹配存在标签的key为nodeenv的节点
接下来首先演示一下requireDuringSchedullingIgnoreDuringExecution
创建pod-nodeaffinity-required.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-nodeaffinity-required
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
affinity: #亲和性设置
nodeAffinity: #设置node亲和性
requiredDuringSchedulingIgnoredDuringExecution: #硬限制
nodeSelectorTerms:
- matchExpressions:
- key: nodeenv
operator: In
values: ["xxx","yyy"]
1.硬策略:可以理解为必须,就是如果没有满足条件的节点的话,就不断重试直到满足条件为止。对应的配置规则为 requiredDuringSchedulingIgnoredDuringExecution。
2.软策略:可以理解为尽量,就是如果现在没有满足调度要求的节点的话,pod就会忽略这条规则,继续完成调度的过程,说白了就是满足条件最好了,没有的话也无所谓。对应的配置规则为 preferredDuringSchedulingIgnoredDuringExecution。
2.
亲和性的配置规则分为 Node亲和性、Pod亲和性、Pod反亲和性:
nodeAffinity :节点亲和性,用来控制 Pod 要部署在哪些节点上,以及不能部署在哪些节点上的,这个是 Pod 与 Node 之间匹配规则的。
podAffinity :pod 亲和性,这个是 Pod 与 Pod 之间匹配规则的。
podAntiAffinity :pod 反亲和性,与 podAffinity 相反。
一种是全局的调度策略,要在启动调度器时配置,包括kubernetes调度器自带的各种predicates和priorities算法
另一种是运行时调度策略,包括nodeAffinity(主机亲和性),podAffinity(POD亲和性)以及podAntiAffinity(POD反亲和性)
1)nodeAffinity
主要解决POD要部署在哪些主机,以及POD不能部署在哪些主机上的问题,处理的是POD和主机之间的关系。
2)podAffinity
主要解决POD可以和哪些POD部署在同一个拓扑域中的问题(拓扑域用主机标签实现,可以是单个主机,也可以是多个主机组成的cluster、zone等。)
3)podAntiAffinity
主要解决POD不能和哪些POD部署在同一个拓扑域中的问题。它们处理的是Kubernetes集群内部POD和POD之间的关系。
亲和性:应用A与应用B两个应用频繁交互,所以有必要利用亲和性让两个应用的尽可能的靠近,甚至在一个node上,以减少因网络通信而带来的性能损耗。
反亲和性:当应用的采用多副本部署时,有必要采用反亲和性让各个应用实例打散分布在各个node上,以提高HA。
主要介绍kubernetes的中调度算法中的Node affinity和Pod affinity用法
实际上是对前文提到的优选策略中的NodeAffinityPriority策略和InterPodAffinityPriority策略的具体应用。
kubectl explain pods.spec.affinity
podAntiAffinity
requiredDuringSchedulingIgnoredDuringExecution,硬约束,一定要满足,效果同NodeSelector,Pod只能调度到具有kubernetes.io/hostname=k8s-node01标签的Node节点。
preferredDuringSchedulingIgnoredDuringExecution,软约束,不一定满足,k8s调度会尽量不调度Pod到具有kubernetes.io/hostname=k8s-node01或kubernetes.io/hostname=k8s-node02标签的Node节点。
nodeSelectorTerms可以定义多条约束,只需满足其中一条。
matchExpressions可以定义多条约束,必须满足全部约束。
-
亲和特性包括两种类型:node节点亲和性/反亲和性 和 pod亲和性/反亲和性。pod亲和性/反亲和性约束针对的是pod标签而不是节点标签。
-
拓扑域是什么:多个node节点,拥有相同的label标签【节点标签的键值相同】,那么这些节点就处于同一个拓扑域。
-
node节点亲和性
当前有两种类型的节点亲和性,称为requiredDuringSchedulingIgnoredDuringExecution和 preferredDuringSchedulingIgnoredDuringExecution,能够将它们分别视为“硬”【必须知足条件】和“软”【优选知足条件】要求。安全
前者表示Pod要调度到的节点必须知足规则条件,不知足则不会调度,pod会一直处于Pending状态;后者表示优先调度到知足规则条件的节点,若是不能知足再调度到其余节点。服务器
名称中的 IgnoredDuringExecution 部分意味着,与nodeSelector的工做方式相似,若是节点上的标签在Pod运行时发生更改,使得pod上的亲和性规则再也不知足,那么pod仍将继续在该节点上运行。app
在将来,会计划提供requiredDuringSchedulingRequiredDuringExecution,相似requiredDuringSchedulingIgnoredDuringExecution。不一样之处就是pod运行过程当中若是节点再也不知足pod的亲和性,则pod会在该节点中逐出。
节点亲和性语法支持如下运算符:In,NotIn,Exists,DoesNotExist,Gt,Lt。能够使用NotIn和DoesNotExist实现节点的反亲和行为。
运算符关系:
In:label的值在某个列表中
NotIn:label的值不在某个列表中
Gt:label的值大于某个值
Lt:label的值小于某个值
Exists:某个label存在
DoesNotExist:某个label不存在
其余重要说明:
一、若是同时指定nodeSelector和nodeAffinity,则必须知足两个条件,才能将Pod调度到候选节点上。
二、若是在nodeAffinity类型下指定了多个nodeSelectorTerms对象【对象不能有多个,若是存在多个只有最后一个生效】,那么只有最后一个nodeSelectorTerms对象生效。 -
使用kubeadm初始化的集群,出于安全考虑Pod不会被调度到Master Node上,不参与工作负载。
kubectl get no -o yaml | grep taint -A 5
允许master节点部署pod即可解决问题,命令如下:
kubectl taint nodes --all node-role.kubernetes.io/master-
补充点(禁止master部署pod命令):
kubectl taint nodes k8s node-role.kubernetes.io/master=true:NoSchedule -
节点初始化报错
[ERROR FileContent–proc-sys-net-ipv4-ip_forward]: /proc/sys/net/ipv4/ip_forward contents are not set to 1
使用命令如下解决:
echo 1 > /proc/sys/net/ipv4/ip_forward -
节点资源不足导致的问题
FailedScheduling 55s (x4 over 3m28s) default-scheduler 0/3 nodes are available: 3 Insufficient cpu
节点4c8g规格,创建副本两个且资源限制2000m时候出现问题,修改成1000m或修改副本等方式小时4000m即可解决
-
kubectl get 报错:kubectl Unable to connect to the server: dial tcp 10.12.2.199:6443: i/o timeout
具体输出如下:
场景:高可用集群 master 节点,当我执行:kubectl get nodes,出现如下错误:
kubectl Unable to connect to the server: dial tcp 10.12.2.199:6443: i/o timeout
原因是 Master 节点初始化集群的时候,没有执行:
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown ( i d − u ) : (id -u): (id−u):(id -g) $HOME/.kube/config
执行完毕即可。 -
Unable to connect to the server: x509: certificate signed by unknown authority
问题复现
昨天按照教程搭建了一个集群,今天想重新实验下,于是执行kubeadm reset命令清除集群所有的配置。
接着按照部署的常规流程执行kubeadm init --kubernetes-version=v1.14.3 --pod-network-cidr=10.244.0.0/16 --apiserver-advertise-address=0.0.0.0命令创建集群。然后执行以下几个命令:
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown ( i d − u ) : (id -u): (id−u):(id -g) $HOME/.kube/config
接着当我执行kubectl get nodes等命令时,所有的命令都会打印出错误:Unable to connect to the server: x509: certificate signed by unknown authority (possibly because of “crypto/rsa: verification error” while trying to verify candidate authority certificate “kubernetes”)
当在这些 kubectl 命令后加入 --insecure-skip-tls-verify 参数时,就会报如下错误:error: You must be logged in to the server (Unauthorized)
问题解决过程
期间,我尝试了所有能搜索的相关资料,都没有一个好使的。我还确认了kubeadm reset命令会完全清除已创建的集群配置,那么为什么清配置后重新创建集群却不行呢?实在没办法我把注意力集中到额外执行的这几个命令上:
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown ( i d − u ) : (id -u): (id−u):(id -g) $HOME/.kube/config
这几个命令会创建一个目录,并复制几个配置文件,重新创建集群时,这个目录还是存在的,于是我尝试在执行这几个命令前先执行rm -rf $HOME/.kube命令删除这个目录,最后终于解决了这个问题!!!
7日志
docker日志默认在
/var/lib/docker/containers/cname/cname-json.log
LogPilot
更多推荐
所有评论(0)