一、认识Kubernetes调度器
Kubernetes系统的核心任务在于创建客户端请求创建的Pod对象并确保其以期望的状态运行。创建Pod对象时,调度器(scheduler)负责为每一个未经调度的Pod资源、基于一系列的规则集从集群中挑选出一个合适的节点来运行它,因此它也可以称作Pod调度器。调度过程中,调度器不会修改Pod资源,而是从中读取数据,并根据配置的策略挑选出最适合的节点,而后通过API调用将Pod绑定至挑选出的节点之上以完成调度过程。
Kubernetes内建了适合绝大多数场景中Pod资源调度需求的默认调度器,它支持同时使用算法基于原生及可定制的工具来选出集群中最合适运行当前Pod资源的一个节点,其核心目标是基于资源可用性将各Pod资源公平地分布于集群节点之上。目前,平台提供的默认调度器也称为"通用调度器",它通过三个步骤完成调度操作:节点预选(Predicate)、节点优先级排序(Priority)及节点择优(Select)。
节点预选:基于一系列预选规则(如PodFitsResources和MatchNode-Selector等等)对每个节点进行检查,将那些不符合条件的节点过滤掉从而完成节点预选。
节点优选:对预选出的节点进行优先级排序,以便选出最适合运行Pod对象的节点。
节点选定:从优先级排序结果中挑出优先级最高的节点运行Pod对象,当此类节点多于一个时,则从中随机选择一个。
二、节点亲和性
节点亲和性是调度程序用来确定Pod对象调度位置的一组规则,这些规则基于节点上的自定义标签和Pod对象上指定的标签选择器进行定义。节点亲和性允许Pod对象定义针对一组可以调度于其上的节点的亲和性或反亲和性,不过,它无法具体到某个特定的节点。例如,将Pod调度至有着特殊CPU的节点或一个可用区域内的节点之上。
定义节点亲和性规则时有两种类型的节点亲和性规则:硬亲和性(required)和软亲和性(preferred)。硬亲和性实现的是强制性规则,它是Pod调度时必须要满足的条件,而在不存在满足规则的节点时,Pod对象会被置为Pending状态。而软亲和性规则实现的是一种柔性调度限制,它倾向于将Pod对象运行于某类特定的节点之上,而调度器也将尽量满足此需求,但在无法满足调度需求时它将退而求其次地选择一个不匹配规则的节点。
定义节点亲和性规则的关键点有两个,一是为节点配置合乎需求的标签,另一个是为Pod对象定义合理的标签选择器,从而能够基于标签选择出符合期望的目标节点。不过,如preferredDuringSchedulingIgnoreDuringExecution和requiredDuringSchedulingIgnoredDuringExecution名字中的后半段符串IgnoredDuringExecution隐含的意义所指,在Pod资源基于节点亲和性规则调度至某节点之后,节点标签发生了改变而不再符合此节点亲和性规则时,调度器不会将Pod对象从此节点上移出,因为,它仅对新建的Pod对象生效。
三、节点硬亲和性
为Pod对象使用nodeSelector属性可以基于节点表现匹配的方式将Pod对象强制调度至某一类特定的节点之上,不过它仅能够基于简单的等值关系定义标签选择器,而nodeAffinity中支持使用matchExperssions属性构建更为复杂的标签选择机制。
在定义节点亲和性时,requiredDuringSchedulingIgnoredDuringExecution字段的值是一个对象列表,用于定义节点硬亲和性,它可由一到多个nodeSelectorTerm定义的对象组成,彼此间为"逻辑或"的关系,进行匹配度检查时,在多个nodeSelectTerm之间只要满足其中之一即可。nodeSelectorTerm用于定义节点选择器条目,其值为对象列表,它可由一个或多个matchExpressions对象定义的匹配规则组成,多个规则彼此之间为"逻辑与"的关系,这就意味着某节点的标签需要完成匹配同一个nodeSelectorTerm下所有的matchExpression对象定义的规则才算成功通过节点选择器条目的检查。而matchExmpressions又可由一到多个标签选择器组成,多个标签选择器彼此间为"逻辑与"的关系。
1)编写创建Pod硬亲和性的yaml文件
]# cat required-nodeAffinity-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: with-required-nodeaffinity
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- {
key: zone, operator: In, values: ["foo"] }
containers:
- name: myapp
image: ikubernetes/myapp:v1
]# kubectl apply -f required-nodeAffinity-pod.yaml
pod/with-required-nodeaffinity created
从配置清单文件中可以看到,其定义的Pod对象使用节点硬亲和性和规则定义可将当前Pod对象调度至拥有zone标签且其值为foo的节点之上。
2)查看Pod资源状态与详细信息
]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
with-required-nodeaffinity 0/1 Pending 0 109s <none> <none> <none> <none>
]# kubectl describe pods with-required-nodeaffinity
Name: with-required-nodeaffinity
Namespace: default
Priority: 0
Node: <none>
Labels: <none>
Annotations: Status: Pending
IP:
IPs: <none>
Containers:
myapp:
Image: ikubernetes/myapp:v1
Port: <none>
Host Port: <none>
Environment: <none>
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from default-token-47pch (ro)
Conditions:
Type Status
PodScheduled False
Volumes:
default-token-47pch:
Type: Secret (a volume populated by a Secret)
SecretName: default-token-47pch
Optional: false
QoS Class: BestEffort
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute for 300s
node.kubernetes.io/unreachable:NoExecute for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedScheduling <unknown> default-scheduler 0/3 nodes are available: 3 node(s) didn't match node selector.
Warning FailedScheduling <unknown> default-scheduler 0/3 nodes are available: 3 node(s) didn't match node selector.
通过查看Pod状态只知,由其状态信息可知它一直处于Pending阶段,这是由于强制性的节点亲和限制场景中不存在能够满足匹配条件的节点所致的。
3)为节点添加标签
]# kubectl label node node1 zone=foo
node/node1 labeled
]# kubectl label node node2 zone=bar
node/node2 labeled
]# kubectl label node node1 ssd=true
node/node1 labeled
]# kubectl label node node2 ssd=true
node/node2 labeled
]# kubectl get nodes --show-labels
NAME STATUS ROLES AGE VERSION LABELS
master Ready master 66d v1.18.5 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=master,kubernetes.io/os=linux,node-role.kubernetes.io/master=
node1 Ready <none> 66d v1.18.4 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=node1,kubernetes.io/os=linux,ssd=true,zone=foo
node2 Ready <none> 66d v1.18.5 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch