0.前言
在k8s中,调度是指调度器将pod放置到合适的node节点上,之后node节点上的kubelet就可以运行该pod了。调度器通过监测机制来发现集群中新创建且未被调度到节点的pod,并且调度器会分析node现有的资源,以及用户定义的调度策略,然后在执行调度,以此保证pod的正常运行。
1.kube-scheduler描述
kube-scheduler是k8s集群中默认的调度器,是集群控制面的重要部分,kube-scheduler做调度时通常需要考虑的因素有:单独和整体的资源请求、硬件/软件/策略限制、 亲和以及反亲和要求、数据局部性、负载间的干扰等等。
kube-scheduler给一个Pod做调度通常包括两个步骤:
(1)过滤
(2)打分
过滤阶段会将所有满足 Pod 调度需求的节点选出来。例如,PodFitsResources 过滤函数会检查候选节点的可用资源能否满足 Pod 的资源请求。在过滤之后,得出一个节点列表,里面包含了所有可调度节点。通常情况下,这个节点列表包含不止一个节点。如果这个列表是空的,代表这个 Pod 不可调度。
在打分阶段,调度器会为 Pod 从所有可调度节点中选取一个最合适的节点。 根据当前启用的打分规则,调度器会给每一个可调度节点进行打分。
最后,kube-scheduler 会将 Pod 调度到得分最高的节点上。如果存在多个得分最高的节点,kube-scheduler 会从中随机选取一个。
kube-scheduler支持以下两种方式配置调度器的过滤和打分行为:
(1)调度策略 允许你配置过滤所用的 断言(Predicates) 和打分所用的 优先级(Priorities);
(2)调度配置 允许你配置实现不同调度阶段的插件, 包括:QueueSort、Filter、Score、Bind、Reserve、Permit 等等。
接下来,我们看下如何基于污点、亲和性和反亲和性等策略将pod调度到指定的node上。
2.node节点选择
2.1 指定node名称
nodeSelector提供了一种非常简单的方式将pod调度到指定的node,我们来测试一下。 (1)创建声明文件
cd /data/affinity/node
cat node-name-test.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment-node
labels:
app: nginx-node
spec:
replicas: 1
selector:
matchLabels:
app: nginx-node
template:
metadata:
labels:
app: nginx-node
spec:
nodeName: k8s-node1
containers:
- name: nginx
image: docker.m.daocloud.io/library/nginx
ports:
- containerPort: 8090
(2)创建deployment
kubectl apply -f node-name-test.yaml
(3)查看pod的运行情况
kubectl get pod -o wide|grep nginx
可以看到pod运行在了k8s-node1节点上。
2.2 nodeSelector方式
(1)创建声明文件
cd /data/affinity/node
cat node-selector-test.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-selector-test
labels:
app: nginx-selector-test
spec:
replicas: 1
selector:
matchLabels:
app: nginx-selector
template:
metadata:
labels:
app: nginx-selector
spec:
nodeSelector:
app/env: dev
containers:
- name: nginx-selector-pod
image: docker.m.daocloud.io/library/nginx
ports:
- containerPort: 8090
(2)给node打标签
kubectl label nodes k8s-node2 app/env=dev
(3)创建deployment
kubectl apply -f node-selector-test.yaml
(4)查看pod的运行情况
kubectl get pod -o wide|grep nginx
可以看到pod运行在了k8s-node2节点上。
3.node亲和性
在k8s集群中,目前有两种类型的节点亲和性:
(1)requiredDuringSchedulingIgnoredDuringExecution:硬亲和性,也即node必须满足条件pod才可以调度到该node上;
(2)preferredDuringSchedulingIgnoredDuringExecution:软亲和性,pod会优先调度到满足条件的node上,如果找不到满足条件的,也可以调度到其他节点。
节点亲和性语法支持以下运算符:In,NotIn,Exists,DoesNotExist,Gt,Lt。
运算符含义:
In:label的值在某个列表中;
NotIn:label的值不在某个列表中;
Gt:label的值大于某个值;
Lt:label的值小于某个值;
Exists:某个label存在;
DoesNotExist:某个label不存在。
可以使用NotIn和DoesNotExist实现节点的反亲和行为。
下面用几个实例来验证一下node的亲和性。
3.1 准备工作
(1)给node节点添加标签,用来标明节点的存储特性和cpu核数
kubectl label nodes k8s-node1 disk-type=ssd --overwrite
kubectl label nodes k8s-node1 cpu-num=12
kubectl label nodes k8s-node2 disk-type=sata
kubectl label nodes k8s-node2 cpu-num=24
(2)查看node标签
kubectl get node -o wide --show-labels
可以看到,标签添加成功。
3.2 node硬亲和性
(1)创建声明文件:
cd /data/affinity/node
cat node_required_affinity.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: node-affinity-deploy
labels:
app: node-affinity-deploy
spec:
replicas: 3
selector:
matchLabels:
app: node-affinity-pod
template:
metadata:
labels:
app: node-affinity-pod
spec:
containers:
- name: node-affinity-container
image: docker.m.daocloud.io/library/nginx
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
# 表示node标签存在 disk-type=ssd 或 disk-type=sas
- key: disk-type
operator: In
values:
- ssd
- sas
# 表示node标签存在 cpu-num且值大于6
- key: cpu-num
operator: Gt
values:
- "6"
(2)创建deployment
kubectl apply -f node-required-affinity.yaml
(3)查看pod运行情况
kubectl get pod -o wide|grep affinity
可以看到所有的pod都运行在k8s-node1上,因为只有node1满足pod调度的硬性条件。
3.3 node软亲和性
(1)创建声明文件
cd /data/affinity/node
cat node-required-affinity.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: node-affinity-deploy
labels:
app: node-affinity-deploy
spec:
replicas: 5
selector:
matchLabels:
app: node-affinity-pod
template:
metadata:
labels:
app: node-affinity-pod
spec:
containers:
- name: node-affinity-container
image: docker.m.daocloud.io/library/nginx
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
# 表示node标签存在 disk-type=ssd 或 disk-type=sas
- key: disk-type
operator: In
values:
- ssd
- sas
- weight: 50
preference:
matchExpressions:
# 表示node标签存在 cpu-num且值大于16
- key: cpu-num
operator: Gt
values:
- "16"
(2)创建deployment
kubectl apply -f node-preferre-affinity.yaml
(3)查看pod运行情况
kubectl get pod -o wide |grep affinity
可以看到pod优先选择运行在k8s-node2上面了,但当k8s-node2出现资源不够等情况时,pod也是可以调度到k8s-node1的。
3.4 node软硬亲和性
(1)创建声明文件
cd /data/affinity/node
cat node-affinity.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: node-affinity-deploy
labels:
app: node-affinity-deploy
spec:
replicas: 5
selector:
matchLabels:
app: node-affinity-pod
template:
metadata:
labels:
app: node-affinity-pod
spec:
containers:
- name: node-affinity-container
image: docker.m.daocloud.io/library/nginx
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
# 表示node标签存在 cpu-num且值大于10
- matchExpressions:
- key: cpu-num
operator: Gt
values:
- "10"
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 50
preference:
matchExpressions:
# 表示node标签存在 disk-type=ssd 或 disk-type=sas
- key: disk-type
operator: In
values:
- ssd
- sas
(2)创建deployment
kubectl apply -f node-affinity.yaml
(3)查看pod运行情况
kubectl get pod -o wide |grep affinity
根据声明文件的定义,k8s-node1和k8s-node2两个节点都是满足调度要求的,但是pod会优先调度到k8s-node1上。
4.pod亲和性/反亲和性示例
与节点亲和性一样,Pod亲和性/反亲和性都有两种类型,称为requiredDuringSchedulingIgnoredDuringExecution和 preferredDuringSchedulingIgnoredDuringExecution,分别表示“硬”与“软”要求。对于硬要求,如果不满足则pod会一直处于Pending状态。Pod的亲和性与反亲和性是基于Node节点上已经运行pod的标签(而不是节点上的标签)决定的,用于控制哪些节点必须部署在一起,哪些节点不能部署在一起。
Pod亲和性/反亲和性语法支持以下运算符:
(1)In
(2)NotIn
(3)Exists
(4)DoesNotExist
4.1 准备工作
(1)删除之前的标签
kubectl label nodes k8s-node1 cpu-num-
kubectl label nodes k8s-node1 disk-type-
kubectl label nodes k8s-node2 cpu-num-
kubectl label nodes k8s-node2 disk-type-
(2)创建新标签
kubectl label nodes k8s-master business=www
kubectl label nodes k8s-master disk-type=ssd
kubectl label nodes k8s-master db=redis
kubectl label nodes k8s-node1 business=www
kubectl label nodes k8s-node1 disk-type=sata
kubectl label nodes k8s-node1 db=redis
kubectl label nodes k8s-node2 business=www
kubectl label nodes k8s-node2 disk-type=ssd
kubectl label nodes k8s-node2 db=etcd
(3)查看node标签
kubectl get node -o wide --show-labels
可以看到三个节点的标签都创建完成了。
4.2 pod硬亲和性
(1)创建一个带标签pod的声明文件
cd /data/affinity/pod
cat pod-proxy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: proxy
labels:
app: proxy
spec:
replicas: 1
selector:
matchLabels:
app: proxy
template:
metadata:
labels:
app: proxy
version: v1
spec:
containers:
- name: proxy-container
image: docker.m.daocloud.io/library/nginx
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
(2)创建deployment
kubectl apply -f pod-proxy.yaml
(3)查看proxy pod运行情况
kubectl get pod -o wide --show-labels |grep proxy
可以看到proxy运行在k8s-node2上,并且带有标签app=proxy,version=v1
(4)硬亲和性deployment声明文件
cd /data/affinity/pod
cat pod-required-affinity.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: pod-affinity-deploy
labels:
app: pod-affinity-deploy
spec:
replicas: 6
selector:
matchLabels:
app: pod-affinity-pod
template:
metadata:
labels:
app: pod-affinity-pod
spec:
containers:
- name: pod-affinity-container
image: docker.m.daocloud.io/library/nginx
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
# 由于是Pod亲和性/反亲和性;因此这里匹配规则写的是Pod的标签信息
matchExpressions:
- key: app
operator: In
values:
- proxy
# 拓扑域 若多个node节点具有相同的标签信息【标签键值相同】,则表示这些node节点就在同一拓扑域
# 请对比如下两个不同的拓扑域,Pod的调度结果
topologyKey: disk-type
(6)创建deployment
kubectl apply -f pod-required-affinity.yaml
(7)查看pod运行情况
kubectl get pod -o wide |grep affinity
可以看到pod运行在k8s-master和k8s-node2两个节点,运行在k8s-node2节点很好理解,因为pod的亲和性缘故,前面部署的proxy pod运行在k8s-node2上。那么pod又为何会运行在k8s-master节点呢,这里就必须提到一个很重要的概念,叫拓扑域,当两个node有标签相等的时候,我们称这两个节点属于一个拓扑域,上面k8s-master和k8s-node2中disk-type标签值相等,所以这两个节点属于一个拓扑域,而pod会被调用到同一个拓扑域里,所以pod也会被调用到k8s-master节点。
4.3 pod软亲和性
(1)创建声明文件
cd /data/affinity/pod
cat pod-preferred-affinity.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: pod-affinity-deploy
labels:
app: pod-affinity-deploy
spec:
replicas: 6
selector:
matchLabels:
app: pod-affinity
template:
metadata:
labels:
app: pod-affinity
spec:
containers:
- name: affinity-container
image: docker.m.daocloud.io/library/nginx
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
affinity:
podAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
# 由于是Pod亲和性/反亲和性;因此这里匹配规则写的是Pod的标签信息
matchExpressions:
- key: version
operator: In
values:
- v1
- v2
# 拓扑域 若多个node节点具有相同的标签信息【标签键值相同】,则表示这些node节点就在同一拓扑域
topologyKey: disk-type
(2)创建deployment
kubectl apply -f pod-preferred-affinity.yaml
(3)查看pod运行状态
kubectl get pod -o wide |grep affinity
可以看到pod优先被调度到了k8s-master和k8s-node2上面。
4.4 pod硬反亲和性
(1)创建声明文件
cd /data/affinity/pod
cat pod-required-anti-affinity.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: pod-anti-affinity-deploy
labels:
app: pod-anti-affinity-deploy
spec:
replicas: 6
selector:
matchLabels:
app: pod-anti-affinity
template:
metadata:
labels:
app: pod-anti-affinity
spec:
containers:
- name: pod-anti-affinity-container
image: docker.m.daocloud.io/library/nginx
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
# 由于是Pod亲和性/反亲和性;因此这里匹配规则写的是Pod的标签信息
matchExpressions:
- key: app
operator: In
values:
- proxy
# 拓扑域 若多个node节点具有相同的标签信息【标签键值相同】,则表示这些node节点就在同一拓扑域
topologyKey: disk-type
(2)创建deployment
kubectl apply -f pod-required-anti-affinity.yaml
(3)查看pod运行情况
kubectl get pod -o wide|grep anti
可以看到由于反亲和性的存在,pod被调度到了k8s-node1节点上。
4.5 pod亲和性与反亲和性联合使用
(1)创建声明文件
cd /data/affinity/pod
cat pod-affinity-all.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: pod-affinity-all-deploy
labels:
app: pod-affinity-all-deploy
spec:
replicas: 6
selector:
matchLabels:
app: pod-affinity-all
template:
metadata:
labels:
app: pod-affinity-all
spec:
containers:
- name: pod-affinity-all-container
image: docker.m.daocloud.io/library/nginx
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
# 由于是Pod亲和性/反亲和性;因此这里匹配规则写的是Pod的标签信息
matchExpressions:
- key: app
operator: In
values:
- proxy
# 拓扑域 若多个node节点具有相同的标签信息【标签键值相同】,则表示这些node节点就在同一拓扑域
topologyKey: disk-type
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: version
operator: In
values:
- v1
- v2
topologyKey: db
(2)创建deployment
kubectl apply -f pod-affinity-all.yaml
(3)查看pod运行情况
kubectl get pod -o wide|grep aff
可以看到pod运行在k8s-master节点,我们来分析一下原因,首先看亲和性,pod应该被调度到k8s-master和k8s-node2节点。再看反亲和性pod不应该被调用到k8s-node2节点,所以最终所有节点都被调k8s-master节点。
5.污点和容忍
在k8s中污点(Taints)是node上的一个属性,可以让pod不能调度到带污点的节点上,甚至可以让带污点的节点驱逐node上原有的pod。当然也可以给pod设置容忍属性(Tolerations),让pod忽略node上带的污点,从而将pod调度到该node上。通常情况下,Taints和Tolerations是配合使用的。之前实验中pod可以被调度到master节点,也是因为我没有在master节点上设置污点。
注意:
(1)一个 node 可以有多个污点;
(2)一个 pod 可以有多个容忍;
(3)如果一个 node 有多个污点,且 pod 上也有多个容忍,只要 pod 中容忍能包含 node 上设置的全部污点,就可以将 pod 调度到该 node 上;
(4)kubernetes 执行多个污点和容忍方法类似于过滤器。
5.1 污点
(1)查看污点
kubectl get nodes k8s-master -o go-template={{.spec.taints}}
(2)设置污点
污点的effect可以取以下三个值:
1)PreferNoSchedule:尽量不要调度。
2)NoSchedule:一定不能被调度。
3)NoExecute:不仅不会调度, 还会驱逐 Node 上已有的 Pod。
设置污点的命令如下:
kubectl taint node k8s-master key1=value1:NoSchedule
kubectl taint node k8s-master key2=value2:PreferNoSchedule
kubectl taint node k8s-master key3=value3:NoExecute
(3)删除污点 指定key和effect
kubectl taint node k8s-master key1:PreferNoSchedule-
只指定key
kubectl taint node k8s-master key2-
5.2 容忍
(1)在k8s-node2上创建污点
kubectl taint k8s-node2 key1=value:NoSchedule
kubectl taint k8s-node2 key2=value:NoExecute
(2)创建声明文件
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: docker.m.daocloud.io/library/nginx
ports:
- containerPort: 80
tolerations:
- key: "key1"
operator: "Equal"
value: "value"
effect: "NoSchedule"
- key: "key2"
operator: "Equal"
value: "value"
effect: "NoExecute"
(3)创建deployment
kubectl apply -f nginx-deployment-tole.yaml
(4)查看pod运行状态
kubectl get pod -o wide |grep nginx
可以看到即使node上有污点,pod依然可以调度到上面。
6.总结
kube-scheduler可以按照k8s预定的算法实现pod的自动调度,同时配合亲和性/反亲和性以及污点等属性,用户可以自行决定pod调度到哪个节点上。