调度器通过kubernetes的Watch机制来发现集群中新创建且尚未被调度到Node上的Pod。调度器会将发现的每一个未调度的Pod调度到一个合适 Node上来运行,从而更加合理、更加充分的利用集群的资源。
kube-scheduler的主要作用就是根据特定的调度算法和调度策略将Pod调度到合适的Node节点上去,是一个独立的二进制程序,启动之后会一直监听API Server,获取Pod Spec.NodeName为空的Pod,对每个Pod都会创建一个binding。
kube-scheduler给一个pod做调度包含两个步骤,具体流程如下:
1.)首先,客户端通过API Server的REST API或者kubectl 工具创 Pod资源API Server收到用户请求后,存储相关数据到etcd数据库中
2.)调度器监听API Server查看到还未被调度(bind)的Pod列表,循环遍历地为每个 Pod 尝试分配节点。
a.)预选阶段(Predicates),过滤节点,调度器用一组规则过滤掉不符合要求的 Node 节点,比如 Pod 设置了资源的 request,那么可用资源比 Pod 需要的资源少的主机显然就会被过滤掉
b.)优选阶段(Priorities),为节点的优先级打分,将上一阶段过滤出来的 Node 列表进行打分,调度器会考虑一些整体的优化策略,比如把 Deployment 控制的多个 Pod 副本尽量分布到不同的主机上,使用最低负载的主机等等策略
经过上面的阶段过滤后选择打分最高的 Node 节点和 Pod 进行 binding 操作,然后将结果存储到 etcd 中 最后被选择出来的 Node 节点对应的 kubelet 去执行创建 Pod 的相关操作(当然也是 watch APIServer 发现的)。
最后,kube-scheduler会将Pod调度到得分最高的Node上。如果存在多个得分最高的Node,kube-scheduler会从中随机选取一个。最后,调度器将这个调度决定告知kube-apiserver,这个过程叫做绑定(Binding)。
默认情况下集群使用自动调度策略调度Pod,保证每个节点的资源,负载情况一致。但是有的时候需要调度Pod到指定节点,比如针对数据库这类对IO要求高的Pod调度到全闪服务器;人工智能(深度学习)调度到带有GPU的节点等等,这些就需要亲和性和反亲和性。
nodeSelector是最长使用且简单的方式,通过label标签选着调度到某个节点。
# cat pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
app: web
spec:
containers:
- name: nginx
image: nginx
nodeSelector:
servicetype: DELL-sc7020
# kubectl get pod nginx 可以看到pod一致处于pending状态,因为找不到存在‘servicetype: DELL-sc7020’的节点
NAME READY STATUS RESTARTS AGE
nginx 0/1 Pending 0 76s
# kubectl describe pod nginx
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedScheduling 2m34s default-scheduler 0/1 nodes are available: 1 node(s) didn't match Pod's node affinity.
Warning FailedScheduling 2m34s default-scheduler 0/1 nodes are available: 1 node(s) didn't match Pod's node affinity.
# kubectl label nodes yc servicetype=DELL-sc7020
node/yc labeled
# kubectl get pod nginx 给节点打上标签之后就正常了
NAME READY STATUS RESTARTS AGE
nginx 1/1 Running 0 4m4s
亲和性分为nodeAffinity和podAffinity。亲和性存在requiredDuringSchedulingIgnoredDuringExecution(硬策略,如果没有满足条件的节点的话,就不断重试直到满足条件为止)和preferredDuringSchedulingIgnoredDuringExecution(软策略,如果现在没有满足调度要求的节点的话,Pod 就会忽略这条规则,继续完成调度过程)。
# cat pod-affinity.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: pod-affinity
labels:
app: pod-affinity
spec:
replicas: 2
selector:
matchLabels:
app: pod-affinity
template:
metadata:
labels:
app: pod-affinity
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
name: nginxweb
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- yc
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
- key: environment
operator: In
values:
- dev
# kubectl apply -f pod-affinity.yaml
deployment.apps/pod-affinity created
# kubectl get deployment pod-affinity
NAME READY UP-TO-DATE AVAILABLE AGE
pod-affinity 2/2 2 2 84s
# kubectl get pods -l app=pod-affinity -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-affinity-747b4d579-b2wrk 1/1 Running 0 7m7s 10.255.0.38 yc
pod-affinity-747b4d579-xb4gc 1/1 Running 0 7m7s 10.255.0.39 yc
上面例子首先要满足kubernetes.io/hostname=yc
,不满足则pod一直处于pending状态,然后检查是否满足environment=dev
,不满足依然调度至该节点。
亲和性设置中的operator字段可以成:
In:label 的值在某个列表中
NotIn:label 的值不在某个列表中
Gt:label 的值大于某个值
Lt:label 的值小于某个值
Exists:某个 label 存在
DoesNotExist:某个 label 不存在
需要注意的是如果nodeSelectorTerms下面有多个选项的话,满足任何一个条件就可以;如果matchExpressions有多个选项的话,则必须同时满足这些条件才能正常调度 Pod。
Pod 亲和性(podAffinity)主要解决 Pod 可以和哪些 Pod部署在同一个拓扑域中的问题(其中拓扑域用主机标签实现,可以是单个主机,也可以是多个主机组成的 cluster、zone 等等),而 Pod 反亲和性主要是解决 Pod 不能和哪些 Pod部署在同一个拓扑域中的问题,它们都是处理的 Pod 与 Pod 之间的关系,比如一个Pod在一个节点上了,那么我这个也得在这个节点,或者你这个Pod在节点上了,那么我就不想和你待在同一个节点上。
# cat pod-affinity.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: pod-affinity
labels:
app: pod-affinity
spec:
replicas: 3
selector:
matchLabels:
app: pod-affinity
template:
metadata:
labels:
app: pod-affinity
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
name: nginxweb
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution: # 硬策略
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- nginx
topologyKey: kubernetes.io/hostname
反着来,不和某个pod调度到同一节点
apiVersion: apps/v1
kind: Deployment
metadata:
name: pod-antiaffinity
labels:
app: pod-antiaffinity
spec:
replicas: 3
selector:
matchLabels:
app: pod-antiaffinity
template:
metadata:
labels:
app: pod-antiaffinity
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
name: nginxweb
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution: # 硬策略
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- busybox-pod
topologyKey: kubernetes.io/hostname
污点和亲和和反亲和不一样,污点是拒绝某些Pod调度到该节点。
容忍度(Toleration)是应用于Pod上的,允许(但并不要求)Pod 调度到带有与之匹配的污点的节点上。比如说Master节点通过污点node-role.kubernetes.io/master:NoSchedule
使得pod不会调度到master。
# kubectl describe pod client1
QoS Class: BestEffort
Node-Selectors:
Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedScheduling 7m41s default-scheduler 0/1 nodes are available: 1 node(s) had taint {node-role.kubernetes.io/master: }, that the pod didn't tolerate.
Warning FailedScheduling 7m41s default-scheduler 0/1 nodes are available: 1 node(s) had taint {node-role.kubernetes.io/master: }, that the pod didn't tolerate.
Warning FailedScheduling 116s default-scheduler 0/1 nodes are available: 1 node(s) had taint {node-role.kubernetes.io/master: }, that the pod didn't tolerate.
污点影响策略有三种:
1.)NoSchedule:只有拥有和这个污点相匹配的容忍度的Pod才能够被分配到该节点
2.)PreferNoSchedule:NoSchedule的软策略版本,表示尽量不调度到污点节点上去
3.)NoExecute:该选项意味着一旦Taint生效,如该节点内正在运行的 Pod 没有对应容忍(Tolerate)设置,则会直接被驱逐。
如果需要调度Pod调度到有污点的节点可以定义Toleration 。例如:
# cat taint.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: taint
labels:
app: taint
spec:
replicas: 3
selector:
matchLabels:
app: taint
template:
metadata:
labels:
app: taint
spec:
containers:
- name: nginx
image: nginx
ports:
- name: http
containerPort: 80
tolerations:
- key: "node-role.kubernetes.io/master"
operator: "Exists"
effect: "NoSchedule
# kubectl apply -f taint.yaml
# kubectl get pod -l app=taint
NAME READY STATUS RESTARTS AGE
taint-7bd54b74c8-mp2s8 1/1 Running 0 106s
taint-7bd54b74c8-q8w2b 1/1 Running 0 106s
taint-7bd54b74c8-vfg2m 1/1 Running 0 106s
对于tolerations属性的写法,其中的key、value、effect 与Node的Taint设置需保持一致, 还有以下几点说明:
a.)如果 operator 的值是 Exists,则 value 属性可省略
a.)如果 operator 的值是 Equal,则表示其 key 与 value 之间的关系是 equal(等于)
c.)如果不指定operator属性,则默认值为Equal
以上说明还存在两种特殊情况:
a.)空的key如果再配Exists就能匹配所有的key与value,也就是是能容忍所有节点的所有Taints
b.)空的effect匹配所有的effect
污点标记及取消,可以使用下面的命令:
# kubectl taint nodes node1 key1=value1:NoSchedule
给节点yc打污点,
# kubectl taint node yc key1=value1:NoSchedule-